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