Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qtessellator.cpp
Go to the documentation of this file.
1// Copyright (C) 2018 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6
7#include <QRect>
8#include <QList>
9#include <QMap>
10#include <QDebug>
11
12#include <qmath.h>
13#include <limits.h>
14#include <algorithm>
15
17
18//#define DEBUG
19#ifdef DEBUG
20#define QDEBUG qDebug
21#else
22#define QDEBUG if (1){} else qDebug
23#endif
24
25static const bool emit_clever = true;
26static const bool mark_clever = false;
27
36
37
38
40public:
41 struct Vertices;
42
44
45 QRectF collectAndSortVertices(const QPointF *points, int *maxActiveEdges);
47
48 void emitEdges(QTessellator *tessellator);
51 void addEdges();
53
54 struct Vertex : public QTessellator::Vertex
55 {
56 int flags;
57 };
58
60 {
62 int edge;
63 bool operator <(const Intersection &other) const {
64 if (y != other.y)
65 return y < other.y;
66 return edge < other.edge;
67 }
68 };
70 {
71 int next;
72 int prev;
73 };
75
76 struct Edge {
77 Edge(const Vertices &v, int _edge);
78 int edge;
79 const Vertex *v0;
80 const Vertex *v1;
83 signed int winding : 8;
84 bool mark;
85 bool free;
88 bool isLeftOf(const Edge &other, Q27Dot5 y) const;
90 bool intersect(const Edge &other, Q27Dot5 *y, bool *det_positive) const;
91
92 };
93
95 {
96 public:
97 EdgeSorter(int _y) : y(_y) {}
98 bool operator() (const Edge *e1, const Edge *e2);
99 int y;
100 };
101
102 class Scanline {
103 public:
106
107 void init(int maxActiveEdges);
108 void done();
109
111 int findEdgePosition(const Edge &e) const;
112 int findEdge(int edge) const;
114
115 void swap(int p1, int p2) {
116 Edge *tmp = edges[p1];
117 edges[p1] = edges[p2];
118 edges[p2] = tmp;
119 }
120 void insert(int pos, const Edge &e);
121 void removeAt(int pos);
122 void markEdges(int pos1, int pos2);
123
125 void lineDone();
126
129
131 int size;
132
133 private:
134 Edge *edge_table;
135 int first_unused;
136 int max_edges;
137 enum { default_alloc = 32 };
138 };
139
140 struct Vertices {
141 enum { default_alloc = 128 };
144 void init(int maxVertices);
145 void done();
148
149 Vertex *operator[] (int i) { return storage + i; }
150 const Vertex *operator[] (int i) const { return storage + i; }
151 int position(const Vertex *v) const {
152 return v - storage;
153 }
155 ++v;
156 if (v == storage + nPoints)
157 v = storage;
158 return v;
159 }
160 const Vertex *next(const Vertex *v) const {
161 ++v;
162 if (v == storage + nPoints)
163 v = storage;
164 return v;
165 }
166 int nextPos(const Vertex *v) const {
167 ++v;
168 if (v == storage + nPoints)
169 return 0;
170 return v - storage;
171 }
173 if (v == storage)
174 v = storage + nPoints;
175 --v;
176 return v;
177 }
178 const Vertex *prev(const Vertex *v) const {
179 if (v == storage)
180 v = storage + nPoints;
181 --v;
182 return v;
183 }
184 int prevPos(const Vertex *v) const {
185 if (v == storage)
186 v = storage + nPoints;
187 --v;
188 return v - storage;
189 }
192 };
199
200private:
201 void addIntersection(const Edge *e1, const Edge *e2);
202 bool edgeInChain(Intersection i, int edge);
203};
204
205
207{
208 this->edge = edge;
210 mark = false;
211 free = false;
212
213 v0 = vertices[edge];
214 v1 = vertices.next(v0);
215
216 Q_ASSERT(v0->y != v1->y);
217
218 if (v0->y > v1->y) {
219 qSwap(v0, v1);
220 winding = -1;
221 } else {
222 winding = 1;
223 }
224 y_left = y_right = v0->y;
225}
226
227// This is basically the algorithm from graphics gems. The algorithm
228// is cubic in the coordinates at one place. Since we use 64bit
229// integers, this implies, that the allowed range for our coordinates
230// is limited to 21 bits. With 5 bits behind the decimal, this
231// implies that differences in coordaintes can range from 2*SHORT_MIN
232// to 2*SHORT_MAX, giving us efficiently a coordinate system from
233// SHORT_MIN to SHORT_MAX.
234//
235
236// WARNING: It's absolutely critical that the intersect() and isLeftOf() methods use
237// exactly the same algorithm to calculate yi. It's also important to be sure the algorithms
238// are transitive (ie. the conditions below are true for all input data):
239//
240// a.intersect(b) == b.intersect(a)
241// a.isLeftOf(b) != b.isLeftOf(a)
242//
243// This is tricky to get right, so be very careful when changing anything in here!
244
245static inline bool sameSign(qint64 a, qint64 b) {
246 return (((qint64) ((quint64) a ^ (quint64) b)) >= 0 );
247}
248
249bool QTessellatorPrivate::Edge::intersect(const Edge &other, Q27Dot5 *y, bool *det_positive) const
250{
251 qint64 a1 = v1->y - v0->y;
252 qint64 b1 = v0->x - v1->x;
253
254 qint64 a2 = other.v1->y - other.v0->y;
255 qint64 b2 = other.v0->x - other.v1->x;
256
257 qint64 det = a1 * b2 - a2 * b1;
258 if (det == 0)
259 return false;
260
261 qint64 c1 = qint64(v1->x) * v0->y - qint64(v0->x) * v1->y;
262
263 qint64 r3 = a1 * other.v0->x + b1 * other.v0->y + c1;
264 qint64 r4 = a1 * other.v1->x + b1 * other.v1->y + c1;
265
266 // Check signs of r3 and r4. If both point 3 and point 4 lie on
267 // same side of line 1, the line segments do not intersect.
268 QDEBUG() << " " << r3 << r4;
269 if (r3 != 0 && r4 != 0 && sameSign( r3, r4 ))
270 return false;
271
272 qint64 c2 = qint64(other.v1->x) * other.v0->y - qint64(other.v0->x) * other.v1->y;
273
274 qint64 r1 = a2 * v0->x + b2 * v0->y + c2;
275 qint64 r2 = a2 * v1->x + b2 * v1->y + c2;
276
277 // Check signs of r1 and r2. If both point 1 and point 2 lie
278 // on same side of second line segment, the line segments do not intersect.
279 QDEBUG() << " " << r1 << r2;
280 if (r1 != 0 && r2 != 0 && sameSign( r1, r2 ))
281 return false;
282
283 // The det/2 is to get rounding instead of truncating. It
284 // is added or subtracted to the numerator, depending upon the
285 // sign of the numerator.
286 qint64 offset = det < 0 ? -det : det;
287 offset >>= 1;
288
289 qint64 num = a2 * c1 - a1 * c2;
290 *y = ( num < 0 ? num - offset : num + offset ) / det;
291
292 *det_positive = (det > 0);
293
294 return true;
295}
296
297#undef SAME_SIGNS
298
299bool QTessellatorPrivate::Edge::isLeftOf(const Edge &other, Q27Dot5 y) const
300{
301// QDEBUG() << "isLeftOf" << edge << other.edge << y;
302 qint64 a1 = v1->y - v0->y;
303 qint64 b1 = v0->x - v1->x;
304 qint64 a2 = other.v1->y - other.v0->y;
305 qint64 b2 = other.v0->x - other.v1->x;
306
307 qint64 c2 = qint64(other.v1->x) * other.v0->y - qint64(other.v0->x) * other.v1->y;
308
309 qint64 det = a1 * b2 - a2 * b1;
310 if (det == 0) {
311 // lines are parallel. Only need to check side of one point
312 // fixed ordering for coincident edges
313 qint64 r1 = a2 * v0->x + b2 * v0->y + c2;
314// QDEBUG() << "det = 0" << r1;
315 if (r1 == 0)
316 return edge < other.edge;
317 return (r1 < 0);
318 }
319
320 // not parallel, need to find the y coordinate of the intersection point
321 qint64 c1 = qint64(v1->x) * v0->y - qint64(v0->x) * v1->y;
322
323 qint64 offset = det < 0 ? -det : det;
324 offset >>= 1;
325
326 qint64 num = a2 * c1 - a1 * c2;
327 qint64 yi = ( num < 0 ? num - offset : num + offset ) / det;
328// QDEBUG() << " num=" << num << "offset=" << offset << "det=" << det;
329
330 return ((yi > y) ^ (det < 0));
331}
332
333static inline bool compareVertex(const QTessellatorPrivate::Vertex *p1,
334 const QTessellatorPrivate::Vertex *p2)
335{
336 if (p1->y == p2->y) {
337 if (p1->x == p2->x)
338 return p1 < p2;
339 return p1->x < p2->x;
340 }
341 return p1->y < p2->y;
342}
343
345{
346 if (y == v0->y)
347 return v0->x;
348 else if (y == v1->y)
349 return v1->x;
350
351 qint64 d = v1->x - v0->x;
352 return (v0->x + d*(y - v0->y)/(v1->y-v0->y));
353}
354
355bool QTessellatorPrivate::EdgeSorter::operator() (const Edge *e1, const Edge *e2)
356{
357 return e1->isLeftOf(*e2, y);
358}
359
360
362{
363 edges = 0;
364 edge_table = 0;
365 old = 0;
366}
367
368void QTessellatorPrivate::Scanline::init(int maxActiveEdges)
369{
370 maxActiveEdges *= 2;
371 if (!edges || maxActiveEdges > default_alloc) {
372 max_edges = maxActiveEdges;
373 int s = qMax(maxActiveEdges + 1, default_alloc + 1);
374 edges = q_check_ptr((Edge **)realloc(edges, s*sizeof(Edge *)));
375 edge_table = q_check_ptr((Edge *)realloc(edge_table, s*sizeof(Edge)));
376 old = q_check_ptr((Edge **)realloc(old, s*sizeof(Edge *)));
377 }
378 size = 0;
379 old_size = 0;
380 first_unused = 0;
381 for (int i = 0; i < maxActiveEdges; ++i)
382 edge_table[i].edge = i+1;
383 edge_table[maxActiveEdges].edge = -1;
384}
385
387{
388 if (max_edges > default_alloc) {
389 free(edges);
390 free(old);
391 free(edge_table);
392 edges = 0;
393 old = 0;
394 edge_table = 0;
395 }
396}
397
399{
400 free(edges);
401 free(old);
402 free(edge_table);
403}
404
406{
407 int min = 0;
408 int max = size - 1;
409 while (min < max) {
410 int pos = min + ((max - min + 1) >> 1);
411 Q27Dot5 ax = edges[pos]->positionAt(y);
412 if (ax > x) {
413 max = pos - 1;
414 } else {
415 min = pos;
416 }
417 }
418 return min;
419}
420
422{
423// qDebug() << ">> findEdgePosition";
424 int min = 0;
425 int max = size;
426 while (min < max) {
427 int pos = min + ((max - min) >> 1);
428// qDebug() << " " << min << max << pos << edges[pos]->isLeftOf(e, e.y0);
429 if (edges[pos]->isLeftOf(e, e.v0->y)) {
430 min = pos + 1;
431 } else {
432 max = pos;
433 }
434 }
435// qDebug() << "<< findEdgePosition got" << min;
436 return min;
437}
438
440{
441 for (int i = 0; i < size; ++i) {
442 int item_edge = edges[i]->edge;
443 if (item_edge == edge)
444 return i;
445 }
446 //Q_ASSERT(false);
447 return -1;
448}
449
451{
452 for (int i = 0; i < size; ++i) {
453 edges[i]->mark = false;
454 edges[i]->intersect_left = false;
455 edges[i]->intersect_right = false;
456 }
457}
458
460{
461 Edge **end = edges + size;
462 Edge **e = edges;
463 Edge **o = old;
464 while (e < end) {
465 *o = *e;
466 ++o;
467 ++e;
468 }
469 old_size = size;
470}
471
473{
474 Edge **end = old + old_size;
475 Edge **e = old;
476 while (e < end) {
477 if ((*e)->free) {
478 (*e)->edge = first_unused;
479 first_unused = (*e - edge_table);
480 }
481 ++e;
482 }
483}
484
485void QTessellatorPrivate::Scanline::insert(int pos, const Edge &e)
486{
487 Edge *edge = edge_table + first_unused;
488 first_unused = edge->edge;
489 Q_ASSERT(first_unused != -1);
490 *edge = e;
491 memmove(edges + pos + 1, edges + pos, (size - pos)*sizeof(Edge *));
492 edges[pos] = edge;
493 ++size;
494}
495
497{
498 Edge *e = edges[pos];
499 e->free = true;
500 --size;
501 memmove(edges + pos, edges + pos + 1, (size - pos)*sizeof(Edge *));
502}
503
504void QTessellatorPrivate::Scanline::markEdges(int pos1, int pos2)
505{
506 if (pos2 < pos1)
507 return;
508
509 for (int i = pos1; i <= pos2; ++i)
510 edges[i]->mark = true;
511}
512
513
515{
516 storage = 0;
517 sorted = 0;
518 allocated = 0;
519 nPoints = 0;
520}
521
523{
524 if (storage) {
525 free(storage);
526 free(sorted);
527 }
528}
529
530void QTessellatorPrivate::Vertices::init(int maxVertices)
531{
532 if (!storage || maxVertices > allocated) {
533 int size = qMax((int)default_alloc, maxVertices);
534 storage = q_check_ptr((Vertex *)realloc(storage, size*sizeof(Vertex)));
535 sorted = q_check_ptr((Vertex **)realloc(sorted, size*sizeof(Vertex *)));
536 allocated = maxVertices;
537 }
538}
539
541{
542 if (allocated > default_alloc) {
543 free(storage);
544 free(sorted);
545 storage = 0;
546 sorted = 0;
547 allocated = 0;
548 }
549}
550
551
552
553static inline void fillTrapezoid(Q27Dot5 y1, Q27Dot5 y2, int left, int right,
554 const QTessellatorPrivate::Vertices &vertices,
555 QTessellator::Trapezoid *trap)
556{
557 trap->top = y1;
558 trap->bottom = y2;
559 const QTessellatorPrivate::Vertex *v = vertices[left];
560 trap->topLeft = v;
561 trap->bottomLeft = vertices.next(v);
562 if (trap->topLeft->y > trap->bottomLeft->y)
563 qSwap(trap->topLeft,trap->bottomLeft);
564 v = vertices[right];
565 trap->topRight = v;
566 trap->bottomRight = vertices.next(v);
567 if (trap->topRight->y > trap->bottomRight->y)
568 qSwap(trap->topRight, trap->bottomRight);
569}
570
571QRectF QTessellatorPrivate::collectAndSortVertices(const QPointF *points, int *maxActiveEdges)
572{
573 *maxActiveEdges = 0;
575 Vertex **vv = vertices.sorted;
576
577 qreal xmin(points[0].x());
578 qreal xmax(points[0].x());
579 qreal ymin(points[0].y());
580 qreal ymax(points[0].y());
581
582 // collect vertex data
583 Q27Dot5 y_prev = FloatToQ27Dot5(points[vertices.nPoints-1].y());
584 Q27Dot5 x_next = FloatToQ27Dot5(points[0].x());
585 Q27Dot5 y_next = FloatToQ27Dot5(points[0].y());
586 int j = 0;
587 int i = 0;
588 while (i < vertices.nPoints) {
589 Q27Dot5 y_curr = y_next;
590
591 *vv = v;
592
593 v->x = x_next;
594 v->y = y_next;
595 v->flags = 0;
596
597 next_point:
598
599 xmin = qMin(xmin, points[i+1].x());
600 xmax = qMax(xmax, points[i+1].x());
601 ymin = qMin(ymin, points[i+1].y());
602 ymax = qMax(ymax, points[i+1].y());
603
604 y_next = FloatToQ27Dot5(points[i+1].y());
605 x_next = FloatToQ27Dot5(points[i+1].x());
606
607 // skip vertices on top of each other
608 if (v->x == x_next && v->y == y_next) {
609 ++i;
610 if (i < vertices.nPoints)
611 goto next_point;
614 if (y_prev < y_curr)
616 else if (y_prev > y_curr)
618 else
622 *maxActiveEdges += 2;
623 break;
624 }
625
626 if (y_prev < y_curr)
628 else if (y_prev > y_curr)
630 else
632
633
634 if (y_curr < y_next)
636 else if (y_curr > y_next)
638 else
640 // ### could probably get better limit by looping over sorted list and counting down on ending edges
643 *maxActiveEdges += 2;
644 y_prev = y_curr;
645 ++v;
646 ++vv;
647 ++j;
648 ++i;
649 }
651
652 QDEBUG() << "maxActiveEdges=" << *maxActiveEdges;
653 vv = vertices.sorted;
654 std::sort(vv, vv + vertices.nPoints, compareVertex);
655
656 return QRectF(xmin, ymin, xmax-xmin, ymax-ymin);
657}
658
662 bool used;
663 bool before;
664
665 inline bool operator<(const QCoincidingEdge &e2) const
666 {
667 return end->y == e2.end->y ? end->x < e2.end->x : end->y < e2.end->y;
668 }
669};
670
689
691{
692 Vertex **vv = vertices.sorted;
693
694 QCoincidingEdge *tl = nullptr;
695 int tlSize = 0;
696
697 for (int i = 0; i < vertices.nPoints - 1; ++i) {
698 Vertex *v = vv[i];
699 int testListSize = 0;
700 while (i < vertices.nPoints - 1) {
701 Vertex *n = vv[i];
702 if (v->x != n->x || v->y != n->y)
703 break;
704
705 if (testListSize > tlSize - 2) {
706 tlSize = qMax(tlSize*2, 16);
707 tl = q_check_ptr((QCoincidingEdge *)realloc(tl, tlSize*sizeof(QCoincidingEdge)));
708 }
710 tl[testListSize].start = n;
711 tl[testListSize].end = vertices.prev(n);
712 tl[testListSize].used = false;
713 tl[testListSize].before = true;
714 ++testListSize;
715 }
717 tl[testListSize].start = n;
718 tl[testListSize].end = vertices.next(n);
719 tl[testListSize].used = false;
720 tl[testListSize].before = false;
721 ++testListSize;
722 }
723 ++i;
724 }
725 if (!testListSize)
726 continue;
727
728 std::sort(tl, tl + testListSize);
729
730QT_WARNING_PUSH
731QT_WARNING_DISABLE_CLANG("-Wfor-loop-analysis")
732 for (int j = 0; j < testListSize; ++j) {
733 if (tl[j].used)
734 continue;
735
736 for (int k = j + 1; k < testListSize; ++k) {
737 if (tl[j].end->x != tl[k].end->x
738 || tl[j].end->y != tl[k].end->y
739 || tl[k].used)
740 break;
741
742 if (!winding || tl[j].before != tl[k].before) {
743 cancelEdges(tl[j], tl[k]);
744 break;
745 }
746 ++k;
747 }
748 ++j;
749 }
750QT_WARNING_POP
751 }
752 free(tl);
753}
754
755
757{
758 //QDEBUG() << "TRAPS:";
760 return;
761
762 // emit edges
763 if (winding) {
764 // winding fill rule
765 int w = 0;
766
768
769 for (int i = 0; i < scanline.old_size - 1; ++i) {
770 Edge *left = scanline.old[i];
771 Edge *right = scanline.old[i+1];
772 w += left->winding;
773// qDebug() << "i=" << i << "edge->winding=" << left->winding << "winding=" << winding;
774 if (w == 0) {
775 left->y_right = y;
776 right->y_left = y;
777 } else if (!emit_clever || left->mark || right->mark) {
778 Q27Dot5 top = qMax(left->y_right, right->y_left);
779 if (top != y) {
781 fillTrapezoid(top, y, left->edge, right->edge, vertices, &trap);
782 tessellator->addTrap(trap);
783// QDEBUG() << " top=" << Q27Dot5ToDouble(top) << "left=" << left->edge << "right=" << right->edge;
784 }
785 right->y_left = y;
786 left->y_right = y;
787 }
788 left->mark = false;
789 }
793 }
794 } else {
795 // odd-even fill rule
796 for (int i = 0; i < scanline.old_size; i += 2) {
797 Edge *left = scanline.old[i];
798 Edge *right = scanline.old[i+1];
799 if (!emit_clever || left->mark || right->mark) {
800 Q27Dot5 top = qMax(left->y_right, right->y_left);
801 if (top != y) {
803 fillTrapezoid(top, y, left->edge, right->edge, vertices, &trap);
804 tessellator->addTrap(trap);
805 }
806// QDEBUG() << " top=" << Q27Dot5ToDouble(top) << "left=" << left->edge << "right=" << right->edge;
807 left->y_left = y;
808 left->y_right = y;
809 right->y_left = y;
810 right->y_right = y;
811 left->mark = right->mark = false;
812 }
813 }
814 }
815}
816
817
819{
820 QDEBUG() << "PROCESS INTERSECTIONS";
821 // process intersections
822 while (!intersections.isEmpty()) {
823 Intersections::iterator it = intersections.begin();
824 if (it.key().y != y)
825 break;
826
827 // swap edges
828 QDEBUG() << " swapping intersecting edges ";
829 int min = scanline.size;
830 int max = 0;
831 Q27Dot5 xmin = INT_MAX;
832 Q27Dot5 xmax = INT_MIN;
833 int num = 0;
834 while (1) {
835 const Intersection i = it.key();
836 int next = it->next;
837
838 int edgePos = scanline.findEdge(i.edge);
839 if (edgePos >= 0) {
840 ++num;
841 min = qMin(edgePos, min);
842 max = qMax(edgePos, max);
843 Edge *edge = scanline.edges[edgePos];
844 xmin = qMin(xmin, edge->positionAt(y));
845 xmax = qMax(xmax, edge->positionAt(y));
846 }
847 Intersection key;
848 key.y = y;
849 key.edge = next;
850 it = intersections.find(key);
851 intersections.remove(i);
852 if (it == intersections.end())
853 break;
854 }
855 if (num < 2)
856 continue;
857
858 Q_ASSERT(min != max);
859 QDEBUG() << "sorting between" << min << "and" << max << "xpos=" << xmin << xmax;
860 while (min > 0 && scanline.edges[min - 1]->positionAt(y) >= xmin) {
861 QDEBUG() << " adding edge on left";
862 --min;
863 }
864 while (max < scanline.size - 1 && scanline.edges[max + 1]->positionAt(y) <= xmax) {
865 QDEBUG() << " adding edge on right";
866 ++max;
867 }
868
869 std::sort(scanline.edges + min, scanline.edges + max + 1, EdgeSorter(y));
870#ifdef DEBUG
871 for (int i = min; i <= max; ++i)
872 QDEBUG() << " " << scanline.edges[i]->edge << "at pos" << i;
873#endif
874 for (int i = min; i <= max; ++i) {
875 Edge *edge = scanline.edges[i];
876 edge->intersect_left = true;
877 edge->intersect_right = true;
878 edge->mark = true;
879 }
880 }
881}
882
884{
885 int cv = currentVertex;
886 while (cv < vertices.nPoints) {
887 const Vertex *v = vertices.sorted[cv];
888 if (v->y > y)
889 break;
890 if (v->flags & LineBeforeEnds) {
891 QDEBUG() << " removing edge" << vertices.prevPos(v);
893 if (pos == -1)
894 continue;
895 scanline.edges[pos]->mark = true;
896 if (pos > 0)
897 scanline.edges[pos - 1]->intersect_right = true;
898 if (pos < scanline.size - 1)
899 scanline.edges[pos + 1]->intersect_left = true;
901 }
902 if (v->flags & LineAfterEnds) {
903 QDEBUG() << " removing edge" << vertices.position(v);
905 if (pos == -1)
906 continue;
907 scanline.edges[pos]->mark = true;
908 if (pos > 0)
909 scanline.edges[pos - 1]->intersect_right = true;
910 if (pos < scanline.size - 1)
911 scanline.edges[pos + 1]->intersect_left = true;
913 }
914 ++cv;
915 }
916}
917
919{
922 if (v->y > y)
923 break;
924 if (v->flags & LineBeforeStarts) {
925 // add new edge
926 int start = vertices.prevPos(v);
927 Edge e(vertices, start);
929 QDEBUG() << " adding edge" << start << "at position" << pos;
931 if (!mark_clever || !(v->flags & LineAfterEnds)) {
932 if (pos > 0)
933 scanline.edges[pos - 1]->mark = true;
934 if (pos < scanline.size - 1)
935 scanline.edges[pos + 1]->mark = true;
936 }
937 }
938 if (v->flags & LineAfterStarts) {
941 QDEBUG() << " adding edge" << vertices.position(v) << "at position" << pos;
943 if (!mark_clever || !(v->flags & LineBeforeEnds)) {
944 if (pos > 0)
945 scanline.edges[pos - 1]->mark = true;
946 if (pos < scanline.size - 1)
947 scanline.edges[pos + 1]->mark = true;
948 }
949 }
952 const Vertex *next = vertices.next(v);
953 Q_ASSERT(v->y == next->y);
954 int pos2 = scanline.findEdgePosition(next->x, next->y);
955 if (pos2 < pos1)
956 qSwap(pos1, pos2);
957 if (pos1 > 0)
958 --pos1;
959 if (pos2 == scanline.size)
960 --pos2;
961 //QDEBUG() << "marking horizontal edge from " << pos1 << "to" << pos2;
962 scanline.markEdges(pos1, pos2);
963 }
965 }
966}
967
968#ifdef DEBUG
969static void checkLinkChain(const QTessellatorPrivate::Intersections &intersections,
970 QTessellatorPrivate::Intersection i)
971{
972// qDebug() << " Link chain: ";
973 int end = i.edge;
974 while (1) {
975 QTessellatorPrivate::IntersectionLink l = intersections.value(i);
976// qDebug() << " " << i.edge << "next=" << l.next << "prev=" << l.prev;
977 if (l.next == end)
978 break;
979 Q_ASSERT(l.next != -1);
980 Q_ASSERT(l.prev != -1);
981
982 QTessellatorPrivate::Intersection i2 = i;
983 i2.edge = l.next;
984 QTessellatorPrivate::IntersectionLink l2 = intersections.value(i2);
985
986 Q_ASSERT(l2.next != -1);
987 Q_ASSERT(l2.prev != -1);
988 Q_ASSERT(l.next == i2.edge);
989 Q_ASSERT(l2.prev == i.edge);
990 i = i2;
991 }
992}
993#endif
994
995bool QTessellatorPrivate::edgeInChain(Intersection i, int edge)
996{
997 int end = i.edge;
998 while (1) {
999 if (i.edge == edge)
1000 return true;
1001 IntersectionLink l = intersections.value(i);
1002 if (l.next == end)
1003 break;
1004 Q_ASSERT(l.next != -1);
1005 Q_ASSERT(l.prev != -1);
1006
1007 Intersection i2 = i;
1008 i2.edge = l.next;
1009
1010#ifndef QT_NO_DEBUG
1011 IntersectionLink l2 = intersections.value(i2);
1012 Q_ASSERT(l2.next != -1);
1013 Q_ASSERT(l2.prev != -1);
1014 Q_ASSERT(l.next == i2.edge);
1015 Q_ASSERT(l2.prev == i.edge);
1016#endif
1017 i = i2;
1018 }
1019 return false;
1020}
1021
1022
1023void QTessellatorPrivate::addIntersection(const Edge *e1, const Edge *e2)
1024{
1025 const IntersectionLink emptyLink = {-1, -1};
1026
1028 if (e2->edge == next)
1029 return;
1031 if (e2->edge == prev)
1032 return;
1033
1034 Q27Dot5 yi;
1035 bool det_positive;
1036 bool isect = e1->intersect(*e2, &yi, &det_positive);
1037 QDEBUG("checking edges %d and %d", e1->edge, e2->edge);
1038 if (!isect) {
1039 QDEBUG() << " no intersection";
1040 return;
1041 }
1042
1043 // don't emit an intersection if it's at the start of a line segment or above us
1044 if (yi <= y) {
1045 if (!det_positive)
1046 return;
1047 QDEBUG() << " ----->>>>>> WRONG ORDER!";
1048 yi = y;
1049 }
1050 QDEBUG() << " between edges " << e1->edge << "and" << e2->edge << "at point ("
1051 << Q27Dot5ToDouble(yi) << ')';
1052
1053 Intersection i1;
1054 i1.y = yi;
1055 i1.edge = e1->edge;
1056 IntersectionLink link1 = intersections.value(i1, emptyLink);
1057 Intersection i2;
1058 i2.y = yi;
1059 i2.edge = e2->edge;
1060 IntersectionLink link2 = intersections.value(i2, emptyLink);
1061
1062 // new pair of edges
1063 if (link1.next == -1 && link2.next == -1) {
1064 link1.next = link1.prev = i2.edge;
1065 link2.next = link2.prev = i1.edge;
1066 } else if (link1.next == i2.edge || link1.prev == i2.edge
1067 || link2.next == i1.edge || link2.prev == i1.edge) {
1068#ifdef DEBUG
1069 checkLinkChain(intersections, i1);
1070 checkLinkChain(intersections, i2);
1071 Q_ASSERT(edgeInChain(i1, i2.edge));
1072#endif
1073 return;
1074 } else if (link1.next == -1 || link2.next == -1) {
1075 if (link2.next == -1) {
1076 qSwap(i1, i2);
1077 qSwap(link1, link2);
1078 }
1079 Q_ASSERT(link1.next == -1);
1080#ifdef DEBUG
1081 checkLinkChain(intersections, i2);
1082#endif
1083 // only i2 in list
1084 link1.next = i2.edge;
1085 link1.prev = link2.prev;
1086 link2.prev = i1.edge;
1087 Intersection other;
1088 other.y = yi;
1089 other.edge = link1.prev;
1090 IntersectionLink link = intersections.value(other, emptyLink);
1091 Q_ASSERT(link.next == i2.edge);
1092 Q_ASSERT(link.prev != -1);
1093 link.next = i1.edge;
1094 intersections.insert(other, link);
1095 } else {
1096 bool connected = edgeInChain(i1, i2.edge);
1097 if (connected)
1098 return;
1099#ifdef DEBUG
1100 checkLinkChain(intersections, i1);
1101 checkLinkChain(intersections, i2);
1102#endif
1103 // both already in some list. Have to make sure they are connected
1104 // this can be done by cutting open the ring(s) after the two eges and
1105 // connecting them again
1106 Intersection other1;
1107 other1.y = yi;
1108 other1.edge = link1.next;
1109 IntersectionLink linko1 = intersections.value(other1, emptyLink);
1110 Intersection other2;
1111 other2.y = yi;
1112 other2.edge = link2.next;
1113 IntersectionLink linko2 = intersections.value(other2, emptyLink);
1114
1115 linko1.prev = i2.edge;
1116 link2.next = other1.edge;
1117
1118 linko2.prev = i1.edge;
1119 link1.next = other2.edge;
1120 intersections.insert(other1, linko1);
1121 intersections.insert(other2, linko2);
1122 }
1123 intersections.insert(i1, link1);
1124 intersections.insert(i2, link2);
1125#ifdef DEBUG
1126 checkLinkChain(intersections, i1);
1127 checkLinkChain(intersections, i2);
1128 Q_ASSERT(edgeInChain(i1, i2.edge));
1129#endif
1130 return;
1131
1132}
1133
1134
1136{
1137 if (scanline.size) {
1138 QDEBUG() << "INTERSECTIONS";
1139 // check marked edges for intersections
1140#ifdef DEBUG
1141 for (int i = 0; i < scanline.size; ++i) {
1142 Edge *e = scanline.edges[i];
1143 QDEBUG() << " " << i << e->edge << "isect=(" << e->intersect_left << e->intersect_right
1144 << ')';
1145 }
1146#endif
1147
1148 for (int i = 0; i < scanline.size - 1; ++i) {
1149 Edge *e1 = scanline.edges[i];
1150 Edge *e2 = scanline.edges[i + 1];
1151 // check for intersection
1153 addIntersection(e1, e2);
1154 }
1155 }
1156#if 0
1157 if (intersections.constBegin().key().y == y) {
1158 QDEBUG() << "----------------> intersection on same line";
1159 scanline.clearMarks();
1160 scanline.processIntersections(y, &intersections);
1161 goto redo;
1162 }
1163#endif
1164}
1165
1166
1168{
1169 d = new QTessellatorPrivate;
1170}
1171
1173{
1174 delete d;
1175}
1176
1178{
1179 d->winding = w;
1180}
1181
1182
1183QRectF QTessellator::tessellate(const QPointF *points, int nPoints)
1184{
1185 Q_ASSERT(points[0] == points[nPoints-1]);
1186 --nPoints;
1187
1188#ifdef DEBUG
1189 QDEBUG()<< "POINTS:";
1190 for (int i = 0; i < nPoints; ++i) {
1191 QDEBUG() << points[i];
1192 }
1193#endif
1194
1195 // collect edges and calculate bounds
1196 d->vertices.nPoints = nPoints;
1197 d->vertices.init(nPoints);
1198
1199 int maxActiveEdges = 0;
1200 QRectF br = d->collectAndSortVertices(points, &maxActiveEdges);
1202
1203#ifdef DEBUG
1204 QDEBUG() << "nPoints = " << nPoints << "using " << d->vertices.nPoints;
1205 QDEBUG()<< "VERTICES:";
1206 for (int i = 0; i < d->vertices.nPoints; ++i) {
1207 QDEBUG() << " " << i << ": "
1208 << "point=" << d->vertices.position(d->vertices.sorted[i])
1209 << "flags=" << d->vertices.sorted[i]->flags
1210 << "pos=(" << Q27Dot5ToDouble(d->vertices.sorted[i]->x) << '/'
1211 << Q27Dot5ToDouble(d->vertices.sorted[i]->y) << ')';
1212 }
1213#endif
1214
1215 d->scanline.init(maxActiveEdges);
1216 d->y = INT_MIN/256;
1217 d->currentVertex = 0;
1218
1221
1223 if (!d->intersections.isEmpty())
1224 d->y = qMin(d->y, d->intersections.constBegin().key().y);
1225
1226 QDEBUG()<< "===== SCANLINE: y =" << Q27Dot5ToDouble(d->y) << " =====";
1227
1231 d->addEdges();
1233 d->emitEdges(this);
1235
1236#ifdef DEBUG
1237 QDEBUG()<< "===== edges:";
1238 for (int i = 0; i < d->scanline.size; ++i) {
1239 QDEBUG() << " " << d->scanline.edges[i]->edge
1240 << "p0= (" << Q27Dot5ToDouble(d->scanline.edges[i]->v0->x)
1241 << '/' << Q27Dot5ToDouble(d->scanline.edges[i]->v0->y)
1242 << ") p1= (" << Q27Dot5ToDouble(d->scanline.edges[i]->v1->x)
1243 << '/' << Q27Dot5ToDouble(d->scanline.edges[i]->v1->y) << ')'
1244 << "x=" << Q27Dot5ToDouble(d->scanline.edges[i]->positionAt(d->y))
1245 << "isLeftOfNext="
1246 << ((i < d->scanline.size - 1)
1247 ? d->scanline.edges[i]->isLeftOf(*d->scanline.edges[i+1], d->y)
1248 : true);
1249 }
1250#endif
1251}
1252
1254 d->intersections.clear();
1255 return br;
1256}
1257
1258// tessellates the given convex polygon
1259void QTessellator::tessellateConvex(const QPointF *points, int nPoints)
1260{
1261 Q_ASSERT(points[0] == points[nPoints-1]);
1262 --nPoints;
1263
1264 d->vertices.nPoints = nPoints;
1265 d->vertices.init(nPoints);
1266
1267 for (int i = 0; i < nPoints; ++i) {
1268 d->vertices[i]->x = FloatToQ27Dot5(points[i].x());
1269 d->vertices[i]->y = FloatToQ27Dot5(points[i].y());
1270 }
1271
1272 int left = 0, right = 0;
1273
1274 int top = 0;
1275 for (int i = 1; i < nPoints; ++i) {
1276 if (d->vertices[i]->y < d->vertices[top]->y)
1277 top = i;
1278 }
1279
1280 left = (top + nPoints - 1) % nPoints;
1281 right = (top + 1) % nPoints;
1282
1283 while (d->vertices[left]->x == d->vertices[top]->x && d->vertices[left]->y == d->vertices[top]->y && left != right)
1284 left = (left + nPoints - 1) % nPoints;
1285
1286 while (d->vertices[right]->x == d->vertices[top]->x && d->vertices[right]->y == d->vertices[top]->y && left != right)
1287 right = (right + 1) % nPoints;
1288
1289 if (left == right)
1290 return;
1291
1292 int dir = 1;
1293
1294 Vertex dLeft = { d->vertices[top]->x - d->vertices[left]->x,
1295 d->vertices[top]->y - d->vertices[left]->y };
1296
1297 Vertex dRight = { d->vertices[right]->x - d->vertices[top]->x,
1298 d->vertices[right]->y - d->vertices[top]->y };
1299
1300 Q27Dot5 cross = dLeft.x * dRight.y - dLeft.y * dRight.x;
1301
1302 // flip direction if polygon is clockwise
1303 if (cross < 0 || (cross == 0 && dLeft.x > 0)) {
1304 qSwap(left, right);
1305 dir = -1;
1306 }
1307
1308 Vertex *lastLeft = d->vertices[top];
1309 Vertex *lastRight = d->vertices[top];
1310
1311 QTessellator::Trapezoid trap;
1312
1313 while (lastLeft->y == d->vertices[left]->y && left != right) {
1314 lastLeft = d->vertices[left];
1315 left = (left + nPoints - dir) % nPoints;
1316 }
1317
1318 while (lastRight->y == d->vertices[right]->y && left != right) {
1319 lastRight = d->vertices[right];
1320 right = (right + nPoints + dir) % nPoints;
1321 }
1322
1323 while (true) {
1324 trap.top = qMax(lastRight->y, lastLeft->y);
1325 trap.bottom = qMin(d->vertices[left]->y, d->vertices[right]->y);
1326 trap.topLeft = lastLeft;
1327 trap.topRight = lastRight;
1328 trap.bottomLeft = d->vertices[left];
1329 trap.bottomRight = d->vertices[right];
1330
1331 if (trap.bottom > trap.top)
1332 addTrap(trap);
1333
1334 if (left == right)
1335 break;
1336
1337 if (d->vertices[right]->y < d->vertices[left]->y) {
1338 do {
1339 lastRight = d->vertices[right];
1340 right = (right + nPoints + dir) % nPoints;
1341 }
1342 while (lastRight->y == d->vertices[right]->y && left != right);
1343 } else {
1344 do {
1345 lastLeft = d->vertices[left];
1346 left = (left + nPoints - dir) % nPoints;
1347 }
1348 while (lastLeft->y == d->vertices[left]->y && left != right);
1349 }
1350 }
1351}
1352
1353// tessellates the stroke of the line from a_ to b_ with the given width and a flat cap
1354void QTessellator::tessellateRect(const QPointF &a_, const QPointF &b_, qreal width)
1355{
1356 Vertex a = { FloatToQ27Dot5(a_.x()), FloatToQ27Dot5(a_.y()) };
1357 Vertex b = { FloatToQ27Dot5(b_.x()), FloatToQ27Dot5(b_.y()) };
1358
1359 QPointF pa = a_, pb = b_;
1360
1361 if (a.y > b.y) {
1362 qSwap(a, b);
1363 qSwap(pa, pb);
1364 }
1365
1366 Vertex delta = { b.x - a.x, b.y - a.y };
1367
1368 if (delta.x == 0 && delta.y == 0)
1369 return;
1370
1371 qreal hw = 0.5 * width;
1372
1373 if (delta.x == 0) {
1374 Q27Dot5 halfWidth = FloatToQ27Dot5(hw);
1375
1376 if (halfWidth == 0)
1377 return;
1378
1379 Vertex topLeft = { a.x - halfWidth, a.y };
1380 Vertex topRight = { a.x + halfWidth, a.y };
1381 Vertex bottomLeft = { a.x - halfWidth, b.y };
1382 Vertex bottomRight = { a.x + halfWidth, b.y };
1383
1384 QTessellator::Trapezoid trap = { topLeft.y, bottomLeft.y, &topLeft, &bottomLeft, &topRight, &bottomRight };
1385 addTrap(trap);
1386 } else if (delta.y == 0) {
1387 Q27Dot5 halfWidth = FloatToQ27Dot5(hw);
1388
1389 if (halfWidth == 0)
1390 return;
1391
1392 if (a.x > b.x)
1393 qSwap(a.x, b.x);
1394
1395 Vertex topLeft = { a.x, a.y - halfWidth };
1396 Vertex topRight = { b.x, a.y - halfWidth };
1397 Vertex bottomLeft = { a.x, a.y + halfWidth };
1398 Vertex bottomRight = { b.x, a.y + halfWidth };
1399
1400 QTessellator::Trapezoid trap = { topLeft.y, bottomLeft.y, &topLeft, &bottomLeft, &topRight, &bottomRight };
1401 addTrap(trap);
1402 } else {
1403 QPointF perp(pb.y() - pa.y(), pa.x() - pb.x());
1404 qreal length = qSqrt(perp.x() * perp.x() + perp.y() * perp.y());
1405
1406 if (qFuzzyIsNull(length))
1407 return;
1408
1409 // need the half of the width
1410 perp *= hw / length;
1411
1412 QPointF pta = pa + perp;
1413 QPointF ptb = pa - perp;
1414 QPointF ptc = pb - perp;
1415 QPointF ptd = pb + perp;
1416
1417 Vertex ta = { FloatToQ27Dot5(pta.x()), FloatToQ27Dot5(pta.y()) };
1418 Vertex tb = { FloatToQ27Dot5(ptb.x()), FloatToQ27Dot5(ptb.y()) };
1419 Vertex tc = { FloatToQ27Dot5(ptc.x()), FloatToQ27Dot5(ptc.y()) };
1420 Vertex td = { FloatToQ27Dot5(ptd.x()), FloatToQ27Dot5(ptd.y()) };
1421
1422 if (ta.y < tb.y) {
1423 if (tb.y < td.y) {
1424 QTessellator::Trapezoid top = { ta.y, tb.y, &ta, &tb, &ta, &td };
1425 QTessellator::Trapezoid bottom = { td.y, tc.y, &tb, &tc, &td, &tc };
1426 addTrap(top);
1427 addTrap(bottom);
1428
1429 QTessellator::Trapezoid middle = { tb.y, td.y, &tb, &tc, &ta, &td };
1430 addTrap(middle);
1431 } else {
1432 QTessellator::Trapezoid top = { ta.y, td.y, &ta, &tb, &ta, &td };
1433 QTessellator::Trapezoid bottom = { tb.y, tc.y, &tb, &tc, &td, &tc };
1434 addTrap(top);
1435 addTrap(bottom);
1436
1437 if (tb.y != td.y) {
1438 QTessellator::Trapezoid middle = { td.y, tb.y, &ta, &tb, &td, &tc };
1439 addTrap(middle);
1440 }
1441 }
1442 } else {
1443 if (ta.y < tc.y) {
1444 QTessellator::Trapezoid top = { tb.y, ta.y, &tb, &tc, &tb, &ta };
1445 QTessellator::Trapezoid bottom = { tc.y, td.y, &tc, &td, &ta, &td };
1446 addTrap(top);
1447 addTrap(bottom);
1448
1449 QTessellator::Trapezoid middle = { ta.y, tc.y, &tb, &tc, &ta, &td };
1450 addTrap(middle);
1451 } else {
1452 QTessellator::Trapezoid top = { tb.y, tc.y, &tb, &tc, &tb, &ta };
1453 QTessellator::Trapezoid bottom = { ta.y, td.y, &tc, &td, &ta, &td };
1454 addTrap(top);
1455 addTrap(bottom);
1456
1457 if (ta.y != tc.y) {
1458 QTessellator::Trapezoid middle = { tc.y, ta.y, &tc, &td, &tb, &ta };
1459 addTrap(middle);
1460 }
1461 }
1462 }
1463 }
1464}
1465
1466QT_END_NAMESPACE
bool operator()(const Edge *e1, const Edge *e2)
void insert(int pos, const Edge &e)
int findEdgePosition(Q27Dot5 x, Q27Dot5 y) const
void markEdges(int pos1, int pos2)
int findEdgePosition(const Edge &e) const
void init(int maxActiveEdges)
QRectF collectAndSortVertices(const QPointF *points, int *maxActiveEdges)
QMap< Intersection, IntersectionLink > Intersections
Intersections intersections
void emitEdges(QTessellator *tessellator)
void tessellateConvex(const QPointF *points, int nPoints)
void tessellateRect(const QPointF &a, const QPointF &b, qreal width)
void setWinding(bool w)
virtual ~QTessellator()
QRectF tessellate(const QPointF *points, int nPoints)
virtual void addTrap(const Trapezoid &trap)=0
static bool compareVertex(const QTessellatorPrivate::Vertex *p1, const QTessellatorPrivate::Vertex *p2)
static const bool emit_clever
static void cancelEdges(QCoincidingEdge &e1, QCoincidingEdge &e2)
VertexFlags
@ LineBeforeEnds
@ LineAfterStarts
@ LineAfterEnds
@ LineBeforeStarts
@ LineAfterHorizontal
@ LineBeforeHorizontal
static const bool mark_clever
static void fillTrapezoid(Q27Dot5 y1, Q27Dot5 y2, int left, int right, const QTessellatorPrivate::Vertices &vertices, QTessellator::Trapezoid *trap)
static bool sameSign(qint64 a, qint64 b)
#define QDEBUG
#define FloatToQ27Dot5(i)
#define Q27Dot5ToDouble(i)
int Q27Dot5
bool operator<(const QCoincidingEdge &e2) const
QTessellatorPrivate::Vertex * start
QTessellatorPrivate::Vertex * end
Edge(const Vertices &v, int _edge)
bool intersect(const Edge &other, Q27Dot5 *y, bool *det_positive) const
bool isLeftOf(const Edge &other, Q27Dot5 y) const
Q27Dot5 positionAt(Q27Dot5 y) const
bool operator<(const Intersection &other) const
int nextPos(const Vertex *v) const
void init(int maxVertices)
int position(const Vertex *v) const
const Vertex * operator[](int i) const
int prevPos(const Vertex *v) const
const Vertex * next(const Vertex *v) const
const Vertex * prev(const Vertex *v) const
const Vertex * bottomRight
const Vertex * bottomLeft