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.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);
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
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
//-----------------------------------------------------------------------------------------------
/** 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" ;
// (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 = 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(m_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";
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 >= 4)
throw std::runtime_error("LineViewer::apply(): too many dimensions!");
}
}
alg->setPropertyValue("Origin", origin.toString(",") );
alg->setProperty("IterateEvents", true);
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();
}
// ==============================================================================================
// ================================== 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 curve :: curve to set
*/
void LineViewer::calculateCurve(IMDWorkspace_sptr ws, VMD start, VMD end, 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 < 20) numPoints = 20;
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));
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
}
/** Calculate and show the preview (non-integrated) line */
void LineViewer::showPreview()
{
calculateCurve(m_ws, m_start, m_end, 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_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");