Mantid
Loading...
Searching...
No Matches
CoordTransformAffine.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 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/Matrix.h"
13#include "MantidKernel/System.h"
15
16#include <boost/algorithm/string.hpp>
17#include <boost/format.hpp>
18
19using namespace Mantid::Geometry;
20using namespace Mantid::Kernel;
22
23namespace Mantid::DataObjects {
24
25//----------------------------------------------------------------------------------------------
32CoordTransformAffine::CoordTransformAffine(const size_t inD, const size_t outD)
33 : CoordTransform(inD, outD), m_affineMatrix(outD + 1, inD + 1), m_rawMatrix(nullptr), m_rawMemory(nullptr) {
35
36 // Allocate the raw matrix
37 size_t nx = m_affineMatrix.numRows();
38 size_t ny = m_affineMatrix.numCols();
39 // vector of pointers
40 m_rawMatrix = new coord_t *[nx];
41 // memory itself
42 m_rawMemory = new coord_t[nx * ny];
43 for (size_t i = 0; i < nx; i++)
44 m_rawMatrix[i] = m_rawMemory + (i * ny);
45 // Copy into the raw matrix (for speed)
47}
48
50 this->setMatrix(other.getMatrix());
51}
52
54 using std::swap;
56 swap(obj1.m_rawMatrix, obj2.m_rawMatrix);
57 swap(obj1.m_rawMemory, obj2.m_rawMemory);
58}
59
61 swap(*this, other);
62 return *this;
63}
64
65//----------------------------------------------------------------------------------------------
69 // delete array of pointers to rows
70 delete[] m_rawMatrix;
71 m_rawMatrix = nullptr;
72
73 // delete large mem block holding the matrix
74 delete[] m_rawMemory;
75 m_rawMemory = nullptr;
76}
77
78//----------------------------------------------------------------------------------------------
83 for (size_t x = 0; x < m_affineMatrix.numRows(); ++x)
84 for (size_t y = 0; y < m_affineMatrix.numCols(); ++y)
86}
87
88//----------------------------------------------------------------------------------------------
92 auto out = new CoordTransformAffine(inD, outD);
93 out->setMatrix(this->getMatrix());
94 return out;
95}
96
97//----------------------------------------------------------------------------------------------
104 if (newMatrix.numRows() != outD + 1)
105 throw std::runtime_error("setMatrix(): Number of rows must match!");
106 if (newMatrix.numCols() != inD + 1)
107 throw std::runtime_error("setMatrix(): Number of columns must match!");
108 m_affineMatrix = newMatrix;
109 // Copy into the raw matrix (for speed)
111}
112
113//----------------------------------------------------------------------------------------------
117
120
121//----------------------------------------------------------------------------------------------
127void CoordTransformAffine::addTranslation(const coord_t *translationVector) {
128 Matrix<coord_t> translationMatrix(outD + 1, inD + 1);
129 // Start with identity
130 translationMatrix.identityMatrix();
131 // Fill the last column with the translation value
132 for (size_t i = 0; i < outD; i++)
133 translationMatrix[i][inD] = translationVector[i];
134
135 // Multiply the affine matrix by the translation affine matrix to combine them
136 m_affineMatrix *= translationMatrix;
137
138 // Copy into the raw matrix (for speed)
140}
141
142//----------------------------------------------------------------------------------------------
174 const std::vector<Mantid::Kernel::VMD> &axes,
175 const Mantid::Kernel::VMD &scaling) {
176 if (origin.size() != inD)
177 throw std::runtime_error("CoordTransformAffine::buildOrthogonal(): the "
178 "origin must be in the dimensions of the input "
179 "workspace (length inD).");
180 if (axes.size() != outD)
181 throw std::runtime_error("CoordTransformAffine::buildOrthogonal(): you "
182 "must give as many basis vectors as there are "
183 "dimensions in the output workspace.");
184 if (scaling.size() != outD)
185 throw std::runtime_error("CoordTransformAffine::buildOrthogonal(): the "
186 "size of the scaling vector must be the same as "
187 "the number of dimensions in the output "
188 "workspace.");
189
190 // Start with identity
192
193 for (size_t i = 0; i < axes.size(); i++) {
194 if (axes[i].length() == 0.0)
195 throw std::runtime_error("CoordTransformAffine::buildOrthogonal(): one "
196 "of the basis vector was of zero length.");
197 if (axes[i].size() != inD)
198 throw std::runtime_error("CoordTransformAffine::buildOrthogonal(): one "
199 "of the basis vectors had the wrong number of "
200 "dimensions (must be inD).");
201 // Normalize each axis to unity
202 VMD basis = axes[i];
203 basis.normalize();
204 // The row of the affine matrix = the unit vector
205 for (size_t j = 0; j < basis.size(); j++)
206 m_affineMatrix[i][j] = static_cast<coord_t>(basis[j] * scaling[i]);
207
208 // Now account for the translation
209 coord_t transl = 0;
210 for (size_t j = 0; j < basis.size(); j++)
211 transl += static_cast<coord_t>(origin[j] * basis[j]); // dot product of origin * basis aka ( X0 . U )
212 // The last column of the matrix = the translation movement
213 m_affineMatrix[i][inD] = -transl * static_cast<coord_t>(scaling[i]);
214 }
215
216 // Copy into the raw matrix (for speed)
218}
219
221 const std::vector<Mantid::Kernel::VMD> &axes,
222 const Mantid::Kernel::VMD &scaling) {
223 if (origin.size() != inD)
224 throw std::runtime_error("CoordTransformAffine::buildNonOrthogonal(): the "
225 "origin must be in the dimensions of the input "
226 "workspace (length inD).");
227 if (axes.size() != outD)
228 throw std::runtime_error("CoordTransformAffine::buildNonOrthogonal(): you "
229 "must give as many basis vectors as there are "
230 "dimensions in the output workspace.");
231 if (scaling.size() != outD)
232 throw std::runtime_error("CoordTransformAffine::buildNonOrthogonal(): the "
233 "size of the scaling vector must be the same as "
234 "the number of dimensions in the output "
235 "workspace.");
236
237 // Start with identity
239 // A matrix is columns of basis vectors
241 for (size_t i = 0; i < outD; i++) {
242 for (size_t j = 0; j < inD; j++) {
243 A[j][i] = axes[i][j];
244 }
245 }
247 AT.Transpose();
249 ATA.Invert();
250 Mantid::Kernel::Matrix<coord_t> Ainv = ATA * AT;
252 for (size_t j = 0; j < inD; j++) {
253 offset[j][0] = origin[j];
254 }
255 Mantid::Kernel::Matrix<coord_t> outoffset = Ainv * offset;
256
257 for (size_t i = 0; i < outD; i++) {
258 for (size_t j = 0; j < inD; j++) {
259 m_affineMatrix[i][j] = Ainv[i][j] * scaling[i];
260 }
261 m_affineMatrix[i][inD] = -outoffset[i][0] * scaling[i];
262 }
263 // Copy into the raw matrix (for speed)
265}
266
267//----------------------------------------------------------------------------------------------
273void CoordTransformAffine::apply(const coord_t *inputVector, coord_t *outVector) const {
274 // For each output dimension
275 for (size_t out = 0; out < outD; ++out) {
276 // Cache the row pointer to make the matrix access a bit faster
277 coord_t *rawMatrixRow = m_rawMatrix[out];
278 coord_t outVal = 0.0;
279 size_t in;
280 for (in = 0; in < inD; ++in)
281 outVal += rawMatrixRow[in] * inputVector[in];
282
283 // The last input coordinate is "1" always (made homogenous coordinate out
284 // of the input x,y,etc.)
285 outVal += rawMatrixRow[in];
286 // Save in the output
287 outVector[out] = outVal;
288 }
289}
290
291//----------------------------------------------------------------------------------------------
297 using namespace Poco::XML;
298
299 AutoPtr<Document> pDoc = new Document;
300 AutoPtr<Element> coordTransformElement = pDoc->createElement("CoordTransform");
301 pDoc->appendChild(coordTransformElement);
302
303 AutoPtr<Element> coordTransformTypeElement = pDoc->createElement("Type");
304 coordTransformTypeElement->appendChild(AutoPtr<Node>(pDoc->createTextNode("CoordTransformAffine")));
305 coordTransformElement->appendChild(coordTransformTypeElement);
306
307 AutoPtr<Element> paramListElement = pDoc->createElement("ParameterList");
308
309 AutoPtr<Text> formatText = pDoc->createTextNode("%s%s%s");
310 paramListElement->appendChild(formatText);
311
312 coordTransformElement->appendChild(paramListElement);
313
314 std::stringstream xmlstream;
315
316 DOMWriter writer;
317 writer.writeNode(xmlstream, pDoc);
318
319 // Convert the members to parameters
320 AffineMatrixParameter affineMatrixParameter(inD, outD);
321 affineMatrixParameter.setMatrix(m_affineMatrix);
322 Mantid::API::InDimParameter inD_param(inD);
323 Mantid::API::OutDimParameter outD_param(outD);
324
325 std::string formattedXMLString =
326 boost::str(boost::format(xmlstream.str().c_str()) % inD_param.toXMLString().c_str() %
327 outD_param.toXMLString().c_str() % affineMatrixParameter.toXMLString().c_str());
328 return formattedXMLString;
329}
330
335std::string CoordTransformAffine::id() const { return "CoordTransformAffine"; }
336
337//----------------------------------------------------------------------------------------------
347 if (!first || !second)
348 throw std::runtime_error("CoordTransformAffine::combineTransformations(): Null input provided.");
349 if (second->getInD() != first->getOutD())
350 throw std::runtime_error("CoordTransformAffine::combineTransformations(): "
351 "The # of output dimensions of first must be the "
352 "same as the # of input dimensions of second.");
353 // Convert both inputs to affine matrices, if needed
354 auto *firstAff = dynamic_cast<CoordTransformAffine *>(first);
355 bool ownFirstAff(false);
356 if (!firstAff) {
357 auto *firstAl = dynamic_cast<CoordTransformAligned *>(first);
358 if (!firstAl)
359 throw std::runtime_error("CoordTransformAffine::combineTransformations(): first transform "
360 "must be either CoordTransformAffine or CoordTransformAligned.");
361 firstAff = new CoordTransformAffine(firstAl->getInD(), firstAl->getOutD());
362 firstAff->setMatrix(firstAl->makeAffineMatrix());
363 ownFirstAff = true;
364 }
365 auto *secondAff = dynamic_cast<CoordTransformAffine *>(second);
366 bool ownSecondAff(false);
367 if (!secondAff) {
368 auto *secondAl = dynamic_cast<CoordTransformAligned *>(second);
369 if (!secondAl)
370 throw std::runtime_error("CoordTransformAffine::combineTransformations(): second transform "
371 "must be either CoordTransformAffine or CoordTransformAligned.");
372 secondAff = new CoordTransformAffine(secondAl->getInD(), secondAl->getOutD());
373 secondAff->setMatrix(secondAl->makeAffineMatrix());
374 ownSecondAff = true;
375 }
376 // Initialize the affine matrix
377 auto out = new CoordTransformAffine(firstAff->getInD(), secondAff->getOutD());
378 // Multiply the two matrices together
379 Matrix<coord_t> outMat = secondAff->getMatrix() * firstAff->getMatrix();
380 // Set in the output
381 out->setMatrix(outMat);
382 // Clean up
383 if (ownFirstAff)
384 delete firstAff;
385 if (ownSecondAff)
386 delete secondAff;
387 return out;
388}
389
390} // namespace Mantid::DataObjects
Unique SingleValueParameter Declaration for InputNDimensions.
size_t inD
Input number of dimensions.
size_t outD
Output number of dimensions.
Type to wrap an affine matrix and allow serialization via xml.
std::string toXMLString() const override
Serialize the Affine Matrix Parameter.
void setMatrix(const AffineMatrixType &newMatrix)
Setter for the internal affine matrix.
Generic class to transform from M input dimensions to N output dimensions.
const Mantid::Kernel::Matrix< coord_t > & getMatrix() const
Return the affine matrix in the transform.
std::string toXMLString() const override
Serialize the coordinate transform.
coord_t ** m_rawMatrix
Raw pointer to the same underlying matrix as affineMatrix.
void copyRawMatrix()
Copies the affine matrix into a local raw pointer, for speed.
CoordTransformAffine & operator=(CoordTransformAffine)
static CoordTransformAffine * combineTransformations(CoordTransform *first, CoordTransform *second)
Combine two transformations into a single affine transformations.
void buildNonOrthogonal(const Mantid::Kernel::VMD &origin, const std::vector< Mantid::Kernel::VMD > &axes, const Mantid::Kernel::VMD &scaling)
void addTranslation(const coord_t *translationVector)
Add a translation (in the output coordinates) to the transform.
std::string id() const override
Coordinate transform id.
CoordTransformAffine(const size_t inD, const size_t outD)
Constructor.
void setMatrix(const Mantid::Kernel::Matrix< coord_t > &newMatrix)
Directly set the affine matrix to use.
Mantid::Kernel::Matrix< coord_t > makeAffineMatrix() const override
friend void swap(CoordTransformAffine &, CoordTransformAffine &)
void buildOrthogonal(const Mantid::Kernel::VMD &origin, const std::vector< Mantid::Kernel::VMD > &axes, const Mantid::Kernel::VMD &scaling)
Build a coordinate transformation based on an origin and orthogonal basis vectors.
coord_t * m_rawMemory
raw pointer to the memory block, referred by the raw Matrix;
CoordTransform * clone() const override
Virtual cloner.
void apply(const coord_t *inputVector, coord_t *outVector) const override
Apply the coordinate transformation.
Mantid::Kernel::Matrix< coord_t > m_affineMatrix
Affine Matrix to perform the transformation.
Unique type declaration for which dimensions are used in the input workspace.
Numerical Matrix class.
Definition: Matrix.h:42
T Invert()
LU inversion routine.
Definition: Matrix.cpp:924
void identityMatrix()
Makes the matrix an idenity matrix.
Definition: Matrix.cpp:661
size_t numRows() const
Return the number of rows in the matrix.
Definition: Matrix.h:144
size_t numCols() const
Return the number of columns in the matrix.
Definition: Matrix.h:147
Matrix< T > & Transpose()
Transpose the matrix.
Definition: Matrix.cpp:793
TYPE normalize()
Normalize this vector to unity length.
Definition: VMD.cpp:427
size_t size() const
Definition: VMD.cpp:239
void swap(MDLeanEvent< nd > &first, MDLeanEvent< nd > &second)
float coord_t
Typedef for the data type to use for coordinate axes in MD objects such as MDBox, MDEventWorkspace,...
Definition: MDTypes.h:27