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