File indexing completed on 2024-04-28 04:32:36

0001 /*
0002     SPDX-FileCopyrightText: 2004-05 Enrico Ros <eros.kde@email.it>
0003     SPDX-FileCopyrightText: 2005 Piotr Szymanski <niedakh@gmail.com>
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "area.h"
0008 
0009 #include <QPolygonF>
0010 #include <QRect>
0011 
0012 #include "action.h"
0013 #include "annotations.h"
0014 #include "annotations_p.h"
0015 #include "debug_p.h"
0016 #include "sourcereference.h"
0017 
0018 using namespace Okular;
0019 
0020 /** class NormalizedPoint **/
0021 NormalizedPoint::NormalizedPoint()
0022     : x(0.0)
0023     , y(0.0)
0024 {
0025 }
0026 
0027 NormalizedPoint::NormalizedPoint(double dX, double dY)
0028     : x(dX)
0029     , y(dY)
0030 {
0031 }
0032 
0033 NormalizedPoint::NormalizedPoint(int iX, int iY, int xScale, int yScale)
0034     : x((double)iX / (double)xScale)
0035     , y((double)iY / (double)yScale)
0036 {
0037 }
0038 
0039 NormalizedPoint &NormalizedPoint::operator=(const NormalizedPoint &p) = default;
0040 NormalizedPoint::NormalizedPoint(const NormalizedPoint &) = default;
0041 
0042 void NormalizedPoint::transform(const QTransform &matrix)
0043 {
0044     qreal tmp_x = (qreal)x;
0045     qreal tmp_y = (qreal)y;
0046     matrix.map(tmp_x, tmp_y, &tmp_x, &tmp_y);
0047     x = tmp_x;
0048     y = tmp_y;
0049 }
0050 
0051 double NormalizedPoint::distanceSqr(double x, double y, double xScale, double yScale) const
0052 {
0053     return pow((this->x - x) * xScale, 2) + pow((this->y - y) * yScale, 2);
0054 }
0055 
0056 /**
0057  * Returns a vector from the given points @p a and @p b
0058  * @internal
0059  */
0060 NormalizedPoint operator-(const NormalizedPoint &a, const NormalizedPoint &b)
0061 {
0062     return NormalizedPoint(a.x - b.x, a.y - b.y);
0063 }
0064 
0065 /**
0066  * @brief Calculates distance of the point @p x @p y @p xScale @p yScale to the line segment from @p start to @p end
0067  */
0068 double NormalizedPoint::distanceSqr(double x, double y, double xScale, double yScale, const NormalizedPoint &start, const NormalizedPoint &end)
0069 {
0070     NormalizedPoint point(x, y);
0071     double thisDistance;
0072     NormalizedPoint lineSegment(end - start);
0073     const double lengthSqr = pow(lineSegment.x, 2) + pow(lineSegment.y, 2);
0074 
0075     // if the length of the current segment is null, we can just
0076     // measure the distance to either end point
0077     if (lengthSqr == 0.0) {
0078         thisDistance = end.distanceSqr(x, y, xScale, yScale);
0079     } else {
0080         // vector from the start point of the current line segment to the measurement point
0081         NormalizedPoint a = point - start;
0082         // vector from the same start point to the end point of the current line segment
0083         NormalizedPoint b = end - start;
0084 
0085         // we're using a * b (dot product) := |a| * |b| * cos(phi) and the knowledge
0086         // that cos(phi) is adjacent side / hypotenuse (hypotenuse = |b|)
0087         // therefore, t becomes the length of the vector that represents the projection of
0088         // the point p onto the current line segment
0089         //(hint: if this is still unclear, draw it!)
0090         float t = (a.x * b.x + a.y * b.y) / lengthSqr;
0091 
0092         if (t < 0) {
0093             // projection falls outside the line segment on the side of "start"
0094             thisDistance = point.distanceSqr(start.x, start.y, xScale, yScale);
0095         } else if (t > 1) {
0096             // projection falls outside the line segment on the side of the current point
0097             thisDistance = point.distanceSqr(end.x, end.y, xScale, yScale);
0098         } else {
0099             // projection is within [start, *i];
0100             // determine the length of the perpendicular distance from the projection to the actual point
0101             NormalizedPoint direction = end - start;
0102             NormalizedPoint projection = start - NormalizedPoint(-t * direction.x, -t * direction.y);
0103             thisDistance = projection.distanceSqr(x, y, xScale, yScale);
0104         }
0105     }
0106     return thisDistance;
0107 }
0108 
0109 QDebug operator<<(QDebug str, const Okular::NormalizedPoint &p)
0110 {
0111     str.nospace() << "NormPt(" << p.x << "," << p.y << ")";
0112     return str.space();
0113 }
0114 
0115 /** class NormalizedRect **/
0116 
0117 NormalizedRect::NormalizedRect()
0118     : left(0.0)
0119     , top(0.0)
0120     , right(0.0)
0121     , bottom(0.0)
0122 {
0123 }
0124 
0125 NormalizedRect::NormalizedRect(double l, double t, double r, double b)
0126     // note: check for swapping coords?
0127     : left(l)
0128     , top(t)
0129     , right(r)
0130     , bottom(b)
0131 {
0132 }
0133 
0134 NormalizedRect::NormalizedRect(const QRect r, double xScale, double yScale)
0135     : left((double)r.left() / xScale)
0136     , top((double)r.top() / yScale)
0137     , right((double)r.right() / xScale)
0138     , bottom((double)r.bottom() / yScale)
0139 {
0140 }
0141 
0142 NormalizedRect::NormalizedRect(const NormalizedRect &rect) = default;
0143 
0144 NormalizedRect NormalizedRect::fromQRectF(const QRectF &rect)
0145 {
0146     QRectF nrect = rect.normalized();
0147     NormalizedRect ret;
0148     ret.left = nrect.left();
0149     ret.top = nrect.top();
0150     ret.right = nrect.right();
0151     ret.bottom = nrect.bottom();
0152     return ret;
0153 }
0154 
0155 bool NormalizedRect::isNull() const
0156 {
0157     return left == 0 && top == 0 && right == 0 && bottom == 0;
0158 }
0159 
0160 bool NormalizedRect::contains(double x, double y) const
0161 {
0162     return x >= left && x <= right && y >= top && y <= bottom;
0163 }
0164 
0165 bool NormalizedRect::intersects(const NormalizedRect &r) const
0166 {
0167     return (r.left <= right) && (r.right >= left) && (r.top <= bottom) && (r.bottom >= top);
0168 }
0169 
0170 bool NormalizedRect::intersects(const NormalizedRect *r) const
0171 {
0172     return (r->left <= right) && (r->right >= left) && (r->top <= bottom) && (r->bottom >= top);
0173 }
0174 
0175 bool NormalizedRect::intersects(double l, double t, double r, double b) const
0176 {
0177     return (l <= right) && (r >= left) && (t <= bottom) && (b >= top);
0178 }
0179 
0180 NormalizedRect NormalizedRect::operator|(const NormalizedRect &r) const
0181 {
0182     NormalizedRect ret;
0183     ret.left = qMin(left, r.left);
0184     ret.top = qMin(top, r.top);
0185     ret.bottom = qMax(bottom, r.bottom);
0186     ret.right = qMax(right, r.right);
0187     return ret;
0188 }
0189 
0190 NormalizedRect &NormalizedRect::operator|=(const NormalizedRect &r)
0191 {
0192     left = qMin(left, r.left);
0193     top = qMin(top, r.top);
0194     bottom = qMax(bottom, r.bottom);
0195     right = qMax(right, r.right);
0196     return *this;
0197 }
0198 
0199 NormalizedRect NormalizedRect::operator&(const NormalizedRect &r) const
0200 {
0201     if (isNull() || r.isNull()) {
0202         return NormalizedRect();
0203     }
0204 
0205     NormalizedRect ret;
0206     ret.left = qMax(left, r.left);
0207     ret.top = qMax(top, r.top);
0208     ret.bottom = qMin(bottom, r.bottom);
0209     ret.right = qMin(right, r.right);
0210     return ret;
0211 }
0212 
0213 NormalizedRect &NormalizedRect::operator=(const NormalizedRect &r) = default;
0214 
0215 bool NormalizedRect::operator==(const NormalizedRect &r) const
0216 {
0217     return (isNull() && r.isNull()) || (fabs(left - r.left) < 1e-4 && fabs(right - r.right) < 1e-4 && fabs(top - r.top) < 1e-4 && fabs(bottom - r.bottom) < 1e-4);
0218 }
0219 
0220 NormalizedPoint NormalizedRect::center() const
0221 {
0222     return NormalizedPoint((left + right) / 2.0, (top + bottom) / 2.0);
0223 }
0224 
0225 /*
0226 QDebug operator << (QDebug str , const NormalizedRect &r)
0227 {
0228     str << "[" <<r.left() << "," << r.top() << "] x "<< "[" <<r.right() << "," << r.bottom() << "]";
0229     return str;
0230 }*/
0231 
0232 QRect NormalizedRect::geometry(int xScale, int yScale) const
0233 {
0234     int l = (int)(left * xScale), t = (int)(top * yScale), r = (int)(right * xScale), b = (int)(bottom * yScale);
0235 
0236     return QRect(l, t, r - l + 1, b - t + 1);
0237 }
0238 
0239 QRect NormalizedRect::roundedGeometry(int xScale, int yScale) const
0240 {
0241     int l = (int)(left * xScale + 0.5), t = (int)(top * yScale + 0.5), r = (int)(right * xScale + 0.5), b = (int)(bottom * yScale + 0.5);
0242 
0243     return QRect(l, t, r - l + 1, b - t + 1);
0244 }
0245 
0246 QRectF NormalizedRect::geometryF(float xScale, float yScale) const
0247 {
0248     float l = (left * xScale), t = (top * yScale), r = (right * xScale), b = (bottom * yScale);
0249 
0250     return QRectF(l, t, r - l, b - t);
0251 }
0252 
0253 void NormalizedRect::transform(const QTransform &matrix)
0254 {
0255     QRectF rect(left, top, right - left, bottom - top);
0256     rect = matrix.mapRect(rect);
0257 
0258     left = rect.left();
0259     top = rect.top();
0260     right = rect.right();
0261     bottom = rect.bottom();
0262 }
0263 
0264 size_t Okular::qHash(const NormalizedRect &r, size_t seed)
0265 {
0266     return ::qHashMulti(seed, r.bottom, r.right, r.top, r.left);
0267 }
0268 
0269 QDebug operator<<(QDebug str, const Okular::NormalizedRect &r)
0270 {
0271     str.nospace() << "NormRect(" << r.left << "," << r.top << " x " << (r.right - r.left) << "+" << (r.bottom - r.top) << ")";
0272     return str.space();
0273 }
0274 
0275 RegularAreaRect::RegularAreaRect()
0276     : RegularArea<NormalizedRect, QRect>()
0277     , d(nullptr)
0278 {
0279 }
0280 
0281 RegularAreaRect::RegularAreaRect(const RegularAreaRect &rar)
0282     : RegularArea<NormalizedRect, QRect>(rar)
0283     , d(nullptr)
0284 {
0285 }
0286 
0287 RegularAreaRect::~RegularAreaRect()
0288 {
0289 }
0290 
0291 RegularAreaRect &RegularAreaRect::operator=(const RegularAreaRect &rar)
0292 {
0293     if (this != &rar) {
0294         RegularArea<NormalizedRect, QRect>::operator=(rar);
0295     }
0296     return *this;
0297 }
0298 
0299 HighlightAreaRect::HighlightAreaRect(const RegularAreaRect *area)
0300     : RegularAreaRect()
0301     , s_id(-1)
0302 {
0303     if (area) {
0304         RegularAreaRect::ConstIterator it = area->begin();
0305         RegularAreaRect::ConstIterator itEnd = area->end();
0306         for (; it != itEnd; ++it) {
0307             append(NormalizedRect(*it));
0308         }
0309     }
0310 }
0311 
0312 /** class ObjectRect **/
0313 
0314 ObjectRect::ObjectRect(double l, double t, double r, double b, bool ellipse, ObjectType type, void *object)
0315     : m_objectType(type)
0316     , m_object(object)
0317 {
0318     // assign coordinates swapping them if negative width or height
0319     QRectF rect(r > l ? l : r, b > t ? t : b, fabs(r - l), fabs(b - t));
0320     if (ellipse) {
0321         m_path.addEllipse(rect);
0322     } else {
0323         m_path.addRect(rect);
0324     }
0325 
0326     m_transformedPath = m_path;
0327 }
0328 
0329 ObjectRect::ObjectRect(const NormalizedRect &r, bool ellipse, ObjectType type, void *object)
0330     : m_objectType(type)
0331     , m_object(object)
0332 {
0333     QRectF rect(r.left, r.top, fabs(r.right - r.left), fabs(r.bottom - r.top));
0334     if (ellipse) {
0335         m_path.addEllipse(rect);
0336     } else {
0337         m_path.addRect(rect);
0338     }
0339 
0340     m_transformedPath = m_path;
0341 }
0342 
0343 ObjectRect::ObjectRect(const QPolygonF &poly, ObjectType type, void *object)
0344     : m_objectType(type)
0345     , m_object(object)
0346 {
0347     m_path.addPolygon(poly);
0348 
0349     m_transformedPath = m_path;
0350 }
0351 
0352 ObjectRect::ObjectType ObjectRect::objectType() const
0353 {
0354     return m_objectType;
0355 }
0356 
0357 const void *ObjectRect::object() const
0358 {
0359     return m_object;
0360 }
0361 
0362 const QPainterPath &ObjectRect::region() const
0363 {
0364     return m_transformedPath;
0365 }
0366 
0367 QRect ObjectRect::boundingRect(double xScale, double yScale) const
0368 {
0369     const QRectF &br = m_transformedPath.boundingRect();
0370 
0371     return QRect((int)(br.left() * xScale), (int)(br.top() * yScale), (int)(br.width() * xScale), (int)(br.height() * yScale));
0372 }
0373 
0374 bool ObjectRect::contains(double x, double y, double, double) const
0375 {
0376     return m_transformedPath.contains(QPointF(x, y));
0377 }
0378 
0379 void ObjectRect::transform(const QTransform &matrix)
0380 {
0381     m_transformedPath = matrix.map(m_path);
0382 }
0383 
0384 double ObjectRect::distanceSqr(double x, double y, double xScale, double yScale) const
0385 {
0386     switch (m_objectType) {
0387     case Action:
0388     case Image: {
0389         const QRectF &rect(m_transformedPath.boundingRect());
0390         return NormalizedRect(rect.x(), rect.y(), rect.right(), rect.bottom()).distanceSqr(x, y, xScale, yScale);
0391     }
0392     case OAnnotation: {
0393         return static_cast<Annotation *>(m_object)->d_func()->distanceSqr(x, y, xScale, yScale);
0394     }
0395     case SourceRef: {
0396         const SourceRefObjectRect *sr = static_cast<const SourceRefObjectRect *>(this);
0397         const NormalizedPoint &point = sr->m_point;
0398         if (point.x == -1.0) {
0399             return pow((y - point.y) * yScale, 2);
0400         } else if (point.y == -1.0) {
0401             return pow((x - point.x) * xScale, 2);
0402         } else {
0403             return pow((x - point.x) * xScale, 2) + pow((y - point.y) * yScale, 2);
0404         }
0405     }
0406     }
0407     return 0.0;
0408 }
0409 
0410 ObjectRect::~ObjectRect()
0411 {
0412     if (!m_object) {
0413         return;
0414     }
0415 
0416     if (m_objectType == Action) {
0417         delete static_cast<Okular::Action *>(m_object);
0418     } else if (m_objectType == SourceRef) {
0419         delete static_cast<Okular::SourceReference *>(m_object);
0420     } else {
0421         qCDebug(OkularCoreDebug).nospace() << "Object deletion not implemented for type '" << m_objectType << "'.";
0422     }
0423 }
0424 
0425 /** class AnnotationObjectRect **/
0426 
0427 AnnotationObjectRect::AnnotationObjectRect(Annotation *annotation)
0428     : ObjectRect(QPolygonF(), OAnnotation, annotation)
0429     , m_annotation(annotation)
0430 {
0431 }
0432 
0433 Annotation *AnnotationObjectRect::annotation() const
0434 {
0435     return m_annotation;
0436 }
0437 
0438 QRect AnnotationObjectRect::boundingRect(double xScale, double yScale) const
0439 {
0440     const QRect annotRect = AnnotationUtils::annotationGeometry(m_annotation, xScale, yScale);
0441     const QPoint center = annotRect.center();
0442 
0443     // Make sure that the rectangle has a minimum size, so that it's possible
0444     // to click on it
0445     const int minSize = 14;
0446     const QRect minRect(center.x() - minSize / 2, center.y() - minSize / 2, minSize, minSize);
0447 
0448     return annotRect | minRect;
0449 }
0450 
0451 bool AnnotationObjectRect::contains(double x, double y, double xScale, double yScale) const
0452 {
0453     return boundingRect(xScale, yScale).contains((int)(x * xScale), (int)(y * yScale), false);
0454 }
0455 
0456 AnnotationObjectRect::~AnnotationObjectRect()
0457 {
0458     // the annotation pointer is kept elsewehere (in Page, most probably),
0459     // so just release its pointer
0460     m_object = nullptr;
0461 }
0462 
0463 void AnnotationObjectRect::transform(const QTransform &matrix)
0464 {
0465     m_annotation->d_func()->annotationTransform(matrix);
0466 }
0467 
0468 /** class SourceRefObjectRect **/
0469 
0470 SourceRefObjectRect::SourceRefObjectRect(const NormalizedPoint &point, void *srcRef)
0471     : ObjectRect(point.x, point.y, .0, .0, false, SourceRef, srcRef)
0472     , m_point(point)
0473 {
0474     const double x = m_point.x < 0.0 ? 0.5 : m_point.x;
0475     const double y = m_point.y < 0.0 ? 0.5 : m_point.y;
0476     const QRectF rect(x - 2, y - 2, 5, 5);
0477     m_path.addRect(rect);
0478 
0479     m_transformedPath = m_path;
0480 }
0481 
0482 QRect SourceRefObjectRect::boundingRect(double xScale, double yScale) const
0483 {
0484     const double x = m_point.x < 0.0 ? 0.5 : m_point.x;
0485     const double y = m_point.y < 0.0 ? 0.5 : m_point.y;
0486 
0487     return QRect(x * xScale, y * yScale, 1, 1);
0488 }
0489 
0490 bool SourceRefObjectRect::contains(double x, double y, double xScale, double yScale) const
0491 {
0492     return distanceSqr(x, y, xScale, yScale) < (pow(7.0 / xScale, 2) + pow(7.0 / yScale, 2));
0493 }
0494 
0495 /** class NonOwningObjectRect **/
0496 
0497 NonOwningObjectRect::NonOwningObjectRect(double left, double top, double right, double bottom, bool ellipse, ObjectType type, void *object)
0498     : ObjectRect(left, top, right, bottom, ellipse, type, object)
0499 {
0500 }
0501 
0502 NonOwningObjectRect::~NonOwningObjectRect()
0503 {
0504     // Set m_object so that ~ObjectRect() doesn't delete it
0505     m_object = nullptr;
0506 }