From patchwork Wed May 29 19:26:08 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Paul Elder X-Patchwork-Id: 20127 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id CA2A7BDE6B for ; Wed, 29 May 2024 19:26:30 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 75B07634BB; Wed, 29 May 2024 21:26:30 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=pass (1024-bit key; unprotected) header.d=ideasonboard.com header.i=@ideasonboard.com header.b="pAXLj4s/"; dkim-atps=neutral Received: from perceval.ideasonboard.com (perceval.ideasonboard.com [213.167.242.64]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 3F4D36347E for ; Wed, 29 May 2024 21:26:28 +0200 (CEST) Received: from neptunite.hamster-moth.ts.net (h175-177-049-156.catv02.itscom.jp [175.177.49.156]) by perceval.ideasonboard.com (Postfix) with ESMTPSA id CDD0A2D31; Wed, 29 May 2024 21:26:22 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ideasonboard.com; s=mail; t=1717010784; bh=IuTxl67ABbyA4wUlOjmbwphef6+ZzuUjvdZZlGKiwW4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=pAXLj4s/F5LNVN4EDP7o3gTFnJUmw5NfIiGeMPzD2TcmaZnOSmxJkqT3covM6cepO 7N4VZIH0HYBlPGtxUIYg+yymvDHm6FyrzaWBQWvuqwmVBwcOaCaAX/6ZnchYb2QdLs zUIXwFS8lqzA+G7mSDHl5R+HUbkGsysI/1vujHns= From: Paul Elder To: libcamera-devel@lists.libcamera.org Cc: Paul Elder , Naushir Patuck , David Plowman , Stefan Klug Subject: [PATCH v3 1/4] libcamera: geometry: Add floating-point version of Point class Date: Thu, 30 May 2024 04:26:08 +0900 Message-Id: <20240529192612.814515-2-paul.elder@ideasonboard.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20240529192612.814515-1-paul.elder@ideasonboard.com> References: <20240529192612.814515-1-paul.elder@ideasonboard.com> MIME-Version: 1.0 X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" 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 Reviewed-by: Stefan Klug --- 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(-) 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 +#include #include #include @@ -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 + 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 + 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 + 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(50.1, 100.1), PointF(50.1, 100.1), + static_cast(&PointF::operator-), "-", + PointF(0, 0))) + return TestFail; + + if (!compareF(PointF(-50.1, 100.1), PointF(-50.1, 100.1), + static_cast(&PointF::operator-), "-", + PointF(0, 0))) + return TestFail; + + if (!compareF(PointF(50.1, -100.1), PointF(50.1, -100.1), + static_cast(&PointF::operator-), "-", + PointF(0, 0))) + return TestFail; + + if (!compareF(PointF(-50.1, -100.1), PointF(-50.1, -100.1), + static_cast(&PointF::operator-), "-", + PointF(0, 0))) + return TestFail; + + if (!compareF(PointF(50.1, 100.1), PointF(-50.1, 100.1), + static_cast(&PointF::operator-), "-", + PointF(100.2, 0))) + return TestFail; + + if (!compareF(PointF(50.1, 100.1), PointF(50.1, -100.1), + static_cast(&PointF::operator-), "-", + PointF(0, 200.2))) + return TestFail; + + if (!compareF(PointF(50.1, 100.1), PointF(-50.1, -100.1), + static_cast(&PointF::operator-), "-", + PointF(100.2, 200.2))) + return TestFail; + + if (!compareF(PointF(-50.1, 100.1), PointF(50.1, 100.1), + static_cast(&PointF::operator-), "-", + PointF(-100.2, 0))) + return TestFail; + + /* Addition */ + if (!compareF(PointF(50.1, 100.1), PointF(50.1, 100.1), + static_cast(&PointF::operator+), "+", + PointF(100.2, 200.2))) + return TestFail; + + if (!compareF(PointF(-50.1, 100.1), PointF(-50.1, 100.1), + static_cast(&PointF::operator+), "+", + PointF(-100.2, 200.2))) + return TestFail; + + if (!compareF(PointF(50.1, -100.1), PointF(50.1, -100.1), + static_cast(&PointF::operator+), "+", + PointF(100.2, -200.2))) + return TestFail; + + if (!compareF(PointF(-50.1, -100), PointF(-50.1, -100.1), + static_cast(&PointF::operator+), "+", + PointF(-100.2, -200.1))) + return TestFail; + + if (!compareF(PointF(50.1, 100.1), PointF(-50.1, 100.1), + static_cast(&PointF::operator+), "+", + PointF(0, 200.2))) + return TestFail; + + if (!compareF(PointF(50.1, 100.0), PointF(50.1, -100.1), + static_cast(&PointF::operator+), "+", + PointF(100.2, -0.09999999999999432))) + return TestFail; + + if (!compareF(PointF(50.1, 100.1), PointF(-50.1, -100.1), + static_cast(&PointF::operator+), "+", + PointF(0, 0))) + return TestFail; + + if (!compareF(PointF(-50.1, 100.1), PointF(50.1, 100.1), + static_cast(&PointF::operator+), "+", + PointF(0, 200.2))) + return TestFail; + + /* Dot product */ + if (!compareF(PointF(50.1, 100.1), PointF(50.1, 100.1), + static_cast(&PointF::operator%), "%", + 12530.019999999999)) + return TestFail; + + if (!compareF(PointF(50.1, 100.1), PointF(50.1, -100.1), + static_cast(&PointF::operator%), "%", + -7509.999999999998)) + return TestFail; + + if (!compareF(PointF(50.1, 100.1), PointF(-50.1, 100.1), + static_cast(&PointF::operator%), "%", + 7509.999999999998)) + return TestFail; + + if (!compareF(PointF(50.1, 100.1), PointF(-50.1, -100.1), + static_cast(&PointF::operator%), "%", + -12530.019999999999)) + return TestFail; + + if (!compareF(PointF(-50.1, 100.1), PointF(-50.1, 100.1), + static_cast(&PointF::operator%), "%", + 12530.019999999999)) + return TestFail; + + if (!compareF(PointF(50.1, -100.1), PointF(50.1, -100.1), + static_cast(&PointF::operator%), "%", + 12530.019999999999)) + return TestFail; + + if (!compareF(PointF(-50.1, -100.1), PointF(-50.1, -100.1), + static_cast(&PointF::operator%), "%", + 12530.019999999999)) + return TestFail; + + if (!compareF(PointF(0, 0), PointF(0, 0), + static_cast(&PointF::operator%), "%", + 0)) + return TestFail; + + + /* Scaling up */ + if (!compareFScale(PointF(10.5, -100.1), 0, + static_cast(&PointF::operator*), "*", + PointF(0, 0))) + return TestFail; + + if (!compareFScale(PointF(10.5, -100.1), 1, + static_cast(&PointF::operator*), "*", + PointF(10.5, -100.1))) + return TestFail; + + if (!compareFScale(PointF(10.5, -100.1), 1.0, + static_cast(&PointF::operator*), "*", + PointF(10.5, -100.1))) + return TestFail; + + if (!compareFScale(PointF(10.5, -100.1), -4.2, + static_cast(&PointF::operator*), "*", + PointF(-44.1, 420.42))) + return TestFail; + + if (!compareFScale(PointF(10.5, -100.1), 4.2, + static_cast(&PointF::operator*), "*", + PointF(44.1, -420.42))) + return TestFail; + + if (!compareFScale(PointF(0, -100.1), 4.2, + static_cast(&PointF::operator*), "*", + PointF(0, -420.42))) + return TestFail; + + if (!compareFScale(PointF(-50.1, -100.1), 4.2, + static_cast(&PointF::operator*), "*", + PointF(-210.42000000000002, -420.42))) + return TestFail; + + /* Scaling down */ + if (!compareFScale(PointF(10.5, -100.1), 1, + static_cast(&PointF::operator/), "/", + PointF(10.5, -100.1))) + return TestFail; + + if (!compareFScale(PointF(10.5, -100.1), 1.0, + static_cast(&PointF::operator/), "/", + PointF(10.5, -100.1))) + return TestFail; + + if (!compareFScale(PointF(10.5, -100.1), -4.2, + static_cast(&PointF::operator/), "/", + PointF(-2.5, 23.833333333333332))) + return TestFail; + + if (!compareFScale(PointF(10.5, -100.1), 4.2, + static_cast(&PointF::operator/), "/", + PointF(2.5, -23.833333333333332))) + return TestFail; + + if (!compareFScale(PointF(0, -100.1), 4.2, + static_cast(&PointF::operator/), "/", + PointF(0, -23.833333333333332))) + return TestFail; + + if (!compareFScale(PointF(-50.1, -100.1), 4.2, + static_cast(&PointF::operator/), "/", + PointF(-11.928571428571429, -23.833333333333332))) + return TestFail; + + + /* Squared length */ + if (!compareFLen(PointF(0, 0), + static_cast(&PointF::len2), "len2", + 0)) + return TestFail; + + if (!compareFLen(PointF(10.4, 0), + static_cast(&PointF::len2), "len2", + 108.16000000000001)) + return TestFail; + + if (!compareFLen(PointF(10.4, 50.1), + static_cast(&PointF::len2), "len2", + 2618.17)) + return TestFail; + + if (!compareFLen(PointF(-10.4, 50.1), + static_cast(&PointF::len2), "len2", + 2618.17)) + return TestFail; + + if (!compareFLen(PointF(-10.4, -50.1), + static_cast(&PointF::len2), "len2", + 2618.17)) + return TestFail; + + /* Length */ + if (!compareFLen(PointF(0, 0), + static_cast(&PointF::len), "len", + 0)) + return TestFail; + + if (!compareFLen(PointF(10.4, 0), + static_cast(&PointF::len), "len", + 10.4)) + return TestFail; + + if (!compareFLen(PointF(10.4, 50.1), + static_cast(&PointF::len), "len", + 51.16805644149483)) + return TestFail; + + if (!compareFLen(PointF(-10.4, 50.1), + static_cast(&PointF::len), "len", + 51.16805644149483)) + return TestFail; + + if (!compareFLen(PointF(-10.4, -50.1), + static_cast(&PointF::len), "len", + 51.16805644149483)) + return TestFail; + + /* Default constructor */ + if (PointF() != PointF(0, 0)) { + cout << "Default constructor test failed" << endl; + return TestFail; + } + /* * Size tests */