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 }