Newer
Older
#include "MantidQtSliceViewer/LineViewer.h"
#include <qwt_plot_curve.h>
#include "MantidKernel/VMD.h"
#include "MantidGeometry/MDGeometry/MDTypes.h"
#include "MantidAPI/IAlgorithm.h"
#include "MantidAPI/FrameworkManager.h"
#include "MantidAPI/AnalysisDataService.h"
using namespace Mantid::API;
namespace MantidQt
{
namespace SliceViewer
{
LineViewer::LineViewer(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
// --------- Create the plot -----------------
m_plotLayout = new QHBoxLayout(ui.frmPlot);
m_plot = new QwtPlot();
m_plot->autoRefresh();
m_plot->setBackgroundColor(QColor(255,255,255)); // White background
m_plotLayout->addWidget(m_plot, 1);
// Make the 2 curves
m_previewCurve = new QwtPlotCurve("Preview");
m_fullCurve = new QwtPlotCurve("Integrated");
m_previewCurve->attach(m_plot);
m_fullCurve->attach(m_plot);
m_previewCurve->setVisible(false);
m_fullCurve->setVisible(false);
// Make the splitter use the minimum size for the controls and not stretch out
ui.splitter->setStretchFactor(0, 0);
ui.splitter->setStretchFactor(1, 1);
//----------- Connect signals -------------
QObject::connect(ui.btnApply, SIGNAL(clicked()), this, SLOT(apply()));
QObject::connect(ui.chkAdaptiveBins, SIGNAL( stateChanged(int)), this, SLOT(adaptiveBinsChanged()));
QObject::connect(ui.spinNumBins, SIGNAL(valueChanged(int)), this, SLOT(numBinsChanged()));
}
LineViewer::~LineViewer()
{
}
//-----------------------------------------------------------------------------------------------
/** With the workspace set, create the dimension text boxes */
void LineViewer::createDimensionWidgets()
{
// Create all necessary widgets
if (m_startText.size() < int(m_ws->getNumDims()))
{
for (size_t d=m_startText.size(); d<m_ws->getNumDims(); d++)
{
QLabel * dimLabel = new QLabel(this);
dimLabel->setAlignment(Qt::AlignHCenter);
ui.gridLayout->addWidget(dimLabel, 0, int(d)+1);
m_dimensionLabel.push_back(dimLabel);
QLineEdit * startText = new QLineEdit(this);
QLineEdit * endText = new QLineEdit(this);
QLineEdit * widthText = new QLineEdit(this);
startText->setMaximumWidth(120);
endText->setMaximumWidth(120);
widthText->setMaximumWidth(120);
startText->setToolTip("Start point of the line in this dimension");
endText->setToolTip("End point of the line in this dimension");
widthText->setToolTip("Width of the line in this dimension");
ui.gridLayout->addWidget(startText, 1, int(d)+1);
ui.gridLayout->addWidget(endText, 2, int(d)+1);
ui.gridLayout->addWidget(widthText, 3, int(d)+1);
m_startText.push_back(startText);
m_endText.push_back(endText);
m_widthText.push_back(widthText);
QObject::connect(startText, SIGNAL(textEdited(QString)), this, SLOT(startEndTextEdited()));
QObject::connect(endText, SIGNAL(textEdited(QString)), this, SLOT(startEndTextEdited()));
QObject::connect(widthText, SIGNAL(textEdited(QString)), this, SLOT(startEndTextEdited()));
}
}
// ------ Update the widgets -------------------------
for (int d=0; d<int(m_ws->getNumDims()); d++)
{
m_dimensionLabel[d]->setText( QString::fromStdString(m_ws->getDimension( size_t(d))->getName() ) );
}
}
//-----------------------------------------------------------------------------------------------
/** Disable any controls relating to dimensions that are not "free"
* e.g. if you are in the X-Y plane, the Z position cannot be changed.
*/
for (int d=0; d<int(m_ws->getNumDims()); d++)
{
// This dimension is free to move if b == true
bool b = (m_allDimsFree || d == m_freeDimX || d == m_freeDimY);
m_startText[d]->setEnabled(b);
m_endText[d]->setEnabled(b);
// If all dims are free, width makes little sense. Only allow one (circular) width
if (m_allDimsFree)
m_widthText[d]->setEnabled(d != 0);
else
m_widthText[d]->setEnabled(!b);
}
// But enable the width setting on the free X dimension
if (!m_allDimsFree)
m_widthText[m_freeDimX]->setEnabled(true);
//-----------------------------------------------------------------------------------------------
/** Show the start/end/width points in the GUI */
void LineViewer::updateStartEnd()
{
for (int d=0; d<int(m_ws->getNumDims()); d++)
{
m_startText[d]->setText(QString::number(m_start[d]));
m_endText[d]->setText(QString::number(m_end[d]));
m_widthText[d]->setText(QString::number(m_width[d]));
}
}
//-----------------------------------------------------------------------------------------------
/** Perform the 1D integration using the current parameters */
void LineViewer::apply()
{
if (m_allDimsFree)
throw std::runtime_error("Not currently supported with all dimensions free!");
std::string outWsName = m_ws->getName() + "_line" ;
bool adaptive = ui.chkAdaptiveBins->isChecked();
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
// (half-width in the plane)
double planeWidth = m_width[m_freeDimX];
// Length of the line
double length = (m_end - m_start).norm();
double dx = m_end[m_freeDimX] - m_start[m_freeDimX];
double dy = m_end[m_freeDimY] - m_start[m_freeDimY];
// Angle of the line
double angle = atan2(dy, dx);
double perpAngle = angle + M_PI / 2.0;
// Build the basis vectors using the angles
VMD basisX = m_start * 0;
basisX[m_freeDimX] = cos(angle);
basisX[m_freeDimY] = sin(angle);
VMD basisY = m_start * 0;
basisY[m_freeDimX] = cos(perpAngle);
basisY[m_freeDimY] = sin(perpAngle);
// Offset the origin in the plane by the width
VMD origin = m_start - basisY * planeWidth;
// And now offset by the width in each direction
for (int d=0; d<int(m_ws->getNumDims()); d++)
{
if ((d != m_freeDimX) && (d != m_freeDimY))
origin[d] -= m_width[d];
}
IAlgorithm * alg = NULL;
size_t numBins = m_numBins;
if (adaptive)
{
alg = FrameworkManager::Instance().createAlgorithm("SliceMD");
// "SplitInto" parameter
numBins = 2;
}
else
alg = FrameworkManager::Instance().createAlgorithm("BinToMDHistoWorkspace");
alg->setProperty("InputWorkspace", m_ws);
alg->setPropertyValue("OutputWorkspace", outWsName);
alg->setProperty("AxisAligned", false);
// The X basis vector
alg->setPropertyValue("BasisVectorX", "X,units," + basisX.toString(",")
+ "," + Strings::toString(length) + "," + Strings::toString(numBins) );
// The Y basis vector, with one bin
alg->setPropertyValue("BasisVectorY", "Y,units," + basisY.toString(",")
+ "," + Strings::toString(planeWidth*2.0) + ",1" );
// Now each remaining dimension
std::string dimChars = "XYZT"; // SlicingAlgorithm::getDimensionChars();
size_t propNum = 2;
for (int d=0; d<int(m_ws->getNumDims()); d++)
{
if ((d != m_freeDimX) && (d != m_freeDimY))
{
// Letter of the dimension
std::string dim(" "); dim[0] = dimChars[propNum];
// Simple basis vector going only in this direction
VMD basis = m_start * 0;
basis[d] = 1.0;
// Set the basis vector with the width *2 and 1 bin
alg->setPropertyValue("BasisVector" + dim, dim +",units," + basis.toString(",")
+ "," + Strings::toString(m_width[d]*2.0) + ",1" );
propNum++;
if (propNum >= dimChars.size())
throw std::runtime_error("LineViewer::apply(): too many dimensions!");
}
}
alg->setPropertyValue("Origin", origin.toString(",") );
if (!adaptive)
{
alg->setProperty("IterateEvents", true);
}
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
alg->execute();
if (alg->isExecuted())
{
//m_sliceWS = alg->getProperty("OutputWorkspace");
m_sliceWS = boost::dynamic_pointer_cast<IMDWorkspace>(AnalysisDataService::Instance().retrieve(outWsName));
this->showFull();
}
delete alg;
}
// ==============================================================================================
// ================================== SLOTS =====================================================
// ==============================================================================================
/** Slot called when any of the start/end text boxes are edited
* in GUI. Only changes the values if they are all valid.
*/
void LineViewer::startEndTextEdited()
{
VMD start = m_start;
VMD end = m_start;
VMD width = m_width;
bool allOk = true;
for (int d=0; d<int(m_ws->getNumDims()); d++)
{
bool ok;
start[d] = m_startText[d]->text().toDouble(&ok);
allOk = allOk && ok;
end[d] = m_endText[d]->text().toDouble(&ok);
allOk = allOk && ok;
width[d] = m_widthText[d]->text().toDouble(&ok);
allOk = allOk && ok;
//TODO: Color the textbox if it is not a valid number.
//m_startText[d]->setBackgroundColor( ok ? QColor::)
}
// Only continue if all values typed were valid numbers.
if (!allOk) return;
m_start = start;
m_end = end;
m_width = width;
this->showPreview();
}
/** Slot called when the number of bins changes */
void LineViewer::numBinsChanged()
{
m_numBins = ui.spinNumBins->value();
//TODO: Don't always auto-apply
this->apply();
}
/** Slot called when checking the adaptive box */
void LineViewer::adaptiveBinsChanged()
{
//TODO: Don't always auto-apply
this->apply();
}
// ==============================================================================================
// ================================== External Setters ==========================================
// ==============================================================================================
//-----------------------------------------------------------------------------------------------
/** Set the workspace being sliced
*
* @param ws :: IMDWorkspace */
void LineViewer::setWorkspace(Mantid::API::IMDWorkspace_sptr ws)
{
m_ws = ws;
createDimensionWidgets();
}
/** Set the start point of the line to integrate
* @param start :: vector for the start point */
void LineViewer::setStart(Mantid::Kernel::VMD start)
{
if (m_ws && start.getNumDims() != m_ws->getNumDims())
throw std::runtime_error("LineViewer::setStart(): Invalid number of dimensions in the start vector.");
}
/** Set the end point of the line to integrate
* @param end :: vector for the end point */
void LineViewer::setEnd(Mantid::Kernel::VMD end)
{
if (m_ws && end.getNumDims() != m_ws->getNumDims())
throw std::runtime_error("LineViewer::setEnd(): Invalid number of dimensions in the end vector.");
updateStartEnd();
}
/** Set the width of the line in each dimensions
* @param width :: vector for the width in each dimension. X dimension stands in for the XY plane width */
void LineViewer::setWidth(Mantid::Kernel::VMD width)
{
if (m_ws && width.getNumDims() != m_ws->getNumDims())
throw std::runtime_error("LineViewer::setwidth(): Invalid number of dimensions in the width vector.");
m_width = width;
updateStartEnd();
}
/** Set the number of bins in the line
* @param nbins :: # of bins */
void LineViewer::setNumBins(size_t numBins)
{
m_numBins = numBins;
ui.spinNumBins->blockSignals(true);
ui.spinNumBins->setValue( int(numBins) );
ui.spinNumBins->blockSignals(false);
/** Set the free dimensions - dimensions that are allowed to change
*
* @param all :: Flag that is true when all dimensions are allowed to change
* @param dimX :: Index of the X dimension in the 2D slice
* @param dimY :: Index of the Y dimension in the 2D slice
*/
void LineViewer::setFreeDimensions(bool all, int dimX, int dimY)
{
int nd = int(m_ws->getNumDims());
if (dimX < 0 || dimX >= nd)
throw std::runtime_error("LineViewer::setFreeDimensions(): Free X dimension index is out of range.");
if (dimY < 0 || dimY >= nd)
throw std::runtime_error("LineViewer::setFreeDimensions(): Free Y dimension index is out of range.");
m_allDimsFree = all;
m_freeDimX = dimX;
m_freeDimY = dimY;
this->updateFreeDimensions();
// ==============================================================================================
// ================================== Rendering =================================================
// ==============================================================================================
/** Calculate a curve between two points given a linear start/end point
*
* @param ws :: MDWorkspace to plot
* @param start :: start point in ND
* @param end :: end point in ND
* @param minNumPoints :: minimum number of points to plot
* @param curve :: curve to set
*/
void LineViewer::calculateCurve(IMDWorkspace_sptr ws, VMD start, VMD end,
size_t minNumPoints, QwtPlotCurve * curve)
// Use the width of the plot (in pixels) to choose the fineness)
// That way, there is ~1 point per pixel = as fine as it needs to be
size_t numPoints = size_t(m_plot->width());
if (numPoints < minNumPoints) numPoints = minNumPoints;
VMD step = (end-start) / double(numPoints);
double stepLength = step.norm();
// These will be the curve as plotted
double * x = new double[numPoints];
double * y = new double[numPoints];
for (size_t i=0; i<numPoints; i++)
{
// Coordinate along the line
VMD coord = start + step * double(i);
// Signal in the WS at that coordinate
signal_t signal = ws->getSignalAtCoord(coord);
// Make into array
x[i] = stepLength * double(i);
y[i] = signal;
}
// Make the curve
curve->setData(x,y, int(numPoints));
}
/** Calculate and show the preview (non-integrated) line */
void LineViewer::showPreview()
{
calculateCurve(m_ws, m_start, m_end, 100, m_previewCurve);
if (m_fullCurve->isVisible())
{
m_fullCurve->setVisible(false);
m_fullCurve->detach();
m_previewCurve->attach(m_plot);
}
m_previewCurve->setVisible(true);
m_plot->replot();
m_plot->setTitle("Preview Plot");
}
/** Calculate and show the full (integrated) line */
void LineViewer::showFull()
{
if (!m_sliceWS) return;
VMD start(m_sliceWS->getNumDims());
start *= 0;
VMD end = start;
end[0] = m_sliceWS->getDimension(0)->getMaximum();
calculateCurve(m_sliceWS, start, end, m_numBins, m_fullCurve);
if (m_previewCurve->isVisible())
{
m_previewCurve->setVisible(false);
m_previewCurve->detach();
m_fullCurve->attach(m_plot);
}
m_fullCurve->setVisible(true);
m_plot->replot();
m_plot->setTitle("Integrated Line Plot");