Mantid
Loading...
Searching...
No Matches
ShapeRotation.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2026 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
12#include "MantidKernel/Logger.h"
13
14#include <stdexcept>
15
16namespace Mantid::Geometry {
17
18namespace {
19Kernel::Logger g_log("ShapeRotation");
20
21const Kernel::Matrix<double> &identityMatrix() {
22 static const Kernel::Matrix<double> identity(3, 3, true);
23 return identity;
24}
25} // namespace
26
28 const Kernel::Matrix<double> &alreadyApplied = shape.getAppliedRotation();
29 if (alreadyApplied == identityMatrix()) {
30 return goniometerR;
31 }
32 g_log.information("The sample shape already carries a rotation, so only the remainder of the "
33 "goniometer rotation is applied");
34 // Tprime rather than Invert: these are orthonormal, so the transpose is the inverse exactly, and
35 // Invert would both mutate the matrix and return a determinant we have no use for.
36 return goniometerR * alreadyApplied.Tprime();
37}
38
39std::shared_ptr<IObject> getLabFrameShape(const IObject &shape, const Kernel::Matrix<double> &goniometerR) {
40 const auto outstanding = outstandingGoniometerRotation(shape, goniometerR);
41 if (outstanding == identityMatrix()) {
42 // Already in the lab frame. Returning the clone unrotated is not a shortcut - rotating by an
43 // identity built from R and its transpose would perturb the last bits for no gain.
44 return std::shared_ptr<IObject>(shape.clone());
45 }
46
47 if (const auto *csgShape = dynamic_cast<const CSGObject *>(&shape)) {
48 // Rotating a CSG shape means rewriting its definition, so one assembled surface by surface
49 // rather than parsed - ShapeFactory::createSphere and createHexahedralShape both do this - has
50 // nothing to rewrite. Rebasing an empty string yields a bare pair of tags that will not parse,
51 // which createShape answers with an empty shape: silently no sample at all, where the caller
52 // asked for a rotated one. Unlike the MeshObject2D below this shape could be rotated, so the
53 // missing definition is a defect to report rather than a frame to accept.
54 if (csgShape->getShapeXML().empty()) {
55 throw std::invalid_argument("The sample shape ('" + shape.id() +
56 "') carries no XML definition, so it cannot be rotated into the lab frame. It was "
57 "built directly from surfaces rather than parsed from a shape definition. Define "
58 "the sample with SetSample or CreateSampleShape to make it rotatable.");
59 }
60
61 // Rebase so the baked part becomes exactly goniometerR, preserving any rotation of the shape
62 // within its own frame, then rebuild. createShape carries over neither the material nor the id.
63 ShapeFactory factory;
64 auto labShape = factory.createShape(
65 factory.rebakeGoniometer(goniometerR, csgShape->getShapeXML(), shape.getAppliedRotation()), false);
66 labShape->setMaterial(shape.material());
67 labShape->setID(shape.id());
68 return labShape;
69 }
70
71 auto labShape = std::shared_ptr<IObject>(shape.clone());
72 if (auto *meshShape = dynamic_cast<MeshObject *>(labShape.get())) {
73 meshShape->bakeGoniometerRotation(outstanding);
74 return labShape;
75 }
76
77 // MeshObject2D, the flat plate, is the only shape in the tree with no rotation mechanism at all.
78 g_log.warning("The sample shape is of a type that cannot be rotated ('" + shape.id() +
79 "'), so it is taken to be defined in the lab frame already and is used unchanged. "
80 "The goniometer rotation on the run has not been applied to it.");
81 return labShape;
82}
83
84} // namespace Mantid::Geometry
Constructive Solid Geometry object.
Definition CSGObject.h:51
IObject : Interface for geometry objects.
Definition IObject.h:43
virtual const Kernel::Matrix< double > & getAppliedRotation() const
The goniometer rotation baked into this shape, i.e.
Definition IObject.h:59
virtual const std::string & id() const =0
virtual const Kernel::Material & material() const =0
virtual IObject * clone() const =0
Triangular Mesh Object.
Definition MeshObject.h:53
Class originally intended to be used with the DataHandling 'LoadInstrument' algorithm.
std::shared_ptr< CSGObject > createShape(Poco::XML::Element *pElem)
Creates a geometric object from a DOM-element-node pointing to an element whose child nodes contain t...
std::string rebakeGoniometer(const Kernel::Matrix< double > &newBake, std::string xml, const Kernel::Matrix< double > &currentBake)
Rebase the shape so the baked part becomes exactly newBake, preserving the definition-frame rotation.
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
Numerical Matrix class.
Definition Matrix.h:41
Matrix< T > Tprime() const
Transpose the matrix.
Definition Matrix.cpp:766
Mantid::Kernel::Logger g_log("Goniometer")
MANTID_GEOMETRY_DLL std::shared_ptr< IObject > getLabFrameShape(const IObject &shape, const Kernel::Matrix< double > &goniometerR)
The shape as it sits in the lab frame: a clone rotated by whatever part of goniometerR it does not al...
MANTID_GEOMETRY_DLL Kernel::Matrix< double > outstandingGoniometerRotation(const IObject &shape, const Kernel::Matrix< double > &goniometerR)
The part of goniometerR that the shape does not already carry.