Gemmi C++ API
Loading...
Searching...
No Matches
math.hpp
Go to the documentation of this file.
1// Copyright 2018 Global Phasing Ltd.
2//
3// Math utilities. 3D linear algebra.
4
5#ifndef GEMMI_MATH_HPP_
6#define GEMMI_MATH_HPP_
7
8#include <cmath> // for fabs, cos, sqrt, round
9#include <algorithm> // for min
10#include <array>
11#include <stdexcept> // for out_of_range
12#include <type_traits> // for enable_if, is_integral
13
14namespace gemmi {
15
16constexpr double pi() { return 3.1415926535897932384626433832795029; }
17
18// The value used in converting between energy[eV] and wavelength[Angstrom].
19// $ units -d15 'h * c / eV / angstrom'
20constexpr double hc() { return 12398.4197386209; }
21
22// The Bohr radius (a0) in Angstroms.
23constexpr double bohrradius() { return 0.529177210903; }
24
25// for Mott-Bethe factor
26constexpr double mott_bethe_const() { return 1. / (2 * pi() * pi() * bohrradius()); }
27
28// Used in conversion of ADPs (atomic displacement parameters).
29constexpr double u_to_b() { return 8 * pi() * pi(); }
30
31constexpr double deg(double angle) { return 180.0 / pi() * angle; }
32constexpr double rad(double angle) { return pi() / 180.0 * angle; }
33
34constexpr float sq(float x) { return x * x; }
35constexpr double sq(double x) { return x * x; }
36
37inline double log_cosh(double x) {
38 // cosh(x) would overflow for x > 710.5, so we calculate:
39 // ln(cosh(x)) = ln(e^x + e^-x) - ln(2) = ln(e^x * (1 + e^-2x)) - ln(2)
40 x = std::abs(x);
41 return x - std::log(2) + std::log1p(std::exp(-2 * x));
42}
43
44inline int iround(double d) { return static_cast<int>(std::round(d)); }
45
46inline double angle_abs_diff(double a, double b, double full=360.0) {
47 double d = std::fabs(a - b);
48 if (d > full)
49 d -= std::floor(d / full) * full;
50 return std::min(d, full - d);
51}
52
53// similar to C++17 std::clamp()
54template<class T> constexpr T clamp(T v, T lo, T hi) {
55 return std::min(std::max(v, lo), hi);
56}
57
58template <typename Real>
59struct Vec3_ {
60 Real x, y, z;
61
62 Vec3_() : x(0), y(0), z(0) {}
63 Vec3_(Real x_, Real y_, Real z_) : x(x_), y(y_), z(z_) {}
64 explicit Vec3_(std::array<int, 3> h) : x(h[0]), y(h[1]), z(h[2]) {}
65
66 Real& at(int i) {
67 switch (i) {
68 case 0: return x;
69 case 1: return y;
70 case 2: return z;
71 default: throw std::out_of_range("Vec3 index must be 0, 1 or 2.");
72 }
73 }
74 Real at(int i) const { return const_cast<Vec3_*>(this)->at(i); }
75
76 Vec3_ operator-() const { return {-x, -y, -z}; }
77 Vec3_ operator-(const Vec3_& o) const { return {x-o.x, y-o.y, z-o.z}; }
78 Vec3_ operator+(const Vec3_& o) const { return {x+o.x, y+o.y, z+o.z}; }
79 Vec3_ operator*(Real d) const { return {x*d, y*d, z*d}; }
80 Vec3_ operator/(Real d) const { return *this * (1.0/d); }
81 Vec3_& operator-=(const Vec3_& o) { *this = *this - o; return *this; }
82 Vec3_& operator+=(const Vec3_& o) { *this = *this + o; return *this; }
83 Vec3_& operator*=(Real d) { *this = *this * d; return *this; }
84 Vec3_& operator/=(Real d) { return operator*=(1.0/d); }
85
86 Vec3_ negated() const { return {-x, -y, -z}; }
87 Real dot(const Vec3_& o) const { return x*o.x + y*o.y + z*o.z; }
88 Vec3_ cross(const Vec3_& o) const {
89 return {y*o.z - z*o.y, z*o.x - x*o.z, x*o.y - y*o.x};
90 }
91 Real length_sq() const { return x * x + y * y + z * z; }
92 Real length() const { return std::sqrt(length_sq()); }
93 Vec3_ changed_magnitude(Real m) const { return operator*(m / length()); }
94 Vec3_ normalized() const { return changed_magnitude(1.0); }
95 Real dist_sq(const Vec3_& o) const { return (*this - o).length_sq(); }
96 Real dist(const Vec3_& o) const { return std::sqrt(dist_sq(o)); }
97 Real cos_angle(const Vec3_& o) const {
98 return dot(o) / std::sqrt(length_sq() * o.length_sq());
99 }
100 Real angle(const Vec3_& o) const {
101 return std::acos(clamp(cos_angle(o), -1., 1.));
102 }
103 bool approx(const Vec3_& o, Real epsilon) const {
104 return std::fabs(x - o.x) <= epsilon &&
105 std::fabs(y - o.y) <= epsilon &&
106 std::fabs(z - o.z) <= epsilon;
107 }
108 bool has_nan() const {
109 return std::isnan(x) || std::isnan(y) || std::isnan(z);
110 }
111};
112
115
116inline Vec3 operator*(double d, const Vec3& v) { return v * d; }
117
120inline Vec3 rotate_about_axis(const Vec3& v, const Vec3& axis, double theta) {
121 double sin_theta = std::sin(theta);
122 double cos_theta = std::cos(theta);
123 return v * cos_theta + axis.cross(v) * sin_theta +
124 axis * (axis.dot(v) * (1 - cos_theta));
125}
126
127struct Mat33 {
128 double a[3][3] = { {1.,0.,0.}, {0.,1.,0.}, {0.,0.,1.} };
129
130 // make it accessible with ".a"
131 typedef double row_t[3];
132 const row_t& operator[](int i) const { return a[i]; }
133 row_t& operator[](int i) { return a[i]; }
134
135 Mat33() = default;
136 explicit Mat33(double d) : a{{d, d, d}, {d, d, d}, {d, d, d}} {}
137 Mat33(double a1, double a2, double a3, double b1, double b2, double b3,
138 double c1, double c2, double c3)
139 : a{{a1, a2, a3}, {b1, b2, b3}, {c1, c2, c3}} {}
140
141 static Mat33 from_columns(const Vec3& c1, const Vec3& c2, const Vec3& c3) {
142 return Mat33(c1.x, c2.x, c3.x, c1.y, c2.y, c3.y, c1.z, c2.z, c3.z);
143 }
144
145 Vec3 row_copy(int i) const {
146 if (i < 0 || i > 2)
147 throw std::out_of_range("Mat33 row index must be 0, 1 or 2.");
148 return Vec3(a[i][0], a[i][1], a[i][2]);
149 }
150
151 Vec3 column_copy(int i) const {
152 if (i < 0 || i > 2)
153 throw std::out_of_range("Mat33 column index must be 0, 1 or 2.");
154 return Vec3(a[0][i], a[1][i], a[2][i]);
155 }
156
157 Mat33 operator+(const Mat33& b) const {
158 return Mat33(a[0][0] + b[0][0], a[0][1] + b[0][1], a[0][2] + b[0][2],
159 a[1][0] + b[1][0], a[1][1] + b[1][1], a[1][2] + b[1][2],
160 a[2][0] + b[2][0], a[2][1] + b[2][1], a[2][2] + b[2][2]);
161 }
162 Mat33 operator-(const Mat33& b) const {
163 return Mat33(a[0][0] - b[0][0], a[0][1] - b[0][1], a[0][2] - b[0][2],
164 a[1][0] - b[1][0], a[1][1] - b[1][1], a[1][2] - b[1][2],
165 a[2][0] - b[2][0], a[2][1] - b[2][1], a[2][2] - b[2][2]);
166 }
167
168 Vec3 multiply(const Vec3& p) const {
169 return {a[0][0] * p.x + a[0][1] * p.y + a[0][2] * p.z,
170 a[1][0] * p.x + a[1][1] * p.y + a[1][2] * p.z,
171 a[2][0] * p.x + a[2][1] * p.y + a[2][2] * p.z};
172 }
173 Vec3 left_multiply(const Vec3& p) const {
174 return {a[0][0] * p.x + a[1][0] * p.y + a[2][0] * p.z,
175 a[0][1] * p.x + a[1][1] * p.y + a[2][1] * p.z,
176 a[0][2] * p.x + a[1][2] * p.y + a[2][2] * p.z};
177 }
178 // p has elements from the main diagonal of a 3x3 diagonal matrix
180 return Mat33(a[0][0] * p.x, a[0][1] * p.y, a[0][2] * p.z,
181 a[1][0] * p.x, a[1][1] * p.y, a[1][2] * p.z,
182 a[2][0] * p.x, a[2][1] * p.y, a[2][2] * p.z);
183 }
184 Mat33 multiply(const Mat33& b) const {
185 Mat33 r;
186 for (int i = 0; i != 3; ++i)
187 for (int j = 0; j != 3; ++j)
188 r[i][j] = a[i][0] * b[0][j] + a[i][1] * b[1][j] + a[i][2] * b[2][j];
189 return r;
190 }
191 Mat33 transpose() const {
192 return Mat33(a[0][0], a[1][0], a[2][0],
193 a[0][1], a[1][1], a[2][1],
194 a[0][2], a[1][2], a[2][2]);
195 }
196 double trace() const { return a[0][0] + a[1][1] + a[2][2]; }
197
198 bool approx(const Mat33& other, double epsilon) const {
199 for (int i = 0; i < 3; ++i)
200 for (int j = 0; j < 3; ++j)
201 if (std::fabs(a[i][j] - other.a[i][j]) > epsilon)
202 return false;
203 return true;
204 }
205 bool has_nan() const {
206 for (int i = 0; i < 3; ++i)
207 for (int j = 0; j < 3; ++j)
208 if (std::isnan(a[i][j]))
209 return true;
210 return false;
211 }
212
213 double determinant() const {
214 return a[0][0] * (a[1][1]*a[2][2] - a[2][1]*a[1][2]) +
215 a[0][1] * (a[1][2]*a[2][0] - a[2][2]*a[1][0]) +
216 a[0][2] * (a[1][0]*a[2][1] - a[2][0]*a[1][1]);
217 }
218 Mat33 inverse() const {
219 Mat33 inv;
220 double inv_det = 1.0 / determinant();
221 inv[0][0] = inv_det * (a[1][1] * a[2][2] - a[2][1] * a[1][2]);
222 inv[0][1] = inv_det * (a[0][2] * a[2][1] - a[0][1] * a[2][2]);
223 inv[0][2] = inv_det * (a[0][1] * a[1][2] - a[0][2] * a[1][1]);
224 inv[1][0] = inv_det * (a[1][2] * a[2][0] - a[1][0] * a[2][2]);
225 inv[1][1] = inv_det * (a[0][0] * a[2][2] - a[0][2] * a[2][0]);
226 inv[1][2] = inv_det * (a[1][0] * a[0][2] - a[0][0] * a[1][2]);
227 inv[2][0] = inv_det * (a[1][0] * a[2][1] - a[2][0] * a[1][1]);
228 inv[2][1] = inv_det * (a[2][0] * a[0][1] - a[0][0] * a[2][1]);
229 inv[2][2] = inv_det * (a[0][0] * a[1][1] - a[1][0] * a[0][1]);
230 return inv;
231 }
232 bool is_identity() const {
233 return a[0][0] == 1 && a[0][1] == 0 && a[0][2] == 0 &&
234 a[1][0] == 0 && a[1][1] == 1 && a[1][2] == 0 &&
235 a[2][0] == 0 && a[2][1] == 0 && a[2][2] == 1;
236 }
237
238 double column_dot(int i, int j) const {
239 return a[0][i] * a[0][j] + a[1][i] * a[1][j] + a[2][i] * a[2][j];
240 }
241
242 bool is_upper_triangular() const {
243 return a[1][0] == 0 && a[2][0] == 0 && a[2][1] == 0;
244 }
245};
246
248 double a11 = 0, a12 = 0, a13 = 0;
249 double a22 = 0, a23 = 0;
250 double a33 = 0;
253 if (m.is_upper_triangular()) {
254 a11 = m[0][0];
255 a12 = m[0][1];
256 a13 = m[0][2];
257 a22 = m[1][1];
258 a23 = m[1][2];
259 a33 = m[2][2];
260 } else {
261 a11 = a12 = a13 = a22 = a23 = a33 = NAN;
262 }
263 return *this;
264 }
265 Vec3 multiply(const Vec3& p) const {
266 return {a11 * p.x + a12 * p.y + a13 * p.z,
267 a22 * p.y + a23 * p.z,
268 a33 * p.z};
269 }
270};
271
272// Symmetric matrix 3x3. Used primarily for an ADP tensor.
273template<typename T> struct SMat33 {
274 T u11, u22, u33, u12, u13, u23;
275
276 // The PDB ANISOU record has the above order, but in a different context
277 // (such as metric tensor) the order of Voigt notation may be preferred.
278 std::array<T, 6> elements_pdb() const { return {{u11, u22, u33, u12, u13, u23}}; }
279 std::array<T, 6> elements_voigt() const { return {{u11, u22, u33, u23, u13, u12}}; }
280
281 Mat33 as_mat33() const {
282 return Mat33(u11, u12, u13, u12, u22, u23, u13, u23, u33);
283 }
284
285 // the arguments i and j must be in [0,2], i.e. 0, 1 or 2.
286 T& unchecked_ref(int i, int j) {
287 T* ptrs[9] = {&u11, &u12, &u13, &u12, &u22, &u23, &u13, &u23, &u33};
288 return *ptrs[3 * i + j];
289 }
290
291 T trace() const { return u11 + u22 + u33; }
292 bool nonzero() const { return trace() != 0; }
293
294 bool all_zero() const {
295 return u11 == 0 && u22 == 0 && u33 == 0 && u12 == 0 && u13 == 0 && u23 == 0;
296 }
297
298 void scale(T s) const {
299 u11 *= s; u22 *= s; u33 *= s; u12 *= s; u13 *= s; u23 *= s;
300 }
301
302 template<typename Real>
304 return SMat33<Real>{u11*s, u22*s, u33*s, u12*s, u13*s, u23*s};
305 }
306
307 // returns U + kI
309 return {u11+k, u22+k, u33+k, u12, u13, u23};
310 }
311
312 // returns squared norm r^T U r where U is this matrix and vector r is arg
313 template<typename VT>
314 auto r_u_r(const Vec3_<VT>& r) const -> decltype(r.x+u11) {
315 return r.x * r.x * u11 + r.y * r.y * u22 + r.z * r.z * u33 +
316 2 * (r.x * r.y * u12 + r.x * r.z * u13 + r.y * r.z * u23);
317 }
318 double r_u_r(const std::array<int,3>& h) const {
319 // it's faster to first convert ints to doubles (Vec3)
320 return r_u_r(Vec3(h));
321 }
322
323 Vec3 multiply(const Vec3& p) const {
324 return {u11 * p.x + u12 * p.y + u13 * p.z,
325 u12 * p.x + u22 * p.y + u23 * p.z,
326 u13 * p.x + u23 * p.y + u33 * p.z};
327 }
328
329 SMat33 operator-(const SMat33& o) const {
330 return {u11-o.u11, u22-o.u22, u33-o.u33, u12-o.u12, u13-o.u13, u23-o.u23};
331 }
332 SMat33 operator+(const SMat33& o) const {
333 return {u11+o.u11, u22+o.u22, u33+o.u33, u12+o.u12, u13+o.u13, u23+o.u23};
334 }
335
336 // return M U M^T
337 template<typename Real=double>
339 // slightly faster than m.multiply(as_mat33()).multiply(m.transpose());
340 auto elem = [&](int i, int j) {
341 return static_cast<Real>(
342 m[i][0] * (m[j][0] * u11 + m[j][1] * u12 + m[j][2] * u13) +
343 m[i][1] * (m[j][0] * u12 + m[j][1] * u22 + m[j][2] * u23) +
344 m[i][2] * (m[j][0] * u13 + m[j][1] * u23 + m[j][2] * u33));
345 };
346 return SMat33<Real>{elem(0, 0), elem(1, 1), elem(2, 2),
347 elem(0, 1), elem(0, 2), elem(1, 2)};
348 }
349
350 T determinant() const {
351 return u11 * (u22*u33 - u23*u23) +
352 u12 * (u23*u13 - u33*u12) +
353 u13 * (u12*u23 - u13*u22);
354 }
355
356 SMat33 inverse_(T det) const {
357 SMat33 inv;
358 T inv_det = 1.0f / det;
359 inv.u11 = inv_det * (u22 * u33 - u23 * u23);
360 inv.u22 = inv_det * (u11 * u33 - u13 * u13);
361 inv.u33 = inv_det * (u11 * u22 - u12 * u12);
362 inv.u12 = inv_det * (u13 * u23 - u12 * u33);
363 inv.u13 = inv_det * (u12 * u23 - u13 * u22);
364 inv.u23 = inv_det * (u12 * u13 - u11 * u23);
365 return inv;
366 }
367 SMat33 inverse() const {
368 return inverse_(determinant());
369 }
370
373 std::array<double, 3> calculate_eigenvalues() const {
374 double p1 = u12*u12 + u13*u13 + u23*u23;
375 if (p1 == 0)
376 return {{u11, u22, u33}};
377 double q = (1./3.) * trace();
378 SMat33<double> b{u11 - q, u22 - q, u33 - q, u12, u13, u23};
379 double p2 = sq(b.u11) + sq(b.u22) + sq(b.u33) + 2 * p1;
380 double p = std::sqrt((1./6.) * p2);
381 double r = b.determinant() / ((1./3.) * p2 * p);
382 double phi = 0;
383 if (r <= -1)
384 phi = (1./3.) * pi();
385 else if (r < 1)
386 phi = (1./3.) * std::acos(r);
387 double eig1 = q + 2 * p * std::cos(phi);
388 double eig3 = q + 2 * p * std::cos(phi + 2./3.*pi());
389 return {{eig1, 3 * q - eig1 - eig3, eig3}};
390 }
391};
392
393struct Transform {
396
398 Mat33 minv = mat.inverse();
399 return {minv, minv.multiply(vec).negated()};
400 }
401
402 Vec3 apply(const Vec3& x) const { return mat.multiply(x) + vec; }
403
404 Transform combine(const Transform& b) const {
405 return {mat.multiply(b.mat), vec + mat.multiply(b.vec)};
406 }
407
408 bool is_identity() const {
409 return mat.is_identity() && vec.x == 0. && vec.y == 0. && vec.z == 0.;
410 }
411 void set_identity() { mat = Mat33(); vec = Vec3(); }
412
413 bool has_nan() const {
414 return mat.has_nan() || vec.has_nan();
415 }
416
417 bool approx(const Transform& o, double epsilon) const {
418 return mat.approx(o.mat, epsilon) && vec.approx(o.vec, epsilon);
419 }
420};
421
422template<typename Pos>
423struct Box {
426 void extend(const Pos& p) {
427 if (p.x < minimum.x) minimum.x = p.x;
428 if (p.y < minimum.y) minimum.y = p.y;
429 if (p.z < minimum.z) minimum.z = p.z;
430 if (p.x > maximum.x) maximum.x = p.x;
431 if (p.y > maximum.y) maximum.y = p.y;
432 if (p.z > maximum.z) maximum.z = p.z;
433 }
434 Pos get_size() const { return maximum - minimum; }
435 void add_margins(const Pos& p) { minimum -= p; maximum += p; }
436 void add_margin(double m) { add_margins(Pos(m, m, m)); }
437};
438
439// internally used functions
440namespace impl {
441// MSVC is missing isnan(IntegralType), so we define is_nan as a replacement
442template<typename T>
443typename std::enable_if<std::is_integral<T>::value, bool>::type
444is_nan(T) { return false; }
445template<typename T>
446typename std::enable_if<std::is_floating_point<T>::value, bool>::type
447is_nan(T a) { return std::isnan(a); }
448
449template<typename T>
450typename std::enable_if<std::is_integral<T>::value, bool>::type
451is_same(T a, T b) { return a == b; }
452template<typename T>
453typename std::enable_if<std::is_floating_point<T>::value, bool>::type
454is_same(T a, T b) { return std::isnan(b) ? std::isnan(a) : a == b; }
455} // namespace impl
456
457} // namespace gemmi
458#endif
constexpr double deg(double angle)
Definition math.hpp:31
constexpr double bohrradius()
Definition math.hpp:23
int iround(double d)
Definition math.hpp:44
constexpr T clamp(T v, T lo, T hi)
Definition math.hpp:54
Vec3 rotate_about_axis(const Vec3 &v, const Vec3 &axis, double theta)
Rodrigues' rotation formula: rotate vector v about given axis of rotation (which must be a unit vecto...
Definition math.hpp:120
constexpr double hc()
Definition math.hpp:20
constexpr double mott_bethe_const()
Definition math.hpp:26
Vec3 operator*(double d, const Vec3 &v)
Definition math.hpp:116
Op & operator*=(Op &a, const Op &b)
Definition symmetry.hpp:193
double angle_abs_diff(double a, double b, double full=360.0)
Definition math.hpp:46
constexpr double rad(double angle)
Definition math.hpp:32
constexpr float sq(float x)
Definition math.hpp:34
Vec3_< double > Vec3
Definition math.hpp:113
constexpr double pi()
Definition math.hpp:16
double log_cosh(double x)
Definition math.hpp:37
constexpr double u_to_b()
Definition math.hpp:29
Pos get_size() const
Definition math.hpp:434
void extend(const Pos &p)
Definition math.hpp:426
void add_margins(const Pos &p)
Definition math.hpp:435
void add_margin(double m)
Definition math.hpp:436
bool is_upper_triangular() const
Definition math.hpp:242
double a[3][3]
Definition math.hpp:128
Vec3 column_copy(int i) const
Definition math.hpp:151
row_t & operator[](int i)
Definition math.hpp:133
double determinant() const
Definition math.hpp:213
Mat33 operator+(const Mat33 &b) const
Definition math.hpp:157
Mat33 multiply(const Mat33 &b) const
Definition math.hpp:184
Vec3 left_multiply(const Vec3 &p) const
Definition math.hpp:173
Mat33(double a1, double a2, double a3, double b1, double b2, double b3, double c1, double c2, double c3)
Definition math.hpp:137
Mat33()=default
bool approx(const Mat33 &other, double epsilon) const
Definition math.hpp:198
Mat33 multiply_by_diagonal(const Vec3 &p) const
Definition math.hpp:179
Mat33 transpose() const
Definition math.hpp:191
double trace() const
Definition math.hpp:196
bool has_nan() const
Definition math.hpp:205
double row_t[3]
Definition math.hpp:131
Mat33(double d)
Definition math.hpp:136
const row_t & operator[](int i) const
Definition math.hpp:132
bool is_identity() const
Definition math.hpp:232
Vec3 row_copy(int i) const
Definition math.hpp:145
Mat33 inverse() const
Definition math.hpp:218
static Mat33 from_columns(const Vec3 &c1, const Vec3 &c2, const Vec3 &c3)
Definition math.hpp:141
Vec3 multiply(const Vec3 &p) const
Definition math.hpp:168
double column_dot(int i, int j) const
Definition math.hpp:238
Mat33 operator-(const Mat33 &b) const
Definition math.hpp:162
SMat33 operator+(const SMat33 &o) const
Definition math.hpp:332
T trace() const
Definition math.hpp:291
void scale(T s) const
Definition math.hpp:298
T & unchecked_ref(int i, int j)
Definition math.hpp:286
SMat33 inverse() const
Definition math.hpp:367
Mat33 as_mat33() const
Definition math.hpp:281
std::array< T, 6 > elements_voigt() const
Definition math.hpp:279
SMat33 operator-(const SMat33 &o) const
Definition math.hpp:329
Vec3 multiply(const Vec3 &p) const
Definition math.hpp:323
SMat33< Real > scaled(Real s) const
Definition math.hpp:303
T determinant() const
Definition math.hpp:350
bool nonzero() const
Definition math.hpp:292
SMat33< Real > transformed_by(const Mat33 &m) const
Definition math.hpp:338
bool all_zero() const
Definition math.hpp:294
std::array< double, 3 > calculate_eigenvalues() const
Based on https://en.wikipedia.org/wiki/Eigenvalue_algorithm To calculate both eigenvalues and eigenve...
Definition math.hpp:373
double r_u_r(const std::array< int, 3 > &h) const
Definition math.hpp:318
auto r_u_r(const Vec3_< VT > &r) const -> decltype(r.x+u11)
Definition math.hpp:314
SMat33< T > added_kI(T k) const
Definition math.hpp:308
std::array< T, 6 > elements_pdb() const
Definition math.hpp:278
Vec3 apply(const Vec3 &x) const
Definition math.hpp:402
bool approx(const Transform &o, double epsilon) const
Definition math.hpp:417
Transform combine(const Transform &b) const
Definition math.hpp:404
void set_identity()
Definition math.hpp:411
bool is_identity() const
Definition math.hpp:408
bool has_nan() const
Definition math.hpp:413
Transform inverse() const
Definition math.hpp:397
UpperTriangularMat33 & operator=(const Mat33 &m)
Definition math.hpp:252
Vec3 multiply(const Vec3 &p) const
Definition math.hpp:265