/**
 * Create the output workspace in the right shape.
 * @param inWS : Input workspace for dimensionality
 * @param pbins : User provided binning
 * @param logger : Logging object
 * @return
 */
MDHistoWorkspace_sptr createShapedOutput(IMDHistoWorkspace const *const inWS,
                                         std::vector<std::vector<double>> pbins,
                                         Logger &logger) {
  const size_t nDims = inWS->getNumDims();
  std::vector<Mantid::Geometry::IMDDimension_sptr> dimensions(nDims);
  for (size_t i = 0; i < nDims; ++i) {

    IMDDimension_const_sptr inDim = inWS->getDimension(i);
    auto outDim = boost::make_shared<MDHistoDimension>(inDim.get());
    // Apply dimensions as inputs.
    if (i < pbins.size() && integrationBinning(pbins[i])) {
      auto binning = pbins[i];
      outDim->setRange(
          1 /*single bin*/,
          static_cast<Mantid::coord_t>(binning.front()) /*min*/,
          static_cast<Mantid::coord_t>(
              binning.back()) /*max*/); // Set custom min, max and nbins.
    } else if (i < pbins.size() && similarBinning(pbins[i])) {
      auto binning = pbins[i];

      Mantid::coord_t pMin = static_cast<Mantid::coord_t>(binning.front());
      Mantid::coord_t pMax = static_cast<Mantid::coord_t>(binning.back());
      size_t numberOfBins;

      setMinMaxBins(pMin, pMax, numberOfBins, inDim, logger);

      outDim->setRange(numberOfBins, static_cast<Mantid::coord_t>(pMin) /*min*/,
                       static_cast<Mantid::coord_t>(
                           pMax) /*max*/); // Set custom min, max and nbins.
    }
    dimensions[i] = outDim;
  }
  return boost::make_shared<MDHistoWorkspace>(dimensions);
}
//-----------------------------------------------------------------------------
/// @return the label for the X axis
QString MantidQwtIMDWorkspaceData::getXAxisLabel() const
{
  QString xLabel;
  if ( m_originalWorkspace.expired() )
    return xLabel; // Empty string
  if (m_currentPlotAxis >= 0)
  {
    // One of the dimensions of the original
    IMDDimension_const_sptr dim = m_originalWorkspace.lock()->getDimension(m_currentPlotAxis);
    xLabel = QString::fromStdString(dim->getName()) + " (" + QString::fromStdWString(dim->getUnits().utf8()) + ")";
  }
  else
  {
    // Distance
    // Distance, or not set.
    xLabel = "Distance from start";
//    if (dimX->getUnits() == dimY->getUnits())
//      xLabel += " (" + dimX->getUnits() + ")";
//    else
//      xLabel += " (undefined units)";
//    break;
  }

  return xLabel;
}
Esempio n. 3
0
/** @return true if the point is within the workspace (including the max edges)
 * */
bool pointInWorkspace(const MDHistoWorkspace *ws, const VMD &point) {
  for (size_t d = 0; d < ws->getNumDims(); d++) {
    IMDDimension_const_sptr dim = ws->getDimension(d);
    if ((point[d] < dim->getMinimum()) || (point[d] > dim->getMaximum()))
      return false;
  }
  return true;
}
  /**
   * Constructor helper
   * @param workspace :: MDWorkspace
   * @param function :: implicit function or NULL for none. Gains ownership of the pointer.
   * @param beginPos :: Start position
   * @param endPos :: End position
   */
  void MDHistoWorkspaceIterator::init(const MDHistoWorkspace * workspace,
      Mantid::Geometry::MDImplicitFunction * function,
      size_t beginPos, size_t endPos)
  {
    m_ws = workspace;
    if (m_ws == NULL)
      throw std::invalid_argument("MDHistoWorkspaceIterator::ctor(): NULL workspace given.");

    m_begin = beginPos;
    m_pos = m_begin;
    m_function = function;

    m_max = endPos;
    if (m_max > m_ws->getNPoints())
      m_max = m_ws->getNPoints();
    if (m_max < m_pos)
      throw std::invalid_argument("MDHistoWorkspaceIterator::ctor(): End point given is before the start point.");

    m_nd = m_ws->getNumDims();
    m_center = new coord_t[m_nd];
    m_origin = new coord_t[m_nd];
    m_binWidth = new coord_t[m_nd];
    m_index = new size_t[m_nd];
    m_indexMax = new size_t[m_nd];
    m_indexMaker = new size_t[m_nd];
    Utils::NestedForLoop::SetUp(m_nd, m_index, 0);
    // Initalize all these values
    for (size_t d=0; d<m_nd; d++)
    {
      IMDDimension_const_sptr dim = m_ws->getDimension(d);
      m_center[d] = 0;
      m_origin[d] = dim->getMinimum();
      m_binWidth[d] = dim->getBinWidth();
      m_indexMax[d] = dim->getNBins();
    }
    Utils::NestedForLoop::SetUpIndexMaker(m_nd, m_indexMaker, m_indexMax);

    // Initialize the current index from the start position.
    Utils::NestedForLoop::GetIndicesFromLinearIndex(m_nd, m_pos, m_indexMaker, m_indexMax,
        m_index);

    // Make sure that the first iteration is at a point inside the implicit function
    if (m_function)
    {
      // Calculate the center of the 0-th bin
      for (size_t d=0; d<m_nd; d++)
        m_center[d] = m_origin[d] + 0.5f * m_binWidth[d];
      // Skip on if the first point is NOT contained
      if (!m_function->isPointContained(m_center))
        next();
    }
  }
Esempio n. 5
0
     /*
    Extract the geometry and function information 
    @param eventWs : event workspace to get the information from.
    */
    void MDEWLoadingPresenter::extractMetadata(Mantid::API::IMDEventWorkspace_sptr eventWs)
    {
      using namespace Mantid::Geometry;
      MDGeometryBuilderXML<NoDimensionPolicy> refresh;
      xmlBuilder= refresh; //Reassign.
      std::vector<MDDimensionExtents<coord_t> > ext = eventWs->getMinimumExtents(5);
      std::vector<IMDDimension_sptr> dimensions;
      size_t nDimensions = eventWs->getNumDims();
      for (size_t d=0; d<nDimensions; d++)
      {
        IMDDimension_const_sptr inDim = eventWs->getDimension(d);
        coord_t min = ext[d].getMin();
        coord_t max = ext[d].getMax();
        if (min > max)
        {
          min = 0.0;
          max = 1.0;
        }
        //std::cout << "dim " << d << min << " to " <<  max << std::endl;
        axisLabels.push_back(makeAxisTitle(inDim));
        MDHistoDimension_sptr dim(new MDHistoDimension(inDim->getName(), inDim->getName(), inDim->getUnits(), min, max, inDim->getNBins()));
        dimensions.push_back(dim);
      }

      //Configuring the geometry xml builder allows the object panel associated with this reader to later
      //determine how to display all geometry related properties.
      if(nDimensions > 0)
      {
        xmlBuilder.addXDimension( dimensions[0] );
      }
      if(nDimensions > 1)
      {
        xmlBuilder.addYDimension( dimensions[1] );
      }
      if(nDimensions > 2)
      {
        xmlBuilder.addZDimension( dimensions[2]  );
      }
      if(nDimensions > 3)
      {
        tDimension = dimensions[3];
        xmlBuilder.addTDimension(tDimension);
      }
      m_isSetup = true;
    }
Esempio n. 6
0
/** Set the original workspace, to show the axes plot choice */
void LinePlotOptions::setOriginalWorkspace(Mantid::API::IMDWorkspace_sptr ws) {
  if (!ws)
    return;

  for (size_t d = 0; d < (ws->getNumDims()); d++) {
    IMDDimension_const_sptr dim = ws->getDimension(d);
    std::string text = dim->getName();
    std::string tooltip =
        "Use the " + dim->getName() + " dimension as the X plot axis.";
    const bool bIntegrated = dim->getIsIntegrated();
    // Index into the radio buttons array
    int index = int(d) + 2;
    if (m_radPlots.size() > index) {
      m_radPlots[index]->setText(QString::fromStdString(text));
      m_radPlots[index]->setToolTip(QString::fromStdString(tooltip));
    } else
      addPlotRadioButton(text, tooltip, bIntegrated);
  }
}
Esempio n. 7
0
bool MDGeometryBuilderXML<CheckDimensionPolicy>::addZDimension(
    IMDDimension_const_sptr dimension) const {
  bool bAdded = false;
  if (dimension.get() != NULL) {
    applyPolicyChecking(dimension);
    addOrdinaryDimension(dimension);
    m_spZDimension = dimension;
    m_changed = true;
    bAdded = true;
  }
  return bAdded;
}
Esempio n. 8
0
/*
Extract the geometry and function information

This implementation is an override of the base-class method, which deals with
the more common event based route. However the SQW files will provide complete
dimensions with ranges already set. Less work needs to be done here than for
event workspaces where the extents of each dimension need to be individually
extracted.

@param eventWs : event workspace to get the information from.
*/
void SQWLoadingPresenter::extractMetadata(
    const Mantid::API::IMDEventWorkspace &eventWs) {
  using namespace Mantid::Geometry;
  MDGeometryBuilderXML<NoDimensionPolicy> refresh;
  this->xmlBuilder = refresh; // Reassign.
  std::vector<IMDDimension_sptr> dimensions;
  size_t nDimensions = eventWs.getNumDims();
  for (size_t d = 0; d < nDimensions; d++) {
    IMDDimension_const_sptr inDim = eventWs.getDimension(d);
    axisLabels.push_back(makeAxisTitle(*inDim));
    // Copy the dimension, but set the ID and name to be the same. This is an
    // assumption in bintohistoworkspace.
    dimensions.push_back(boost::make_shared<MDHistoDimension>(
        inDim->getName(), inDim->getName(), inDim->getMDFrame(),
        inDim->getMinimum(), inDim->getMaximum(), size_t{10}));
  }

  // Configuring the geometry xml builder allows the object panel associated
  // with this reader to later
  // determine how to display all geometry related properties.
  if (nDimensions > 0) {
    this->xmlBuilder.addXDimension(dimensions[0]);
  }
  if (nDimensions > 1) {
    this->xmlBuilder.addYDimension(dimensions[1]);
  }
  if (nDimensions > 2) {
    this->xmlBuilder.addZDimension(dimensions[2]);
  }
  if (nDimensions > 3) {
    this->tDimension = dimensions[3];
    this->xmlBuilder.addTDimension(this->tDimension);
  }
  this->m_isSetup = true;
}
Esempio n. 9
0
bool MDGeometryBuilderXML<CheckDimensionPolicy>::addOrdinaryDimension(
    IMDDimension_const_sptr dimensionToAdd) const {
  bool bAdded = false; // Addition fails by default.
  if (dimensionToAdd.get() != NULL) {
    CompareIMDDimension_const_sptr comparitor(dimensionToAdd);
    DimensionContainerType::iterator location = std::find_if(
        m_vecDimensions.begin(), m_vecDimensions.end(), comparitor);
    if (location == m_vecDimensions.end()) {
      m_vecDimensions.push_back(dimensionToAdd);
      bAdded = true;
      m_changed = true;
    }
  }
  return bAdded;
}
Esempio n. 10
0
/** Get ordered list of boundaries in position-along-the-line coordinates
 *
 * @param start :: start of the line
 * @param end :: end of the line
 * @param nd :: number of dimensions
 * @param dir :: vector of the direction
 * @param length :: unit-vector of the direction
 * @returns :: ordered list of boundaries
 */
std::set<coord_t>
MDHistoWorkspace::getBinBoundariesOnLine(const VMD &start, const VMD &end,
                                         size_t nd, const VMD &dir,
                                         coord_t length) const {
  std::set<coord_t> boundaries;

  // Start with the start/end points, if they are within range.
  if (pointInWorkspace(this, start))
    boundaries.insert(0.0f);
  if (pointInWorkspace(this, end))
    boundaries.insert(length);

  // Next, we go through each dimension and see where the bin boundaries
  // intersect the line.
  for (size_t d = 0; d < nd; d++) {
    IMDDimension_const_sptr dim = getDimension(d);
    coord_t lineStartX = start[d];

    if (dir[d] != 0.0) {
      for (size_t i = 0; i <= dim->getNBins(); i++) {
        // Position in this coordinate
        coord_t thisX = dim->getX(i);
        // Position along the line. Is this between the start and end of it?
        coord_t linePos = (thisX - lineStartX) / dir[d];
        if (linePos >= 0 && linePos <= length) {
          // Full position
          VMD pos = start + (dir * linePos);
          // This is a boundary if the line point is inside the workspace
          if (pointInWorkspace(this, pos))
            boundaries.insert(linePos);
        }
      }
    }
  }
  return boundaries;
}
/** Constructor
 *
 * @param workspace :: IMDWorkspace to plot
 * @param logScale :: true to plot Y in log scale
 * @param start :: start point in N-dimensions of the line
 * @param end :: end point in N-dimensions of the line
 * @param normalize :: method for normalizing the line
 * @param isDistribution :: is this a distribution (divide by bin width?)
 * @return
 */
MantidQwtIMDWorkspaceData::MantidQwtIMDWorkspaceData(Mantid::API::IMDWorkspace_const_sptr workspace, const bool logScale,
    Mantid::Kernel::VMD start, Mantid::Kernel::VMD end,
    Mantid::API::MDNormalization normalize,
    bool isDistribution)
 : m_workspace(workspace),
   m_logScale(logScale), m_minPositive(0),
   m_preview(false),
   m_start(start),
   m_end(end),
   m_normalization(normalize),
   m_isDistribution(isDistribution),
   m_transform(NULL),
   m_plotAxis(PlotDistance), m_currentPlotAxis(PlotDistance)
{
  if (start.getNumDims() == 1 && end.getNumDims() == 1)
  {
    if (start[0] == 0.0 && end[0] == 0.0)
    {
      // Default start and end. Find the limits
      Mantid::Geometry::VecIMDDimension_const_sptr nonIntegDims = m_workspace->getNonIntegratedDimensions();
      std::string alongDim = "";
      if (!nonIntegDims.empty())
        alongDim = nonIntegDims[0]->getName();
      else
        alongDim = m_workspace->getDimension(0)->getName();

      size_t nd = m_workspace->getNumDims();
      m_start = VMD(nd);
      m_end = VMD(nd);
      for (size_t d=0; d<nd; d++)
      {
        IMDDimension_const_sptr dim = m_workspace->getDimension(d);
        if (dim->getDimensionId() == alongDim)
        {
          // All the way through in the single dimension
          m_start[d] = dim->getMinimum();
          m_end[d] = dim->getMaximum();
        }
        else
        {
          // Mid point along each dimension
          m_start[d] = (dim->getMaximum() + dim->getMinimum()) / 2.0f;
          m_end[d] = m_start[d];
        }
      }
    }
  }
  // Unit direction of the line
  m_dir = m_end - m_start;
  m_dir.normalize();
  // And cache the X/Y values
  this->cacheLinePlot();
}
/*
Extract the geometry and function information
@param histoWs : histogram workspace to get the information from.
*/
void MDHWLoadingPresenter::extractMetadata(
    const Mantid::API::IMDHistoWorkspace &histoWs) {
  using namespace Mantid::Geometry;
  MDGeometryBuilderXML<NoDimensionPolicy> refresh;
  xmlBuilder = refresh; // Reassign.
  std::vector<IMDDimension_sptr> dimensions;
  size_t nDimensions = histoWs.getNumDims();
  for (size_t d = 0; d < nDimensions; d++) {
    IMDDimension_const_sptr inDim = histoWs.getDimension(d);
    coord_t min = inDim->getMinimum();
    coord_t max = inDim->getMaximum();
    if (min > max) {
      min = 0.0;
      max = 1.0;
    }
    // std::cout << "dim " << d << min << " to " <<  max << '\n';
    axisLabels.push_back(makeAxisTitle(*inDim));
    dimensions.push_back(boost::make_shared<MDHistoDimension>(
        inDim->getName(), inDim->getName(), inDim->getMDFrame(), min, max,
        inDim->getNBins()));
  }

  // Configuring the geometry xml builder allows the object panel associated
  // with this reader to later
  // determine how to display all geometry related properties.
  if (nDimensions > 0) {
    xmlBuilder.addXDimension(dimensions[0]);
  }
  if (nDimensions > 1) {
    xmlBuilder.addYDimension(dimensions[1]);
  }
  if (nDimensions > 2) {
    xmlBuilder.addZDimension(dimensions[2]);
  }
  if (nDimensions > 3) {
    tDimension = dimensions[3];
    xmlBuilder.addTDimension(tDimension);
  }
  m_isSetup = true;
}
Esempio n. 13
0
 bool operator()(IMDDimension_const_sptr b) {
   return _a->getDimensionId() == b->getDimensionId();
 }
/** Execute the algorithm.
 */
void BinaryOperationMD::exec() {
  // Get the properties
  m_lhs = getProperty(inputPropName1());
  m_rhs = getProperty(inputPropName2());
  m_out = getProperty(outputPropName());

  // Flip LHS and RHS if commutative and :
  //  1. A = B + A -> becomes -> A += B
  //  1. C = 1 + A -> becomes -> C = A + 1   (number is always on RHS if
  //  possible)
  if (this->commutative() &&
      ((m_out == m_rhs) ||
       boost::dynamic_pointer_cast<WorkspaceSingleValue>(m_lhs))) {
    // So we flip RHS/LHS
    Mantid::API::IMDWorkspace_sptr temp = m_lhs;
    m_lhs = m_rhs;
    m_rhs = temp;
  }

  // Do not compare conventions if one is single value
  if (!boost::dynamic_pointer_cast<WorkspaceSingleValue>(m_rhs)) {
    if (m_lhs->getConvention() != m_rhs->getConvention()) {
      throw std::runtime_error(
          "Workspaces have different conventions for Q. "
          "Use algorithm ChangeQConvention on one workspace. ");
    }
  }

  // Can't do A = 1 / B
  if (boost::dynamic_pointer_cast<MatrixWorkspace>(m_lhs))
    throw std::invalid_argument("BinaryOperationMD: can't have a "
                                "MatrixWorkspace (e.g. WorkspaceSingleValue) "
                                "as the LHS argument of " +
                                this->name() + ".");

  // Check the inputs. First cast to everything
  m_lhs_event = boost::dynamic_pointer_cast<IMDEventWorkspace>(m_lhs);
  m_lhs_histo = boost::dynamic_pointer_cast<MDHistoWorkspace>(m_lhs);
  m_lhs_scalar = boost::dynamic_pointer_cast<WorkspaceSingleValue>(m_lhs);
  m_rhs_event = boost::dynamic_pointer_cast<IMDEventWorkspace>(m_rhs);
  m_rhs_histo = boost::dynamic_pointer_cast<MDHistoWorkspace>(m_rhs);
  m_rhs_scalar = boost::dynamic_pointer_cast<WorkspaceSingleValue>(m_rhs);

  // MDEventWorkspaces only:
  // If you have to clone any WS, and the operation is commutative, and is NOT
  // in-place, then clone the one that is file-backed.
  if (this->commutative() && (m_lhs_event && m_rhs_event) && (m_out != m_lhs)) {
    if (m_rhs_event->isFileBacked() && !m_lhs_event->isFileBacked()) {
      // So we flip RHS/LHS
      Mantid::API::IMDWorkspace_sptr temp = m_lhs;
      m_lhs = m_rhs;
      m_rhs = temp;
      m_lhs_event = boost::dynamic_pointer_cast<IMDEventWorkspace>(m_lhs);
      m_rhs_event = boost::dynamic_pointer_cast<IMDEventWorkspace>(m_rhs);
    }
  }

  this->checkInputs();

  if (m_out == m_lhs) {
    // A = A * B. -> we will do A *= B
  } else {
    // C = A + B. -> So first we clone A (lhs) into C
    IAlgorithm_sptr clone =
        this->createChildAlgorithm("CloneMDWorkspace", 0.0, 0.5, true);
    clone->setProperty("InputWorkspace", m_lhs);
    clone->executeAsChildAlg();
    m_out = clone->getProperty("OutputWorkspace");
  }

  // Okay, at this point we are ready to do, e.g.,
  //  "m_out /= m_rhs"
  if (!m_out)
    throw std::runtime_error("Error creating the output workspace");
  if (!m_rhs)
    throw std::runtime_error("No RHS workspace specified!");

  m_operand_event = boost::dynamic_pointer_cast<IMDEventWorkspace>(m_rhs);
  m_operand_histo = boost::dynamic_pointer_cast<MDHistoWorkspace>(m_rhs);
  m_operand_scalar = boost::dynamic_pointer_cast<WorkspaceSingleValue>(m_rhs);

  m_out_event = boost::dynamic_pointer_cast<IMDEventWorkspace>(m_out);
  m_out_histo = boost::dynamic_pointer_cast<MDHistoWorkspace>(m_out);

  if (m_out_event) {
    // Call the templated virtual function for this type of MDEventWorkspace
    this->execEvent();
  } else if (m_out_histo) {
    // MDHistoWorkspace as the output
    if (m_operand_histo) {
      if (m_out_histo->getNumDims() != m_operand_histo->getNumDims())
        throw std::invalid_argument(
            "Cannot perform " + this->name() +
            " on MDHistoWorkspace's with a different number of dimensions.");
      if (m_out_histo->getNPoints() != m_operand_histo->getNPoints())
        throw std::invalid_argument(
            "Cannot perform " + this->name() +
            " on MDHistoWorkspace's with a different number of points.");

      // Check that the dimensions span the same size, warn if they don't
      for (size_t d = 0; d < m_out_histo->getNumDims(); d++) {
        IMDDimension_const_sptr dimA = m_out_histo->getDimension(0);
        IMDDimension_const_sptr dimB = m_operand_histo->getDimension(0);
        if (dimA->getMinimum() != dimB->getMinimum() ||
            dimA->getMaximum() != dimB->getMaximum())
          g_log.warning() << "Dimension " << d << " (" << dimA->getName()
                          << ") has different extents in the two "
                             "MDHistoWorkspaces. The operation may not make "
                             "sense!" << std::endl;
      }
      this->execHistoHisto(m_out_histo, m_operand_histo);
    } else if (m_operand_scalar)
      this->execHistoScalar(m_out_histo, m_operand_scalar);
    else
      throw std::runtime_error(
          "Unexpected operand workspace type. Expected MDHistoWorkspace or "
          "WorkspaceSingleValue, got " +
          m_rhs->id());

    // When operating on MDHistoWorkspaces, add a simple flag
    // that will be checked in BinMD to avoid binning a modified workspace
    if (m_out_histo->getNumExperimentInfo() == 0) // Create a run if needed
      m_out_histo->addExperimentInfo(ExperimentInfo_sptr(new ExperimentInfo()));
    m_out_histo->getExperimentInfo(0)->mutableRun().addProperty(
        new PropertyWithValue<std::string>("mdhisto_was_modified", "1"), true);
  } else {
    throw std::runtime_error(
        "Unexpected output workspace type. Expected MDEventWorkspace or "
        "MDHistoWorkspace, got " +
        m_out->id());
  }

  // Give the output
  setProperty("OutputWorkspace", m_out);
}
Esempio n. 15
0
/** Obtain coordinates for a line plot through a MDWorkspace.
 * Cross the workspace from start to end points, recording the signal along the
 *lin at either bin boundaries, or halfway between bin boundaries (which is bin
 *centres if the line is dimension aligned). If recording halfway values then
 *omit points in masked bins.
 *
 * @param start :: coordinates of the start point of the line
 * @param end :: coordinates of the end point of the line
 * @param normalize :: how to normalize the signal
 * @returns :: LinePlot with x as the boundaries of the bins, relative
 * to start of the line, y set to the normalized signal for each bin with
 * Length = length(x) - 1 and e as the error vector for each bin.
 * @param bin_centres :: if true then record points halfway between bin
 *boundaries, otherwise record on bin boundaries
 */
IMDWorkspace::LinePlot MDHistoWorkspace::getLinePoints(
    const Mantid::Kernel::VMD &start, const Mantid::Kernel::VMD &end,
    Mantid::API::MDNormalization normalize, const bool bin_centres) const {
  LinePlot line;

  size_t nd = this->getNumDims();
  if (start.getNumDims() != nd)
    throw std::runtime_error("Start point must have the same number of "
                             "dimensions as the workspace.");
  if (end.getNumDims() != nd)
    throw std::runtime_error(
        "End point must have the same number of dimensions as the workspace.");

  // Unit-vector of the direction
  VMD dir = end - start;
  const auto length = dir.normalize();

// Vector with +1 where direction is positive, -1 where negative
#define sgn(x) ((x < 0) ? -1.0f : ((x > 0.) ? 1.0f : 0.0f))
  VMD dirSign(nd);
  for (size_t d = 0; d < nd; d++) {
    dirSign[d] = sgn(dir[d]);
  }
  const size_t BADINDEX = size_t(-1);

  // Dimensions of the workspace
  boost::scoped_array<size_t> index(new size_t[nd]);
  boost::scoped_array<size_t> numBins(new size_t[nd]);
  for (size_t d = 0; d < nd; d++) {
    IMDDimension_const_sptr dim = this->getDimension(d);
    index[d] = BADINDEX;
    numBins[d] = dim->getNBins();
  }

  const std::set<coord_t> boundaries =
      getBinBoundariesOnLine(start, end, nd, dir, length);

  if (boundaries.empty()) {
    this->makeSinglePointWithNaN(line.x, line.y, line.e);

    // Require x.size() = y.size()+1 if recording bin boundaries
    if (!bin_centres)
      line.x.push_back(length);

    return line;
  } else {
    // Get the first point
    std::set<coord_t>::iterator it;
    it = boundaries.cbegin();

    coord_t lastLinePos = *it;
    VMD lastPos = start + (dir * lastLinePos);
    if (!bin_centres) {
      line.x.push_back(lastLinePos);
    }

    ++it;
    coord_t linePos = 0;
    for (; it != boundaries.cend(); ++it) {
      // This is our current position along the line
      linePos = *it;

      // This is the full position at this boundary
      VMD pos = start + (dir * linePos);

      // Position in the middle of the bin
      VMD middle = (pos + lastPos) * 0.5;

      // Find the signal in this bin
      const auto linearIndex =
          this->getLinearIndexAtCoord(middle.getBareArray());

      if (bin_centres && !this->getIsMaskedAt(linearIndex)) {
        coord_t bin_centrePos =
            static_cast<coord_t>((linePos + lastLinePos) * 0.5);
        line.x.push_back(bin_centrePos);
      } else if (!bin_centres)
        line.x.push_back(linePos);

      if (linearIndex < m_length) {

        auto normalizer = getNormalizationFactor(normalize, linearIndex);
        // And add the normalized signal/error to the list too
        auto signal = this->getSignalAt(linearIndex) * normalizer;
        if (boost::math::isinf(signal)) {
          // The plotting library (qwt) doesn't like infs.
          signal = std::numeric_limits<signal_t>::quiet_NaN();
        }
        if (!bin_centres || !this->getIsMaskedAt(linearIndex)) {
          line.y.push_back(signal);
          line.e.push_back(this->getErrorAt(linearIndex) * normalizer);
        }
        // Save the position for next bin
        lastPos = pos;
      } else {
        // Invalid index. This shouldn't happen
        line.y.push_back(std::numeric_limits<signal_t>::quiet_NaN());
        line.e.push_back(std::numeric_limits<signal_t>::quiet_NaN());
      }

      lastLinePos = linePos;

    } // for each unique boundary

    // If all bins were masked
    if (line.x.size() == 0) {
      this->makeSinglePointWithNaN(line.x, line.y, line.e);
    }
  }
  return line;
}