[v3,1/4] libcamera: geometry: Add floating-point version of Point class
diff mbox series

Message ID 20240529192612.814515-2-paul.elder@ideasonboard.com
State Superseded
Headers show
Series
  • ipa: Move Pwl from Raspberry Pi to libipa
Related show

Commit Message

Paul Elder May 29, 2024, 7:26 p.m. UTC
The piecewise linear function (Pwl) class from the Raspberry Pi IPA has
its own Point class while one already exists in libcamera's geometry.h
header. The reason is because libcamera's Point is on the plane of
integers, while Raspberry Pi's is on the plane of reals.

While making this a template class might be cleaner, it was deemed to be
too intrusive of a change, and it might not feel nice to need to specify
the type from a public API point of view. Hence we introduce a PointF
class to designate a Point class on the plane of reals.

This is in preparation for copying/moving the Pwl class from the
Raspberry Pi IPA to libipa.

Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Stefan Klug <stefan.klug@ideasonboard.com>

---
Changes in v3:
- fix typoes in commit message

Changes in v2:
- renamed FPoint to PointF
- add documentation
- add tests
---
 include/libcamera/geometry.h |  65 +++++++
 src/libcamera/geometry.cpp   | 123 +++++++++++-
 test/geometry.cpp            | 355 +++++++++++++++++++++++++++++++++++
 3 files changed, 542 insertions(+), 1 deletion(-)

Patch
diff mbox series

diff --git a/include/libcamera/geometry.h b/include/libcamera/geometry.h
index 3e6f0f5d7..81a697398 100644
--- a/include/libcamera/geometry.h
+++ b/include/libcamera/geometry.h
@@ -8,6 +8,7 @@ 
 #pragma once
 
 #include <algorithm>
+#include <cmath>
 #include <ostream>
 #include <string>
 
@@ -49,6 +50,70 @@  static inline bool operator!=(const Point &lhs, const Point &rhs)
 
 std::ostream &operator<<(std::ostream &out, const Point &p);
 
+struct PointF {
+	constexpr PointF()
+		: x(0), y(0)
+	{
+	}
+
+	constexpr PointF(double _x, double _y)
+		: x(_x), y(_y)
+	{
+	}
+
+	constexpr PointF operator-() const
+	{
+		return PointF{ -x, -y };
+	}
+
+	constexpr PointF operator-(const PointF &p) const
+	{
+		return PointF(x - p.x, y - p.y);
+	}
+
+	constexpr PointF operator+(const PointF &p) const
+	{
+		return PointF(x + p.x, y + p.y);
+	}
+
+	constexpr double operator%(const PointF &p) const
+	{
+		return x * p.x + y * p.y;
+	}
+
+	constexpr PointF operator*(double f) const
+	{
+		return PointF(x * f, y * f);
+	}
+
+	constexpr PointF operator/(double f) const
+	{
+		return PointF(x / f, y / f);
+	}
+
+	constexpr double len2() const
+	{
+		return x * x + y * y;
+	}
+
+	constexpr double len() const
+	{
+		return std::sqrt(len2());
+	}
+
+	const std::string toString() const;
+
+	double x, y;
+};
+
+bool operator==(const PointF &lhs, const PointF &rhs);
+static inline bool operator!=(const PointF &lhs, const PointF &rhs)
+{
+	return !(lhs == rhs);
+}
+
+std::ostream &operator<<(std::ostream &out, const PointF &p);
+
 class Size
 {
 public:
diff --git a/src/libcamera/geometry.cpp b/src/libcamera/geometry.cpp
index 000151364..2ab4c60f4 100644
--- a/src/libcamera/geometry.cpp
+++ b/src/libcamera/geometry.cpp
@@ -21,7 +21,7 @@  namespace libcamera {
 
 /**
  * \class Point
- * \brief Describe a point in two-dimensional space
+ * \brief Describe a point in two-dimensional integer space
  *
  * The Point structure defines a point in two-dimensional space with integer
  * precision. The coordinates of a Point may be negative as well as positive.
@@ -94,6 +94,127 @@  std::ostream &operator<<(std::ostream &out, const Point &p)
 	return out;
 }
 
+/**
+ * \class PointF
+ * \brief Describe a point in two-dimensional real space
+ *
+ * The Point structure defines a point in two-dimensional space with double
+ * precision. The coordinates of a Point may be negative as well as positive.
+ *
+ * This class exists separately from Point to not require all users of the
+ * Point class to have to use template parameters to specify a type.
+ */
+
+/**
+ * \fn PointF::PointF()
+ * \copydoc libcamera::Point::Point
+ */
+
+/**
+ * \fn PointF::PointF(double _x, double _y)
+ * \brief Construct a PointF at given \a _x and \a _y values
+ * \param[in] _x The x-coordinate
+ * \param[in] _y The y-coordinate
+ */
+
+/**
+ * \var PointF::x
+ * \copydoc libcamera::Point::x
+ */
+
+/**
+ * \var PointF::y
+ * \copydoc libcamera::Point::y
+ */
+
+/**
+ * \fn constexpr PointF PointF::operator-() const
+ * \copydoc libcamera::Point::operator-
+ */
+
+/**
+ * \fn constexpr PointF PointF::operator-(PointF const &p) const
+ * \brief Subtract one point from another, as if they were vectors
+ * \param[in] p The other point
+ * \return The difference of p from this point
+ */
+
+/**
+ * \fn PointF::operator+()
+ * \brief Add two points together, as if they were vectors
+ * \param[in] p The other point
+ * \return The sum of the two points
+ */
+
+/**
+ * \fn PointF::operator%()
+ * \brief Compute the dot product, treating the points as vectors
+ * \param[in] p The other point
+ * \return The dot product of the two points
+ */
+
+/**
+ * \fn PointF::operator*()
+ * \brief Scale up the point, as if it were a vector
+ * \param[in] f The factor
+ * \return The scaled point
+ */
+
+/**
+ * \fn PointF::operator/()
+ * \brief Scale down the point, as if it were a vector
+ * \param[in] f The factor
+ * \return The scaled point
+ */
+
+/**
+ * \fn PointF::len2()
+ * \brief Get the squared length of the point, as if it were a vector
+ * \return The squared length of the point
+ */
+
+/**
+ * \fn PointF::len()
+ * \brief Get the length of the point, as if it were a vector
+ * \return The length of the point
+ */
+
+/**
+ * \copydoc Point::toString()
+ */
+const std::string PointF::toString() const
+{
+	std::stringstream ss;
+	ss << *this;
+
+	return ss.str();
+}
+
+/**
+ * \copydoc operator==(const Point &lhs, const Point &rhs)
+ */
+bool operator==(const PointF &lhs, const PointF &rhs)
+{
+	return lhs.x == rhs.x && lhs.y == rhs.y;
+}
+
+/**
+ * \fn bool operator!=(const Point &lhs, const Point &rhs)
+ * \copydoc libcamera::Point::operator!=
+ */
+
+/**
+ * \brief Insert a text representation of a PointF into an output stream
+ * \param[in] out The output stream
+ * \param[in] p The point
+ * \return The output stream \a out
+ */
+std::ostream &operator<<(std::ostream &out, const PointF &p)
+{
+	out << "(" << p.x << ", " << p.y << ")";
+	return out;
+}
+
 /**
  * \class Size
  * \brief Describe a two-dimensional size
diff --git a/test/geometry.cpp b/test/geometry.cpp
index 64169206a..54f1ce4ab 100644
--- a/test/geometry.cpp
+++ b/test/geometry.cpp
@@ -33,6 +33,53 @@  protected:
 		return true;
 	}
 
+	template<typename T, typename U, typename V>
+	bool compareF(const T &lhs, const U &rhs,
+		      V (PointF::*op)(const U &) const,
+		      const char *opName, V expect)
+	{
+		V result = (lhs.*op)(rhs);
+
+		if (result != expect) {
+			cout << lhs << opName << " " << rhs
+			     << "test failed" << std::endl;
+			return false;
+		}
+
+		return true;
+	}
+
+	template<typename T, typename U, typename V>
+	bool compareFScale(const T &lhs, const U &rhs,
+			   V (PointF::*op)(U) const,
+			   const char *opName, V expect)
+	{
+		V result = (lhs.*op)(rhs);
+
+		if (result != expect) {
+			cout << lhs << opName << " " << rhs
+			     << "test failed" << std::endl;
+			return false;
+		}
+
+		return true;
+	}
+
+	template<typename T>
+	bool compareFLen(const T &lhs, double (PointF::*op)() const,
+			 const char *opName, double expect)
+	{
+		double result = (lhs.*op)();
+
+		if (result != expect) {
+			cout << lhs << opName
+			     << "test failed" << std::endl;
+			return false;
+		}
+
+		return true;
+	}
+
 	int run()
 	{
 		/*
@@ -88,6 +135,314 @@  protected:
 			return TestFail;
 		}
 
+		/*
+		 * PointF tests
+		 */
+
+		/* Equality */
+		if (!compare(PointF(50.1, 100.1), PointF(50.1, 100.1), &operator==, "==", true))
+			return TestFail;
+
+		if (!compare(PointF(-50.1, 100.1), PointF(-50.1, 100.1), &operator==, "==", true))
+			return TestFail;
+
+		if (!compare(PointF(50.1, -100.1), PointF(50.1, -100.1), &operator==, "==", true))
+			return TestFail;
+
+		if (!compare(PointF(-50.1, -100.1), PointF(-50.1, -100.1), &operator==, "==", true))
+			return TestFail;
+
+		if (!compare(PointF(-50.1, -100.0), PointF(-50.1, -100), &operator==, "==", true))
+			return TestFail;
+
+		/* Inequality */
+		if (!compare(PointF(50.1, 100.1), PointF(50.1, 100.2), &operator!=, "!=", true))
+			return TestFail;
+
+		if (!compare(PointF(-50.1, 100.1), PointF(-50.1, 100.01), &operator!=, "!=", true))
+			return TestFail;
+
+		if (!compare(PointF(-50.1, 100.1), PointF(-50.1, 100.1), &operator!=, "!=", false))
+			return TestFail;
+
+		if (!compare(PointF(50.1, -100.1), PointF(50.2, -100.1), &operator!=, "!=", true))
+			return TestFail;
+
+		if (!compare(PointF(-50.1, -100.1), PointF(-50.01, -100.0), &operator!=, "!=", true))
+			return TestFail;
+
+		if (!compare(PointF(-50.1, 100.1), PointF(50.1, 100.1), &operator!=, "!=", true))
+			return TestFail;
+
+		if (!compare(PointF(50.1, -100.1), PointF(50.1, 100.1), &operator!=, "!=", true))
+			return TestFail;
+
+		if (!compare(PointF(-50.1, -100.1), PointF(-50.1, -100.1), &operator!=, "!=", false))
+			return TestFail;
+
+		/* Negation */
+		if (PointF(50.1, 100.1) != -PointF(-50.1, -100.1) ||
+		    PointF(50.1, 100.1) == -PointF(50.1, -100.1) ||
+		    PointF(50.1, 100.1) == -PointF(-50.1, 100.1)) {
+			cout << "PointF negation test failed" << endl;
+			return TestFail;
+		}
+
+		typedef PointF (PointF::*pointfOp)(const PointF &) const;
+		typedef double (PointF::*pointfDotProd)(const PointF &) const;
+		typedef PointF (PointF::*pointfScale)(double) const;
+		typedef double (PointF::*pointfLen)() const;
+
+		/* Subtraction */
+		if (!compareF<PointF, PointF, PointF>(PointF(50.1, 100.1), PointF(50.1, 100.1),
+			      static_cast<pointfOp>(&PointF::operator-), "-",
+			      PointF(0, 0)))
+			return TestFail;
+
+		if (!compareF<PointF, PointF, PointF>(PointF(-50.1, 100.1), PointF(-50.1, 100.1),
+			      static_cast<pointfOp>(&PointF::operator-), "-",
+			      PointF(0, 0)))
+			return TestFail;
+
+		if (!compareF<PointF, PointF, PointF>(PointF(50.1, -100.1), PointF(50.1, -100.1),
+			      static_cast<pointfOp>(&PointF::operator-), "-",
+			      PointF(0, 0)))
+			return TestFail;
+
+		if (!compareF<PointF, PointF, PointF>(PointF(-50.1, -100.1), PointF(-50.1, -100.1),
+			      static_cast<pointfOp>(&PointF::operator-), "-",
+			      PointF(0, 0)))
+			return TestFail;
+
+		if (!compareF<PointF, PointF, PointF>(PointF(50.1, 100.1), PointF(-50.1, 100.1),
+			      static_cast<pointfOp>(&PointF::operator-), "-",
+			      PointF(100.2, 0)))
+			return TestFail;
+
+		if (!compareF<PointF, PointF, PointF>(PointF(50.1, 100.1), PointF(50.1, -100.1),
+			      static_cast<pointfOp>(&PointF::operator-), "-",
+			      PointF(0, 200.2)))
+			return TestFail;
+
+		if (!compareF<PointF, PointF, PointF>(PointF(50.1, 100.1), PointF(-50.1, -100.1),
+			      static_cast<pointfOp>(&PointF::operator-), "-",
+			      PointF(100.2, 200.2)))
+			return TestFail;
+
+		if (!compareF<PointF, PointF, PointF>(PointF(-50.1, 100.1), PointF(50.1, 100.1),
+			      static_cast<pointfOp>(&PointF::operator-), "-",
+			      PointF(-100.2, 0)))
+			return TestFail;
+
+		/* Addition */
+		if (!compareF<PointF, PointF, PointF>(PointF(50.1, 100.1), PointF(50.1, 100.1),
+			      static_cast<pointfOp>(&PointF::operator+), "+",
+			      PointF(100.2, 200.2)))
+			return TestFail;
+
+		if (!compareF<PointF, PointF, PointF>(PointF(-50.1, 100.1), PointF(-50.1, 100.1),
+			      static_cast<pointfOp>(&PointF::operator+), "+",
+			      PointF(-100.2, 200.2)))
+			return TestFail;
+
+		if (!compareF<PointF, PointF, PointF>(PointF(50.1, -100.1), PointF(50.1, -100.1),
+			      static_cast<pointfOp>(&PointF::operator+), "+",
+			      PointF(100.2, -200.2)))
+			return TestFail;
+
+		if (!compareF<PointF, PointF, PointF>(PointF(-50.1, -100), PointF(-50.1, -100.1),
+			      static_cast<pointfOp>(&PointF::operator+), "+",
+			      PointF(-100.2, -200.1)))
+			return TestFail;
+
+		if (!compareF<PointF, PointF, PointF>(PointF(50.1, 100.1), PointF(-50.1, 100.1),
+			      static_cast<pointfOp>(&PointF::operator+), "+",
+			      PointF(0, 200.2)))
+			return TestFail;
+
+		if (!compareF<PointF, PointF, PointF>(PointF(50.1, 100.0), PointF(50.1, -100.1),
+			      static_cast<pointfOp>(&PointF::operator+), "+",
+			      PointF(100.2, -0.09999999999999432)))
+			return TestFail;
+
+		if (!compareF<PointF, PointF, PointF>(PointF(50.1, 100.1), PointF(-50.1, -100.1),
+			      static_cast<pointfOp>(&PointF::operator+), "+",
+			      PointF(0, 0)))
+			return TestFail;
+
+		if (!compareF<PointF, PointF, PointF>(PointF(-50.1, 100.1), PointF(50.1, 100.1),
+			      static_cast<pointfOp>(&PointF::operator+), "+",
+			      PointF(0, 200.2)))
+			return TestFail;
+
+		/* Dot product */
+		if (!compareF<PointF, PointF, double>(PointF(50.1, 100.1), PointF(50.1, 100.1),
+			      static_cast<pointfDotProd>(&PointF::operator%), "%",
+			      12530.019999999999))
+			return TestFail;
+
+		if (!compareF<PointF, PointF, double>(PointF(50.1, 100.1), PointF(50.1, -100.1),
+			      static_cast<pointfDotProd>(&PointF::operator%), "%",
+			      -7509.999999999998))
+			return TestFail;
+
+		if (!compareF<PointF, PointF, double>(PointF(50.1, 100.1), PointF(-50.1, 100.1),
+			      static_cast<pointfDotProd>(&PointF::operator%), "%",
+			      7509.999999999998))
+			return TestFail;
+
+		if (!compareF<PointF, PointF, double>(PointF(50.1, 100.1), PointF(-50.1, -100.1),
+			      static_cast<pointfDotProd>(&PointF::operator%), "%",
+			      -12530.019999999999))
+			return TestFail;
+
+		if (!compareF<PointF, PointF, double>(PointF(-50.1, 100.1), PointF(-50.1, 100.1),
+			      static_cast<pointfDotProd>(&PointF::operator%), "%",
+			      12530.019999999999))
+			return TestFail;
+
+		if (!compareF<PointF, PointF, double>(PointF(50.1, -100.1), PointF(50.1, -100.1),
+			      static_cast<pointfDotProd>(&PointF::operator%), "%",
+			      12530.019999999999))
+			return TestFail;
+
+		if (!compareF<PointF, PointF, double>(PointF(-50.1, -100.1), PointF(-50.1, -100.1),
+			      static_cast<pointfDotProd>(&PointF::operator%), "%",
+			      12530.019999999999))
+			return TestFail;
+
+		if (!compareF<PointF, PointF, double>(PointF(0, 0), PointF(0, 0),
+			      static_cast<pointfDotProd>(&PointF::operator%), "%",
+			      0))
+			return TestFail;
+
+
+		/* Scaling up */
+		if (!compareFScale<PointF, double, PointF>(PointF(10.5, -100.1), 0,
+				   static_cast<pointfScale>(&PointF::operator*), "*",
+				   PointF(0, 0)))
+			return TestFail;
+
+		if (!compareFScale<PointF, double, PointF>(PointF(10.5, -100.1), 1,
+				   static_cast<pointfScale>(&PointF::operator*), "*",
+				   PointF(10.5, -100.1)))
+			return TestFail;
+
+		if (!compareFScale<PointF, double, PointF>(PointF(10.5, -100.1), 1.0,
+				   static_cast<pointfScale>(&PointF::operator*), "*",
+				   PointF(10.5, -100.1)))
+			return TestFail;
+
+		if (!compareFScale<PointF, double, PointF>(PointF(10.5, -100.1), -4.2,
+				   static_cast<pointfScale>(&PointF::operator*), "*",
+				   PointF(-44.1, 420.42)))
+			return TestFail;
+
+		if (!compareFScale<PointF, double, PointF>(PointF(10.5, -100.1), 4.2,
+				   static_cast<pointfScale>(&PointF::operator*), "*",
+				   PointF(44.1, -420.42)))
+			return TestFail;
+
+		if (!compareFScale<PointF, double, PointF>(PointF(0, -100.1), 4.2,
+				   static_cast<pointfScale>(&PointF::operator*), "*",
+				   PointF(0, -420.42)))
+			return TestFail;
+
+		if (!compareFScale<PointF, double, PointF>(PointF(-50.1, -100.1), 4.2,
+				   static_cast<pointfScale>(&PointF::operator*), "*",
+				   PointF(-210.42000000000002, -420.42)))
+			return TestFail;
+
+		/* Scaling down */
+		if (!compareFScale<PointF, double, PointF>(PointF(10.5, -100.1), 1,
+				   static_cast<pointfScale>(&PointF::operator/), "/",
+				   PointF(10.5, -100.1)))
+			return TestFail;
+
+		if (!compareFScale<PointF, double, PointF>(PointF(10.5, -100.1), 1.0,
+				   static_cast<pointfScale>(&PointF::operator/), "/",
+				   PointF(10.5, -100.1)))
+			return TestFail;
+
+		if (!compareFScale<PointF, double, PointF>(PointF(10.5, -100.1), -4.2,
+				   static_cast<pointfScale>(&PointF::operator/), "/",
+				   PointF(-2.5, 23.833333333333332)))
+			return TestFail;
+
+		if (!compareFScale<PointF, double, PointF>(PointF(10.5, -100.1), 4.2,
+				   static_cast<pointfScale>(&PointF::operator/), "/",
+				   PointF(2.5, -23.833333333333332)))
+			return TestFail;
+
+		if (!compareFScale<PointF, double, PointF>(PointF(0, -100.1), 4.2,
+				   static_cast<pointfScale>(&PointF::operator/), "/",
+				   PointF(0, -23.833333333333332)))
+			return TestFail;
+
+		if (!compareFScale<PointF, double, PointF>(PointF(-50.1, -100.1), 4.2,
+				   static_cast<pointfScale>(&PointF::operator/), "/",
+				   PointF(-11.928571428571429, -23.833333333333332)))
+			return TestFail;
+
+
+		/* Squared length */
+		if (!compareFLen<PointF>(PointF(0, 0),
+					 static_cast<pointfLen>(&PointF::len2), "len2",
+					 0))
+			return TestFail;
+
+		if (!compareFLen<PointF>(PointF(10.4, 0),
+					 static_cast<pointfLen>(&PointF::len2), "len2",
+					 108.16000000000001))
+			return TestFail;
+
+		if (!compareFLen<PointF>(PointF(10.4, 50.1),
+					 static_cast<pointfLen>(&PointF::len2), "len2",
+					 2618.17))
+			return TestFail;
+
+		if (!compareFLen<PointF>(PointF(-10.4, 50.1),
+					 static_cast<pointfLen>(&PointF::len2), "len2",
+					 2618.17))
+			return TestFail;
+
+		if (!compareFLen<PointF>(PointF(-10.4, -50.1),
+					 static_cast<pointfLen>(&PointF::len2), "len2",
+					 2618.17))
+			return TestFail;
+
+		/* Length */
+		if (!compareFLen<PointF>(PointF(0, 0),
+					 static_cast<pointfLen>(&PointF::len), "len",
+					 0))
+			return TestFail;
+
+		if (!compareFLen<PointF>(PointF(10.4, 0),
+					 static_cast<pointfLen>(&PointF::len), "len",
+					 10.4))
+			return TestFail;
+
+		if (!compareFLen<PointF>(PointF(10.4, 50.1),
+					 static_cast<pointfLen>(&PointF::len), "len",
+					 51.16805644149483))
+			return TestFail;
+
+		if (!compareFLen<PointF>(PointF(-10.4, 50.1),
+					 static_cast<pointfLen>(&PointF::len), "len",
+					 51.16805644149483))
+			return TestFail;
+
+		if (!compareFLen<PointF>(PointF(-10.4, -50.1),
+					 static_cast<pointfLen>(&PointF::len), "len",
+					 51.16805644149483))
+			return TestFail;
+
+		/* Default constructor */
+		if (PointF() != PointF(0, 0)) {
+			cout << "Default constructor test failed" << endl;
+			return TestFail;
+		}
+
 		/*
 		 * Size tests
 		 */