2020-01-18 08:38:21 +00:00
|
|
|
/*
|
2024-10-04 11:19:50 +00:00
|
|
|
* Copyright (c) 2018-2021, Andreas Kling <andreas@ladybird.org>
|
2020-01-18 08:38:21 +00:00
|
|
|
*
|
2021-04-22 08:24:48 +00:00
|
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
2020-01-18 08:38:21 +00:00
|
|
|
*/
|
|
|
|
|
2018-10-10 18:06:58 +00:00
|
|
|
#pragma once
|
|
|
|
|
2021-01-03 14:26:47 +00:00
|
|
|
#include <AK/Format.h>
|
2021-07-17 16:29:28 +00:00
|
|
|
#include <AK/Math.h>
|
2020-02-14 23:58:14 +00:00
|
|
|
#include <AK/StdLibExtras.h>
|
2021-04-12 18:47:09 +00:00
|
|
|
#include <LibGfx/AffineTransform.h>
|
2020-07-26 04:31:47 +00:00
|
|
|
#include <LibGfx/Forward.h>
|
2020-08-05 11:10:56 +00:00
|
|
|
#include <LibGfx/Orientation.h>
|
2020-03-29 17:03:13 +00:00
|
|
|
#include <LibIPC/Forward.h>
|
2021-09-18 10:03:36 +00:00
|
|
|
#include <math.h>
|
2019-02-09 10:19:38 +00:00
|
|
|
|
2020-02-06 10:56:38 +00:00
|
|
|
namespace Gfx {
|
|
|
|
|
2020-07-26 04:31:47 +00:00
|
|
|
template<typename T>
|
|
|
|
class Point {
|
2018-10-10 18:06:58 +00:00
|
|
|
public:
|
2021-04-12 18:47:09 +00:00
|
|
|
Point() = default;
|
2020-07-26 04:31:47 +00:00
|
|
|
|
2022-06-16 13:45:00 +00:00
|
|
|
constexpr Point(T x, T y)
|
2020-07-26 04:31:47 +00:00
|
|
|
: m_x(x)
|
|
|
|
, m_y(y)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename U>
|
2022-06-16 13:45:00 +00:00
|
|
|
constexpr Point(U x, U y)
|
2019-06-07 09:46:55 +00:00
|
|
|
: m_x(x)
|
|
|
|
, m_y(y)
|
|
|
|
{
|
|
|
|
}
|
2018-10-10 18:06:58 +00:00
|
|
|
|
2020-07-26 04:31:47 +00:00
|
|
|
template<typename U>
|
2021-06-16 17:24:27 +00:00
|
|
|
explicit Point(Point<U> const& other)
|
2020-07-26 04:31:47 +00:00
|
|
|
: m_x(other.x())
|
|
|
|
, m_y(other.y())
|
|
|
|
{
|
|
|
|
}
|
2020-07-22 06:46:15 +00:00
|
|
|
|
2023-09-01 22:16:28 +00:00
|
|
|
[[nodiscard]] constexpr ALWAYS_INLINE T x() const { return m_x; }
|
|
|
|
[[nodiscard]] constexpr ALWAYS_INLINE T y() const { return m_y; }
|
2018-10-10 18:06:58 +00:00
|
|
|
|
2021-04-12 18:47:09 +00:00
|
|
|
ALWAYS_INLINE void set_x(T x) { m_x = x; }
|
|
|
|
ALWAYS_INLINE void set_y(T y) { m_y = y; }
|
2018-10-10 18:06:58 +00:00
|
|
|
|
2022-12-28 21:43:30 +00:00
|
|
|
[[nodiscard]] ALWAYS_INLINE bool is_zero() const { return m_x == 0 && m_y == 0; }
|
2021-04-12 18:47:09 +00:00
|
|
|
|
|
|
|
void translate_by(T dx, T dy)
|
2018-10-10 18:06:58 +00:00
|
|
|
{
|
|
|
|
m_x += dx;
|
|
|
|
m_y += dy;
|
|
|
|
}
|
|
|
|
|
2021-04-12 18:47:09 +00:00
|
|
|
ALWAYS_INLINE void translate_by(T dboth) { translate_by(dboth, dboth); }
|
2021-06-16 17:24:27 +00:00
|
|
|
ALWAYS_INLINE void translate_by(Point<T> const& delta) { translate_by(delta.x(), delta.y()); }
|
2021-04-12 18:47:09 +00:00
|
|
|
|
|
|
|
void scale_by(T dx, T dy)
|
2018-10-12 00:41:27 +00:00
|
|
|
{
|
2021-04-12 18:47:09 +00:00
|
|
|
m_x *= dx;
|
|
|
|
m_y *= dy;
|
2018-10-12 00:41:27 +00:00
|
|
|
}
|
|
|
|
|
2021-04-12 18:47:09 +00:00
|
|
|
ALWAYS_INLINE void scale_by(T dboth) { scale_by(dboth, dboth); }
|
2021-06-16 17:24:27 +00:00
|
|
|
ALWAYS_INLINE void scale_by(Point<T> const& delta) { scale_by(delta.x(), delta.y()); }
|
2021-04-12 18:47:09 +00:00
|
|
|
|
2021-06-16 17:24:27 +00:00
|
|
|
void transform_by(AffineTransform const& transform) { *this = transform.map(*this); }
|
2021-04-12 18:47:09 +00:00
|
|
|
|
2021-06-16 17:26:50 +00:00
|
|
|
[[nodiscard]] Point<T> translated(Point<T> const& delta) const
|
2019-03-31 20:09:10 +00:00
|
|
|
{
|
2020-07-26 04:31:47 +00:00
|
|
|
Point<T> point = *this;
|
2021-04-12 18:47:09 +00:00
|
|
|
point.translate_by(delta);
|
2019-03-31 20:09:10 +00:00
|
|
|
return point;
|
|
|
|
}
|
|
|
|
|
2021-06-16 17:26:50 +00:00
|
|
|
[[nodiscard]] Point<T> translated(T dx, T dy) const
|
2019-02-07 22:13:47 +00:00
|
|
|
{
|
2020-07-26 04:31:47 +00:00
|
|
|
Point<T> point = *this;
|
2021-04-12 18:47:09 +00:00
|
|
|
point.translate_by(dx, dy);
|
2019-02-07 22:13:47 +00:00
|
|
|
return point;
|
|
|
|
}
|
|
|
|
|
2021-06-16 17:26:50 +00:00
|
|
|
[[nodiscard]] Point<T> translated(T dboth) const
|
2020-07-26 04:31:47 +00:00
|
|
|
{
|
|
|
|
Point<T> point = *this;
|
2021-04-12 18:47:09 +00:00
|
|
|
point.translate_by(dboth, dboth);
|
|
|
|
return point;
|
|
|
|
}
|
|
|
|
|
2023-08-17 00:21:59 +00:00
|
|
|
[[nodiscard]] Point<T> scaled(T dboth) const
|
|
|
|
{
|
|
|
|
Point<T> point = *this;
|
|
|
|
point.scale_by(dboth);
|
|
|
|
return point;
|
|
|
|
}
|
|
|
|
|
2021-06-16 17:26:50 +00:00
|
|
|
[[nodiscard]] Point<T> scaled(Point<T> const& delta) const
|
2021-04-12 18:47:09 +00:00
|
|
|
{
|
|
|
|
Point<T> point = *this;
|
|
|
|
point.scale_by(delta);
|
|
|
|
return point;
|
|
|
|
}
|
|
|
|
|
2021-06-16 17:26:50 +00:00
|
|
|
[[nodiscard]] Point<T> scaled(T sx, T sy) const
|
2021-04-12 18:47:09 +00:00
|
|
|
{
|
|
|
|
Point<T> point = *this;
|
|
|
|
point.scale_by(sx, sy);
|
|
|
|
return point;
|
|
|
|
}
|
|
|
|
|
2021-06-16 17:26:50 +00:00
|
|
|
[[nodiscard]] Point<T> transformed(AffineTransform const& transform) const
|
2021-04-12 18:47:09 +00:00
|
|
|
{
|
|
|
|
Point<T> point = *this;
|
|
|
|
point.transform_by(transform);
|
2020-07-26 04:31:47 +00:00
|
|
|
return point;
|
|
|
|
}
|
|
|
|
|
2021-06-16 17:24:27 +00:00
|
|
|
void constrain(Rect<T> const&);
|
2021-06-16 17:26:50 +00:00
|
|
|
[[nodiscard]] Point<T> constrained(Rect<T> const& rect) const
|
2020-08-05 11:10:56 +00:00
|
|
|
{
|
|
|
|
Point<T> point = *this;
|
|
|
|
point.constrain(rect);
|
|
|
|
return point;
|
|
|
|
}
|
2019-01-12 00:00:24 +00:00
|
|
|
|
2021-06-16 17:26:50 +00:00
|
|
|
[[nodiscard]] Point<T> moved_left(T amount) const { return { x() - amount, y() }; }
|
|
|
|
[[nodiscard]] Point<T> moved_right(T amount) const { return { x() + amount, y() }; }
|
|
|
|
[[nodiscard]] Point<T> moved_up(T amount) const { return { x(), y() - amount }; }
|
|
|
|
[[nodiscard]] Point<T> moved_down(T amount) const { return { x(), y() + amount }; }
|
2021-04-12 18:47:09 +00:00
|
|
|
|
2021-01-22 20:12:37 +00:00
|
|
|
template<class U>
|
2021-06-16 17:26:50 +00:00
|
|
|
[[nodiscard]] bool operator==(Point<U> const& other) const
|
2018-10-11 23:03:22 +00:00
|
|
|
{
|
2021-01-22 20:12:37 +00:00
|
|
|
return x() == other.x() && y() == other.y();
|
2018-10-11 23:03:22 +00:00
|
|
|
}
|
|
|
|
|
2021-06-16 17:26:50 +00:00
|
|
|
[[nodiscard]] Point<T> operator+(Point<T> const& other) const { return { m_x + other.m_x, m_y + other.m_y }; }
|
2019-09-27 16:59:00 +00:00
|
|
|
|
2021-06-16 17:24:27 +00:00
|
|
|
Point<T>& operator+=(Point<T> const& other)
|
2019-09-27 16:59:00 +00:00
|
|
|
{
|
2020-07-26 04:31:47 +00:00
|
|
|
m_x += other.m_x;
|
|
|
|
m_y += other.m_y;
|
2019-09-27 16:59:00 +00:00
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2021-06-16 17:26:50 +00:00
|
|
|
[[nodiscard]] Point<T> operator-() const { return { -m_x, -m_y }; }
|
2020-07-26 04:31:47 +00:00
|
|
|
|
2021-06-16 17:26:50 +00:00
|
|
|
[[nodiscard]] Point<T> operator-(Point<T> const& other) const { return { m_x - other.m_x, m_y - other.m_y }; }
|
2020-07-26 04:31:47 +00:00
|
|
|
|
2021-06-16 17:24:27 +00:00
|
|
|
Point<T>& operator-=(Point<T> const& other)
|
2019-09-27 16:59:00 +00:00
|
|
|
{
|
2020-07-26 04:31:47 +00:00
|
|
|
m_x -= other.m_x;
|
|
|
|
m_y -= other.m_y;
|
2019-09-27 16:59:00 +00:00
|
|
|
return *this;
|
|
|
|
}
|
2019-03-09 15:54:41 +00:00
|
|
|
|
2021-06-16 17:26:50 +00:00
|
|
|
[[nodiscard]] Point<T> operator*(T factor) const { return { m_x * factor, m_y * factor }; }
|
2020-07-26 04:31:47 +00:00
|
|
|
|
|
|
|
Point<T>& operator*=(T factor)
|
2020-05-05 01:15:17 +00:00
|
|
|
{
|
|
|
|
m_x *= factor;
|
|
|
|
m_y *= factor;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2021-06-16 17:26:50 +00:00
|
|
|
[[nodiscard]] Point<T> operator/(T factor) const { return { m_x / factor, m_y / factor }; }
|
2020-07-26 04:31:47 +00:00
|
|
|
|
2020-12-18 16:16:17 +00:00
|
|
|
Point<T>& operator/=(T factor)
|
2020-05-05 01:15:17 +00:00
|
|
|
{
|
|
|
|
m_x /= factor;
|
|
|
|
m_y /= factor;
|
|
|
|
return *this;
|
|
|
|
}
|
2019-01-14 13:21:51 +00:00
|
|
|
|
2021-06-16 17:26:50 +00:00
|
|
|
[[nodiscard]] T primary_offset_for_orientation(Orientation orientation) const
|
2019-07-20 14:46:33 +00:00
|
|
|
{
|
|
|
|
return orientation == Orientation::Vertical ? y() : x();
|
|
|
|
}
|
|
|
|
|
2020-07-26 04:31:47 +00:00
|
|
|
void set_primary_offset_for_orientation(Orientation orientation, T value)
|
2019-07-20 14:46:33 +00:00
|
|
|
{
|
2020-07-26 04:31:47 +00:00
|
|
|
if (orientation == Orientation::Vertical) {
|
2019-07-20 14:46:33 +00:00
|
|
|
set_y(value);
|
2020-07-26 04:31:47 +00:00
|
|
|
} else {
|
2019-07-20 14:46:33 +00:00
|
|
|
set_x(value);
|
2020-07-26 04:31:47 +00:00
|
|
|
}
|
2019-07-20 14:46:33 +00:00
|
|
|
}
|
|
|
|
|
2021-06-16 17:26:50 +00:00
|
|
|
[[nodiscard]] T secondary_offset_for_orientation(Orientation orientation) const
|
2019-07-20 14:46:33 +00:00
|
|
|
{
|
|
|
|
return orientation == Orientation::Vertical ? x() : y();
|
|
|
|
}
|
|
|
|
|
2020-07-26 04:31:47 +00:00
|
|
|
void set_secondary_offset_for_orientation(Orientation orientation, T value)
|
2019-07-20 14:46:33 +00:00
|
|
|
{
|
2020-07-26 04:31:47 +00:00
|
|
|
if (orientation == Orientation::Vertical) {
|
2019-07-20 14:46:33 +00:00
|
|
|
set_x(value);
|
2020-07-26 04:31:47 +00:00
|
|
|
} else {
|
2019-07-20 14:46:33 +00:00
|
|
|
set_y(value);
|
2020-07-26 04:31:47 +00:00
|
|
|
}
|
2019-07-20 14:46:33 +00:00
|
|
|
}
|
|
|
|
|
2021-06-16 17:26:50 +00:00
|
|
|
[[nodiscard]] T dx_relative_to(Point<T> const& other) const
|
2019-12-26 07:21:03 +00:00
|
|
|
{
|
|
|
|
return x() - other.x();
|
|
|
|
}
|
|
|
|
|
2021-06-16 17:26:50 +00:00
|
|
|
[[nodiscard]] T dy_relative_to(Point<T> const& other) const
|
2019-12-26 07:21:03 +00:00
|
|
|
{
|
|
|
|
return y() - other.y();
|
|
|
|
}
|
|
|
|
|
2019-11-10 17:28:01 +00:00
|
|
|
// Returns pixels moved from other in either direction
|
2021-06-16 17:26:50 +00:00
|
|
|
[[nodiscard]] T pixels_moved(Point<T> const& other) const
|
2019-11-10 17:28:01 +00:00
|
|
|
{
|
AK+Userland: Add generic `AK::abs()` function and use it
Previously, in LibGFX's `Point` class, calculated distances were passed
to the integer `abs` function, even if the stored type was a float. This
caused the value to unexpectedly be truncated. Luckily, this API was not
used with floating point types, but that can change in the future, so
why not fix it now :^)
Since we are in C++, we can use function overloading to make things
easy, and to automatically use the right version.
This is even better than the LibC/LibM functions, as using a bit of
hackery, they are able to be constant-evaluated. They use compiler
intrinsics, so they do not depend on external code and the compiler can
emit the most optimized code by default.
Since we aren't using the C++ standard library's trick of importing
everything into the `AK` namespace, this `abs` function cannot be
exported to the global namespace, as the names would clash.
2021-07-05 16:38:17 +00:00
|
|
|
return max(AK::abs(dx_relative_to(other)), AK::abs(dy_relative_to(other)));
|
2019-11-10 17:28:01 +00:00
|
|
|
}
|
|
|
|
|
2021-06-16 17:26:50 +00:00
|
|
|
[[nodiscard]] float distance_from(Point<T> const& other) const
|
2020-07-26 04:31:47 +00:00
|
|
|
{
|
|
|
|
if (*this == other)
|
|
|
|
return 0;
|
2021-07-17 16:29:28 +00:00
|
|
|
return AK::hypot<float>(m_x - other.m_x, m_y - other.m_y);
|
2020-07-26 04:31:47 +00:00
|
|
|
}
|
|
|
|
|
2021-06-16 17:26:50 +00:00
|
|
|
[[nodiscard]] Point absolute_relative_distance_to(Point const& other) const
|
2020-11-30 06:30:15 +00:00
|
|
|
{
|
AK+Userland: Add generic `AK::abs()` function and use it
Previously, in LibGFX's `Point` class, calculated distances were passed
to the integer `abs` function, even if the stored type was a float. This
caused the value to unexpectedly be truncated. Luckily, this API was not
used with floating point types, but that can change in the future, so
why not fix it now :^)
Since we are in C++, we can use function overloading to make things
easy, and to automatically use the right version.
This is even better than the LibC/LibM functions, as using a bit of
hackery, they are able to be constant-evaluated. They use compiler
intrinsics, so they do not depend on external code and the compiler can
emit the most optimized code by default.
Since we aren't using the C++ standard library's trick of importing
everything into the `AK` namespace, this `abs` function cannot be
exported to the global namespace, as the names would clash.
2021-07-05 16:38:17 +00:00
|
|
|
return { AK::abs(dx_relative_to(other)), AK::abs(dy_relative_to(other)) };
|
2020-11-30 06:30:15 +00:00
|
|
|
}
|
|
|
|
|
2021-09-15 23:34:50 +00:00
|
|
|
[[nodiscard]] Point end_point_for_aspect_ratio(Point const& previous_end_point, float aspect_ratio) const;
|
2021-08-24 21:39:07 +00:00
|
|
|
|
2020-07-26 04:31:47 +00:00
|
|
|
template<typename U>
|
2022-11-24 16:04:45 +00:00
|
|
|
requires(!IsSame<T, U>)
|
2022-10-16 22:06:11 +00:00
|
|
|
[[nodiscard]] Point<U> to_type() const
|
2020-07-26 04:31:47 +00:00
|
|
|
{
|
|
|
|
return Point<U>(*this);
|
|
|
|
}
|
|
|
|
|
2021-09-18 10:03:36 +00:00
|
|
|
template<typename U>
|
|
|
|
[[nodiscard]] Point<U> to_rounded() const
|
|
|
|
{
|
|
|
|
return Point<U>(roundf(x()), roundf(y()));
|
|
|
|
}
|
|
|
|
|
2022-08-29 20:47:33 +00:00
|
|
|
template<typename U>
|
|
|
|
requires FloatingPoint<T>
|
|
|
|
[[nodiscard]] Point<U> to_ceiled() const
|
|
|
|
{
|
|
|
|
return Point<U>(ceil(x()), ceil(y()));
|
|
|
|
}
|
|
|
|
|
2023-04-07 12:25:15 +00:00
|
|
|
template<typename U>
|
|
|
|
requires FloatingPoint<T>
|
|
|
|
[[nodiscard]] Point<U> to_floored() const
|
|
|
|
{
|
|
|
|
return Point<U>(AK::floor(x()), AK::floor(y()));
|
|
|
|
}
|
|
|
|
|
2023-12-16 14:19:34 +00:00
|
|
|
[[nodiscard]] ByteString to_byte_string() const;
|
2020-07-26 04:31:47 +00:00
|
|
|
|
2018-10-10 18:06:58 +00:00
|
|
|
private:
|
2020-07-26 04:31:47 +00:00
|
|
|
T m_x { 0 };
|
|
|
|
T m_y { 0 };
|
2018-10-10 18:06:58 +00:00
|
|
|
};
|
2020-07-26 04:31:47 +00:00
|
|
|
using IntPoint = Point<int>;
|
|
|
|
using FloatPoint = Point<float>;
|
2020-02-06 10:56:38 +00:00
|
|
|
|
2021-09-15 17:46:52 +00:00
|
|
|
template<typename T>
|
|
|
|
inline Point<T> linear_interpolate(Point<T> const& p1, Point<T> const& p2, float t)
|
|
|
|
{
|
|
|
|
return Point<T> { p1.x() + t * (p2.x() - p1.x()), p1.y() + t * (p2.y() - p1.y()) };
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
inline Point<T> quadratic_interpolate(Point<T> const& p1, Point<T> const& p2, Point<T> const& c1, float t)
|
|
|
|
{
|
|
|
|
return linear_interpolate(linear_interpolate(p1, c1, t), linear_interpolate(c1, p2, t), t);
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
inline Point<T> cubic_interpolate(Point<T> const& p1, Point<T> const& p2, Point<T> const& c1, Point<T> const& c2, float t)
|
|
|
|
{
|
|
|
|
return linear_interpolate(quadratic_interpolate(p1, c1, c2, t), quadratic_interpolate(c1, c2, p2, t), t);
|
|
|
|
}
|
|
|
|
|
2020-02-06 10:56:38 +00:00
|
|
|
}
|
2020-02-15 11:04:35 +00:00
|
|
|
|
2021-01-03 14:26:47 +00:00
|
|
|
namespace AK {
|
|
|
|
|
|
|
|
template<typename T>
|
2022-10-26 19:26:14 +00:00
|
|
|
struct Formatter<Gfx::Point<T>> : Formatter<FormatString> {
|
2021-11-16 00:15:21 +00:00
|
|
|
ErrorOr<void> format(FormatBuilder& builder, Gfx::Point<T> const& value)
|
2021-01-03 14:26:47 +00:00
|
|
|
{
|
2022-10-26 19:26:14 +00:00
|
|
|
return Formatter<FormatString>::format(builder, "[{},{}]"sv, value.x(), value.y());
|
2021-01-03 14:26:47 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-02-15 11:04:35 +00:00
|
|
|
namespace IPC {
|
2020-07-26 04:31:47 +00:00
|
|
|
|
2022-11-15 16:24:59 +00:00
|
|
|
template<>
|
2023-01-02 04:37:35 +00:00
|
|
|
ErrorOr<void> encode(Encoder&, Gfx::IntPoint const&);
|
2024-02-14 06:07:55 +00:00
|
|
|
template<>
|
|
|
|
ErrorOr<void> encode(Encoder&, Gfx::FloatPoint const&);
|
2022-11-15 16:24:59 +00:00
|
|
|
|
|
|
|
template<>
|
2022-12-23 01:40:33 +00:00
|
|
|
ErrorOr<Gfx::IntPoint> decode(Decoder&);
|
2024-02-14 06:07:55 +00:00
|
|
|
template<>
|
|
|
|
ErrorOr<Gfx::FloatPoint> decode(Decoder&);
|
2020-07-26 04:31:47 +00:00
|
|
|
|
2020-02-15 11:04:35 +00:00
|
|
|
}
|
2021-09-04 23:15:16 +00:00
|
|
|
|
|
|
|
template<typename T>
|
2023-11-08 19:29:12 +00:00
|
|
|
struct AK::Traits<Gfx::Point<T>> : public AK::DefaultTraits<Gfx::Point<T>> {
|
2021-09-04 23:15:16 +00:00
|
|
|
static constexpr bool is_trivial() { return false; }
|
|
|
|
static unsigned hash(Gfx::Point<T> const& point)
|
|
|
|
{
|
|
|
|
return pair_int_hash(AK::Traits<T>::hash(point.x()), AK::Traits<T>::hash(point.y()));
|
|
|
|
}
|
|
|
|
};
|