Marble

ClipPainter.cpp
1// SPDX-License-Identifier: LGPL-2.1-or-later
2//
3// SPDX-FileCopyrightText: 2006-2009 Torsten Rahn <tackat@kde.org>
4// SPDX-FileCopyrightText: 2007 Inge Wallin <ingwa@kde.org>
5//
6
7#include "ClipPainter.h"
8
9#include <cmath>
10
11#include "MarbleDebug.h"
12
13namespace Marble
14{
15
16class ClipPainterPrivate
17{
18public:
19 explicit ClipPainterPrivate(ClipPainter *parent);
20
21 ClipPainter *q;
22
23 // true if clipping is on.
24 bool m_doClip;
25
26 // The limits
27 qreal m_left;
28 qreal m_right;
29 qreal m_top;
30 qreal m_bottom;
31
32 // Used in the paint process of vectors..
33 int m_currentSector;
34 int m_previousSector;
35
36 // int m_debugNodeCount;
37
38 QPointF m_currentPoint;
39 QPointF m_previousPoint;
40
41 inline int sector(const QPointF &point) const;
42
43 inline QPointF clipTop(qreal m, const QPointF &point) const;
44 inline QPointF clipLeft(qreal m, const QPointF &point) const;
45 inline QPointF clipBottom(qreal m, const QPointF &point) const;
46 inline QPointF clipRight(qreal m, const QPointF &point) const;
47
48 inline void initClipRect();
49
50 inline void clipPolyObject(const QPolygonF &sourcePolygon, QList<QPolygonF> &clippedPolyObjects, bool isClosed);
51
52 inline void clipMultiple(QPolygonF &clippedPolyObject, QList<QPolygonF> &clippedPolyObjects, bool isClosed);
53 inline void clipOnce(QPolygonF &clippedPolyObject, QList<QPolygonF> &clippedPolyObjects, bool isClosed);
54 inline void
55 clipOnceCorner(QPolygonF &clippedPolyObject, QList<QPolygonF> &clippedPolyObjects, const QPointF &corner, const QPointF &point, bool isClosed) const;
56 inline void clipOnceEdge(QPolygonF &clippedPolyObject, QList<QPolygonF> &clippedPolyObjects, const QPointF &point, bool isClosed) const;
57
58 void labelPosition(const QPolygonF &polygon, QList<QPointF> &labelNodes, LabelPositionFlags labelPositionFlags) const;
59
60 bool pointAllowsLabel(const QPointF &point) const;
61 QPointF interpolateLabelPoint(const QPointF &previousPoint, const QPointF &currentPoint, LabelPositionFlags labelPositionFlags) const;
62
63 static inline qreal _m(const QPointF &start, const QPointF &end);
64
65 void debugDrawNodes(const QPolygonF &);
66
67 qreal m_labelAreaMargin;
68
69 int m_debugPenBatchColor;
70 int m_debugBrushBatchColor;
71 int m_debugPolygonsLevel;
72 bool m_debugBatchRender;
73};
74
75}
76
77using namespace Marble;
78
79// #define MARBLE_DEBUG
80
81ClipPainter::ClipPainter(QPaintDevice *pd, bool clip)
82 : QPainter(pd)
83 , d(new ClipPainterPrivate(this))
84{
85 d->initClipRect();
86
87 // m_debugNodeCount = 0;
88 d->m_doClip = clip;
89}
90
91ClipPainter::ClipPainter()
92 : d(new ClipPainterPrivate(this))
93{
94}
95
96ClipPainter::~ClipPainter()
97{
98 delete d;
99}
100
101void ClipPainter::setScreenClip(bool enable)
102{
103 d->m_doClip = enable;
104}
105
106bool ClipPainter::hasScreenClip() const
107{
108 return d->m_doClip;
109}
110
111void ClipPainter::drawPolygon(const QPolygonF &polygon, Qt::FillRule fillRule)
112{
113 if (d->m_doClip) {
114 d->initClipRect();
115 QList<QPolygonF> clippedPolyObjects;
116
117 d->clipPolyObject(polygon, clippedPolyObjects, true);
118
119 for (const QPolygonF &clippedPolyObject : clippedPolyObjects) {
120 if (clippedPolyObject.size() > 2) {
121 // mDebug() << "Size: " << clippedPolyObject.size();
122 if (d->m_debugPolygonsLevel) {
124 QBrush originalBrush = brush;
125 QColor color = brush.color();
126 color.setAlpha(color.alpha() * 0.75);
127 brush.setColor(color);
129
130 QPainter::drawPolygon(clippedPolyObject, fillRule);
131
132 QPainter::setBrush(originalBrush);
133
134 d->debugDrawNodes(clippedPolyObject);
135 } else {
136 QPainter::drawPolygon(clippedPolyObject, fillRule);
137 }
138 }
139 }
140 } else {
141 if (d->m_debugPolygonsLevel) {
143 QBrush originalBrush = brush;
144 QColor color = brush.color();
145 color.setAlpha(color.alpha() * 0.75);
146 brush.setColor(color);
148
149 QPainter::drawPolygon(polygon, fillRule);
150
151 QPainter::setBrush(originalBrush);
152
153 d->debugDrawNodes(polygon);
154 } else {
155 QPainter::drawPolygon(polygon, fillRule);
156 }
157 }
158}
159
160void ClipPainter::drawPolyline(const QPolygonF &polygon)
161{
162 if (d->m_doClip) {
163 d->initClipRect();
164 QList<QPolygonF> clippedPolyObjects;
165
166 d->clipPolyObject(polygon, clippedPolyObjects, false);
167
168 for (const QPolygonF &clippedPolyObject : clippedPolyObjects) {
169 if (clippedPolyObject.size() > 1) {
170 if (d->m_debugPolygonsLevel) {
172 QPen originalPen = pen;
173 QColor color = pen.color();
174 color.setAlpha(color.alpha() * 0.75);
175 pen.setColor(color);
177
178 QPainter::drawPolyline(clippedPolyObject);
179
180 QPainter::setPen(originalPen);
181
182 d->debugDrawNodes(clippedPolyObject);
183 } else {
184 QPainter::drawPolyline(clippedPolyObject);
185 }
186 }
187 }
188 } else {
189 if (d->m_debugPolygonsLevel) {
191 QPen originalPen = pen;
192 QColor color = pen.color();
193 color.setAlpha(color.alpha() * 0.75);
194 pen.setColor(color);
196
197 QPainter::drawPolyline(polygon);
198
199 QPainter::setPen(originalPen);
200
201 d->debugDrawNodes(polygon);
202 } else {
203 QPainter::drawPolyline(polygon);
204 }
205 }
206}
207
208void ClipPainter::drawPolyline(const QPolygonF &polygon, QList<QPointF> &labelNodes, LabelPositionFlags positionFlags)
209{
210 if (d->m_doClip) {
211 d->initClipRect();
212 QList<QPolygonF> clippedPolyObjects;
213
214 d->clipPolyObject(polygon, clippedPolyObjects, false);
215
216 for (const QPolygonF &clippedPolyObject : clippedPolyObjects) {
217 if (d->m_debugPolygonsLevel) {
219 QPen originalPen = pen;
220 QColor color = pen.color();
221 color.setAlpha(color.alpha() * 0.75);
222 pen.setColor(color);
224
225 QPainter::drawPolyline(clippedPolyObject);
226
227 QPainter::setPen(originalPen);
228
229 d->debugDrawNodes(clippedPolyObject);
230 } else {
231 QPainter::drawPolyline(clippedPolyObject);
232 }
233 }
234 } else {
235 if (d->m_debugPolygonsLevel) {
237 QPen originalPen = pen;
238 QColor color = pen.color();
239 color.setAlpha(color.alpha() * 0.75);
240 pen.setColor(color);
242
243 QPainter::drawPolyline(polygon);
244
245 QPainter::setPen(originalPen);
246
247 d->debugDrawNodes(polygon);
248 } else {
249 QPainter::drawPolyline(polygon);
250 }
251
252 d->labelPosition(polygon, labelNodes, positionFlags);
253 }
254}
255
256void ClipPainter::labelPosition(const QPolygonF &polygon, QList<QPointF> &labelNodes, LabelPositionFlags labelPositionFlags) const
257{
258 d->labelPosition(polygon, labelNodes, labelPositionFlags);
259}
260
261void ClipPainter::setPen(const QColor &color)
262{
263 if (d->m_debugBatchRender) {
264 qDebug();
265 }
266 setPen(QPen(color));
267}
268
269void ClipPainter::setPen(Qt::PenStyle style)
270{
271 if (d->m_debugBatchRender) {
272 qDebug();
273 }
274 setPen(QPen(style));
275}
276
277void ClipPainter::setPen(const QPen &pen)
278{
279 if (d->m_debugBatchRender) {
280 qDebug();
281 if (pen != QPainter::pen()) {
282 qDebug() << "--" << pen.color() << QPainter::pen().color();
283 QPen newPen = pen;
284 newPen.setColor((Qt::GlobalColor)(d->m_debugPenBatchColor + 4));
285 QPainter::setPen(newPen);
286 d->m_debugPenBatchColor++;
287 d->m_debugPenBatchColor %= 14;
288 } else {
289 qDebug() << "++";
291 }
292 } else {
294 }
295}
296
297void ClipPainter::setBrush(const QBrush &brush)
298{
299 if (d->m_debugBatchRender) {
300 qDebug();
301 if (brush != QPainter::brush()) {
302 qDebug() << "--" << brush.color() << QPainter::brush().color();
303 QBrush batchColor(QColor((Qt::GlobalColor)(d->m_debugBrushBatchColor)));
304 QPainter::setBrush(batchColor);
305 d->m_debugBrushBatchColor++;
306 d->m_debugBrushBatchColor %= 20;
307 } else {
308 qDebug() << "++";
310 }
311 } else {
313 }
314}
315
316void ClipPainterPrivate::labelPosition(const QPolygonF &polygon, QList<QPointF> &labelNodes, LabelPositionFlags labelPositionFlags) const
317{
318 if (labelPositionFlags.testFlag(LineCenter)) {
319 // The Label at the center of the polyline:
320 if (!polygon.isEmpty()) {
321 const int labelPosition = polygon.size() / 2; // implied: 0 <= labelPosition < polygon.size()
322 labelNodes << polygon.at(labelPosition);
323 }
324 }
325
326 if (!polygon.isEmpty() && labelPositionFlags.testFlag(LineStart)) {
327 if (pointAllowsLabel(polygon.first())) {
328 labelNodes << polygon.first();
329 }
330
331 // The Label at the start of the polyline:
332 for (int it = 1; it < polygon.size(); ++it) {
333 const bool currentAllowsLabel = pointAllowsLabel(polygon.at(it));
334
335 if (currentAllowsLabel) {
336 // As polygon.size() > 0 it's ensured that it-1 exists.
337 QPointF node = interpolateLabelPoint(polygon.at(it - 1), polygon.at(it), labelPositionFlags);
338 if (node != QPointF(-1.0, -1.0)) {
339 labelNodes << node;
340 }
341 break;
342 }
343 }
344 }
345
346 if (polygon.size() > 1 && labelPositionFlags.testFlag(LineEnd)) {
347 if (pointAllowsLabel(polygon.at(polygon.size() - 1))) {
348 labelNodes << polygon.at(polygon.size() - 1);
349 }
350
351 // The Label at the end of the polyline:
352 for (int it = polygon.size() - 2; it > 0; --it) {
353 const bool currentAllowsLabel = pointAllowsLabel(polygon.at(it));
354
355 if (currentAllowsLabel) {
356 QPointF node = interpolateLabelPoint(polygon.at(it + 1), polygon.at(it), labelPositionFlags);
357 if (node != QPointF(-1.0, -1.0)) {
358 labelNodes << node;
359 }
360 break;
361 }
362 }
363 }
364}
365
366bool ClipPainterPrivate::pointAllowsLabel(const QPointF &point) const
367{
368 return point.x() > m_labelAreaMargin && point.x() < q->viewport().width() - m_labelAreaMargin && point.y() > m_labelAreaMargin
369 && point.y() < q->viewport().height() - m_labelAreaMargin;
370}
371
372QPointF ClipPainterPrivate::interpolateLabelPoint(const QPointF &previousPoint, const QPointF &currentPoint, LabelPositionFlags labelPositionFlags) const
373{
374 qreal m = _m(previousPoint, currentPoint);
375 if (previousPoint.x() <= m_labelAreaMargin) {
376 if (labelPositionFlags.testFlag(IgnoreXMargin)) {
377 return {-1.0, -1.0};
378 }
379 return {m_labelAreaMargin, previousPoint.y() + (m_labelAreaMargin - previousPoint.x()) * m};
380 } else if (previousPoint.x() >= q->viewport().width() - m_labelAreaMargin) {
381 if (labelPositionFlags.testFlag(IgnoreXMargin)) {
382 return {-1.0, -1.0};
383 }
384 return {q->viewport().width() - m_labelAreaMargin, previousPoint.y() - (previousPoint.x() - q->viewport().width() + m_labelAreaMargin) * m};
385 }
386
387 if (previousPoint.y() <= m_labelAreaMargin) {
388 if (labelPositionFlags.testFlag(IgnoreYMargin)) {
389 return {-1.0, -1.0};
390 }
391 return {previousPoint.x() + (m_labelAreaMargin - previousPoint.y()) / m, m_labelAreaMargin};
392 } else if (previousPoint.y() >= q->viewport().height() - m_labelAreaMargin) {
393 if (labelPositionFlags.testFlag(IgnoreYMargin)) {
394 return {-1.0, -1.0};
395 }
396 return {previousPoint.x() - (previousPoint.y() - q->viewport().height() + m_labelAreaMargin) / m, q->viewport().height() - m_labelAreaMargin};
397 }
398
399 // mDebug() << "Previous and current node position are allowed!";
400
401 return {-1.0, -1.0};
402}
403
404ClipPainterPrivate::ClipPainterPrivate(ClipPainter *parent)
405 : m_doClip(true)
406 , m_left(0.0)
407 , m_right(0.0)
408 , m_top(0.0)
409 , m_bottom(0.0)
410 , m_currentSector(4)
411 , m_previousSector(4)
412 , m_currentPoint(QPointF())
413 , m_previousPoint(QPointF())
414 , m_labelAreaMargin(10.0)
415 , m_debugPenBatchColor(0)
416 , m_debugBrushBatchColor(0)
417 , m_debugPolygonsLevel(0)
418 , m_debugBatchRender(false)
419{
420 q = parent;
421}
422
423void ClipPainterPrivate::initClipRect()
424{
425 qreal penHalfWidth = q->pen().widthF() / 2.0 + 1.0;
426
427 m_left = -penHalfWidth;
428 m_right = (qreal)(q->device()->width()) + penHalfWidth;
429 m_top = -penHalfWidth;
430 m_bottom = (qreal)(q->device()->height()) + penHalfWidth;
431}
432
433qreal ClipPainterPrivate::_m(const QPointF &start, const QPointF &end)
434{
435 qreal divisor = end.x() - start.x();
436 if (std::fabs(divisor) < 0.000001) {
437 // this is in screencoordinates so the difference
438 // between 0, 0.000001 and -0.000001 isn't visible at all
439 divisor = 0.000001;
440 }
441
442 return (end.y() - start.y()) / divisor;
443}
444
445QPointF ClipPainterPrivate::clipTop(qreal m, const QPointF &point) const
446{
447 return {(m_top - point.y()) / m + point.x(), m_top};
448}
449
450QPointF ClipPainterPrivate::clipLeft(qreal m, const QPointF &point) const
451{
452 return {m_left, (m_left - point.x()) * m + point.y()};
453}
454
455QPointF ClipPainterPrivate::clipBottom(qreal m, const QPointF &point) const
456{
457 return {(m_bottom - point.y()) / m + point.x(), m_bottom};
458}
459
460QPointF ClipPainterPrivate::clipRight(qreal m, const QPointF &point) const
461{
462 return {m_right, (m_right - point.x()) * m + point.y()};
463}
464
465int ClipPainterPrivate::sector(const QPointF &point) const
466{
467 // If we think of the image borders as (infinitely long) parallel
468 // lines then the plane is divided into 9 sectors. Each of these
469 // sections is identified by a unique keynumber (currentSector):
470 //
471 // 0 | 1 | 2
472 // --+---+--
473 // 3 | 4 | 5 <- sector number "4" represents the onscreen sector / viewport
474 // --+---+--
475 // 6 | 7 | 8
476 //
477
478 // Figure out the section of the current point.
479 int xSector = 1;
480 if (point.x() < m_left)
481 xSector = 0;
482 else if (point.x() > m_right)
483 xSector = 2;
484
485 int ySector = 3;
486 if (point.y() < m_top)
487 ySector = 0;
488 else if (point.y() > m_bottom)
489 ySector = 6;
490
491 // By adding xSector and ySector we get a
492 // sector number of the values shown in the ASCII-art graph above.
493 return ySector + xSector;
494}
495
496void ClipPainterPrivate::clipPolyObject(const QPolygonF &polygon, QList<QPolygonF> &clippedPolyObjects, bool isClosed)
497{
498 // mDebug() << "ClipPainter enabled." ;
499
500 // Only create a new polyObject as soon as we know for sure that
501 // the current point is on the screen.
502 QPolygonF clippedPolyObject = QPolygonF();
503
504 const QList<QPointF>::const_iterator itStartPoint = polygon.constBegin();
505 const QList<QPointF>::const_iterator itEndPoint = polygon.constEnd();
506 QList<QPointF>::const_iterator itPoint = itStartPoint;
507
508 // We use a while loop to be able to cover linestrings as well as linear rings:
509 // Linear rings require to tessellate the path from the last node to the first node
510 // which isn't really convenient to achieve with a for loop ...
511
512 bool processingLastNode = false;
513
514 while (itPoint != itEndPoint) {
515 m_currentPoint = (*itPoint);
516 // mDebug() << "m_currentPoint.x()" << m_currentPoint.x() << "m_currentPOint.y()" << m_currentPoint.y();
517
518 // Figure out the sector of the current point.
519 m_currentSector = sector(m_currentPoint);
520
521 // Initialize the variables related to the previous point.
522 if (itPoint == itStartPoint && processingLastNode == false) {
523 if (isClosed) {
524 m_previousPoint = polygon.last();
525
526 // Figure out the sector of the previous point.
527 m_previousSector = sector(m_previousPoint);
528 } else {
529 m_previousSector = m_currentSector;
530 }
531 }
532
533 // If the current point reaches a new sector, take care of clipping.
534 if (m_currentSector != m_previousSector) {
535 if (m_currentSector == 4 || m_previousSector == 4) {
536 // In this case the current or the previous point is visible on the
537 // screen but not both. Hence we only need to clip once and require
538 // only one interpolation for both cases.
539
540 clipOnce(clippedPolyObject, clippedPolyObjects, isClosed);
541 } else {
542 // This case mostly deals with lines that reach from one
543 // sector that is located off screen to another one that
544 // is located off screen. In this situation the line
545 // can get clipped once, twice, or not at all.
546 clipMultiple(clippedPolyObject, clippedPolyObjects, isClosed);
547 }
548
549 m_previousSector = m_currentSector;
550 }
551
552 // If the current point is onscreen, just add it to our final polygon.
553 if (m_currentSector == 4) {
554 clippedPolyObject << m_currentPoint;
555#ifdef MARBLE_DEBUG
556 ++(m_debugNodeCount);
557#endif
558 }
559
560 m_previousPoint = m_currentPoint;
561
562 // Now let's handle the case where we have a (closed) polygon and where the
563 // last point of the polyline is outside the viewport and the start point
564 // is inside the viewport. This needs special treatment
565 if (processingLastNode) {
566 break;
567 }
568 ++itPoint;
569
570 if (itPoint == itEndPoint && isClosed) {
571 itPoint = itStartPoint;
572 processingLastNode = true;
573 }
574 }
575
576 // Only add the pointer if there's node data available.
577 if (!clippedPolyObject.isEmpty()) {
578 clippedPolyObjects << clippedPolyObject;
579 }
580}
581
582void ClipPainterPrivate::clipMultiple(QPolygonF &clippedPolyObject, QList<QPolygonF> &clippedPolyObjects, bool isClosed)
583{
584 Q_UNUSED(clippedPolyObjects)
585 Q_UNUSED(isClosed)
586
587 // Take care of adding nodes in the image corners if the iterator
588 // traverses off screen sections.
589
590 qreal m = _m(m_previousPoint, m_currentPoint);
591
592 switch (m_currentSector) {
593 case 0:
594 if (m_previousSector == 5) {
595 QPointF pointRight = clipRight(m, m_previousPoint);
596 QPointF pointTop = clipTop(m, m_currentPoint);
597 QPointF pointLeft = clipLeft(m, m_currentPoint);
598
599 if (pointRight.y() > m_top) {
600 clippedPolyObject << pointRight;
601 } else {
602 clippedPolyObject << QPointF(m_right, m_top);
603 }
604 if (pointTop.x() >= m_left && pointTop.x() < m_right)
605 clippedPolyObject << pointTop;
606 if (pointLeft.y() > m_top)
607 clippedPolyObject << pointLeft;
608 } else if (m_previousSector == 7) {
609 QPointF pointBottom = clipBottom(m, m_previousPoint);
610 QPointF pointTop = clipTop(m, m_currentPoint);
611 QPointF pointLeft = clipLeft(m, m_currentPoint);
612
613 if (pointBottom.x() > m_left) {
614 clippedPolyObject << pointBottom;
615 } else {
616 clippedPolyObject << QPointF(m_left, m_bottom);
617 }
618 if (pointLeft.y() >= m_top && pointLeft.y() < m_bottom)
619 clippedPolyObject << pointLeft;
620 if (pointTop.x() > m_left)
621 clippedPolyObject << pointTop;
622 } else if (m_previousSector == 8) {
623 QPointF pointBottom = clipBottom(m, m_previousPoint);
624 QPointF pointRight = clipRight(m, m_previousPoint);
625 QPointF pointTop = clipTop(m, m_currentPoint);
626 QPointF pointLeft = clipLeft(m, m_currentPoint);
627
628 if (pointBottom.x() > m_left && pointBottom.x() < m_right)
629 clippedPolyObject << pointBottom;
630 if (pointRight.y() > m_top && pointRight.y() < m_bottom)
631 clippedPolyObject << pointRight;
632 if (pointTop.x() > m_left && pointTop.x() < m_right)
633 clippedPolyObject << pointTop;
634 if (pointLeft.y() > m_top && pointLeft.y() < m_bottom)
635 clippedPolyObject << pointLeft;
636
637 if (pointBottom.x() <= m_left && pointLeft.y() >= m_bottom)
638 clippedPolyObject << QPointF(m_left, m_bottom);
639 if (pointTop.x() >= m_right && pointRight.y() <= m_top)
640 clippedPolyObject << QPointF(m_right, m_top);
641 }
642
643 clippedPolyObject << QPointF(m_left, m_top);
644 break;
645
646 case 1:
647 if (m_previousSector == 3) {
648 QPointF pointLeft = clipLeft(m, m_previousPoint);
649 QPointF pointTop = clipTop(m, m_currentPoint);
650
651 if (pointLeft.y() > m_top) {
652 clippedPolyObject << pointLeft;
653 } else {
654 clippedPolyObject << QPointF(m_left, m_top);
655 }
656 if (pointTop.x() > m_left)
657 clippedPolyObject << pointTop;
658 } else if (m_previousSector == 5) {
659 QPointF pointRight = clipRight(m, m_previousPoint);
660 QPointF pointTop = clipTop(m, m_currentPoint);
661
662 if (pointRight.y() > m_top) {
663 clippedPolyObject << pointRight;
664 } else {
665 clippedPolyObject << QPointF(m_right, m_top);
666 }
667 if (pointTop.x() < m_right)
668 clippedPolyObject << pointTop;
669 } else if (m_previousSector == 6) {
670 QPointF pointBottom = clipBottom(m, m_previousPoint);
671 QPointF pointLeft = clipLeft(m, m_previousPoint);
672 QPointF pointTop = clipTop(m, m_currentPoint);
673
674 if (pointBottom.x() > m_left)
675 clippedPolyObject << pointBottom;
676 if (pointLeft.y() > m_top && pointLeft.y() <= m_bottom)
677 clippedPolyObject << pointLeft;
678 if (pointTop.x() > m_left) {
679 clippedPolyObject << pointTop;
680 } else {
681 clippedPolyObject << QPointF(m_left, m_top);
682 }
683 } else if (m_previousSector == 7) {
684 clippedPolyObject << clipBottom(m, m_previousPoint);
685 clippedPolyObject << clipTop(m, m_currentPoint);
686 } else if (m_previousSector == 8) {
687 QPointF pointBottom = clipBottom(m, m_previousPoint);
688 QPointF pointRight = clipRight(m, m_previousPoint);
689 QPointF pointTop = clipTop(m, m_currentPoint);
690
691 if (pointBottom.x() < m_right)
692 clippedPolyObject << pointBottom;
693 if (pointRight.y() > m_top && pointRight.y() <= m_bottom)
694 clippedPolyObject << pointRight;
695 if (pointTop.x() < m_right) {
696 clippedPolyObject << pointTop;
697 } else {
698 clippedPolyObject << QPointF(m_right, m_top);
699 }
700 }
701 break;
702
703 case 2:
704 if (m_previousSector == 3) {
705 QPointF pointLeft = clipLeft(m, m_previousPoint);
706 QPointF pointTop = clipTop(m, m_currentPoint);
707 QPointF pointRight = clipRight(m, m_currentPoint);
708
709 if (pointLeft.y() > m_top) {
710 clippedPolyObject << pointLeft;
711 } else {
712 clippedPolyObject << QPointF(m_left, m_top);
713 }
714 if (pointTop.x() > m_left && pointTop.x() <= m_right)
715 clippedPolyObject << pointTop;
716 if (pointRight.y() > m_top)
717 clippedPolyObject << pointRight;
718 } else if (m_previousSector == 7) {
719 QPointF pointBottom = clipBottom(m, m_previousPoint);
720 QPointF pointTop = clipTop(m, m_currentPoint);
721 QPointF pointRight = clipRight(m, m_currentPoint);
722
723 if (pointBottom.x() < m_right) {
724 clippedPolyObject << pointBottom;
725 } else {
726 clippedPolyObject << QPointF(m_right, m_bottom);
727 }
728 if (pointRight.y() >= m_top && pointRight.y() < m_bottom)
729 clippedPolyObject << pointRight;
730 if (pointTop.x() < m_right)
731 clippedPolyObject << pointTop;
732 } else if (m_previousSector == 6) {
733 QPointF pointBottom = clipBottom(m, m_previousPoint);
734 QPointF pointLeft = clipLeft(m, m_currentPoint);
735 QPointF pointTop = clipTop(m, m_currentPoint);
736 QPointF pointRight = clipRight(m, m_previousPoint);
737
738 if (pointBottom.x() > m_left && pointBottom.x() < m_right)
739 clippedPolyObject << pointBottom;
740 if (pointLeft.y() > m_top && pointLeft.y() < m_bottom)
741 clippedPolyObject << pointLeft;
742 if (pointTop.x() > m_left && pointTop.x() < m_right)
743 clippedPolyObject << pointTop;
744 if (pointRight.y() > m_top && pointRight.y() < m_bottom)
745 clippedPolyObject << pointRight;
746
747 if (pointBottom.x() >= m_right && pointRight.y() >= m_bottom)
748 clippedPolyObject << QPointF(m_right, m_bottom);
749 if (pointTop.x() <= m_left && pointLeft.y() <= m_top)
750 clippedPolyObject << QPointF(m_left, m_top);
751 }
752
753 clippedPolyObject << QPointF(m_right, m_top);
754 break;
755
756 case 3:
757 if (m_previousSector == 7) {
758 QPointF pointBottom = clipBottom(m, m_previousPoint);
759 QPointF pointLeft = clipLeft(m, m_currentPoint);
760
761 if (pointBottom.x() > m_left)
762 clippedPolyObject << pointBottom;
763 if (pointLeft.y() < m_bottom) {
764 clippedPolyObject << pointLeft;
765 } else {
766 clippedPolyObject << QPointF(m_left, m_bottom);
767 }
768 } else if (m_previousSector == 1) {
769 QPointF pointTop = clipTop(m, m_previousPoint);
770 QPointF pointLeft = clipLeft(m, m_currentPoint);
771
772 if (pointTop.x() > m_left)
773 clippedPolyObject << pointTop;
774 if (pointLeft.y() > m_top) {
775 clippedPolyObject << pointLeft;
776 } else {
777 clippedPolyObject << QPointF(m_left, m_top);
778 }
779 } else if (m_previousSector == 8) {
780 QPointF pointRight = clipRight(m, m_previousPoint);
781 QPointF pointBottom = clipBottom(m, m_previousPoint);
782 QPointF pointLeft = clipLeft(m, m_currentPoint);
783
784 if (pointRight.y() < m_bottom)
785 clippedPolyObject << pointRight;
786 if (pointBottom.x() > m_left && pointBottom.x() <= m_right)
787 clippedPolyObject << pointBottom;
788 if (pointLeft.y() < m_bottom) {
789 clippedPolyObject << pointLeft;
790 } else {
791 clippedPolyObject << QPointF(m_left, m_bottom);
792 }
793 } else if (m_previousSector == 5) {
794 clippedPolyObject << clipRight(m, m_previousPoint);
795 clippedPolyObject << clipLeft(m, m_currentPoint);
796 } else if (m_previousSector == 2) {
797 QPointF pointRight = clipRight(m, m_previousPoint);
798 QPointF pointTop = clipTop(m, m_previousPoint);
799 QPointF pointLeft = clipLeft(m, m_currentPoint);
800
801 if (pointRight.y() > m_top)
802 clippedPolyObject << pointRight;
803 if (pointTop.x() > m_left && pointTop.x() <= m_right)
804 clippedPolyObject << pointTop;
805 if (pointLeft.y() > m_top) {
806 clippedPolyObject << pointLeft;
807 } else {
808 clippedPolyObject << QPointF(m_left, m_top);
809 }
810 }
811 break;
812
813 case 5:
814 if (m_previousSector == 7) {
815 QPointF pointBottom = clipBottom(m, m_previousPoint);
816 QPointF pointRight = clipRight(m, m_currentPoint);
817
818 if (pointBottom.x() < m_right)
819 clippedPolyObject << pointBottom;
820 if (pointRight.y() < m_bottom) {
821 clippedPolyObject << pointRight;
822 } else {
823 clippedPolyObject << QPointF(m_right, m_bottom);
824 }
825 } else if (m_previousSector == 1) {
826 QPointF pointTop = clipTop(m, m_previousPoint);
827 QPointF pointRight = clipRight(m, m_currentPoint);
828
829 if (pointTop.x() < m_right)
830 clippedPolyObject << pointTop;
831 if (pointRight.y() > m_top) {
832 clippedPolyObject << pointRight;
833 } else {
834 clippedPolyObject << QPointF(m_right, m_top);
835 }
836 } else if (m_previousSector == 6) {
837 QPointF pointLeft = clipLeft(m, m_previousPoint);
838 QPointF pointBottom = clipBottom(m, m_previousPoint);
839 QPointF pointRight = clipRight(m, m_currentPoint);
840
841 if (pointLeft.y() < m_bottom)
842 clippedPolyObject << pointLeft;
843 if (pointBottom.x() >= m_left && pointBottom.x() < m_right)
844 clippedPolyObject << pointBottom;
845 if (pointRight.y() < m_bottom) {
846 clippedPolyObject << pointRight;
847 } else {
848 clippedPolyObject << QPointF(m_right, m_bottom);
849 }
850 } else if (m_previousSector == 3) {
851 clippedPolyObject << clipLeft(m, m_previousPoint);
852 clippedPolyObject << clipRight(m, m_currentPoint);
853 } else if (m_previousSector == 0) {
854 QPointF pointLeft = clipLeft(m, m_previousPoint);
855 QPointF pointTop = clipTop(m, m_previousPoint);
856 QPointF pointRight = clipRight(m, m_currentPoint);
857
858 if (pointLeft.y() > m_top)
859 clippedPolyObject << pointLeft;
860 if (pointTop.x() >= m_left && pointTop.x() < m_right)
861 clippedPolyObject << pointTop;
862 if (pointRight.y() > m_top) {
863 clippedPolyObject << pointRight;
864 } else {
865 clippedPolyObject << QPointF(m_right, m_top);
866 }
867 }
868 break;
869
870 case 6:
871 if (m_previousSector == 5) {
872 QPointF pointRight = clipRight(m, m_previousPoint);
873 QPointF pointBottom = clipBottom(m, m_currentPoint);
874 QPointF pointLeft = clipLeft(m, m_currentPoint);
875
876 if (pointRight.y() < m_bottom) {
877 clippedPolyObject << pointRight;
878 } else {
879 clippedPolyObject << QPointF(m_right, m_bottom);
880 }
881 if (pointBottom.x() >= m_left && pointBottom.x() < m_right)
882 clippedPolyObject << pointBottom;
883 if (pointLeft.y() < m_bottom)
884 clippedPolyObject << pointLeft;
885 } else if (m_previousSector == 1) {
886 QPointF pointTop = clipTop(m, m_previousPoint);
887 QPointF pointLeft = clipLeft(m, m_currentPoint);
888 QPointF pointBottom = clipBottom(m, m_currentPoint);
889
890 if (pointTop.x() > m_left) {
891 clippedPolyObject << pointTop;
892 } else {
893 clippedPolyObject << QPointF(m_left, m_top);
894 }
895 if (pointLeft.y() > m_top && pointLeft.y() <= m_bottom)
896 clippedPolyObject << pointLeft;
897 if (pointBottom.x() > m_left)
898 clippedPolyObject << pointBottom;
899 } else if (m_previousSector == 2) {
900 QPointF pointTop = clipTop(m, m_currentPoint);
901 QPointF pointRight = clipRight(m, m_previousPoint);
902 QPointF pointBottom = clipBottom(m, m_previousPoint);
903 QPointF pointLeft = clipLeft(m, m_currentPoint);
904
905 if (pointTop.x() > m_left && pointTop.x() < m_right)
906 clippedPolyObject << pointTop;
907 if (pointRight.y() > m_top && pointRight.y() < m_bottom)
908 clippedPolyObject << pointRight;
909 if (pointBottom.x() > m_left && pointBottom.x() < m_right)
910 clippedPolyObject << pointBottom;
911 if (pointLeft.y() > m_top && pointLeft.y() < m_bottom)
912 clippedPolyObject << pointLeft;
913
914 if (pointBottom.x() >= m_right && pointRight.y() >= m_bottom)
915 clippedPolyObject << QPointF(m_right, m_bottom);
916 if (pointTop.x() <= m_left && pointLeft.y() <= m_top)
917 clippedPolyObject << QPointF(m_left, m_top);
918 }
919
920 clippedPolyObject << QPointF(m_left, m_bottom);
921 break;
922
923 case 7:
924 if (m_previousSector == 3) {
925 QPointF pointLeft = clipLeft(m, m_previousPoint);
926 QPointF pointBottom = clipBottom(m, m_currentPoint);
927
928 if (pointLeft.y() < m_bottom) {
929 clippedPolyObject << pointLeft;
930 } else {
931 clippedPolyObject << QPointF(m_left, m_bottom);
932 }
933 if (pointBottom.x() > m_left)
934 clippedPolyObject << pointBottom;
935 } else if (m_previousSector == 5) {
936 QPointF pointRight = clipRight(m, m_previousPoint);
937 QPointF pointBottom = clipBottom(m, m_currentPoint);
938
939 if (pointRight.y() < m_bottom) {
940 clippedPolyObject << pointRight;
941 } else {
942 clippedPolyObject << QPointF(m_right, m_bottom);
943 }
944 if (pointBottom.x() < m_right)
945 clippedPolyObject << pointBottom;
946 } else if (m_previousSector == 0) {
947 QPointF pointTop = clipTop(m, m_previousPoint);
948 QPointF pointLeft = clipLeft(m, m_previousPoint);
949 QPointF pointBottom = clipBottom(m, m_currentPoint);
950
951 if (pointTop.x() > m_left)
952 clippedPolyObject << pointTop;
953 if (pointLeft.y() >= m_top && pointLeft.y() < m_bottom)
954 clippedPolyObject << pointLeft;
955 if (pointBottom.x() > m_left) {
956 clippedPolyObject << pointBottom;
957 } else {
958 clippedPolyObject << QPointF(m_left, m_bottom);
959 }
960 } else if (m_previousSector == 1) {
961 clippedPolyObject << clipTop(m, m_previousPoint);
962 clippedPolyObject << clipBottom(m, m_currentPoint);
963 } else if (m_previousSector == 2) {
964 QPointF pointTop = clipTop(m, m_previousPoint);
965 QPointF pointRight = clipRight(m, m_previousPoint);
966 QPointF pointBottom = clipBottom(m, m_currentPoint);
967
968 if (pointTop.x() < m_right)
969 clippedPolyObject << pointTop;
970 if (pointRight.y() >= m_top && pointRight.y() < m_bottom)
971 clippedPolyObject << pointRight;
972 if (pointBottom.x() < m_right) {
973 clippedPolyObject << pointBottom;
974 } else {
975 clippedPolyObject << QPointF(m_right, m_bottom);
976 }
977 }
978 break;
979
980 case 8:
981 if (m_previousSector == 3) {
982 QPointF pointLeft = clipLeft(m, m_previousPoint);
983 QPointF pointBottom = clipBottom(m, m_currentPoint);
984 QPointF pointRight = clipRight(m, m_currentPoint);
985
986 if (pointLeft.y() < m_bottom) {
987 clippedPolyObject << pointLeft;
988 } else {
989 clippedPolyObject << QPointF(m_left, m_bottom);
990 }
991 if (pointBottom.x() > m_left && pointBottom.x() <= m_right)
992 clippedPolyObject << pointBottom;
993 if (pointRight.y() < m_bottom)
994 clippedPolyObject << pointRight;
995 } else if (m_previousSector == 1) {
996 QPointF pointTop = clipTop(m, m_previousPoint);
997 QPointF pointRight = clipRight(m, m_currentPoint);
998 QPointF pointBottom = clipBottom(m, m_currentPoint);
999
1000 if (pointTop.x() < m_right) {
1001 clippedPolyObject << pointTop;
1002 } else {
1003 clippedPolyObject << QPointF(m_right, m_top);
1004 }
1005 if (pointRight.y() > m_top && pointRight.y() <= m_bottom)
1006 clippedPolyObject << pointRight;
1007 if (pointBottom.x() < m_right)
1008 clippedPolyObject << pointBottom;
1009 } else if (m_previousSector == 0) {
1010 QPointF pointTop = clipTop(m, m_currentPoint);
1011 QPointF pointLeft = clipLeft(m, m_currentPoint);
1012 QPointF pointBottom = clipBottom(m, m_previousPoint);
1013 QPointF pointRight = clipRight(m, m_previousPoint);
1014
1015 if (pointTop.x() > m_left && pointTop.x() < m_right)
1016 clippedPolyObject << pointTop;
1017 if (pointLeft.y() > m_top && pointLeft.y() < m_bottom)
1018 clippedPolyObject << pointLeft;
1019 if (pointBottom.x() > m_left && pointBottom.x() < m_right)
1020 clippedPolyObject << pointBottom;
1021 if (pointRight.y() > m_top && pointRight.y() < m_bottom)
1022 clippedPolyObject << pointRight;
1023
1024 if (pointBottom.x() <= m_left && pointLeft.y() >= m_bottom)
1025 clippedPolyObject << QPointF(m_left, m_bottom);
1026 if (pointTop.x() >= m_right && pointRight.y() <= m_top)
1027 clippedPolyObject << QPointF(m_right, m_top);
1028 }
1029
1030 clippedPolyObject << QPointF(m_right, m_bottom);
1031 break;
1032
1033 default:
1034 break;
1035 }
1036}
1037
1038void ClipPainterPrivate::clipOnceCorner(QPolygonF &clippedPolyObject,
1039 QList<QPolygonF> &clippedPolyObjects,
1040 const QPointF &corner,
1041 const QPointF &point,
1042 bool isClosed) const
1043{
1044 Q_UNUSED(clippedPolyObjects)
1045 Q_UNUSED(isClosed)
1046
1047 if (m_currentSector == 4) {
1048 // Appearing
1049 clippedPolyObject << corner;
1050 clippedPolyObject << point;
1051 } else {
1052 // Disappearing
1053 clippedPolyObject << point;
1054 clippedPolyObject << corner;
1055 }
1056}
1057
1058void ClipPainterPrivate::clipOnceEdge(QPolygonF &clippedPolyObject, QList<QPolygonF> &clippedPolyObjects, const QPointF &point, bool isClosed) const
1059{
1060 if (m_currentSector == 4) {
1061 // Appearing
1062 if (!isClosed) {
1063 clippedPolyObject = QPolygonF();
1064 }
1065 clippedPolyObject << point;
1066 } else {
1067 // Disappearing
1068 clippedPolyObject << point;
1069 if (!isClosed) {
1070 clippedPolyObjects << clippedPolyObject;
1071 }
1072 }
1073}
1074
1075void ClipPainterPrivate::clipOnce(QPolygonF &clippedPolyObject, QList<QPolygonF> &clippedPolyObjects, bool isClosed)
1076{
1077 // Interpolate border points (linear interpolation)
1078 QPointF point;
1079
1080 // Calculating the slope.
1081 qreal m = _m(m_previousPoint, m_currentPoint);
1082
1083 // Calculate in which sector the end of the line is located that is off screen
1084 int offscreenpos = (m_currentSector == 4) ? m_previousSector : m_currentSector;
1085
1086 // "Rise over run" for all possible situations .
1087 switch (offscreenpos) {
1088 case 0: // topleft
1089 point = clipTop(m, m_previousPoint);
1090 if (point.x() < m_left) {
1091 point = clipLeft(m, point);
1092 }
1093 clipOnceCorner(clippedPolyObject, clippedPolyObjects, QPointF(m_left, m_top), point, isClosed);
1094 break;
1095 case 1: // top
1096 point = clipTop(m, m_previousPoint);
1097 clipOnceEdge(clippedPolyObject, clippedPolyObjects, point, isClosed);
1098 break;
1099 case 2: // topright
1100 point = clipTop(m, m_previousPoint);
1101 if (point.x() > m_right) {
1102 point = clipRight(m, point);
1103 }
1104 clipOnceCorner(clippedPolyObject, clippedPolyObjects, QPointF(m_right, m_top), point, isClosed);
1105 break;
1106 case 3: // left
1107 point = clipLeft(m, m_previousPoint);
1108 clipOnceEdge(clippedPolyObject, clippedPolyObjects, point, isClosed);
1109 break;
1110 case 5: // right
1111 point = clipRight(m, m_previousPoint);
1112 clipOnceEdge(clippedPolyObject, clippedPolyObjects, point, isClosed);
1113 break;
1114 case 6: // bottomleft
1115 point = clipBottom(m, m_previousPoint);
1116 if (point.x() < m_left) {
1117 point = clipLeft(m, point);
1118 }
1119 clipOnceCorner(clippedPolyObject, clippedPolyObjects, QPointF(m_left, m_bottom), point, isClosed);
1120 break;
1121 case 7: // bottom
1122 point = clipBottom(m, m_previousPoint);
1123 clipOnceEdge(clippedPolyObject, clippedPolyObjects, point, isClosed);
1124 break;
1125 case 8: // bottomright
1126 point = clipBottom(m, m_previousPoint);
1127 if (point.x() > m_right) {
1128 point = clipRight(m, point);
1129 }
1130 clipOnceCorner(clippedPolyObject, clippedPolyObjects, QPointF(m_right, m_bottom), point, isClosed);
1131 break;
1132 default:
1133 break;
1134 }
1135}
1136
1137void ClipPainter::setDebugPolygonsLevel(int level)
1138{
1139 d->m_debugPolygonsLevel = level;
1140}
1141
1142void ClipPainter::setDebugBatchRender(bool enabled)
1143{
1144 d->m_debugBatchRender = enabled;
1145}
1146
1147void ClipPainterPrivate::debugDrawNodes(const QPolygonF &polygon)
1148{
1149 q->save();
1151
1152 q->setPen(Qt::red);
1153 q->setBrush(QBrush("#40FF0000"));
1154
1155 const QList<QPointF>::const_iterator itStartPoint = polygon.constBegin();
1156 const QList<QPointF>::const_iterator itEndPoint = polygon.constEnd();
1157 QList<QPointF>::const_iterator itPoint = itStartPoint;
1158
1159 int i = 0;
1160
1161 for (; itPoint != itEndPoint; ++itPoint) {
1162 ++i;
1163
1164 if (itPoint == itStartPoint || itPoint == itStartPoint + 1 || itPoint == itStartPoint + 2) {
1165 q->setPen(Qt::darkGreen);
1166 q->setBrush(QBrush("#4000FF00"));
1167 if (itPoint == itStartPoint) {
1168 q->drawRect(itPoint->x() - 6.0, itPoint->y() - 6.0, 12.0, 12.0);
1169 } else if (itPoint == itStartPoint + 1) {
1170 q->drawRect(itPoint->x() - 4.0, itPoint->y() - 4.0, 8.0, 8.0);
1171 } else {
1172 q->drawRect(itPoint->x() - 2.0, itPoint->y() - 2.0, 4.0, 4.0);
1173 }
1174 q->setPen(Qt::red);
1175 q->setBrush(QBrush("#40FF0000"));
1176 } else if (itPoint == itEndPoint - 1 || itPoint == itEndPoint - 2 || itPoint == itEndPoint - 3) {
1177 q->setPen(Qt::blue);
1178 q->setBrush(QBrush("#400000FF"));
1179 if (itPoint == itEndPoint - 3) {
1180 q->drawRect(itPoint->x() - 6.0, itPoint->y() - 6.0, 12.0, 12.0);
1181 } else if (itPoint == itEndPoint - 2) {
1182 q->drawRect(itPoint->x() - 4.0, itPoint->y() - 4.0, 8.0, 8.0);
1183 } else {
1184 q->drawRect(itPoint->x() - 2.0, itPoint->y() - 2.0, 4.0, 4.0);
1185 }
1186 q->setPen(Qt::red);
1187 q->setBrush(QBrush("#400000FF"));
1188 } else {
1189 q->drawRect(itPoint->x() - 4, itPoint->y() - 4, 8.0, 8.0);
1190 }
1191 if (m_debugPolygonsLevel == 2) {
1192 q->setFont(QFont(QStringLiteral("Sans Serif"), 7));
1193 q->setPen("black");
1194 q->setBrush(Qt::transparent);
1195 q->drawText(itPoint->x() + 6.0, itPoint->y() + (15 - (i * 5) % 30), QString::number(i));
1196 }
1197 }
1198 q->restore();
1199}
Q_SCRIPTABLE Q_NOREPLY void start()
QStringView level(QStringView ifopt)
const QList< QKeySequence > & end()
Binds a QML item to a specific geodetic location in screen coordinates.
const QColor & color() const const
void setColor(Qt::GlobalColor color)
int alpha() const const
void setAlpha(int alpha)
bool testFlag(Enum flag) const const
const_reference at(qsizetype i) const const
const_iterator constBegin() const const
const_iterator constEnd() const const
T & first()
bool isEmpty() const const
T & last()
qsizetype size() const const
int height() const const
int width() const const
const QBrush & brush() const const
QPaintDevice * device() const const
void drawPolygon(const QPoint *points, int pointCount, Qt::FillRule fillRule)
void drawPolyline(const QPoint *points, int pointCount)
void drawRect(const QRect &rectangle)
void drawText(const QPoint &position, const QString &text)
const QPen & pen() const const
void restore()
void save()
void setBrush(Qt::BrushStyle style)
void setFont(const QFont &font)
void setPen(Qt::PenStyle style)
void setRenderHint(RenderHint hint, bool on)
QRect viewport() const const
QColor color() const const
void setColor(const QColor &color)
qreal widthF() const const
qreal x() const const
qreal y() const const
int height() const const
int width() const const
QString number(double n, char format, int precision)
FillRule
GlobalColor
PenStyle
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 17:04:13 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.