area.cpp 14.5 KB
Newer Older
1
2
3
4
5
6
7
8
/***************************************************************************
 *   Copyright (C) 2004-05 by Enrico Ros <eros.kde@email.it>               *
 *   Copyright (C) 2005 by Piotr Szymanski <niedakh@gmail.com>             *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 ***************************************************************************/
9

Albert Astals Cid's avatar
Albert Astals Cid committed
10
11
#include "area.h"

Albert Astals Cid's avatar
Albert Astals Cid committed
12
13
#include <QPolygonF>
#include <QRect>
14

Pino Toscano's avatar
Pino Toscano committed
15
#include "action.h"
16
#include "annotations.h"
17
#include "annotations_p.h"
18
#include "debug_p.h"
Pino Toscano's avatar
Pino Toscano committed
19
#include "sourcereference.h"
20

21
using namespace Okular;
22

23
24
/** class NormalizedPoint **/
NormalizedPoint::NormalizedPoint()
Albert Astals Cid's avatar
Albert Astals Cid committed
25
26
27
28
    : x(0.0)
    , y(0.0)
{
}
29

Albert Astals Cid's avatar
Albert Astals Cid committed
30
31
32
33
34
NormalizedPoint::NormalizedPoint(double dX, double dY)
    : x(dX)
    , y(dY)
{
}
35

Albert Astals Cid's avatar
Albert Astals Cid committed
36
37
38
39
40
NormalizedPoint::NormalizedPoint(int iX, int iY, int xScale, int yScale)
    : x((double)iX / (double)xScale)
    , y((double)iY / (double)yScale)
{
}
41

Albert Astals Cid's avatar
Albert Astals Cid committed
42
43
NormalizedPoint &NormalizedPoint::operator=(const NormalizedPoint &p) = default;
NormalizedPoint::NormalizedPoint(const NormalizedPoint &) = default;
44
NormalizedPoint::~NormalizedPoint() = default;
45

Albert Astals Cid's avatar
Albert Astals Cid committed
46
void NormalizedPoint::transform(const QTransform &matrix)
47
{
48
49
    qreal tmp_x = (qreal)x;
    qreal tmp_y = (qreal)y;
Albert Astals Cid's avatar
Albert Astals Cid committed
50
    matrix.map(tmp_x, tmp_y, &tmp_x, &tmp_y);
51
52
    x = tmp_x;
    y = tmp_y;
53
}
54

Albert Astals Cid's avatar
Albert Astals Cid committed
55
double NormalizedPoint::distanceSqr(double x, double y, double xScale, double yScale) const
56
{
Albert Astals Cid's avatar
Albert Astals Cid committed
57
    return pow((this->x - x) * xScale, 2) + pow((this->y - y) * yScale, 2);
58
59
60
61
62
63
}

/**
 * Returns a vector from the given points @p a and @p b
 * @internal
 */
Albert Astals Cid's avatar
Albert Astals Cid committed
64
NormalizedPoint operator-(const NormalizedPoint &a, const NormalizedPoint &b)
65
{
Albert Astals Cid's avatar
Albert Astals Cid committed
66
    return NormalizedPoint(a.x - b.x, a.y - b.y);
67
68
69
70
71
}

/**
 * @brief Calculates distance of the point @p x @p y @p xScale @p yScale to the line segment from @p start to @p end
 */
Albert Astals Cid's avatar
Albert Astals Cid committed
72
double NormalizedPoint::distanceSqr(double x, double y, double xScale, double yScale, const NormalizedPoint &start, const NormalizedPoint &end)
73
{
Albert Astals Cid's avatar
Albert Astals Cid committed
74
    NormalizedPoint point(x, y);
75
    double thisDistance;
Albert Astals Cid's avatar
Albert Astals Cid committed
76
77
78
79
80
81
82
83
84
    NormalizedPoint lineSegment(end - start);
    const double lengthSqr = pow(lineSegment.x, 2) + pow(lineSegment.y, 2);

    // if the length of the current segment is null, we can just
    // measure the distance to either end point
    if (lengthSqr == 0.0) {
        thisDistance = end.distanceSqr(x, y, xScale, yScale);
    } else {
        // vector from the start point of the current line segment to the measurement point
85
        NormalizedPoint a = point - start;
Albert Astals Cid's avatar
Albert Astals Cid committed
86
        // vector from the same start point to the end point of the current line segment
87
88
        NormalizedPoint b = end - start;

Albert Astals Cid's avatar
Albert Astals Cid committed
89
90
91
92
        // we're using a * b (dot product) := |a| * |b| * cos(phi) and the knowledge
        // that cos(phi) is adjacent side / hypotenuse (hypotenuse = |b|)
        // therefore, t becomes the length of the vector that represents the projection of
        // the point p onto the current line segment
93
94
95
        //(hint: if this is still unclear, draw it!)
        float t = (a.x * b.x + a.y * b.y) / lengthSqr;

Albert Astals Cid's avatar
Albert Astals Cid committed
96
97
98
99
100
101
102
103
104
        if (t < 0) {
            // projection falls outside the line segment on the side of "start"
            thisDistance = point.distanceSqr(start.x, start.y, xScale, yScale);
        } else if (t > 1) {
            // projection falls outside the line segment on the side of the current point
            thisDistance = point.distanceSqr(end.x, end.y, xScale, yScale);
        } else {
            // projection is within [start, *i];
            // determine the length of the perpendicular distance from the projection to the actual point
105
            NormalizedPoint direction = end - start;
Albert Astals Cid's avatar
Albert Astals Cid committed
106
107
            NormalizedPoint projection = start - NormalizedPoint(-t * direction.x, -t * direction.y);
            thisDistance = projection.distanceSqr(x, y, xScale, yScale);
108
109
110
111
112
        }
    }
    return thisDistance;
}

Albert Astals Cid's avatar
Albert Astals Cid committed
113
QDebug operator<<(QDebug str, const Okular::NormalizedPoint &p)
114
{
115
116
    str.nospace() << "NormPt(" << p.x << "," << p.y << ")";
    return str.space();
117
118
}

119
120
121
/** class NormalizedRect **/

NormalizedRect::NormalizedRect()
Albert Astals Cid's avatar
Albert Astals Cid committed
122
123
124
125
126
127
    : left(0.0)
    , top(0.0)
    , right(0.0)
    , bottom(0.0)
{
}
128

Albert Astals Cid's avatar
Albert Astals Cid committed
129
NormalizedRect::NormalizedRect(double l, double t, double r, double b)
130
    // note: check for swapping coords?
Albert Astals Cid's avatar
Albert Astals Cid committed
131
132
133
134
135
136
    : left(l)
    , top(t)
    , right(r)
    , bottom(b)
{
}
137

Albert Astals Cid's avatar
Albert Astals Cid committed
138
139
140
141
142
143
144
NormalizedRect::NormalizedRect(const QRect &r, double xScale, double yScale) // clazy:exclude=function-args-by-value TODO when BIC changes are allowed
    : left((double)r.left() / xScale)
    , top((double)r.top() / yScale)
    , right((double)r.right() / xScale)
    , bottom((double)r.bottom() / yScale)
{
}
145

Albert Astals Cid's avatar
Albert Astals Cid committed
146
NormalizedRect::NormalizedRect(const NormalizedRect &rect) = default;
147

Albert Astals Cid's avatar
Albert Astals Cid committed
148
NormalizedRect NormalizedRect::fromQRectF(const QRectF &rect)
Pino Toscano's avatar
Pino Toscano committed
149
{
150
    QRectF nrect = rect.normalized();
Pino Toscano's avatar
Pino Toscano committed
151
    NormalizedRect ret;
152
153
154
155
    ret.left = nrect.left();
    ret.top = nrect.top();
    ret.right = nrect.right();
    ret.bottom = nrect.bottom();
Pino Toscano's avatar
Pino Toscano committed
156
157
158
    return ret;
}

159
160
bool NormalizedRect::isNull() const
{
Albert Astals Cid's avatar
Albert Astals Cid committed
161
    return left == 0 && top == 0 && right == 0 && bottom == 0;
162
163
}

Albert Astals Cid's avatar
Albert Astals Cid committed
164
bool NormalizedRect::contains(double x, double y) const
165
166
167
168
{
    return x >= left && x <= right && y >= top && y <= bottom;
}

Albert Astals Cid's avatar
Albert Astals Cid committed
169
bool NormalizedRect::intersects(const NormalizedRect &r) const
170
171
172
173
{
    return (r.left <= right) && (r.right >= left) && (r.top <= bottom) && (r.bottom >= top);
}

Albert Astals Cid's avatar
Albert Astals Cid committed
174
bool NormalizedRect::intersects(const NormalizedRect *r) const
175
176
177
178
{
    return (r->left <= right) && (r->right >= left) && (r->top <= bottom) && (r->bottom >= top);
}

Albert Astals Cid's avatar
Albert Astals Cid committed
179
bool NormalizedRect::intersects(double l, double t, double r, double b) const
180
181
182
183
{
    return (l <= right) && (r >= left) && (t <= bottom) && (b >= top);
}

Albert Astals Cid's avatar
Albert Astals Cid committed
184
NormalizedRect NormalizedRect::operator|(const NormalizedRect &r) const
185
{
Albert Astals Cid's avatar
Albert Astals Cid committed
186
187
188
189
190
191
192
    NormalizedRect ret;
    // todo !
    ret.left = qMin(left, r.left);
    ret.top = qMin(top, r.top);
    ret.bottom = qMax(bottom, r.bottom);
    ret.right = qMax(right, r.right);
    return ret;
193
194
}

Albert Astals Cid's avatar
Albert Astals Cid committed
195
NormalizedRect &NormalizedRect::operator|=(const NormalizedRect &r)
196
{
Albert Astals Cid's avatar
Albert Astals Cid committed
197
198
199
200
    left = qMin(left, r.left);
    top = qMin(top, r.top);
    bottom = qMax(bottom, r.bottom);
    right = qMax(right, r.right);
201
    return *this;
202
}
203

Albert Astals Cid's avatar
Albert Astals Cid committed
204
NormalizedRect NormalizedRect::operator&(const NormalizedRect &r) const
205
{
Albert Astals Cid's avatar
Albert Astals Cid committed
206
    if (isNull() || r.isNull())
207
208
209
        return NormalizedRect();

    NormalizedRect ret;
Albert Astals Cid's avatar
Albert Astals Cid committed
210
211
212
213
    ret.left = qMax(left, r.left);
    ret.top = qMax(top, r.top);
    ret.bottom = qMin(bottom, r.bottom);
    ret.right = qMin(right, r.right);
214
215
216
    return ret;
}

Albert Astals Cid's avatar
Albert Astals Cid committed
217
NormalizedRect &NormalizedRect::operator=(const NormalizedRect &r) = default;
218
219

NormalizedRect::~NormalizedRect() = default;
220

Albert Astals Cid's avatar
Albert Astals Cid committed
221
bool NormalizedRect::operator==(const NormalizedRect &r) const
222
{
Albert Astals Cid's avatar
Albert Astals Cid committed
223
    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);
224
225
}

226
227
NormalizedPoint NormalizedRect::center() const
{
Albert Astals Cid's avatar
Albert Astals Cid committed
228
    return NormalizedPoint((left + right) / 2.0, (top + bottom) / 2.0);
229
230
}

231
/*
232
QDebug operator << (QDebug str , const NormalizedRect &r)
233
234
235
236
237
{
    str << "[" <<r.left() << "," << r.top() << "] x "<< "[" <<r.right() << "," << r.bottom() << "]";
    return str;
}*/

Albert Astals Cid's avatar
Albert Astals Cid committed
238
QRect NormalizedRect::geometry(int xScale, int yScale) const
239
{
Albert Astals Cid's avatar
Albert Astals Cid committed
240
    int l = (int)(left * xScale), t = (int)(top * yScale), r = (int)(right * xScale), b = (int)(bottom * yScale);
241

Albert Astals Cid's avatar
Albert Astals Cid committed
242
    return QRect(l, t, r - l + 1, b - t + 1);
243
244
}

Albert Astals Cid's avatar
Albert Astals Cid committed
245
QRect NormalizedRect::roundedGeometry(int xScale, int yScale) const
246
{
Albert Astals Cid's avatar
Albert Astals Cid committed
247
    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);
248

Albert Astals Cid's avatar
Albert Astals Cid committed
249
    return QRect(l, t, r - l + 1, b - t + 1);
250
251
}

Albert Astals Cid's avatar
Albert Astals Cid committed
252
void NormalizedRect::transform(const QTransform &matrix)
253
{
Albert Astals Cid's avatar
Albert Astals Cid committed
254
255
    QRectF rect(left, top, right - left, bottom - top);
    rect = matrix.mapRect(rect);
256
257
258
259
260
261
262

    left = rect.left();
    top = rect.top();
    right = rect.right();
    bottom = rect.bottom();
}

Albert Astals Cid's avatar
Albert Astals Cid committed
263
uint Okular::qHash(const NormalizedRect &r, uint seed)
264
{
265
    return ::qHash(r.bottom, ::qHash(r.right, ::qHash(r.top, ::qHash(r.left, seed))));
266
267
}

Albert Astals Cid's avatar
Albert Astals Cid committed
268
QDebug operator<<(QDebug str, const Okular::NormalizedRect &r)
269
{
Albert Astals Cid's avatar
Albert Astals Cid committed
270
    str.nospace() << "NormRect(" << r.left << "," << r.top << " x " << (r.right - r.left) << "+" << (r.bottom - r.top) << ")";
271
    return str.space();
272
273
}

274
RegularAreaRect::RegularAreaRect()
Albert Astals Cid's avatar
Albert Astals Cid committed
275
276
    : RegularArea<NormalizedRect, QRect>()
    , d(nullptr)
277
278
279
{
}

Albert Astals Cid's avatar
Albert Astals Cid committed
280
281
282
RegularAreaRect::RegularAreaRect(const RegularAreaRect &rar)
    : RegularArea<NormalizedRect, QRect>(rar)
    , d(nullptr)
283
284
285
286
287
288
289
{
}

RegularAreaRect::~RegularAreaRect()
{
}

Albert Astals Cid's avatar
Albert Astals Cid committed
290
RegularAreaRect &RegularAreaRect::operator=(const RegularAreaRect &rar)
291
{
292
    if (this != &rar) {
Albert Astals Cid's avatar
Albert Astals Cid committed
293
        RegularArea<NormalizedRect, QRect>::operator=(rar);
294
    }
295
296
297
    return *this;
}

Albert Astals Cid's avatar
Albert Astals Cid committed
298
299
300
HighlightAreaRect::HighlightAreaRect(const RegularAreaRect *area)
    : RegularAreaRect()
    , s_id(-1)
301
{
Albert Astals Cid's avatar
Albert Astals Cid committed
302
    if (area) {
303
304
        RegularAreaRect::ConstIterator it = area->begin();
        RegularAreaRect::ConstIterator itEnd = area->end();
Albert Astals Cid's avatar
Albert Astals Cid committed
305
306
        for (; it != itEnd; ++it) {
            append(NormalizedRect(*it));
307
308
        }
    }
309
310
311
312
}

/** class ObjectRect **/

Albert Astals Cid's avatar
Albert Astals Cid committed
313
314
315
ObjectRect::ObjectRect(double l, double t, double r, double b, bool ellipse, ObjectType type, void *object)
    : m_objectType(type)
    , m_object(object)
316
{
317
    // assign coordinates swapping them if negative width or height
Albert Astals Cid's avatar
Albert Astals Cid committed
318
319
320
    QRectF rect(r > l ? l : r, b > t ? t : b, fabs(r - l), fabs(b - t));
    if (ellipse)
        m_path.addEllipse(rect);
321
    else
Albert Astals Cid's avatar
Albert Astals Cid committed
322
        m_path.addRect(rect);
323

324
    m_transformedPath = m_path;
325
326
}

Albert Astals Cid's avatar
Albert Astals Cid committed
327
328
329
ObjectRect::ObjectRect(const NormalizedRect &r, bool ellipse, ObjectType type, void *object)
    : m_objectType(type)
    , m_object(object)
330
{
Albert Astals Cid's avatar
Albert Astals Cid committed
331
332
333
    QRectF rect(r.left, r.top, fabs(r.right - r.left), fabs(r.bottom - r.top));
    if (ellipse)
        m_path.addEllipse(rect);
334
    else
Albert Astals Cid's avatar
Albert Astals Cid committed
335
        m_path.addRect(rect);
336

337
    m_transformedPath = m_path;
338
339
}

Albert Astals Cid's avatar
Albert Astals Cid committed
340
341
342
ObjectRect::ObjectRect(const QPolygonF &poly, ObjectType type, void *object)
    : m_objectType(type)
    , m_object(object)
343
{
Albert Astals Cid's avatar
Albert Astals Cid committed
344
    m_path.addPolygon(poly);
345

346
347
348
349
350
351
352
353
    m_transformedPath = m_path;
}

ObjectRect::ObjectType ObjectRect::objectType() const
{
    return m_objectType;
}

Albert Astals Cid's avatar
Albert Astals Cid committed
354
const void *ObjectRect::object() const
355
356
{
    return m_object;
357
358
359
360
}

const QPainterPath &ObjectRect::region() const
{
361
    return m_transformedPath;
362
363
}

Albert Astals Cid's avatar
Albert Astals Cid committed
364
QRect ObjectRect::boundingRect(double xScale, double yScale) const
365
{
366
    const QRectF &br = m_transformedPath.boundingRect();
367

Albert Astals Cid's avatar
Albert Astals Cid committed
368
    return QRect((int)(br.left() * xScale), (int)(br.top() * yScale), (int)(br.width() * xScale), (int)(br.height() * yScale));
369
370
}

Albert Astals Cid's avatar
Albert Astals Cid committed
371
bool ObjectRect::contains(double x, double y, double, double) const
372
{
Albert Astals Cid's avatar
Albert Astals Cid committed
373
    return m_transformedPath.contains(QPointF(x, y));
374
375
}

Albert Astals Cid's avatar
Albert Astals Cid committed
376
void ObjectRect::transform(const QTransform &matrix)
377
{
Albert Astals Cid's avatar
Albert Astals Cid committed
378
    m_transformedPath = matrix.map(m_path);
379
380
}

Albert Astals Cid's avatar
Albert Astals Cid committed
381
double ObjectRect::distanceSqr(double x, double y, double xScale, double yScale) const
382
{
Albert Astals Cid's avatar
Albert Astals Cid committed
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
    switch (m_objectType) {
    case Action:
    case Image: {
        const QRectF &rect(m_transformedPath.boundingRect());
        return NormalizedRect(rect.x(), rect.y(), rect.right(), rect.bottom()).distanceSqr(x, y, xScale, yScale);
    }
    case OAnnotation: {
        return static_cast<Annotation *>(m_object)->d_func()->distanceSqr(x, y, xScale, yScale);
    }
    case SourceRef: {
        const SourceRefObjectRect *sr = static_cast<const SourceRefObjectRect *>(this);
        const NormalizedPoint &point = sr->m_point;
        if (point.x == -1.0) {
            return pow((y - point.y) * yScale, 2);
        } else if (point.y == -1.0) {
            return pow((x - point.x) * xScale, 2);
        } else {
            return pow((x - point.x) * xScale, 2) + pow((y - point.y) * yScale, 2);
401
402
        }
    }
Albert Astals Cid's avatar
Albert Astals Cid committed
403
    }
404
405
406
    return 0.0;
}

407
408
ObjectRect::~ObjectRect()
{
Albert Astals Cid's avatar
Albert Astals Cid committed
409
    if (!m_object)
410
411
        return;

Albert Astals Cid's avatar
Albert Astals Cid committed
412
413
414
415
    if (m_objectType == Action)
        delete static_cast<Okular::Action *>(m_object);
    else if (m_objectType == SourceRef)
        delete static_cast<Okular::SourceReference *>(m_object);
416
    else
Frederik Gladhorn's avatar
Frederik Gladhorn committed
417
        qCDebug(OkularCoreDebug).nospace() << "Object deletion not implemented for type '" << m_objectType << "'.";
418
}
419
420
421

/** class AnnotationObjectRect **/

Albert Astals Cid's avatar
Albert Astals Cid committed
422
423
424
AnnotationObjectRect::AnnotationObjectRect(Annotation *annotation)
    : ObjectRect(QPolygonF(), OAnnotation, annotation)
    , m_annotation(annotation)
425
426
427
428
{
}

Annotation *AnnotationObjectRect::annotation() const
429
{
430
    return m_annotation;
431
432
}

Albert Astals Cid's avatar
Albert Astals Cid committed
433
QRect AnnotationObjectRect::boundingRect(double xScale, double yScale) const
434
{
Albert Astals Cid's avatar
Albert Astals Cid committed
435
    const QRect annotRect = AnnotationUtils::annotationGeometry(m_annotation, xScale, yScale);
436
437
438
439
440
    const QPoint center = annotRect.center();

    // Make sure that the rectangle has a minimum size, so that it's possible
    // to click on it
    const int minSize = 14;
Albert Astals Cid's avatar
Albert Astals Cid committed
441
    const QRect minRect(center.x() - minSize / 2, center.y() - minSize / 2, minSize, minSize);
442
443

    return annotRect | minRect;
444
445
}

Albert Astals Cid's avatar
Albert Astals Cid committed
446
bool AnnotationObjectRect::contains(double x, double y, double xScale, double yScale) const
447
{
Albert Astals Cid's avatar
Albert Astals Cid committed
448
    return boundingRect(xScale, yScale).contains((int)(x * xScale), (int)(y * yScale), false);
449
450
451
452
}

AnnotationObjectRect::~AnnotationObjectRect()
{
453
    // the annotation pointer is kept elsewehere (in Page, most probably),
454
    // so just release its pointer
455
    m_object = nullptr;
456
457
}

Albert Astals Cid's avatar
Albert Astals Cid committed
458
void AnnotationObjectRect::transform(const QTransform &matrix)
459
{
Albert Astals Cid's avatar
Albert Astals Cid committed
460
    m_annotation->d_func()->annotationTransform(matrix);
461
462
}

463
464
/** class SourceRefObjectRect **/

Albert Astals Cid's avatar
Albert Astals Cid committed
465
466
467
SourceRefObjectRect::SourceRefObjectRect(const NormalizedPoint &point, void *srcRef)
    : ObjectRect(point.x, point.y, .0, .0, false, SourceRef, srcRef)
    , m_point(point)
468
{
469
470
    const double x = m_point.x < 0.0 ? 0.5 : m_point.x;
    const double y = m_point.y < 0.0 ? 0.5 : m_point.y;
Albert Astals Cid's avatar
Albert Astals Cid committed
471
472
    const QRectF rect(x - 2, y - 2, 5, 5);
    m_path.addRect(rect);
473
474

    m_transformedPath = m_path;
475
476
}

Albert Astals Cid's avatar
Albert Astals Cid committed
477
QRect SourceRefObjectRect::boundingRect(double xScale, double yScale) const
478
{
479
480
481
    const double x = m_point.x < 0.0 ? 0.5 : m_point.x;
    const double y = m_point.y < 0.0 ? 0.5 : m_point.y;

Albert Astals Cid's avatar
Albert Astals Cid committed
482
    return QRect(x * xScale, y * yScale, 1, 1);
483
484
}

Albert Astals Cid's avatar
Albert Astals Cid committed
485
bool SourceRefObjectRect::contains(double x, double y, double xScale, double yScale) const
486
{
Albert Astals Cid's avatar
Albert Astals Cid committed
487
    return distanceSqr(x, y, xScale, yScale) < (pow(7.0 / xScale, 2) + pow(7.0 / yScale, 2));
488
}
489
490
491

/** class NonOwningObjectRect **/

Albert Astals Cid's avatar
Albert Astals Cid committed
492
493
NonOwningObjectRect::NonOwningObjectRect(double left, double top, double right, double bottom, bool ellipse, ObjectType type, void *object)
    : ObjectRect(left, top, right, bottom, ellipse, type, object)
494
495
496
497
498
499
500
501
{
}

NonOwningObjectRect::~NonOwningObjectRect()
{
    // Set m_object so that ~ObjectRect() doesn't delete it
    m_object = nullptr;
}