Message ID | 20240610141941.2947785-2-paul.elder@ideasonboard.com |
---|---|
State | Superseded |
Headers | show |
Series |
|
Related | show |
Quoting Paul Elder (2024-06-10 15:19:38) > Add a vector class to libipa. The original purpose of this is to replace > the floating-point Point class that Raspberry Pi used in their Pwl, as > that implementation of Point seemed more akin to a Vector than a Point. > > This is added to libipa instead of to geometry.h to avoid public API > issues, plus this is not expected to be needed by applications. > > Signed-off-by: Paul Elder <paul.elder@ideasonboard.com> > Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com> > > --- > Changes in v7: > - fix documentation > - fix license and copyright > - remove toString() > > No change in v6 > > New in v5 > --- > src/ipa/libipa/vector.cpp | 145 +++++++++++++++++++++++++++ > src/ipa/libipa/vector.h | 199 ++++++++++++++++++++++++++++++++++++++ > 2 files changed, 344 insertions(+) > create mode 100644 src/ipa/libipa/vector.cpp > create mode 100644 src/ipa/libipa/vector.h > > diff --git a/src/ipa/libipa/vector.cpp b/src/ipa/libipa/vector.cpp > new file mode 100644 > index 000000000000..e7d0be95812c > --- /dev/null > +++ b/src/ipa/libipa/vector.cpp > @@ -0,0 +1,145 @@ > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > +/* > + * Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com> > + * > + * Vector and related operations > + */ > + > +#include "vector.h" > + > +#include <libcamera/base/log.h> > + > +/** > + * \file vector.h > + * \brief Vector class > + */ > + > +namespace libcamera { > + > +LOG_DEFINE_CATEGORY(Vector) > + > +namespace ipa { > + > +/** > + * \class Vector > + * \brief Vector class > + * \tparam T Type of numerical values to be stored in the vector > + * \tparam D Number of dimension of the vector (= number of elements) > + */ > + > +/** > + * \fn Vector::Vector() > + * \brief Construct a zero vector > + */ > + > +/** > + * \fn Vector::Vector(const std::array<T, D> &data) > + * \brief Construct vector from supplied data > + * \param data Data from which to construct a vector > + * > + * The size of \a data must be equal to the dimension size D of the vector. > + */ > + > +/** > + * \fn Vector::readYaml > + * \brief Populate the vector with yaml data > + * \param yaml Yaml data to populate the vector with > + * > + * Any existing data in the vector will be overwritten. The size of the data > + * read from \a yaml must be equal to the dimension size D of the vector. > + * > + * The yaml data is expected to be a list with elements of type T. > + * > + * \return 0 on success, negative error code otherwise > + */ > + > +/** > + * \fn T Vector::operator[](size_t i) const > + * \brief Index to an element in the vector > + * \param i Index of element to retrieve > + * \return Element at index \a i from the vector > + */ > + > +/** > + * \fn T &Vector::operator[](size_t i) > + * \copydoc Vector::operator[](size_t i) const > + */ > + > +/** > + * \fn Vector::x() > + * \brief Convenience function to access the first element of the vector > + */ > + > +/** > + * \fn Vector::y() > + * \brief Convenience function to access the second element of the vector > + */ > + > +/** > + * \fn Vector::operator-() const > + * \brief Negate a Vector by negating both all of its coordinates > + * \return The negated vector > + */ > + > +/** > + * \fn Vector::operator-(Vector const &other) const > + * \brief Subtract one vector from another > + * \param[in] other The other vector > + * \return The difference of \a other from this vector > + */ > + > +/** > + * \fn Vector::operator+() > + * \brief Add two vectors together > + * \param[in] other The other vector > + * \return The sum of the two vectors > + */ > + > +/** > + * \fn Vector::operator*(const Vector<T, D> &other) const > + * \brief Compute the dot product > + * \param[in] other The other vector > + * \return The dot product of the two vectors > + */ > + > +/** > + * \fn Vector::operator*(T factor) const > + * \brief Multiply the vector by a scalar > + * \param[in] factor The factor > + * \return The vector multiplied by \a factor > + */ > + > +/** > + * \fn Vector::operator/() > + * \brief Divide the vector by a scalar > + * \param[in] factor The factor > + * \return The vector divided by \a factor > + */ > + > +/** > + * \fn Vector::len2() > + * \brief Get the squared length of the vector > + * \return The squared length of the vector > + */ > + > +/** > + * \fn Vector::len() > + * \brief Get the length of the vector > + * \return The length of the vector > + */ > + > +/** > + * \fn bool operator==(const Vector<T, D> &lhs, const Vector<T, D> &rhs) > + * \brief Compare vectors for equality > + * \return True if the two vectors are equal, false otherwise > + */ > + > +/** > + * \fn bool operator!=(const Vector<T, D> &lhs, const Vector<T, D> &rhs) > + * \brief Compare vectors for inequality > + * \return True if the two vectors are not equal, false otherwise > + */ > + > +} /* namespace ipa */ > + > +} /* namespace libcamera */ > diff --git a/src/ipa/libipa/vector.h b/src/ipa/libipa/vector.h > new file mode 100644 > index 000000000000..b5a6a0e021e0 > --- /dev/null > +++ b/src/ipa/libipa/vector.h > @@ -0,0 +1,199 @@ > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > +/* > + * Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com> > + * > + * Vector and related operations > + */ > +#pragma once > + > +#include <algorithm> > +#include <array> > +#include <cmath> > +#include <sstream> > + > +#include <libcamera/base/log.h> > +#include <libcamera/base/span.h> > + > +#include "libcamera/internal/yaml_parser.h" > + > +namespace libcamera { > + > +LOG_DECLARE_CATEGORY(Vector) > + > +namespace ipa { > + > +#ifndef __DOXYGEN__ > +template<typename T, unsigned int D, > + std::enable_if_t<std::is_arithmetic_v<T> && D >= 2> * = nullptr> > +#else > +template<typename T, unsigned int D> > +#endif /* __DOXYGEN__ */ > +class Vector > +{ > +public: > + Vector() = default; > + > + Vector(const std::array<T, D> &data) > + { > + ASSERT(data.size() == D); > + > + for (unsigned int i = 0; i < D; i++) > + data_[i] = data[i]; > + } > + > + ~Vector() = default; > + > + int readYaml(const libcamera::YamlObject &yaml) > + { > + if (yaml.size() != D) { > + LOG(Vector, Error) > + << "Wrong number of values in vector: expected " > + << D << ", got " << yaml.size(); > + return -EINVAL; > + } > + > + unsigned int i = 0; > + for (const auto &x : yaml.asList()) { > + auto value = x.get<T>(); > + if (!value) { > + LOG(Vector, Error) << "Failed to read vector value"; > + return -EINVAL; > + } > + > + data_[i++] = *value; > + } > + > + return 0; > + } > + > + const T operator[](size_t i) const > + { > + ASSERT(0 < i && i < data_.size()); Why 0 < i ? What's wrong with data_[0] ? > + return data_[i]; > + } > + > + T &operator[](size_t i) > + { > + ASSERT(0 < i && i < data_.size()); Same... With those checked/fixed if needed. Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> > + return data_[i]; > + } > + > + const T x() const > + { > + return data_[0]; > + } > + > + const T y() const > + { > + return data_[1]; > + } > + > + constexpr Vector<T, D> operator-() const > + { > + Vector<T, D> ret; > + for (unsigned int i = 0; i < D; i++) > + ret[i] = -data_[i]; > + return ret; > + } > + > + constexpr Vector<T, D> operator-(const Vector<T, D> &other) const > + { > + Vector<T, D> ret; > + for (unsigned int i = 0; i < D; i++) > + ret[i] = data_[i] - other[i]; > + return ret; > + } > + > + constexpr Vector<T, D> operator+(const Vector<T, D> &other) const > + { > + Vector<T, D> ret; > + for (unsigned int i = 0; i < D; i++) > + ret[i] = data_[i] + other[i]; > + return ret; > + } > + > + constexpr T operator*(const Vector<T, D> &other) const > + { > + T ret = 0; > + for (unsigned int i = 0; i < D; i++) > + ret += data_[i] * other[i]; > + return ret; > + } > + > + constexpr Vector<T, D> operator*(T factor) const > + { > + Vector<T, D> ret; > + for (unsigned int i = 0; i < D; i++) > + ret[i] = data_[i] * factor; > + return ret; > + } > + > + constexpr Vector<T, D> operator/(T factor) const > + { > + Vector<T, D> ret; > + for (unsigned int i = 0; i < D; i++) > + ret[i] = data_[i] / factor; > + return ret; > + } > + > + constexpr T len2() const > + { > + T ret = 0; > + for (unsigned int i = 0; i < D; i++) > + ret += data_[i] * data_[i]; > + return ret; > + } > + > + constexpr double len() const > + { > + return std::sqrt(len2()); > + } > + > +private: > + std::array<T, D> data_; > +}; > + > +#ifndef __DOXYGEN__ > +template<typename T, unsigned int D, > + std::enable_if_t<std::is_arithmetic_v<T>> * = nullptr> > +#endif /* __DOXYGEN__ */ > +bool operator==(const Vector<T, D> &lhs, const Vector<T, D> &rhs) > +{ > + for (unsigned int i = 0; i < D; i++) > + if (lhs[i] != rhs[i]) > + return false; > + > + return true; > +} > + > +#ifndef __DOXYGEN__ > +template<typename T, unsigned int D, > + std::enable_if_t<std::is_arithmetic_v<T>> * = nullptr> > +#endif /* __DOXYGEN__ */ > +bool operator!=(const Vector<T, D> &lhs, const Vector<T, D> &rhs) > +{ > + for (unsigned int i = 0; i < D; i++) > + if (lhs[i] != rhs[i]) > + return true; > + > + return false; > +} > + > +} /* namespace ipa */ > + > +#ifndef __DOXYGEN__ > +template<typename T, unsigned int D> > +std::ostream &operator<<(std::ostream &out, const ipa::Vector<T, D> &v) > +{ > + out << "Vector { "; > + for (unsigned int i = 0; i < D; i++) { > + out << v[i]; > + out << ((i + 1 < D) ? ", " : " "); > + } > + out << " }"; > + > + return out; > +} > +#endif /* __DOXYGEN__ */ > + > +} /* namespace libcamera */ > -- > 2.39.2 >
Hi Paul, Thank you for the patch. On Mon, Jun 10, 2024 at 04:10:31PM +0100, Kieran Bingham wrote: > Quoting Paul Elder (2024-06-10 15:19:38) > > Add a vector class to libipa. The original purpose of this is to replace > > the floating-point Point class that Raspberry Pi used in their Pwl, as > > that implementation of Point seemed more akin to a Vector than a Point. > > > > This is added to libipa instead of to geometry.h to avoid public API > > issues, plus this is not expected to be needed by applications. > > > > Signed-off-by: Paul Elder <paul.elder@ideasonboard.com> > > Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com> > > > > --- > > Changes in v7: > > - fix documentation > > - fix license and copyright > > - remove toString() > > > > No change in v6 > > > > New in v5 > > --- > > src/ipa/libipa/vector.cpp | 145 +++++++++++++++++++++++++++ > > src/ipa/libipa/vector.h | 199 ++++++++++++++++++++++++++++++++++++++ > > 2 files changed, 344 insertions(+) > > create mode 100644 src/ipa/libipa/vector.cpp > > create mode 100644 src/ipa/libipa/vector.h > > > > diff --git a/src/ipa/libipa/vector.cpp b/src/ipa/libipa/vector.cpp > > new file mode 100644 > > index 000000000000..e7d0be95812c > > --- /dev/null > > +++ b/src/ipa/libipa/vector.cpp > > @@ -0,0 +1,145 @@ > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > > +/* > > + * Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com> > > + * > > + * Vector and related operations > > + */ > > + > > +#include "vector.h" > > + > > +#include <libcamera/base/log.h> > > + > > +/** > > + * \file vector.h > > + * \brief Vector class > > + */ > > + > > +namespace libcamera { > > + > > +LOG_DEFINE_CATEGORY(Vector) > > + > > +namespace ipa { > > + > > +/** > > + * \class Vector > > + * \brief Vector class > > + * \tparam T Type of numerical values to be stored in the vector > > + * \tparam D Number of dimension of the vector (= number of elements) > > + */ > > + > > +/** > > + * \fn Vector::Vector() > > + * \brief Construct a zero vector > > + */ > > + > > +/** > > + * \fn Vector::Vector(const std::array<T, D> &data) > > + * \brief Construct vector from supplied data > > + * \param data Data from which to construct a vector > > + * > > + * The size of \a data must be equal to the dimension size D of the vector. > > + */ > > + > > +/** > > + * \fn Vector::readYaml > > + * \brief Populate the vector with yaml data > > + * \param yaml Yaml data to populate the vector with > > + * > > + * Any existing data in the vector will be overwritten. The size of the data > > + * read from \a yaml must be equal to the dimension size D of the vector. > > + * > > + * The yaml data is expected to be a list with elements of type T. > > + * > > + * \return 0 on success, negative error code otherwise > > + */ > > + > > +/** > > + * \fn T Vector::operator[](size_t i) const > > + * \brief Index to an element in the vector > > + * \param i Index of element to retrieve > > + * \return Element at index \a i from the vector > > + */ > > + > > +/** > > + * \fn T &Vector::operator[](size_t i) > > + * \copydoc Vector::operator[](size_t i) const > > + */ > > + > > +/** > > + * \fn Vector::x() > > + * \brief Convenience function to access the first element of the vector > > + */ > > + > > +/** > > + * \fn Vector::y() > > + * \brief Convenience function to access the second element of the vector > > + */ > > + > > +/** > > + * \fn Vector::operator-() const > > + * \brief Negate a Vector by negating both all of its coordinates > > + * \return The negated vector > > + */ > > + > > +/** > > + * \fn Vector::operator-(Vector const &other) const > > + * \brief Subtract one vector from another > > + * \param[in] other The other vector > > + * \return The difference of \a other from this vector > > + */ > > + > > +/** > > + * \fn Vector::operator+() > > + * \brief Add two vectors together > > + * \param[in] other The other vector > > + * \return The sum of the two vectors > > + */ > > + > > +/** > > + * \fn Vector::operator*(const Vector<T, D> &other) const > > + * \brief Compute the dot product > > + * \param[in] other The other vector > > + * \return The dot product of the two vectors > > + */ > > + > > +/** > > + * \fn Vector::operator*(T factor) const > > + * \brief Multiply the vector by a scalar > > + * \param[in] factor The factor > > + * \return The vector multiplied by \a factor > > + */ > > + > > +/** > > + * \fn Vector::operator/() > > + * \brief Divide the vector by a scalar > > + * \param[in] factor The factor > > + * \return The vector divided by \a factor > > + */ > > + > > +/** > > + * \fn Vector::len2() > > + * \brief Get the squared length of the vector > > + * \return The squared length of the vector > > + */ > > + > > +/** > > + * \fn Vector::len() > > + * \brief Get the length of the vector > > + * \return The length of the vector > > + */ > > + > > +/** > > + * \fn bool operator==(const Vector<T, D> &lhs, const Vector<T, D> &rhs) > > + * \brief Compare vectors for equality > > + * \return True if the two vectors are equal, false otherwise > > + */ > > + > > +/** > > + * \fn bool operator!=(const Vector<T, D> &lhs, const Vector<T, D> &rhs) > > + * \brief Compare vectors for inequality > > + * \return True if the two vectors are not equal, false otherwise > > + */ > > + > > +} /* namespace ipa */ > > + > > +} /* namespace libcamera */ > > diff --git a/src/ipa/libipa/vector.h b/src/ipa/libipa/vector.h > > new file mode 100644 > > index 000000000000..b5a6a0e021e0 > > --- /dev/null > > +++ b/src/ipa/libipa/vector.h > > @@ -0,0 +1,199 @@ > > +/* SPDX-License-Identifier: LGPL-2.1-or-later */ > > +/* > > + * Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com> > > + * > > + * Vector and related operations > > + */ > > +#pragma once > > + > > +#include <algorithm> > > +#include <array> > > +#include <cmath> > > +#include <sstream> > > + > > +#include <libcamera/base/log.h> > > +#include <libcamera/base/span.h> > > + > > +#include "libcamera/internal/yaml_parser.h" > > + > > +namespace libcamera { > > + > > +LOG_DECLARE_CATEGORY(Vector) > > + > > +namespace ipa { > > + > > +#ifndef __DOXYGEN__ > > +template<typename T, unsigned int D, > > + std::enable_if_t<std::is_arithmetic_v<T> && D >= 2> * = nullptr> Why 2 ? Is that because of the x() and y() functions ? You can handle that in those functions directly: template<bool Dependent = false, typename = std::enable_if_t<Dependent || D == 2>> T &x() { return data_[0]; } template<bool Dependent = false, typename = std::enable_if_t<Dependent || D == 2>> T &y() { return data_[1]; } and drop the constraint here. > > +#else > > +template<typename T, unsigned int D> > > +#endif /* __DOXYGEN__ */ > > +class Vector What would you think about naming the template parameters Scalar and Rows instead of T and D ? That would be more explicit. > > +{ > > +public: > > + Vector() = default; > > + > > + Vector(const std::array<T, D> &data) Please make the constructors constexpr if possible. > > + { > > + ASSERT(data.size() == D); That's not needed, by definition std::array<T, D>::size() == D. > > + > > + for (unsigned int i = 0; i < D; i++) > > + data_[i] = data[i]; > > + } > > + > > + ~Vector() = default; You can drop this, it's literally the default :-) > > + > > + int readYaml(const libcamera::YamlObject &yaml) > > + { > > + if (yaml.size() != D) { > > + LOG(Vector, Error) > > + << "Wrong number of values in vector: expected " > > + << D << ", got " << yaml.size(); > > + return -EINVAL; > > + } > > + > > + unsigned int i = 0; > > + for (const auto &x : yaml.asList()) { > > + auto value = x.get<T>(); > > + if (!value) { > > + LOG(Vector, Error) << "Failed to read vector value"; > > + return -EINVAL; > > + } > > + > > + data_[i++] = *value; > > + } > > + > > + return 0; > > + } This is the only part that really bothers me. This tight coupling between the Vector class, which is a very generic math object, and YamlObject, isn't nice. One possible option would be to implement this as a specialization of YamlObject::get(), in vector.cpp. I haven't tested it though. > > + > > + const T operator[](size_t i) const Why do you return a copy instead of a const reference ? > > + { > > + ASSERT(0 < i && i < data_.size()); > > Why 0 < i ? What's wrong with data_[0] ? On a side note, it should be written 'i > 0', that's the order we use everywhere. I understand that the above syntex is meant to check 0 < i < data_.size() and I'm fine with that as a mathematical notation, but code-wise, '0 < i' takes more mental energy to read. > > + return data_[i]; > > + } > > + > > + T &operator[](size_t i) > > + { > > + ASSERT(0 < i && i < data_.size()); > > Same... > > With those checked/fixed if needed. > > Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> > > > + return data_[i]; > > + } > > + > > + const T x() const > > + { > > + return data_[0]; > > + } > > + > > + const T y() const > > + { > > + return data_[1]; > > + } > > + > > + constexpr Vector<T, D> operator-() const > > + { > > + Vector<T, D> ret; > > + for (unsigned int i = 0; i < D; i++) > > + ret[i] = -data_[i]; > > + return ret; > > + } > > + > > + constexpr Vector<T, D> operator-(const Vector<T, D> &other) const > > + { > > + Vector<T, D> ret; > > + for (unsigned int i = 0; i < D; i++) > > + ret[i] = data_[i] - other[i]; > > + return ret; > > + } > > + > > + constexpr Vector<T, D> operator+(const Vector<T, D> &other) const > > + { > > + Vector<T, D> ret; > > + for (unsigned int i = 0; i < D; i++) > > + ret[i] = data_[i] + other[i]; > > + return ret; > > + } > > + > > + constexpr T operator*(const Vector<T, D> &other) const > > + { > > + T ret = 0; > > + for (unsigned int i = 0; i < D; i++) > > + ret += data_[i] * other[i]; > > + return ret; > > + } > > + > > + constexpr Vector<T, D> operator*(T factor) const > > + { > > + Vector<T, D> ret; > > + for (unsigned int i = 0; i < D; i++) > > + ret[i] = data_[i] * factor; > > + return ret; > > + } > > + > > + constexpr Vector<T, D> operator/(T factor) const > > + { > > + Vector<T, D> ret; > > + for (unsigned int i = 0; i < D; i++) > > + ret[i] = data_[i] / factor; > > + return ret; > > + } > > + > > + constexpr T len2() const length2() ? And length() below. > > + { > > + T ret = 0; > > + for (unsigned int i = 0; i < D; i++) > > + ret += data_[i] * data_[i]; If T is an integer short integer type (8 or 16 bits) there's a high chance this will overflow silently. Is that an issue ? Should the function return a double ? > > + return ret; > > + } > > + > > + constexpr double len() const > > + { > > + return std::sqrt(len2()); > > + } > > + > > +private: > > + std::array<T, D> data_; > > +}; > > + > > +#ifndef __DOXYGEN__ > > +template<typename T, unsigned int D, > > + std::enable_if_t<std::is_arithmetic_v<T>> * = nullptr> Is this needed, given that the Vector class checks that already ? Same below. I wrote a long time ago an RGB class which morphed into a Vector, before I dropped it because I thought I was reinventing the wheel. As it seems we haven't found a better wheel, I'll probably post a patch on top of this as it extends your implementation with quite a few functions. > > +#endif /* __DOXYGEN__ */ > > +bool operator==(const Vector<T, D> &lhs, const Vector<T, D> &rhs) > > +{ > > + for (unsigned int i = 0; i < D; i++) > > + if (lhs[i] != rhs[i]) > > + return false; > > + > > + return true; > > +} > > + > > +#ifndef __DOXYGEN__ > > +template<typename T, unsigned int D, > > + std::enable_if_t<std::is_arithmetic_v<T>> * = nullptr> > > +#endif /* __DOXYGEN__ */ > > +bool operator!=(const Vector<T, D> &lhs, const Vector<T, D> &rhs) > > +{ > > + for (unsigned int i = 0; i < D; i++) > > + if (lhs[i] != rhs[i]) > > + return true; > > + > > + return false; > > +} > > + > > +} /* namespace ipa */ > > + > > +#ifndef __DOXYGEN__ > > +template<typename T, unsigned int D> > > +std::ostream &operator<<(std::ostream &out, const ipa::Vector<T, D> &v) > > +{ > > + out << "Vector { "; > > + for (unsigned int i = 0; i < D; i++) { > > + out << v[i]; > > + out << ((i + 1 < D) ? ", " : " "); > > + } > > + out << " }"; > > + > > + return out; > > +} > > +#endif /* __DOXYGEN__ */ > > + > > +} /* namespace libcamera */
diff --git a/src/ipa/libipa/vector.cpp b/src/ipa/libipa/vector.cpp new file mode 100644 index 000000000000..e7d0be95812c --- /dev/null +++ b/src/ipa/libipa/vector.cpp @@ -0,0 +1,145 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com> + * + * Vector and related operations + */ + +#include "vector.h" + +#include <libcamera/base/log.h> + +/** + * \file vector.h + * \brief Vector class + */ + +namespace libcamera { + +LOG_DEFINE_CATEGORY(Vector) + +namespace ipa { + +/** + * \class Vector + * \brief Vector class + * \tparam T Type of numerical values to be stored in the vector + * \tparam D Number of dimension of the vector (= number of elements) + */ + +/** + * \fn Vector::Vector() + * \brief Construct a zero vector + */ + +/** + * \fn Vector::Vector(const std::array<T, D> &data) + * \brief Construct vector from supplied data + * \param data Data from which to construct a vector + * + * The size of \a data must be equal to the dimension size D of the vector. + */ + +/** + * \fn Vector::readYaml + * \brief Populate the vector with yaml data + * \param yaml Yaml data to populate the vector with + * + * Any existing data in the vector will be overwritten. The size of the data + * read from \a yaml must be equal to the dimension size D of the vector. + * + * The yaml data is expected to be a list with elements of type T. + * + * \return 0 on success, negative error code otherwise + */ + +/** + * \fn T Vector::operator[](size_t i) const + * \brief Index to an element in the vector + * \param i Index of element to retrieve + * \return Element at index \a i from the vector + */ + +/** + * \fn T &Vector::operator[](size_t i) + * \copydoc Vector::operator[](size_t i) const + */ + +/** + * \fn Vector::x() + * \brief Convenience function to access the first element of the vector + */ + +/** + * \fn Vector::y() + * \brief Convenience function to access the second element of the vector + */ + +/** + * \fn Vector::operator-() const + * \brief Negate a Vector by negating both all of its coordinates + * \return The negated vector + */ + +/** + * \fn Vector::operator-(Vector const &other) const + * \brief Subtract one vector from another + * \param[in] other The other vector + * \return The difference of \a other from this vector + */ + +/** + * \fn Vector::operator+() + * \brief Add two vectors together + * \param[in] other The other vector + * \return The sum of the two vectors + */ + +/** + * \fn Vector::operator*(const Vector<T, D> &other) const + * \brief Compute the dot product + * \param[in] other The other vector + * \return The dot product of the two vectors + */ + +/** + * \fn Vector::operator*(T factor) const + * \brief Multiply the vector by a scalar + * \param[in] factor The factor + * \return The vector multiplied by \a factor + */ + +/** + * \fn Vector::operator/() + * \brief Divide the vector by a scalar + * \param[in] factor The factor + * \return The vector divided by \a factor + */ + +/** + * \fn Vector::len2() + * \brief Get the squared length of the vector + * \return The squared length of the vector + */ + +/** + * \fn Vector::len() + * \brief Get the length of the vector + * \return The length of the vector + */ + +/** + * \fn bool operator==(const Vector<T, D> &lhs, const Vector<T, D> &rhs) + * \brief Compare vectors for equality + * \return True if the two vectors are equal, false otherwise + */ + +/** + * \fn bool operator!=(const Vector<T, D> &lhs, const Vector<T, D> &rhs) + * \brief Compare vectors for inequality + * \return True if the two vectors are not equal, false otherwise + */ + +} /* namespace ipa */ + +} /* namespace libcamera */ diff --git a/src/ipa/libipa/vector.h b/src/ipa/libipa/vector.h new file mode 100644 index 000000000000..b5a6a0e021e0 --- /dev/null +++ b/src/ipa/libipa/vector.h @@ -0,0 +1,199 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Copyright (C) 2024, Paul Elder <paul.elder@ideasonboard.com> + * + * Vector and related operations + */ +#pragma once + +#include <algorithm> +#include <array> +#include <cmath> +#include <sstream> + +#include <libcamera/base/log.h> +#include <libcamera/base/span.h> + +#include "libcamera/internal/yaml_parser.h" + +namespace libcamera { + +LOG_DECLARE_CATEGORY(Vector) + +namespace ipa { + +#ifndef __DOXYGEN__ +template<typename T, unsigned int D, + std::enable_if_t<std::is_arithmetic_v<T> && D >= 2> * = nullptr> +#else +template<typename T, unsigned int D> +#endif /* __DOXYGEN__ */ +class Vector +{ +public: + Vector() = default; + + Vector(const std::array<T, D> &data) + { + ASSERT(data.size() == D); + + for (unsigned int i = 0; i < D; i++) + data_[i] = data[i]; + } + + ~Vector() = default; + + int readYaml(const libcamera::YamlObject &yaml) + { + if (yaml.size() != D) { + LOG(Vector, Error) + << "Wrong number of values in vector: expected " + << D << ", got " << yaml.size(); + return -EINVAL; + } + + unsigned int i = 0; + for (const auto &x : yaml.asList()) { + auto value = x.get<T>(); + if (!value) { + LOG(Vector, Error) << "Failed to read vector value"; + return -EINVAL; + } + + data_[i++] = *value; + } + + return 0; + } + + const T operator[](size_t i) const + { + ASSERT(0 < i && i < data_.size()); + return data_[i]; + } + + T &operator[](size_t i) + { + ASSERT(0 < i && i < data_.size()); + return data_[i]; + } + + const T x() const + { + return data_[0]; + } + + const T y() const + { + return data_[1]; + } + + constexpr Vector<T, D> operator-() const + { + Vector<T, D> ret; + for (unsigned int i = 0; i < D; i++) + ret[i] = -data_[i]; + return ret; + } + + constexpr Vector<T, D> operator-(const Vector<T, D> &other) const + { + Vector<T, D> ret; + for (unsigned int i = 0; i < D; i++) + ret[i] = data_[i] - other[i]; + return ret; + } + + constexpr Vector<T, D> operator+(const Vector<T, D> &other) const + { + Vector<T, D> ret; + for (unsigned int i = 0; i < D; i++) + ret[i] = data_[i] + other[i]; + return ret; + } + + constexpr T operator*(const Vector<T, D> &other) const + { + T ret = 0; + for (unsigned int i = 0; i < D; i++) + ret += data_[i] * other[i]; + return ret; + } + + constexpr Vector<T, D> operator*(T factor) const + { + Vector<T, D> ret; + for (unsigned int i = 0; i < D; i++) + ret[i] = data_[i] * factor; + return ret; + } + + constexpr Vector<T, D> operator/(T factor) const + { + Vector<T, D> ret; + for (unsigned int i = 0; i < D; i++) + ret[i] = data_[i] / factor; + return ret; + } + + constexpr T len2() const + { + T ret = 0; + for (unsigned int i = 0; i < D; i++) + ret += data_[i] * data_[i]; + return ret; + } + + constexpr double len() const + { + return std::sqrt(len2()); + } + +private: + std::array<T, D> data_; +}; + +#ifndef __DOXYGEN__ +template<typename T, unsigned int D, + std::enable_if_t<std::is_arithmetic_v<T>> * = nullptr> +#endif /* __DOXYGEN__ */ +bool operator==(const Vector<T, D> &lhs, const Vector<T, D> &rhs) +{ + for (unsigned int i = 0; i < D; i++) + if (lhs[i] != rhs[i]) + return false; + + return true; +} + +#ifndef __DOXYGEN__ +template<typename T, unsigned int D, + std::enable_if_t<std::is_arithmetic_v<T>> * = nullptr> +#endif /* __DOXYGEN__ */ +bool operator!=(const Vector<T, D> &lhs, const Vector<T, D> &rhs) +{ + for (unsigned int i = 0; i < D; i++) + if (lhs[i] != rhs[i]) + return true; + + return false; +} + +} /* namespace ipa */ + +#ifndef __DOXYGEN__ +template<typename T, unsigned int D> +std::ostream &operator<<(std::ostream &out, const ipa::Vector<T, D> &v) +{ + out << "Vector { "; + for (unsigned int i = 0; i < D; i++) { + out << v[i]; + out << ((i + 1 < D) ? ", " : " "); + } + out << " }"; + + return out; +} +#endif /* __DOXYGEN__ */ + +} /* namespace libcamera */