MantidQt::SliceViewer::PeakBoundingBox getPeakBoundingBoxForEllipsoid( const std::vector<Mantid::Kernel::V3D> &directions, const std::vector<double> &radii, const Mantid::Kernel::V3D &originEllipsoid) { // Get the length of largest projection onto x,y,z auto projectionLengths = getProjectionLengths(directions, radii); using namespace MantidQt::SliceViewer; // Corners EllipsoidPlaneSliceCalculator calc; auto zoomOutFactor = calc.getZoomOutFactor(); const double leftValue = originEllipsoid.X() - zoomOutFactor * projectionLengths[0]; const double rightValue = originEllipsoid.X() + zoomOutFactor * projectionLengths[0]; const double bottomValue = originEllipsoid.Y() - zoomOutFactor * projectionLengths[1]; const double topValue = originEllipsoid.Y() + zoomOutFactor * projectionLengths[1]; Left left(leftValue); Right right(rightValue); Bottom bottom(bottomValue); Top top(topValue); SlicePoint slicePoint(originEllipsoid.Z()); return PeakBoundingBox(left, right, top, bottom, slicePoint); }
Mantid::Kernel::V3D PeakTransform::transformBack(const Mantid::Kernel::V3D &transformed) const { // Will have the plots x, y, and z aligned to the correct h, k, l value. Mantid::Kernel::V3D originalPeakPosition; originalPeakPosition.setX(transformed[m_indexOfPeakX]); originalPeakPosition.setY(transformed[m_indexOfPeakY]); originalPeakPosition.setZ(transformed[m_indexOfPeakZ]); return originalPeakPosition; }
Mantid::Kernel::V3D PeakTransform::transform(const Mantid::Kernel::V3D &original) const { // Will have the plots x, y, and z aligned to the correct h, k, l value. Mantid::Kernel::V3D transformedPeakPosition; transformedPeakPosition.setX(original[m_indexOfPlotX]); transformedPeakPosition.setY(original[m_indexOfPlotY]); transformedPeakPosition.setZ(original[m_indexOfPlotZ]); return transformedPeakPosition; }
/** Calculate the size of the detector in U/V * * @param udet :: UwrappedDetector struct to calculate the size for. udet's size *fields * are updated by this method. */ void UnwrappedSurface::calcSize(UnwrappedDetector &udet) { // U is the horizontal axis on the screen const Mantid::Kernel::V3D U(-1, 0, 0); // V is the vertical axis on the screen const Mantid::Kernel::V3D V(0, 1, 0); // find the detector's rotation Mantid::Kernel::Quat R; this->rotate(udet, R); Mantid::Geometry::BoundingBox bbox = udet.detector->shape()->getBoundingBox(); Mantid::Kernel::V3D scale = udet.detector->getScaleFactor(); // sizes of the detector along each 3D axis Mantid::Kernel::V3D size = bbox.maxPoint() - bbox.minPoint(); size *= scale; Mantid::Kernel::V3D s1(size); Mantid::Kernel::V3D s2 = size + Mantid::Kernel::V3D(-size.X(), 0, 0) - Mantid::Kernel::V3D(size.X(), 0, 0); Mantid::Kernel::V3D s3 = size + Mantid::Kernel::V3D(0, -size.Y(), 0) - Mantid::Kernel::V3D(0, size.Y(), 0); // rotate the size vectors to get the dimensions along axes U and V R.rotate(s1); R.rotate(s2); R.rotate(s3); // get the larges projection to the U axis which is the visible width double d = fabs(s1.scalar_prod(U)); udet.width = d; d = fabs(s2.scalar_prod(U)); if (d > udet.width) udet.width = d; d = fabs(s3.scalar_prod(U)); if (d > udet.width) udet.width = d; // get the larges projection to the V axis which is the visible height d = fabs(s1.scalar_prod(V)); udet.height = d; d = fabs(s2.scalar_prod(V)); if (d > udet.height) udet.height = d; d = fabs(s3.scalar_prod(V)); if (d > udet.height) udet.height = d; // apply the scale factors udet.width *= udet.uscale; udet.height *= udet.vscale; // don't let them be too large if (udet.width > m_width_max) m_width_max = udet.width; if (udet.height > m_height_max) m_height_max = udet.height; }
/** Convert physical position to UV projection * * @param pos :: position in 3D * @param u :: set to U * @param v :: set to V * @param uscale :: scaling for u direction * @param vscale :: scaling for v direction */ void UnwrappedCylinder::project(const Mantid::Kernel::V3D &pos, double &u, double &v, double &uscale, double &vscale) const { // projection to cylinder axis v = pos.scalar_prod(m_zaxis); double x = pos.scalar_prod(m_xaxis); double y = pos.scalar_prod(m_yaxis); u = applyUCorrection(-atan2(y, x)); uscale = 1. / sqrt(x * x + y * y); vscale = 1.; }
void InstrumentWindowPickTab::updateSelectionInfo(int detid) { if (m_instrWindow->blocked()) { m_selectionInfoDisplay->clear(); return; } if (detid >= 0) { InstrumentActor* instrActor = m_instrWindow->getInstrumentActor(); Mantid::Geometry::IDetector_const_sptr det = instrActor->getInstrument()->getDetector(detid); QString text = "Selected detector: " + QString::fromStdString(det->getName()) + "\n"; text += "Detector ID: " + QString::number(detid) + '\n'; QString wsIndex; try { wsIndex = QString::number(instrActor->getWorkspaceIndex(detid)); updatePlot(detid); // Update the plot if the detector links to some data } catch (Mantid::Kernel::Exception::NotFoundError) { // Detector doesn't have a workspace index relating to it wsIndex = "None"; m_plot->clearCurve(); // Clear the plot window m_plot->replot(); } text += "Workspace index: " + wsIndex + '\n'; Mantid::Kernel::V3D pos = det->getPos(); text += "xyz: " + QString::number(pos.X()) + "," + QString::number(pos.Y()) + "," + QString::number(pos.Z()) + '\n'; double r,t,p; pos.getSpherical(r,t,p); text += "rtp: " + QString::number(r) + "," + QString::number(t) + "," + QString::number(p) + '\n'; Mantid::Geometry::ICompAssembly_const_sptr parent = boost::dynamic_pointer_cast<const Mantid::Geometry::ICompAssembly>(det->getParent()); if (parent) { QString textpath; while (parent) { textpath="/"+QString::fromStdString(parent->getName())+textpath; parent=boost::dynamic_pointer_cast<const Mantid::Geometry::ICompAssembly>(parent->getParent()); } text += "Component path:" +textpath+"/"+ QString::fromStdString(det->getName()) +'\n'; } const double integrated = instrActor->getIntegratedCounts(detid); const QString counts = integrated == -1.0 ? "N/A" : QString::number(integrated); text += "Counts: " + counts + '\n'; m_selectionInfoDisplay->setText(text); } else { m_selectionInfoDisplay->clear(); m_plot->clearCurve(); // Clear the plot window m_plot->replot(); } }
void UnwrappedSphere::rotate(const UnwrappedDetector &udet, Mantid::Kernel::Quat &R) const { // rotation from the global axes to those where // the z axis points to the detector Mantid::Kernel::Quat R1; // direction in which to look: from sample to detector Mantid::Kernel::V3D eye; eye = m_pos - udet.detector->getPos(); if (!eye.nullVector()) { InstrumentActor::rotateToLookAt(eye, m_zaxis, R1); } // add detector's own rotation R = R1 * udet.detector->getRotation(); }
/** Convert physical position to UV projection * * @param u :: set to U * @param v :: set to V * @param uscale :: scaling for u direction * @param vscale :: scaling for v direction * @param pos :: position in 3D */ void UnwrappedSphere::project(double & u, double & v, double & uscale, double & vscale, const Mantid::Kernel::V3D & pos) const { // projection to cylinder axis v = pos.scalar_prod(m_zaxis); double x = pos.scalar_prod(m_xaxis); double y = pos.scalar_prod(m_yaxis); double r = sqrt(x*x+y*y+v*v); uscale = 1./sqrt(x*x+y*y); vscale = 1./r; u = applyUCorrection( -atan2(y,x) ); v = -acos(v/r); }
/** Get peak information from peaks workspace * @return */ void IntegratePeaksCWSD::getPeakInformation() { m_vecPeaks = m_peaksWS->getPeaks(); size_t numpeaks = m_vecPeaks.size(); for (size_t ipeak = 0; ipeak < numpeaks; ++ipeak) { DataObjects::Peak &peak = m_vecPeaks[ipeak]; int run_number = peak.getRunNumber(); Mantid::Kernel::V3D qsample = peak.getQSampleFrame(); m_runPeakCenterMap.insert(std::make_pair(run_number, qsample)); // set up the data structure to store integrated peaks' counts m_runPeakCountsMap.insert(std::make_pair(run_number, 0.)); g_log.information() << "From peak workspace: peak " << ipeak << " Center (Qsample) = " << qsample.toString() << "\n"; } }
/** Calculate the weight at distance from epicenter. Uses linear scaling based on distance from epicenter. @param distance : absolute distance from epicenter @return weighting */ double GaussianWeightingnD::weightAt(const Mantid::Kernel::V3D& distance) { /* distance.norm() = r r/R provides normalisation */ double normalisedDistance = distance.norm()/m_cutOff; //Ensures 1 at the edges and zero in the center no matter what the units are return calculateGaussian(normalisedDistance*normalisedDistance); }
/** * Generate the rotation matrix to rotate to this point. * @param a :: The x mouse coordinate * @param b :: The y mouse coordinate */ void Viewport::generateRotationTo(int a,int b) { Mantid::Kernel::V3D newpoint; projectOnSphere( a, b, newpoint ); Mantid::Kernel::V3D diff( m_lastpoint ); // Difference between old point and new point diff -= newpoint; // Angle is given in degrees as the dot product of the two vectors double angle = m_rotationspeed * newpoint.angle( m_lastpoint ); diff = m_lastpoint.cross_prod( newpoint ); // Create a quaternion from the angle and vector direction Mantid::Kernel::Quat temp( angle, diff ); // Left multiply temp *= m_quaternion; // Assignment of _quaternion m_quaternion( temp ); // Get the corresponding OpenGL rotation matrix m_quaternion.GLMatrix( &m_rotationmatrix[0] ); }
/** * Implementation of rendering Sample. */ void SampleActor::draw(bool picking) const { if (!picking && isVisible()) { OpenGLError::check("SampleActor::draw()"); glPushAttrib(GL_ENABLE_BIT); GLboolean hasLight0; glGetBooleanv(GL_LIGHT0, &hasLight0); if (hasLight0) { glEnable(GL_LIGHTING); } glPushMatrix(); m_color.paint(); Mantid::Kernel::V3D pos = m_samplePos->getPos(); glTranslated(pos.X(), pos.Y(), pos.Z()); m_sample.getShape().draw(); glPopMatrix(); glPopAttrib(); OpenGLError::check("SampleActor::draw()"); } }
void UnwrappedSphere::calcUV(UnwrappedDetector& udet) { //static const double pi2 = 2*acos(-1.); Mantid::Kernel::V3D pos = udet.detector->getPos() - m_pos; // projection to cylinder axis udet.v = pos.scalar_prod(m_zaxis); double x = pos.scalar_prod(m_xaxis); double y = pos.scalar_prod(m_yaxis); double r = sqrt(x*x+y*y+udet.v*udet.v); udet.uscale = 1./sqrt(x*x+y*y); udet.vscale = 1./r; udet.u = -atan2(y,x); udet.v = -acos(udet.v/r); calcSize(udet,Mantid::Kernel::V3D(-1,0,0),Mantid::Kernel::V3D(0,1,0)); }
SliceEllipseInfo EllipsoidPlaneSliceCalculator::getSolutionForEllipsoid( const Kernel::Matrix<double> &m, double zPlane, Mantid::Kernel::V3D originEllipsoid) const { // Shift the z value into a suitable frame const double z = zPlane - originEllipsoid.Z(); // Setup the A matrix Mantid::Kernel::DblMatrix A; A.setMem(2, 2); const std::vector<double> ARow0 = {m[0][0], m[0][1]}; const std::vector<double> ARow1 = {m[0][1], m[1][1]}; A.setRow(0, ARow0); A.setRow(1, ARow1); // Setup the inverse Matrix of A Mantid::Kernel::DblMatrix AInverse; const double detA = A.determinant(); AInverse.setMem(2, 2); const std::vector<double> AInverseRow0 = {m[1][1] / detA, -m[0][1] / detA}; const std::vector<double> AInverseRow1 = {-m[0][1] / detA, m[0][0] / detA}; AInverse.setRow(0, AInverseRow0); AInverse.setRow(1, AInverseRow1); // Setup the B vector Mantid::Kernel::DblMatrix B; std::vector<double> BColumn = {m[0][2], m[1][2]}; B.setMem(2, 1); B.setColumn(0, BColumn); B *= 2 * z; // Setip the Transpose B vector Mantid::Kernel::DblMatrix BT; std::vector<double> BTRow = {m[0][2], m[1][2]}; BT.setMem(1, 2); BT.setRow(0, BTRow); BT *= 2 * z; // Setup the C factor const double c = m[2][2] * std::pow(z, 2); // Get the origin const auto origin = getOrigin(AInverse, B, originEllipsoid, z); // Get the radii + directions const auto eigenSystem = getAxesInformation(A, AInverse, B, BT, c); // Get angle. If we have a circle then the angle is 0 (we want to avoid a // divergence here) const auto isCircle = checkIfIsCircle(m); const double angle = isCircle ? 0.0 : getAngle(eigenSystem.majorAxis); return SliceEllipseInfo(origin, eigenSystem.majorRadius, eigenSystem.minorRadius, angle); }
void QPeaksTableModel::updateDataCache(const Mantid::Geometry::IPeak& peak, const int row) const { // if the index is what is already cached just return if (row == m_dataCachePeakIndex) return; // generate the cache m_dataCache.clear(); m_dataCache.push_back(QString::number(peak.getRunNumber())); m_dataCache.push_back(QString::number(peak.getDetectorID())); m_dataCache.push_back(QString::number(peak.getH(), 'f', m_hklPrec)); m_dataCache.push_back(QString::number(peak.getK(), 'f', m_hklPrec)); m_dataCache.push_back(QString::number(peak.getL(), 'f', m_hklPrec)); m_dataCache.push_back(QString::number(peak.getWavelength(), 'f', 4)); double eI = peak.getInitialEnergy(); double eF = peak.getFinalEnergy(); m_dataCache.push_back(QString::number(eI, 'f', 4)); m_dataCache.push_back(QString::number(eF, 'f', 4)); m_dataCache.push_back(QString::number(eI - eF, 'f', 4)); m_dataCache.push_back(QString::number(peak.getTOF(), 'f', 1)); m_dataCache.push_back(QString::number(peak.getDSpacing(), 'f', 4)); double intensity = peak.getIntensity(); double sigma = peak.getSigmaIntensity(); m_dataCache.push_back(QString::number(intensity, 'f', 1)); m_dataCache.push_back(QString::number(sigma, 'f', 1)); m_dataCache.push_back(QString::number(intensity/sigma, 'f', 2)); m_dataCache.push_back(QString::number(peak.getBinCount(), 'g', 2)); m_dataCache.push_back(QString(peak.getBankName().c_str())); m_dataCache.push_back(QString::number(peak.getRow())); m_dataCache.push_back(QString::number(peak.getCol())); const QString COMMA(","); const Mantid::Kernel::V3D qlab = peak.getQLabFrame(); m_dataCache.push_back(QString::number(qlab.X(), 'f', 4) + COMMA + QString::number(qlab.Y(), 'f', 4) + COMMA + QString::number(qlab.Z(), 'f', 4)); const Mantid::Kernel::V3D qsample = peak.getQSampleFrame(); m_dataCache.push_back(QString::number(qsample.X(), 'f', 4) + COMMA + QString::number(qsample.Y(), 'f', 4) + COMMA + QString::number(qsample.Z(), 'f', 4)); }
/** * Convenience overload. * * @param minBounds :: Near-bottom-left corner of the scene. * @param maxBounds :: Far-top-right corner of the scene. * @param type :: Projection type: ORTHO or PERSPECTIVE. PERSPECTIVE isn't fully implemented */ void Viewport::setProjection(const Mantid::Kernel::V3D& minBounds, const Mantid::Kernel::V3D& maxBounds, ProjectionType type) { double radius = minBounds.norm(); double tmp = maxBounds.norm(); if (tmp > radius) radius = tmp; setProjection( minBounds.X(), maxBounds.X(), minBounds.Y(), maxBounds.Y(), -radius, radius, type ); }
/** * Calculate a rotation to look in a particular direction. * * @param eye :: A direction to look in * @param up :: A vector showing the 'up' direction after the rotation. It doesn't have to be normal to eye * just non-collinear. If up is collinear to eye the actual 'up' direction is undefined. * @param R :: The result rotation. */ void InstrumentActor::rotateToLookAt(const Mantid::Kernel::V3D &eye, const Mantid::Kernel::V3D &up, Mantid::Kernel::Quat &R) { if ( eye.nullVector() ) { throw std::runtime_error("The eye vector is null in InstrumentActor::rotateToLookAt."); } // Basis vectors of the OpenGL reference frame. Z points into the screen, Y points up. const Mantid::Kernel::V3D X(1,0,0); const Mantid::Kernel::V3D Y(0,1,0); const Mantid::Kernel::V3D Z(0,0,1); Mantid::Kernel::V3D x,y,z; z = eye; z.normalize(); y = up; x = y.cross_prod(z); if (x.nullVector()) { // up || eye if ( z.X() != 0.0 ) { x.setY(1.0); } else if ( z.Y() != 0.0 ) { x.setZ(1.0); } else { x.setX(1.0); } } x.normalize(); y = z.cross_prod(x); BasisRotation(x,y,z,X,Y,Z,R); }
/** * Check if a cut with the ellipsoid is possible at all * @param directions: the three ellipsoid directions * @param radii: the ellipsoid radii * @param originEllipsoid: the origin of the ellipsoid * @param zPlane: the z plane value * @return true if the a cut exists, else false */ bool checkIfCutExists(const std::vector<Mantid::Kernel::V3D> &directions, const std::vector<double> &radii, const Mantid::Kernel::V3D &originEllipsoid, double zPlane) { // Translate into ellipsoid const double z = zPlane - originEllipsoid.Z(); bool hasCut = false; // For each axis check if the z point is between the z values of the // axis endpoints int counter = 0; for (const auto &direction : directions) { const auto endpoint1 = direction[2] * radii[counter]; const auto endpoint2 = -1 * direction[2] * radii[counter]; if (isBetweenEndpoints(endpoint1, endpoint2, z)) { hasCut = true; break; } ++counter; } return hasCut; }
void UnwrappedCylinder::rotate(const UnwrappedDetector &udet, Mantid::Kernel::Quat &R) const { // direction in which to look Mantid::Kernel::V3D eye; const auto &componentInfo = m_instrActor->componentInfo(); // rotation from the global axes to those where // the z axis points to the detector Mantid::Kernel::Quat R1; eye = m_pos - componentInfo.position(udet.detIndex); if (!eye.nullVector()) { // eye must point towards the detector and be perpendicular to the // cylinder's axis Mantid::Kernel::V3D up = m_zaxis; up.normalize(); eye = eye - up * eye.scalar_prod(up); if (!eye.nullVector()) { eye.normalize(); InstrumentActor::rotateToLookAt(eye, up, R1); } } // add detector's own rotation R = R1 * componentInfo.rotation(udet.detIndex); }
/** Calculate the weight at distance from epicenter. Uses linear scaling based on distance from epicenter. @param distance : absolute distance from epicenter @return weighting */ double LinearWeighting::weightAt(const Mantid::Kernel::V3D& distance) { return 1 - (distance.norm()/m_cutOff); }
/** Implementation doesn't make sense on this type. @param distance : @return weighting */ double ParabolicWeighting::weightAt(const Mantid::Kernel::V3D& distance) { return static_cast<double>(m_cutOff - std::abs(distance.X()) + m_cutOff - std::abs(distance.Y()) + 1); }
/** * Update the info window with information for a selected detector. * @param detid :: ID of the selected detector. */ void InstrumentWindowPickTab::updateSelectionInfo(int detid) { if (m_freezePlot) { // freeze the plot for one update m_freezePlot = false; return; } if (m_instrWindow->blocked()) { m_selectionInfoDisplay->clear(); return; } if (detid >= 0) { InstrumentActor* instrActor = m_instrWindow->getInstrumentActor(); Mantid::Geometry::IDetector_const_sptr det = instrActor->getInstrument()->getDetector(detid); QString text = "Selected detector: " + QString::fromStdString(det->getName()) + "\n"; text += "Detector ID: " + QString::number(detid) + '\n'; QString wsIndex; try { wsIndex = QString::number(instrActor->getWorkspaceIndex(detid)); updatePlot(detid); // Update the plot if the detector links to some data } catch (Mantid::Kernel::Exception::NotFoundError &) { // Detector doesn't have a workspace index relating to it wsIndex = "None"; m_plot->clearCurve(); // Clear the plot window m_plot->replot(); } text += "Workspace index: " + wsIndex + '\n'; Mantid::Kernel::V3D pos = det->getPos(); text += "xyz: " + QString::number(pos.X()) + "," + QString::number(pos.Y()) + "," + QString::number(pos.Z()) + '\n'; double r,t,p; pos.getSpherical(r,t,p); text += "rtp: " + QString::number(r) + "," + QString::number(t) + "," + QString::number(p) + '\n'; Mantid::Geometry::ICompAssembly_const_sptr parent = boost::dynamic_pointer_cast<const Mantid::Geometry::ICompAssembly>(det->getParent()); if (parent) { QString textpath; while (parent) { textpath="/"+QString::fromStdString(parent->getName())+textpath; parent=boost::dynamic_pointer_cast<const Mantid::Geometry::ICompAssembly>(parent->getParent()); } text += "Component path:" +textpath+"/"+ QString::fromStdString(det->getName()) +'\n'; } const double integrated = instrActor->getIntegratedCounts(detid); const QString counts = integrated == -1.0 ? "N/A" : QString::number(integrated); text += "Counts: " + counts + '\n'; QString xUnits; if (m_selectionType > SingleDetectorSelection && !m_plotSum) { switch(m_tubeXUnits) { case DETECTOR_ID: xUnits = "Detector ID"; break; case LENGTH: xUnits = "Length"; break; case PHI: xUnits = "Phi"; break; default: xUnits = "Detector ID"; } } else { xUnits = QString::fromStdString(instrActor->getWorkspace()->getAxis(0)->unit()->caption()); //xUnits = "Time of flight"; } text += "X units: " + xUnits + '\n'; m_selectionInfoDisplay->setText(text); } else { m_selectionInfoDisplay->clear(); m_plot->clearCurve(); // Clear the plot window m_plot->replot(); } }
/** * Find a rotation from one orthonormal basis set (Xfrom,Yfrom,Zfrom) to * another orthonormal basis set (Xto,Yto,Zto). Both sets must be right-handed * (or same-handed, I didn't check). The method doesn't check the sets for orthogonality * or normality. The result is a rotation quaternion such that: * R.rotate(Xfrom) == Xto * R.rotate(Yfrom) == Yto * R.rotate(Zfrom) == Zto * @param Xfrom :: The X axis of the original basis set * @param Yfrom :: The Y axis of the original basis set * @param Zfrom :: The Z axis of the original basis set * @param Xto :: The X axis of the final basis set * @param Yto :: The Y axis of the final basis set * @param Zto :: The Z axis of the final basis set * @param R :: The output rotation as a quaternion * @param out :: Debug printout flag */ void InstrumentActor::BasisRotation(const Mantid::Kernel::V3D& Xfrom, const Mantid::Kernel::V3D& Yfrom, const Mantid::Kernel::V3D& Zfrom, const Mantid::Kernel::V3D& Xto, const Mantid::Kernel::V3D& Yto, const Mantid::Kernel::V3D& Zto, Mantid::Kernel::Quat& R, bool out ) { // Find transformation from (X,Y,Z) to (XX,YY,ZZ) // R = R1*R2*R3, where R1, R2, and R3 are Euler rotations // std::cerr<<"RCRotation-----------------------------\n"; // std::cerr<<"From "<<Xfrom<<Yfrom<<Zfrom<<'\n'; // std::cerr<<"To "<<Xto<<Yto<<Zto<<'\n'; double sZ = Zfrom.scalar_prod(Zto); if (fabs(sZ - 1) < m_tolerance) // vectors the same { double sX = Xfrom.scalar_prod(Xto); if (fabs(sX - 1) < m_tolerance) { R = Mantid::Kernel::Quat(); } else if (fabs(sX + 1) < m_tolerance) { R = Mantid::Kernel::Quat(180,Zfrom); } else { R = Mantid::Kernel::Quat(Xfrom,Xto); } } else if(fabs(sZ + 1) < m_tolerance) // rotated by 180 degrees { if (fabs(Xfrom.scalar_prod(Xto)-1) < m_tolerance) { R = Mantid::Kernel::Quat(180.,Xfrom); } else if (fabs(Yfrom.scalar_prod(Yto)-1) < m_tolerance) { R = Mantid::Kernel::Quat(180.,Yfrom); } else { R = Mantid::Kernel::Quat(180.,Xto)*Mantid::Kernel::Quat(Xfrom,Xto); } } else { // Rotation R1 of system (X,Y,Z) around Z by alpha Mantid::Kernel::V3D X1; Mantid::Kernel::Quat R1; X1 = Zfrom.cross_prod(Zto); X1.normalize(); double sX = Xfrom.scalar_prod(Xto); if (fabs(sX - 1) < m_tolerance) { R = Mantid::Kernel::Quat(Zfrom,Zto); return; } sX = Xfrom.scalar_prod(X1); if (fabs(sX - 1) < m_tolerance) { R1 = Mantid::Kernel::Quat(); } else if (fabs(sX + 1) < m_tolerance) // 180 degree rotation { R1 = Mantid::Kernel::Quat(180.,Zfrom); } else { R1 = Mantid::Kernel::Quat(Xfrom,X1); } if (out) std::cerr<<"R1="<<R1<<'\n'; // Rotation R2 around X1 by beta Mantid::Kernel::Quat R2(Zfrom,Zto); // vectors are different if (out) std::cerr<<"R2="<<R2<<'\n'; // Rotation R3 around ZZ by gamma Mantid::Kernel::Quat R3; sX = Xto.scalar_prod(X1); if (fabs(sX - 1) < m_tolerance) { R3 = Mantid::Kernel::Quat(); } else if (fabs(sX + 1) < m_tolerance) // 180 degree rotation { R3 = Mantid::Kernel::Quat(180.,Zto); } else { R3 = Mantid::Kernel::Quat(X1,Xto); } if (out) std::cerr<<"R3="<<R3<<'\n'; // Combined rotation R = R3*R2*R1; } }