Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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
4#include "qtessellator_p.h"
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);
49 void removeEdges();
50 void addEdges();
51 void addIntersections();
52
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 };
73 typedef QMap<Intersection, IntersectionLink> Intersections;
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:
103 Scanline();
104 ~Scanline();
105
106 void init(int maxActiveEdges);
107 void done();
108
109 int findEdgePosition(Q27Dot5 x, Q27Dot5 y) const;
110 int findEdgePosition(const Edge &e) const;
111 int findEdge(int edge) const;
112 void clearMarks();
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
123 void prepareLine();
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 };
141 Vertices();
142 ~Vertices();
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
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
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
355{
356 return e1->isLeftOf(*e2, y);
357}
358
359
361{
362 edges = 0;
363 edge_table = 0;
364 old = 0;
365}
366
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
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
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
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,
555{
556 trap->top = y1;
557 trap->bottom = y2;
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
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
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)
614 v0->flags |= LineBeforeEnds;
615 else if (y_prev > y_curr)
616 v0->flags |= LineBeforeStarts;
617 else
618 v0->flags |= LineBeforeHorizontal;
619 if ((v0->flags & (LineBeforeStarts|LineAfterStarts))
620 && !(v0->flags & (LineAfterEnds|LineBeforeEnds)))
621 *maxActiveEdges += 2;
622 break;
623 }
624
625 if (y_prev < y_curr)
626 v->flags |= LineBeforeEnds;
627 else if (y_prev > y_curr)
628 v->flags |= LineBeforeStarts;
629 else
630 v->flags |= LineBeforeHorizontal;
631
632
633 if (y_curr < y_next)
634 v->flags |= LineAfterStarts;
635 else if (y_curr > y_next)
636 v->flags |= LineAfterEnds;
637 else
638 v->flags |= LineAfterHorizontal;
639 // ### could probably get better limit by looping over sorted list and counting down on ending edges
640 if ((v->flags & (LineBeforeStarts|LineAfterStarts))
641 && !(v->flags & (LineAfterEnds|LineBeforeEnds)))
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
671{
672 if (e1.before) {
675 } else {
678 }
679 if (e2.before) {
682 } else {
685 }
686 e1.used = e2.used = true;
687}
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 }
708 if (n->flags & (LineBeforeStarts|LineBeforeHorizontal)) {
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 }
715 if (n->flags & (LineAfterStarts|LineAfterHorizontal)) {
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
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 }
750 }
751 free(tl);
752}
753
754
756{
757 //QDEBUG() << "TRAPS:";
758 if (!scanline.old_size)
759 return;
760
761 // emit edges
762 if (winding) {
763 // winding fill rule
764 int w = 0;
765
766 scanline.old[0]->y_left = y;
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 }
789 if (scanline.old[scanline.old_size - 1]->mark) {
791 scanline.old[scanline.old_size - 1]->mark = false;
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 }
847 key.y = y;
848 key.edge = next;
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{
919 while (currentVertex < vertices.nPoints) {
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;
929 scanline.insert(pos, e);
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;
941 scanline.insert(pos, e);
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 }
949 if (v->flags & LineAfterHorizontal) {
950 int pos1 = scanline.findEdgePosition(v->x, v->y);
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,
970{
971// qDebug() << " Link chain: ";
972 int end = i.edge;
973 while (1) {
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
982 i2.edge = l.next;
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
1026 int next = vertices.nextPos(vertices[e1->edge]);
1027 if (e2->edge == next)
1028 return;
1029 int prev = vertices.prevPos(vertices[e1->edge]);
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
1151 if (e1->intersect_right || e2->intersect_left)
1152 addIntersection(e1, e2);
1153 }
1154 }
1155#if 0
1156 if (intersections.constBegin().key().y == y) {
1157 QDEBUG() << "----------------> intersection on same line";
1159 scanline.processIntersections(y, &intersections);
1160 goto redo;
1161 }
1162#endif
1163}
1164
1165
1170
1172{
1173 delete d;
1174}
1175
1177{
1178 d->winding = w;
1179}
1180
1181
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);
1200 d->cancelCoincidingEdges();
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
1218 while (d->currentVertex < d->vertices.nPoints) {
1219 d->scanline.clearMarks();
1220
1221 d->y = d->vertices.sorted[d->currentVertex]->y;
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
1227 d->scanline.prepareLine();
1228 d->processIntersections();
1229 d->removeEdges();
1230 d->addEdges();
1231 d->addIntersections();
1232 d->emitEdges(this);
1233 d->scanline.lineDone();
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
1252 d->scanline.done();
1253 d->intersections.clear();
1254 return br;
1255}
1256
1257// tessellates the given convex polygon
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
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
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
bool connected
iterator insert(const Key &key, const T &value)
Definition qmap.h:688
T value(const Key &key, const T &defaultValue=T()) const
Definition qmap.h:357
size_type remove(const Key &key)
Definition qmap.h:300
iterator find(const Key &key)
Definition qmap.h:641
bool isEmpty() const
Definition qmap.h:269
iterator begin()
Definition qmap.h:598
iterator end()
Definition qmap.h:602
const_iterator constBegin() const
Definition qmap.h:600
\inmodule QtCore\reentrant
Definition qpoint.h:217
\inmodule QtCore\reentrant
Definition qrect.h:484
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)
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)
QPixmap p2
QPixmap p1
[0]
QSet< QString >::iterator it
short next
Definition keywords.cpp:445
Combined button and popup list for selecting options.
#define QT_WARNING_POP
#define QT_WARNING_PUSH
#define QT_WARNING_DISABLE_CLANG(text)
static int perp(bool vertical, const QSize &size)
bool qFuzzyIsNull(qfloat16 f) noexcept
Definition qfloat16.h:349
qfloat16 qSqrt(qfloat16 f)
Definition qfloat16.h:289
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:42
GLboolean GLboolean GLboolean b
GLsizei const GLfloat * v
[13]
GLint GLint GLint GLint GLint x
[0]
GLuint64 key
GLfloat GLfloat GLfloat w
[0]
GLboolean GLboolean GLboolean GLboolean a
[7]
GLuint GLfloat GLfloat GLfloat GLfloat y1
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint GLuint end
GLenum GLuint GLenum GLsizei length
GLdouble GLdouble GLdouble GLdouble top
GLdouble GLdouble right
GLint GLsizei width
GLint left
GLint GLint bottom
GLint GLfloat v0
GLint GLfloat GLfloat v1
GLuint start
GLenum GLuint GLintptr offset
GLfloat n
GLint y
GLfixed GLfixed GLint GLint GLfixed points
GLdouble s
[6]
Definition qopenglext.h:235
GLfixed GLfixed GLfixed y2
const GLfloat * tc
GLuint num
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QT_BEGIN_NAMESPACE constexpr void qSwap(T &value1, T &value2) noexcept(std::is_nothrow_swappable_v< T >)
Definition qswap.h:20
#define a2
#define a1
static bool compareVertex(const QTessellatorPrivate::Vertex *p1, const QTessellatorPrivate::Vertex *p2)
static void cancelEdges(QCoincidingEdge &e1, QCoincidingEdge &e2)
VertexFlags
@ LineBeforeEnds
@ LineAfterHorizontal
@ LineAfterStarts
@ LineBeforeStarts
@ LineAfterEnds
@ LineBeforeHorizontal
static const bool mark_clever
static const bool emit_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
unsigned long long quint64
Definition qtypes.h:61
long long qint64
Definition qtypes.h:60
double qreal
Definition qtypes.h:187
QStorageInfo storage
[1]
MyCustomStruct c2
QRect r1(100, 200, 11, 16)
[0]
QRect r2(QPoint(100, 200), QSize(11, 16))
QSharedPointer< T > other(t)
[5]
QString dir
[11]
QTessellatorPrivate::Vertex * end
bool operator<(const QCoincidingEdge &e2) const
QTessellatorPrivate::Vertex * start
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
int prevPos(const Vertex *v) const
const Vertex * next(const Vertex *v) const
const Vertex * prev(const Vertex *v) const