blob: cfca897b7dcc36834814f0947e5aef4800d12c9d [file] [log] [blame]
James Kuszmaul590e33e2024-01-14 17:54:00 -08001#ifndef FRC971_MATH_FLATBUFFERS_MATRIX_H_
2#define FRC971_MATH_FLATBUFFERS_MATRIX_H_
3// This library provides utilities for converting between a frc971.fbs.Matrix
4// flatbuffer type and an Eigen::Matrix.
5// The interesting methods are ToEigen(), ToEigenOrDie(), and FromEigen().
6#include "glog/logging.h"
7#include "tl/expected.hpp"
8#include <Eigen/Core>
9
10#include "frc971/math/matrix_static.h"
11
12namespace frc971 {
13inline constexpr Eigen::StorageOptions ToEigenStorageOrder(
14 fbs::StorageOrder storage_order, int Rows, int Cols) {
15 // Eigen only implements one *Major version of the Matrix class for vectors.
16 // See https://eigen.tuxfamily.org/bz/show_bug.cgi?id=416
17 if (Rows == 1) {
18 return Eigen::RowMajor;
19 }
20 if (Cols == 1) {
21 return Eigen::ColMajor;
22 }
23 return storage_order == fbs::StorageOrder::ColMajor ? Eigen::ColMajor
24 : Eigen::RowMajor;
25}
26
27template <int Rows, int Cols, fbs::StorageOrder StorageOrder>
28struct EigenMatrix {
29 typedef Eigen::Matrix<double, Rows, Cols,
30 ToEigenStorageOrder(StorageOrder, Rows, Cols)>
31 type;
32};
33
34inline std::ostream &operator<<(std::ostream &os, fbs::MatrixField field) {
35 os << fbs::EnumNameMatrixField(field);
36 return os;
37}
38
39inline std::ostream &operator<<(std::ostream &os, fbs::FieldError error) {
40 os << fbs::EnumNameFieldError(error);
41 return os;
42}
43
44struct ConversionFailure {
45 fbs::MatrixField field;
46 fbs::FieldError error;
47 bool operator==(const ConversionFailure &) const = default;
48};
49
50inline std::ostream &operator<<(std::ostream &os, ConversionFailure failure) {
51 os << "(" << failure.field << ", " << failure.error << ")";
52 return os;
53}
54
55template <int Rows, int Cols,
56 fbs::StorageOrder StorageOrder = fbs::StorageOrder::ColMajor>
57tl::expected<typename EigenMatrix<Rows, Cols, StorageOrder>::type,
58 ConversionFailure>
59ToEigen(const fbs::Matrix &matrix) {
60 if (!matrix.has_rows()) {
61 return tl::unexpected(
62 ConversionFailure{fbs::MatrixField::kRows, fbs::FieldError::kMissing});
63 }
64 if (!matrix.has_cols()) {
65 return tl::unexpected(
66 ConversionFailure{fbs::MatrixField::kCols, fbs::FieldError::kMissing});
67 }
68 if (!matrix.has_data()) {
69 return tl::unexpected(
70 ConversionFailure{fbs::MatrixField::kData, fbs::FieldError::kMissing});
71 }
72 if (matrix.rows() != Rows) {
73 return tl::unexpected(ConversionFailure{
74 fbs::MatrixField::kRows, fbs::FieldError::kInconsistentWithTemplate});
75 }
76 if (matrix.cols() != Cols) {
77 return tl::unexpected(ConversionFailure{
78 fbs::MatrixField::kCols, fbs::FieldError::kInconsistentWithTemplate});
79 }
80 if (matrix.storage_order() != StorageOrder) {
81 return tl::unexpected(
82 ConversionFailure{fbs::MatrixField::kStorageOrder,
83 fbs::FieldError::kInconsistentWithTemplate});
84 }
85 if (matrix.data()->size() != Rows * Cols) {
86 return tl::unexpected(ConversionFailure{
87 fbs::MatrixField::kData, fbs::FieldError::kInconsistentWithTemplate});
88 }
89 return typename EigenMatrix<Rows, Cols, StorageOrder>::type(
90 matrix.data()->data());
91}
92
93template <int Rows, int Cols,
94 fbs::StorageOrder StorageOrder = fbs::StorageOrder::ColMajor>
95typename EigenMatrix<Rows, Cols, StorageOrder>::type ToEigenOrDie(
96 const fbs::Matrix &matrix) {
97 auto result = ToEigen<Rows, Cols, StorageOrder>(matrix);
98 if (!result.has_value()) {
99 LOG(FATAL) << "Failed to convert to matrix with error " << result.error()
100 << ".";
101 }
102 return result.value();
103}
104
105template <int Rows, int Cols,
106 fbs::StorageOrder StorageOrder = fbs::StorageOrder::ColMajor>
107bool FromEigen(
108 const typename EigenMatrix<Rows, Cols, StorageOrder>::type &matrix,
109 fbs::MatrixStatic *flatbuffer) {
110 constexpr size_t kSize = Rows * Cols;
111 auto data = flatbuffer->add_data();
112 if (!data->reserve(kSize)) {
113 return false;
114 }
115 // TODO(james): Use From*() methods once they get upstreamed...
116 data->resize(kSize);
117 std::copy(matrix.data(), matrix.data() + kSize, data->data());
118 flatbuffer->set_rows(Rows);
119 flatbuffer->set_cols(Cols);
120 flatbuffer->set_storage_order(StorageOrder);
121 return true;
122}
123
James Kuszmaul27397652024-03-15 21:44:52 -0700124template <int Rows, int Cols,
125 fbs::StorageOrder StorageOrder = fbs::StorageOrder::ColMajor>
126flatbuffers::Offset<fbs::Matrix> FromEigen(
127 const typename EigenMatrix<Rows, Cols, StorageOrder>::type &matrix,
128 flatbuffers::FlatBufferBuilder *fbb) {
129 constexpr size_t kSize = Rows * Cols;
130 flatbuffers::Offset<flatbuffers::Vector<double>> data_offset =
131 fbb->CreateVector(matrix.data(), kSize);
132 fbs::Matrix::Builder builder(*fbb);
133 builder.add_rows(Rows);
134 builder.add_cols(Cols);
135 builder.add_storage_order(StorageOrder);
136 builder.add_data(data_offset);
137 return builder.Finish();
138}
139
James Kuszmaul590e33e2024-01-14 17:54:00 -0800140template <typename T>
141bool FromEigen(const T &matrix, fbs::MatrixStatic *flatbuffer) {
142 return FromEigen<T::RowsAtCompileTime, T::ColsAtCompileTime,
143 (T::IsRowMajor ? fbs::StorageOrder::RowMajor
144 : fbs::StorageOrder::ColMajor)>(matrix,
145 flatbuffer);
146}
147
148} // namespace frc971
149#endif // FRC971_MATH_FLATBUFFERS_MATRIX_H_