Mantid
Loading...
Searching...
No Matches
V3D.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 +
7#include "MantidKernel/V3D.h"
9#include "MantidKernel/Quat.h"
10#include "MantidNexus/NexusFile.h"
11#include <algorithm>
12#include <boost/version.hpp>
13#include <sstream>
14
15#if BOOST_VERSION < 106700
16#include <boost/math/common_factor.hpp>
17using boost::math::gcd;
18#else
19#include <boost/integer/common_factor.hpp>
20using boost::integer::gcd;
21#endif
22
23namespace {
31double nearInt(double val, double eps, double mult) noexcept {
32 if (val > 0.0) {
33 if (val < 1.0) {
34 mult /= val;
35 } else {
36 if (std::abs(val - std::round(val)) > eps) {
37 mult *= std::ceil(val / eps) * eps / val;
38 }
39 }
40 }
41 return mult;
42}
43} // namespace
44
45namespace Mantid::Kernel {
46
56void V3D::spherical(const double R, const double theta, const double phi) noexcept {
57 constexpr double deg2rad = M_PI / 180.0;
58 spherical_rad(R, theta * deg2rad, phi * deg2rad);
59}
60
69void V3D::spherical_rad(const double R, const double polar, const double azimuth) noexcept {
70 m_pt[2] = R * cos(polar);
71 const double ct = R * sin(polar);
72 m_pt[0] = ct * cos(azimuth);
73 m_pt[1] = ct * sin(azimuth);
74
75 // Setting this way can lead to very small values of x & y that should really
76 // be zero.
77 // This can cause confusion for the atan2 function used in getSpherical.
78 if (std::abs(m_pt[0]) < Tolerance)
79 m_pt[0] = 0.0;
80 if (std::abs(m_pt[1]) < Tolerance)
81 m_pt[1] = 0.0;
82}
83
94void V3D::azimuth_polar_SNS(const double R, const double azimuth, const double polar) noexcept {
95 m_pt[1] = R * cos(polar);
96 const double ct = R * sin(polar);
97 m_pt[0] = ct * cos(azimuth);
98 m_pt[2] = ct * sin(azimuth);
99
100 // Setting this way can lead to very small values of x & y that should really
101 // be zero.
102 // This can cause confusion for the atan2 function used in getSpherical.
103 if (std::abs(m_pt[0]) < Tolerance)
104 m_pt[0] = 0.0;
105 if (std::abs(m_pt[1]) < Tolerance)
106 m_pt[1] = 0.0;
107 if (std::abs(m_pt[2]) < Tolerance)
108 m_pt[2] = 0.0;
109}
110
116void V3D::getSpherical(double &R, double &theta, double &phi) const noexcept {
117 constexpr double rad2deg = 180.0 / M_PI;
118 R = norm();
119 theta = 0.0;
120 if (R != 0.0)
121 theta = acos(m_pt[2] / R) * rad2deg;
122 phi = atan2(m_pt[1], m_pt[0]) * rad2deg;
123}
124
130 const double ND(norm());
131 if (ND == 0) {
132 throw std::runtime_error("Unable to normalize a zero length vector.");
133 }
134 this->operator/=(ND);
135 return ND;
136}
137
139void V3D::round() noexcept {
140 std::transform(m_pt.cbegin(), m_pt.cend(), m_pt.begin(), [](auto p) { return std::round(p); });
141}
142
147double V3D::zenith(const V3D &v) const noexcept {
148 const double R = distance(v);
149 if (R != 0.0) {
150 const double zOffset = m_pt[2] - v.m_pt[2];
151 return acos(zOffset / R);
152 } else {
153 return 0.0;
154 }
155}
156
162double V3D::angle(const V3D &v) const {
163 const double ratio = this->cosAngle(v);
164 if (ratio >= 1.0) // NOTE: Due to rounding errors, if v is
165 return 0.0; // is nearly the same as "this" or
166 else if (ratio <= -1.0) // as "-this", ratio can be slightly
167 return M_PI; // more than 1 in absolute value.
168 // That causes acos() to return NaN.
169 return acos(ratio);
170}
171
177double V3D::cosAngle(const V3D &v) const {
178 const double n1 = norm();
179 const double n2 = v.norm();
180 if (n1 == 0. || n2 == 0.) {
181 throw std::runtime_error("Cannot calculate an angle when one of the vectors has zero length.");
182 }
183 return this->scalar_prod(v) / (n1 * n2);
184}
185
196int V3D::reBase(const V3D &A, const V3D &B, const V3D &C) noexcept {
197 Matrix<double> T(3, 3);
198 for (int i = 0; i < 3; i++) {
199 T[i][0] = A[i];
200 T[i][1] = B[i];
201 T[i][2] = C[i];
202 }
203 const double det = T.Invert();
204 if (std::abs(det) < 1e-13) // failed
205 return -1;
206 rotate(T);
207 return 0;
208}
209
214void V3D::rotate(const Kernel::Matrix<double> &A) noexcept {
215 const double xold(m_pt[0]), yold(m_pt[1]), zold(m_pt[2]);
216 for (size_t i = 0; i < m_pt.size(); ++i) {
217 m_pt[i] = A[i][0] * xold + A[i][1] * yold + A[i][2] * zold;
218 }
219}
220
227bool V3D::coLinear(const V3D &Bv, const V3D &Cv) const noexcept {
228 const V3D &Av = *this;
229 const V3D Tmp((Bv - Av).cross_prod(Cv - Av));
230 return Tmp.norm() <= Tolerance;
231}
232
238bool V3D::nullVector(const double tolerance) const noexcept {
239 return std::none_of(m_pt.cbegin(), m_pt.cend(), [&tolerance](const auto p) { return std::abs(p) > tolerance; });
240}
241
242bool V3D::unitVector(const double tolerance) const noexcept {
243 // NOTE could be made more efficient using norm2()
244 const auto l = norm();
245 return std::abs(l - 1.) < tolerance;
246}
247
256int V3D::masterDir(const double tolerance) const noexcept {
257 // Calc max dist
258 double max = m_pt[0] * m_pt[0];
259 double other = max;
260 double u2 = m_pt[1] * m_pt[1];
261 int idx = (m_pt[0] > 0) ? 1 : -1;
262 if (u2 > max) {
263 max = u2;
264 idx = (m_pt[1] > 0) ? 2 : -2;
265 }
266 other += u2;
267 u2 = m_pt[2] * m_pt[2];
268 if (u2 > max) {
269 max = u2;
270 idx = (m_pt[2] > 0) ? 3 : -3;
271 }
272 other += u2;
273 other -= max;
274 if ((other / max) > tolerance) // doesn't have master direction
275 {
276 return 0;
277 }
278 return idx;
279}
280
290std::vector<V3D> V3D::makeVectorsOrthogonal(const std::vector<V3D> &vectors) {
291 if (vectors.size() != 2)
292 throw std::invalid_argument("makeVectorsOrthogonal() only works with 2 vectors");
293
294 const V3D v0 = Kernel::normalize(vectors[0]);
295 V3D v1 = Kernel::normalize(vectors[1]);
296
297 std::vector<V3D> out;
298 out.reserve(3);
299 out.emplace_back(v0);
300
301 // Make a rotation 90 degrees from 0 to 1
302 Quat q(v0, v1);
303 q.setRotation(90);
304 // Rotate v1 so it is 90 deg
305 v1 = v0;
306 q.rotate(v1);
307 out.emplace_back(v1);
308
309 // Finally, the 3rd vector = cross product of 0 and 1
310 V3D v2 = v0.cross_prod(v1);
311 out.emplace_back(v2);
312 return out;
313}
314
320void V3D::read(std::istream &IX) { IX >> m_pt[0] >> m_pt[1] >> m_pt[2]; }
321
322void V3D::write(std::ostream &OX) const
327{
328 OX << m_pt[0] << " " << m_pt[1] << " " << m_pt[2];
329}
330
332std::string V3D::toString() const {
333 std::ostringstream mess;
334 this->write(mess);
335 return mess.str();
336}
337
340void V3D::fromString(const std::string &str) {
341 std::istringstream mess(str);
342 this->read(mess);
343}
344
349void V3D::printSelf(std::ostream &os) const { os << "[" << m_pt[0] << "," << m_pt[1] << "," << m_pt[2] << "]"; }
350
356void V3D::readPrinted(std::istream &IX) {
357 std::string in;
358 std::getline(IX, in);
359 size_t i = in.find_first_of('[');
360 if (i == std::string::npos)
361 throw std::runtime_error("Wrong format for V3D input: " + in);
362 size_t j = in.find_last_of(']');
363 if (j == std::string::npos || j < i + 6)
364 throw std::runtime_error("Wrong format for V3D input: " + in);
365
366 size_t c1 = in.find_first_of(',');
367 size_t c2 = in.find_first_of(',', c1 + 1);
368 if (c1 == std::string::npos || c2 == std::string::npos)
369 throw std::runtime_error("Wrong format for V3D input: [" + in + "]");
370
371 m_pt[0] = std::stod(in.substr(i + 1, c1 - i - 1));
372 m_pt[1] = std::stod(in.substr(c1 + 1, c2 - c1 - 1));
373 m_pt[2] = std::stod(in.substr(c2 + 1, j - c2 - 1));
374}
375
382std::ostream &operator<<(std::ostream &os, const V3D &v) {
383 v.printSelf(os);
384 return os;
385}
386
387std::istream &operator>>(std::istream &IX, V3D &A)
394{
395 A.readPrinted(IX);
396 return IX;
397}
398
399//--------------------------------------------------------------------------------------------
404void V3D::saveNexus(Nexus::File *file, const std::string &name) const {
405 file->makeData(name, NXnumtype::FLOAT64, 3, true);
406 file->putData(m_pt.data());
407 file->closeData();
408}
409
410//--------------------------------------------------------------------------------------------
415void V3D::loadNexus(Nexus::File *file, const std::string &name) {
416 std::vector<double> data;
417 file->readData(name, data);
418 if (data.size() != 3)
419 throw std::runtime_error("Unexpected data size when reading a V3D NXS field '" + name + "'. Expected 3.");
420 std::copy(data.cbegin(), data.cend(), m_pt.begin());
421}
422
423double V3D::toMillerIndexes(double eps) {
424 if (eps < 0)
425 eps = -eps;
426 if (eps < FLT_EPSILON)
427 eps = FLT_EPSILON;
428
429 // assuming eps is in 1.e-x form
430
431 double ax = std::abs(m_pt[0]);
432 double ay = std::abs(m_pt[1]);
433 double az = std::abs(m_pt[2]);
434
435 double amax = (ax > ay) ? ax : ay;
436 amax = (az > amax) ? az : amax;
437 if (amax < FLT_EPSILON)
438 throw(std::invalid_argument("vector length is less then accuracy requested"));
439
440 if (ax < eps) {
441 m_pt[0] = 0;
442 ax = 0;
443 }
444 if (ay < eps) {
445 m_pt[1] = 0;
446 ay = 0;
447 }
448 if (az < eps) {
449 m_pt[2] = 0;
450 az = 0;
451 }
452
453 double mult(1);
454 mult = nearInt(ax, eps, mult);
455 mult = nearInt(ay, eps, mult);
456 mult = nearInt(az, eps, mult);
457
458 const size_t iax = std::lround(ax * mult / eps);
459 const size_t iay = std::lround(ay * mult / eps);
460 const size_t iaz = std::lround(az * mult / eps);
461
462 const size_t div = gcd(iax, gcd(iay, iaz));
463 mult /= (static_cast<double>(div) * eps);
464 this->operator*=(mult);
465
466 return mult;
467}
468
475bool V3D::compareMagnitude(const V3D &v1, const V3D &v2) {
476 const double mag_sq_1 = v1[0] * v1[0] + v1[1] * v1[1] + v1[2] * v1[2];
477 const double mag_sq_2 = v2[0] * v2[0] + v2[1] * v2[1] + v2[2] * v2[2];
478 return (mag_sq_1 < mag_sq_2);
479}
480
487V3D V3D::directionAngles(bool inDegrees) const {
488 const double conversionFactor = inDegrees ? 180. / M_PI : 1.0;
489 const double divisor = this->norm();
490 if (divisor == 0.) {
491 throw std::runtime_error("Cannot calculate direction angles for zero length vector");
492 }
493 return V3D(conversionFactor * acos(m_pt[0] / divisor), conversionFactor * acos(m_pt[1] / divisor),
494 conversionFactor * acos(m_pt[2] / divisor));
495}
496
501int V3D::maxCoeff() const {
502 int MaxOrder = 0;
503 if (std::abs(static_cast<int>(m_pt[0])) > MaxOrder)
504 MaxOrder = std::abs(static_cast<int>(m_pt[0]));
505 if (std::abs(static_cast<int>(m_pt[1])) > MaxOrder)
506 MaxOrder = std::abs(static_cast<int>(m_pt[1]));
507 if (std::abs(static_cast<int>(m_pt[2])) > MaxOrder)
508 MaxOrder = std::abs(static_cast<int>(m_pt[2]));
509 return MaxOrder;
510}
511
516V3D V3D::absoluteValue() const { return V3D(std::abs(m_pt[0]), std::abs(m_pt[1]), std::abs(m_pt[2])); }
517
522double V3D::hklError() const {
523 return std::abs(m_pt[0] - std::round(m_pt[0])) + //
524 std::abs(m_pt[1] - std::round(m_pt[1])) + //
525 std::abs(m_pt[2] - std::round(m_pt[2]));
526}
527
528} // namespace Mantid::Kernel
std::string name
Definition Run.cpp:60
double tolerance
Numerical Matrix class.
Definition Matrix.h:42
T Invert()
LU inversion routine.
Definition Matrix.cpp:924
Class for quaternions.
Definition Quat.h:39
void rotate(V3D &) const
Rotate a vector.
Definition Quat.cpp:397
void setRotation(const double deg)
Set the rotation (both don't change rotation axis)
Definition Quat.cpp:158
Class for 3D vectors.
Definition V3D.h:34
void spherical_rad(const double R, const double polar, const double azimuth) noexcept
Sets the vector position based on spherical coordinates, in radians.
Definition V3D.cpp:69
std::array< double, 3 > m_pt
Definition V3D.h:340
static bool compareMagnitude(const Kernel::V3D &v1, const Kernel::V3D &v2)
Convenience method for sorting list of V3D objects based on magnitude.
Definition V3D.cpp:475
bool coLinear(const V3D &, const V3D &) const noexcept
Determines if this,B,C are colinear.
Definition V3D.cpp:227
static std::vector< V3D > makeVectorsOrthogonal(const std::vector< V3D > &vectors)
Take a list of 2 vectors and makes a 3D orthogonal system out of them The first vector i0 is taken as...
Definition V3D.cpp:290
void azimuth_polar_SNS(const double R, const double azimuth, const double polar) noexcept
Sets the vector position based on azimuth and polar angle, in RADIANS, in the SNS instrument coordina...
Definition V3D.cpp:94
V3D absoluteValue() const
Absolute value.
Definition V3D.cpp:516
int maxCoeff() const
Maximum absolute integer value.
Definition V3D.cpp:501
double zenith(const V3D &) const noexcept
Zenith (theta) angle between this and another vector.
Definition V3D.cpp:147
void loadNexus(Nexus::File *file, const std::string &name)
Load the object from an open NeXus file.
Definition V3D.cpp:415
constexpr double scalar_prod(const V3D &v) const noexcept
Calculates the cross product.
Definition V3D.h:280
void printSelf(std::ostream &) const
Prints a text representation of itself in format "[x,y,z]".
Definition V3D.cpp:349
V3D & operator*=(const V3D &v) noexcept
Self-Inner product.
Definition V3D.h:116
V3D & operator/=(const V3D &v) noexcept
Self-Inner division.
Definition V3D.h:128
double normalize()
Make a normalized vector (return norm value)
Definition V3D.cpp:129
constexpr V3D cross_prod(const V3D &v) const noexcept
Cross product (this * argument)
Definition V3D.h:284
std::string toString() const
Definition V3D.cpp:332
V3D directionAngles(bool inDegrees=true) const
Direction angles.
Definition V3D.cpp:487
void spherical(const double R, const double theta, const double phi) noexcept
Sets the vector position based on spherical coordinates.
Definition V3D.cpp:56
void read(std::istream &)
Read data from a stream.
Definition V3D.cpp:320
void saveNexus(Nexus::File *file, const std::string &name) const
Save the object to an open NeXus file.
Definition V3D.cpp:404
double angle(const V3D &) const
Angle between this and another vector.
Definition V3D.cpp:162
void rotate(const Matrix< double > &) noexcept
Rotate a point by a matrix.
Definition V3D.cpp:214
double norm() const noexcept
Definition V3D.h:269
int reBase(const V3D &, const V3D &, const V3D &) noexcept
rebase to new basis vector
Definition V3D.cpp:196
void fromString(const std::string &str)
Sets the vector using a string.
Definition V3D.cpp:340
bool unitVector(const double tolerance=Kernel::Tolerance) const noexcept
Definition V3D.cpp:242
double hklError() const
Calculates the error in hkl.
Definition V3D.cpp:522
void readPrinted(std::istream &)
Read data from a stream in the format returned by printSelf ("[x,y,z]").
Definition V3D.cpp:356
void write(std::ostream &) const
Write out the point values.
Definition V3D.cpp:322
void getSpherical(double &R, double &theta, double &phi) const noexcept
Return the vector's position in spherical coordinates.
Definition V3D.cpp:116
double toMillerIndexes(double eps=1.e-3)
transform vector into form, used to describe directions in crystallogaphical coodinate system
Definition V3D.cpp:423
double cosAngle(const V3D &) const
cos(Angle) between this and another vector
Definition V3D.cpp:177
void round() noexcept
Round each component to the nearest integer.
Definition V3D.cpp:139
int masterDir(const double Tol=1e-3) const noexcept
Determine if there is a master direction.
Definition V3D.cpp:256
constexpr V3D() noexcept
Definition V3D.h:36
bool nullVector(const double tolerance=1e-3) const noexcept
Determine if the point is null.
Definition V3D.cpp:238
static unsigned short constexpr FLOAT64
constexpr double deg2rad
Defines units/enum for Crystal work.
Definition AngleUnits.h:20
MANTID_KERNEL_DLL std::ostream & operator<<(std::ostream &, CPUTimer &)
Convenience function to provide for easier debug printing.
Definition CPUTimer.cpp:86
MANTID_KERNEL_DLL std::istream & operator>>(std::istream &, Interpolation &)
Reads in parameter value.
constexpr double Tolerance
Standard tolerance value.
Definition Tolerance.h:12
MANTID_KERNEL_DLL V3D normalize(V3D v)
Normalizes a V3D.
Definition V3D.h:352