/** Build a coordinate transformation based on an origin and orthogonal basis vectors. * This can reduce the number of dimensions. For example: * * - The input position is X=(x,y,z) * - The origin is X0=(x0,y0,z0) * - The basis vectors are U and V (reducing from 3 to 2D) * - The output position u = (X-X0).U = X.U - X0.U = x*Ux + y*Uy + z*Uz + (X0.U) * - The output position v = (X-X0).V = X.V - X0.V = x*Vx + y*Vy + z*Vz + (X0.V) * * And this allows us to create the affine matrix: * * | Ux Uy Uz X0.U | | x | | u | * | Vx Vy Vz X0.V | | y | = | v | * | 0 0 0 1 | | z | | 1 | * | 1 | * * @param origin :: origin (in the inDimension), which corresponds to (0,0,...) in outD * @param axes :: a list of basis vectors. There must be outD vectors (one for each output dimension) * and each vector must be of length inD (all coordinates in the input dimension). * The vectors must be properly orthogonal: not coplanar or collinear. This is not checked! * @param scaling :: a vector of size outD of the scaling to perform in each of the * OUTPUT dimensions. * @throw if inconsistent vector sizes are received, or zero-length */ void CoordTransformAffine::buildOrthogonal(const Mantid::Kernel::VMD & origin, const std::vector< Mantid::Kernel::VMD> & axes, const Mantid::Kernel::VMD & scaling) { if (origin.size() != inD) throw std::runtime_error("CoordTransformAffine::buildOrthogonal(): the origin must be in the dimensions of the input workspace (length inD)."); if (axes.size() != outD) throw std::runtime_error("CoordTransformAffine::buildOrthogonal(): you must give as many basis vectors as there are dimensions in the output workspace."); if (scaling.size() != outD) throw std::runtime_error("CoordTransformAffine::buildOrthogonal(): the size of the scaling vector must be the same as the number of dimensions in the output workspace."); // Start with identity affineMatrix.identityMatrix(); for (size_t i=0; i<axes.size(); i++) { if (axes[i].length() == 0.0) throw std::runtime_error("CoordTransformAffine::buildOrthogonal(): one of the basis vector was of zero length."); if (axes[i].size() != inD) throw std::runtime_error("CoordTransformAffine::buildOrthogonal(): one of the basis vectors had the wrong number of dimensions (must be inD)."); // Normalize each axis to unity VMD basis = axes[i]; basis.normalize(); // The row of the affine matrix = the unit vector for (size_t j=0; j<basis.size(); j++) affineMatrix[i][j] = basis[j] * scaling[i]; // Now account for the translation coord_t transl = 0; for (size_t j=0; j<basis.size(); j++) transl += origin[j] * basis[j]; // dot product of origin * basis aka ( X0 . U ) // The last column of the matrix = the translation movement affineMatrix[i][inD] = -transl * scaling[i]; } // Copy into the raw matrix (for speed) copyRawMatrix(); }
/** 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; }