File indexing completed on 2024-05-05 04:34:53

0001 /* SPDX-FileCopyrightText: 2023 Noah Davis <noahadvs@gmail.com>
0002  * SPDX-License-Identifier: LGPL-2.0-or-later
0003  */
0004 
0005 #include "Geometry.h"
0006 
0007 #include <KWindowSystem>
0008 
0009 #include <QDebug>
0010 #include <QGuiApplication>
0011 #include <QScreen>
0012 #include <QWindow>
0013 
0014 #include <cmath>
0015 
0016 class GeometrySingleton
0017 {
0018 public:
0019     Geometry self;
0020 };
0021 
0022 Q_GLOBAL_STATIC(GeometrySingleton, privateGeometrySelf)
0023 
0024 Geometry::Geometry(QObject *parent)
0025     : QObject(parent)
0026 {}
0027 
0028 Geometry *Geometry::instance()
0029 {
0030     return &privateGeometrySelf->self;
0031 }
0032 
0033 qreal Geometry::dpx(qreal dpr)
0034 {
0035     return 1 / dpr;
0036 }
0037 
0038 qreal Geometry::dprRound(qreal value, qreal dpr)
0039 {
0040     return std::round(value * dpr) / dpr;
0041 }
0042 
0043 qreal Geometry::mapFromPlatformValue(qreal value, qreal dpr)
0044 {
0045     if (KWindowSystem::isPlatformX11()) {
0046         return value / dpr;
0047     }
0048     return value;
0049 }
0050 
0051 QPointF Geometry::mapFromPlatformPoint(const QPointF &point, qreal dpr)
0052 {
0053     if (KWindowSystem::isPlatformX11()) {
0054         return point / dpr;
0055     }
0056     return point;
0057 }
0058 
0059 QRectF Geometry::mapFromPlatformRect(const QRectF &rect, qreal dpr)
0060 {
0061     if (rect.isEmpty() || KWindowSystem::isPlatformWayland()) {
0062         return rect;
0063     }
0064 
0065     // Make the position scaled like Wayland.
0066     return {rect.topLeft() / dpr, rect.size()};
0067 }
0068 
0069 QRectF Geometry::logicalScreensRect()
0070 {
0071     QRectF rect;
0072     const auto &screens = qGuiApp->screens();
0073     for (int i = 0; i < screens.size(); ++i) {
0074         rect |= Geometry::mapFromPlatformRect(screens[i]->geometry(), qGuiApp->devicePixelRatio());
0075     }
0076     return rect;
0077 }
0078 
0079 qreal Geometry::mapToPlatformValue(qreal value, qreal dpr)
0080 {
0081     if (KWindowSystem::isPlatformX11()) {
0082         return value * dpr;
0083     }
0084     return value;
0085 }
0086 
0087 QPointF Geometry::mapToPlatformPoint(const QPointF &point, qreal dpr)
0088 {
0089     if (KWindowSystem::isPlatformX11()) {
0090         return point * dpr;
0091     }
0092     return point;
0093 }
0094 
0095 QRectF Geometry::mapToPlatformRect(const QRectF &rect, qreal dpr)
0096 {
0097     if (rect.isEmpty() || KWindowSystem::isPlatformWayland()) {
0098         return rect;
0099     }
0100     return {rect.topLeft() * dpr, rect.size()};
0101 }
0102 
0103 QRectF Geometry::platformUnifiedRect()
0104 {
0105     QRectF rect;
0106     const auto &screens = qGuiApp->screens();
0107     for (int i = 0; i < screens.size(); ++i) {
0108         rect |= screens[i]->geometry();
0109     }
0110     return rect;
0111 }
0112 
0113 QSize Geometry::rawSize(const QSizeF &size, qreal dpr)
0114 {
0115     return (size * dpr).toSize();
0116 }
0117 
0118 QRectF Geometry::rectAdjusted(const QRectF &rect, qreal xp1, qreal yp1, qreal xp2, qreal yp2)
0119 {
0120     return rect.adjusted(xp1, yp1, xp2, yp2);
0121 }
0122 
0123 QRectF Geometry::rectAdjustedVisually(const QRectF &rect, qreal xp1, qreal yp1, qreal xp2, qreal yp2)
0124 {
0125     if (rect.width() < 0) {
0126         std::swap(xp1, xp2);
0127     }
0128     if (rect.height() < 0) {
0129         std::swap(yp1, yp2);
0130     }
0131     return rect.adjusted(xp1, yp1, xp2, yp2);
0132 }
0133 
0134 QRectF Geometry::rectScaled(const QRectF &rect, qreal scale)
0135 {
0136     if (scale == 1) {
0137         return rect;
0138     }
0139     return {rect.topLeft() * scale, rect.size() * scale};
0140 }
0141 
0142 QRectF Geometry::rectIntersected(const QRectF &rect1, const QRectF &rect2)
0143 {
0144     return rect1.intersected(rect2);
0145 }
0146 
0147 QRectF Geometry::rectBounded(const QRectF &rect, const QRectF &boundsRect,
0148                              Qt::Orientations orientations)
0149 {
0150     if (rect == boundsRect) {
0151         return rect;
0152     }
0153     auto newRect = rect;
0154     const auto &nBoundsRect = boundsRect.normalized(); // normalize to make math easier
0155     if (orientations & Qt::Horizontal) {
0156         if (rect.width() >= 0) {
0157             newRect.moveLeft(std::clamp(rect.x(), nBoundsRect.x(), nBoundsRect.right() - rect.width()));
0158         } else {
0159             newRect.moveRight(std::clamp(rect.right(), nBoundsRect.x(), nBoundsRect.right() - std::abs(rect.width())));
0160         }
0161     }
0162     if (orientations & Qt::Vertical) {
0163         if (rect.height() >= 0) {
0164             newRect.moveTop(std::clamp(rect.y(), nBoundsRect.y(), nBoundsRect.bottom() - std::abs(rect.height())));
0165         } else {
0166             newRect.moveBottom(std::clamp(rect.bottom(), nBoundsRect.y(), nBoundsRect.bottom() - std::abs(rect.height())));
0167         }
0168     }
0169     return newRect;
0170 }
0171 
0172 QRectF Geometry::rectBounded(qreal x, qreal y, qreal width, qreal height, const QRectF &boundsRect,
0173                              Qt::Orientations orientations)
0174 {
0175     return rectBounded({x, y, width, height}, boundsRect, orientations);
0176 }
0177 
0178 QRectF Geometry::rectClipped(const QRectF &rect, const QRectF &clipRect,
0179                              Qt::Orientations orientations)
0180 {
0181     if (rect == clipRect) {
0182         return rect;
0183     }
0184     auto newRect = rect;
0185     const auto &nClipRect = clipRect.normalized(); // normalize to make math easier
0186     if (orientations & Qt::Horizontal) {
0187         if (rect.width() >= 0) {
0188             newRect.setLeft(std::max(rect.x(), nClipRect.x()));
0189             newRect.setRight(std::min(rect.right(), nClipRect.right()));
0190         } else {
0191             newRect.setLeft(std::min(rect.x(), nClipRect.right()));
0192             newRect.setRight(std::max(rect.right(), nClipRect.x()));
0193         }
0194     }
0195     if (orientations & Qt::Vertical) {
0196         if (rect.height() >= 0) {
0197             newRect.setTop(std::max(rect.y(), nClipRect.y()));
0198             newRect.setBottom(std::min(rect.bottom(), nClipRect.bottom()));
0199         } else {
0200             newRect.setTop(std::min(rect.y(), nClipRect.bottom()));
0201             newRect.setBottom(std::max(rect.bottom(), nClipRect.y()));
0202         }
0203     }
0204     return newRect;
0205 }
0206 
0207 bool Geometry::rectContains(const QRectF &rect, qreal v, Qt::Orientations orientations)
0208 {
0209     Q_ASSERT(orientations != Qt::Orientations{});
0210     bool contains = true;
0211     if (orientations.testFlag(Qt::Horizontal)) {
0212         contains &= std::min(rect.left(), rect.right()) <= v && v <= std::max(rect.left(), rect.right());
0213     }
0214     if (contains && orientations.testFlag(Qt::Vertical)) {
0215         contains &= std::min(rect.top(), rect.bottom()) <= v && v <= std::max(rect.top(), rect.bottom());
0216     }
0217     return contains;
0218 }
0219 
0220 bool Geometry::rectContains(const QRectF &rect, qreal x, qreal y)
0221 {
0222     return rect.contains(x, y);
0223 }
0224 
0225 bool Geometry::rectContains(const QRectF &rect, const QPointF &point)
0226 {
0227     return rect.contains(point);
0228 }
0229 
0230 bool Geometry::rectContains(const QRectF &rect1, const QRectF& rect2)
0231 {
0232     return rect1.contains(rect2);
0233 }
0234 
0235 bool Geometry::rectContains(const QRectF &rect, qreal x, qreal y, qreal w, qreal h)
0236 {
0237     return rect.contains({x, y, w, h});
0238 }
0239 
0240 bool Geometry::ellipseContains(qreal ellipseX, qreal ellipseY,
0241                                qreal ellipseWidth, qreal ellipseHeight,
0242                                qreal x, qreal y)
0243 {
0244     if (isEmpty(ellipseWidth, ellipseHeight)) {
0245         return false;
0246     }
0247     auto xRadius = ellipseWidth / 2;
0248     auto yRadius = ellipseHeight / 2;
0249     auto centerX = ellipseX + xRadius;
0250     auto centerY = ellipseY + yRadius;
0251     // is inside or on edge
0252     return std::pow(x - centerX, 2) / std::pow(xRadius, 2)
0253          + std::pow(y - centerY, 2) / std::pow(yRadius, 2) <= 1;
0254 }
0255 
0256 bool Geometry::ellipseContains(const QRectF &rect, qreal x, qreal y)
0257 {
0258     return ellipseContains(rect.x(), rect.y(), rect.width(), rect.height(), x, y);
0259 }
0260 
0261 bool Geometry::ellipseContains(const QRectF &rect, const QPointF &point)
0262 {
0263     return ellipseContains(rect, point.x(), point.y());
0264 }
0265 
0266 bool Geometry::rectIntersects(const QRectF &rect1, const QRectF& rect2)
0267 {
0268     return rect1.intersects(rect2);
0269 }
0270 
0271 bool Geometry::rectIntersects(const QRectF &rect, qreal x, qreal y, qreal w, qreal h)
0272 {
0273     return rect.intersects({x, y, w, h});
0274 }
0275 
0276 bool Geometry::isEmpty(qreal w, qreal h)
0277 {
0278     return w <= 0 || h <= 0;
0279 }
0280 
0281 bool Geometry::isEmpty(const QSizeF &size)
0282 {
0283     return size.isEmpty();
0284 }
0285 
0286 bool Geometry::isEmpty(const QRectF &rect)
0287 {
0288     return rect.isEmpty();
0289 }
0290 
0291 bool Geometry::isNull(qreal w, qreal h)
0292 {
0293     return qIsNull(w) && qIsNull(h);
0294 }
0295 
0296 bool Geometry::isNull(const QSizeF &size)
0297 {
0298     return size.isNull();
0299 }
0300 
0301 bool Geometry::isNull(const QRectF &rect)
0302 {
0303     return rect.isNull();
0304 }
0305 
0306 #include <moc_Geometry.cpp>