ReUseX  0.0.5
3D Point Cloud Processing for Building Reuse
Loading...
Searching...
No Matches
transform_utils.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2025 Povl Filip Sonne-Frederiksen
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#pragma once
6
7#include <Eigen/Core>
8#include <Eigen/Geometry>
9
10#include <algorithm>
11#include <array>
12#include <cmath>
13
14namespace reusex::geometry {
15
17inline Eigen::Affine3d to_affine(const std::array<double, 16> &m) {
18 Eigen::Matrix4d mat;
19 for (int r = 0; r < 4; ++r)
20 for (int c = 0; c < 4; ++c)
21 mat(r, c) = m[r * 4 + c];
22 Eigen::Affine3d aff;
23 aff.matrix() = mat;
24 return aff;
25}
26
28inline std::array<double, 16> to_array16(const Eigen::Affine3d &aff) {
29 std::array<double, 16> m{};
30 const Eigen::Matrix4d mat = aff.matrix();
31 for (int r = 0; r < 4; ++r)
32 for (int c = 0; c < 4; ++c)
33 m[r * 4 + c] = mat(r, c);
34 return m;
35}
36
39inline std::array<double, 16> to_array16(const Eigen::Affine3f &aff) {
40 std::array<double, 16> m{};
41 const Eigen::Matrix4f mat = aff.matrix();
42 for (int r = 0; r < 4; ++r)
43 for (int c = 0; c < 4; ++c)
44 m[r * 4 + c] = static_cast<double>(mat(r, c));
45 return m;
46}
47
54namespace se3 {
55
56using Vector6d = Eigen::Matrix<double, 6, 1>;
57
59inline Eigen::Matrix3d hat3(const Eigen::Vector3d &w) {
60 Eigen::Matrix3d s;
61 s << 0.0, -w.z(), w.y(), w.z(), 0.0, -w.x(), -w.y(), w.x(), 0.0;
62 return s;
63}
64
66inline Eigen::Matrix4d exp(const Vector6d &xi) {
67 const Eigen::Vector3d w = xi.head<3>();
68 const Eigen::Vector3d v = xi.tail<3>();
69 const double theta = w.norm();
70 const Eigen::Matrix3d W = hat3(w);
71 Eigen::Matrix3d R;
72 Eigen::Matrix3d V;
73 if (theta < 1e-12) {
74 R = Eigen::Matrix3d::Identity() + W;
75 V = Eigen::Matrix3d::Identity() + 0.5 * W;
76 } else {
77 const double t2 = theta * theta;
78 const double s = std::sin(theta);
79 const double c = std::cos(theta);
80 R = Eigen::Matrix3d::Identity() + (s / theta) * W +
81 ((1.0 - c) / t2) * (W * W);
82 V = Eigen::Matrix3d::Identity() + ((1.0 - c) / t2) * W +
83 ((theta - s) / (t2 * theta)) * (W * W);
84 }
85 Eigen::Matrix4d T = Eigen::Matrix4d::Identity();
86 T.block<3, 3>(0, 0) = R;
87 T.block<3, 1>(0, 3) = V * v;
88 return T;
89}
90
92inline Vector6d log(const Eigen::Matrix4d &T) {
93 const Eigen::Matrix3d R = T.block<3, 3>(0, 0);
94 const Eigen::Vector3d t = T.block<3, 1>(0, 3);
95 const double cos_theta = std::clamp((R.trace() - 1.0) * 0.5, -1.0, 1.0);
96 const double theta = std::acos(cos_theta);
97 Eigen::Vector3d w;
98 if (theta < 1e-12) {
99 w = 0.5 * Eigen::Vector3d(R(2, 1) - R(1, 2), R(0, 2) - R(2, 0),
100 R(1, 0) - R(0, 1));
101 } else {
102 const double s = std::sin(theta);
103 w = (theta / (2.0 * s)) * Eigen::Vector3d(R(2, 1) - R(1, 2),
104 R(0, 2) - R(2, 0),
105 R(1, 0) - R(0, 1));
106 }
107 const Eigen::Matrix3d W = hat3(w);
108 Eigen::Matrix3d Vinv;
109 if (theta < 1e-12) {
110 Vinv = Eigen::Matrix3d::Identity() - 0.5 * W;
111 } else {
112 const double a =
113 (1.0 / (theta * theta)) *
114 (1.0 - (theta * std::sin(theta)) / (2.0 * (1.0 - std::cos(theta))));
115 Vinv = Eigen::Matrix3d::Identity() - 0.5 * W + a * (W * W);
116 }
117 Vector6d xi;
118 xi.head<3>() = w;
119 xi.tail<3>() = Vinv * t;
120 return xi;
121}
122
123} // namespace se3
124} // namespace reusex::geometry
Minimal se(3) exp/log used by the joint pose optimizer.
Eigen::Matrix< double, 6, 1 > Vector6d
Eigen::Matrix3d hat3(const Eigen::Vector3d &w)
Skew-symmetric matrix [w]_x such that [w]_x v == w x v.
Eigen::Matrix4d exp(const Vector6d &xi)
Exponential map se(3) -> SE(3). xi = [omega; v].
Vector6d log(const Eigen::Matrix4d &T)
Logarithm map SE(3) -> se(3). Returns xi = [omega; v].
Eigen::Affine3d to_affine(const std::array< double, 16 > &m)
Build a 4x4 Eigen affine (double) from a row-major double[16] array.
std::array< double, 16 > to_array16(const Eigen::Affine3d &aff)
Flatten a 4x4 Eigen affine (double) into a row-major double[16] array.