2015-12-10 05:06:13 +02:00
|
|
|
/****************************************************************************
|
|
|
|
**
|
|
|
|
** Copyright (C) 2015 The Qt Company Ltd.
|
2019-06-03 14:21:30 +00:00
|
|
|
** Copyright (C) 2016-2019 Ivailo Monev
|
2015-12-10 05:06:13 +02:00
|
|
|
**
|
2019-06-03 13:38:02 +00:00
|
|
|
** This file is part of the QtGui module of the Katie Toolkit.
|
2015-12-10 05:06:13 +02:00
|
|
|
**
|
|
|
|
** $QT_BEGIN_LICENSE:LGPL$
|
|
|
|
** Commercial License Usage
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
|
|
** and conditions see http://www.qt.io/terms-conditions. For further
|
|
|
|
** information use the contact form at http://www.qt.io/contact-us.
|
|
|
|
**
|
|
|
|
** GNU Lesser General Public License Usage
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
|
|
** General Public License version 2.1 or version 3 as published by the Free
|
|
|
|
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
|
|
|
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
|
|
|
** following information to ensure the GNU Lesser General Public License
|
|
|
|
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
|
|
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
|
|
**
|
|
|
|
** As a special exception, The Qt Company gives you certain additional
|
|
|
|
** rights. These rights are described in The Qt Company LGPL Exception
|
|
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
|
|
**
|
|
|
|
** GNU General Public License Usage
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
|
|
** General Public License version 3.0 as published by the Free Software
|
|
|
|
** Foundation and appearing in the file LICENSE.GPL included in the
|
|
|
|
** packaging of this file. Please review the following information to
|
|
|
|
** ensure the GNU General Public License version 3.0 requirements will be
|
|
|
|
** met: http://www.gnu.org/copyleft/gpl.html.
|
|
|
|
**
|
|
|
|
** $QT_END_LICENSE$
|
|
|
|
**
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include "qtessellator_p.h"
|
|
|
|
|
2019-07-15 20:10:49 +00:00
|
|
|
#include "qrect.h"
|
|
|
|
#include "qlist.h"
|
|
|
|
#include "qdebug.h"
|
|
|
|
#include "qmath.h"
|
2015-12-10 05:06:13 +02:00
|
|
|
|
|
|
|
#include <limits.h>
|
|
|
|
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
2016-09-06 18:00:28 +00:00
|
|
|
//#define DEBUG_TESSALLATOR
|
|
|
|
#ifdef DEBUG_TESSALLATOR
|
2015-12-10 05:06:13 +02:00
|
|
|
#define QDEBUG qDebug
|
|
|
|
#else
|
|
|
|
#define QDEBUG if (1){} else qDebug
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static const bool emit_clever = true;
|
|
|
|
static const bool mark_clever = false;
|
|
|
|
|
|
|
|
enum VertexFlags {
|
|
|
|
LineBeforeStarts = 0x1,
|
|
|
|
LineBeforeEnds = 0x2,
|
|
|
|
LineBeforeHorizontal = 0x4,
|
|
|
|
LineAfterStarts = 0x8,
|
|
|
|
LineAfterEnds = 0x10,
|
|
|
|
LineAfterHorizontal = 0x20
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class QTessellatorPrivate {
|
|
|
|
public:
|
|
|
|
struct Vertices;
|
|
|
|
|
|
|
|
QTessellatorPrivate() {}
|
|
|
|
|
|
|
|
QRectF collectAndSortVertices(const QPointF *points, int *maxActiveEdges);
|
|
|
|
void cancelCoincidingEdges();
|
|
|
|
|
|
|
|
void emitEdges(QTessellator *tessellator);
|
|
|
|
void processIntersections();
|
|
|
|
void removeEdges();
|
|
|
|
void addEdges();
|
|
|
|
void addIntersections();
|
|
|
|
|
|
|
|
struct Vertex : public QTessellator::Vertex
|
|
|
|
{
|
|
|
|
int flags;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Intersection
|
|
|
|
{
|
|
|
|
Q27Dot5 y;
|
|
|
|
int edge;
|
|
|
|
bool operator <(const Intersection &other) const {
|
|
|
|
if (y != other.y)
|
|
|
|
return y < other.y;
|
|
|
|
return edge < other.edge;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
struct IntersectionLink
|
|
|
|
{
|
|
|
|
int next;
|
|
|
|
int prev;
|
|
|
|
};
|
|
|
|
typedef QMap<Intersection, IntersectionLink> Intersections;
|
|
|
|
|
|
|
|
struct Edge {
|
|
|
|
Edge(const Vertices &v, int _edge);
|
|
|
|
int edge;
|
|
|
|
const Vertex *v0;
|
|
|
|
const Vertex *v1;
|
|
|
|
Q27Dot5 y_left;
|
|
|
|
Q27Dot5 y_right;
|
2019-06-22 13:20:54 +00:00
|
|
|
int winding : 8;
|
2015-12-10 05:06:13 +02:00
|
|
|
bool mark;
|
|
|
|
bool free;
|
|
|
|
bool intersect_left;
|
|
|
|
bool intersect_right;
|
|
|
|
bool isLeftOf(const Edge &other, Q27Dot5 y) const;
|
|
|
|
Q27Dot5 positionAt(Q27Dot5 y) const;
|
|
|
|
bool intersect(const Edge &other, Q27Dot5 *y, bool *det_positive) const;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
class EdgeSorter
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
EdgeSorter(int _y) : y(_y) {}
|
|
|
|
bool operator() (const Edge *e1, const Edge *e2);
|
|
|
|
int y;
|
|
|
|
};
|
|
|
|
|
|
|
|
class Scanline {
|
|
|
|
public:
|
|
|
|
Scanline();
|
|
|
|
~Scanline();
|
|
|
|
|
|
|
|
void init(int maxActiveEdges);
|
|
|
|
void done();
|
|
|
|
|
|
|
|
int findEdgePosition(Q27Dot5 x, Q27Dot5 y) const;
|
|
|
|
int findEdgePosition(const Edge &e) const;
|
|
|
|
int findEdge(int edge) const;
|
|
|
|
void clearMarks();
|
|
|
|
|
|
|
|
void swap(int p1, int p2) {
|
2019-06-22 13:20:54 +00:00
|
|
|
qSwap(edges[p1], edges[p2]);
|
2015-12-10 05:06:13 +02:00
|
|
|
}
|
|
|
|
void insert(int pos, const Edge &e);
|
|
|
|
void removeAt(int pos);
|
|
|
|
void markEdges(int pos1, int pos2);
|
|
|
|
|
|
|
|
void prepareLine();
|
|
|
|
void lineDone();
|
|
|
|
|
|
|
|
Edge **old;
|
|
|
|
int old_size;
|
|
|
|
|
|
|
|
Edge **edges;
|
|
|
|
int size;
|
|
|
|
|
|
|
|
private:
|
|
|
|
Edge *edge_table;
|
|
|
|
int first_unused;
|
|
|
|
int max_edges;
|
|
|
|
enum { default_alloc = 32 };
|
|
|
|
};
|
|
|
|
|
|
|
|
struct Vertices {
|
|
|
|
enum { default_alloc = 128 };
|
|
|
|
Vertices();
|
|
|
|
~Vertices();
|
|
|
|
void init(int maxVertices);
|
|
|
|
void done();
|
|
|
|
Vertex *storage;
|
|
|
|
Vertex **sorted;
|
|
|
|
|
|
|
|
Vertex *operator[] (int i) { return storage + i; }
|
|
|
|
const Vertex *operator[] (int i) const { return storage + i; }
|
|
|
|
int position(const Vertex *v) const {
|
|
|
|
return v - storage;
|
|
|
|
}
|
|
|
|
Vertex *next(Vertex *v) {
|
|
|
|
++v;
|
|
|
|
if (v == storage + nPoints)
|
|
|
|
v = storage;
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
const Vertex *next(const Vertex *v) const {
|
|
|
|
++v;
|
|
|
|
if (v == storage + nPoints)
|
|
|
|
v = storage;
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
int nextPos(const Vertex *v) const {
|
|
|
|
++v;
|
|
|
|
if (v == storage + nPoints)
|
|
|
|
return 0;
|
|
|
|
return v - storage;
|
|
|
|
}
|
|
|
|
Vertex *prev(Vertex *v) {
|
|
|
|
if (v == storage)
|
|
|
|
v = storage + nPoints;
|
|
|
|
--v;
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
const Vertex *prev(const Vertex *v) const {
|
|
|
|
if (v == storage)
|
|
|
|
v = storage + nPoints;
|
|
|
|
--v;
|
|
|
|
return v;
|
|
|
|
}
|
|
|
|
int prevPos(const Vertex *v) const {
|
|
|
|
if (v == storage)
|
|
|
|
v = storage + nPoints;
|
|
|
|
--v;
|
|
|
|
return v - storage;
|
|
|
|
}
|
|
|
|
int nPoints;
|
|
|
|
int allocated;
|
|
|
|
};
|
|
|
|
Vertices vertices;
|
|
|
|
Intersections intersections;
|
|
|
|
Scanline scanline;
|
|
|
|
bool winding;
|
|
|
|
Q27Dot5 y;
|
|
|
|
int currentVertex;
|
|
|
|
|
|
|
|
private:
|
|
|
|
void addIntersection(const Edge *e1, const Edge *e2);
|
|
|
|
bool edgeInChain(Intersection i, int edge);
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
QTessellatorPrivate::Edge::Edge(const QTessellatorPrivate::Vertices &vertices, int edge)
|
|
|
|
{
|
|
|
|
this->edge = edge;
|
|
|
|
intersect_left = intersect_right = true;
|
|
|
|
mark = false;
|
|
|
|
free = false;
|
|
|
|
|
|
|
|
v0 = vertices[edge];
|
|
|
|
v1 = vertices.next(v0);
|
|
|
|
|
|
|
|
Q_ASSERT(v0->y != v1->y);
|
|
|
|
|
|
|
|
if (v0->y > v1->y) {
|
|
|
|
qSwap(v0, v1);
|
|
|
|
winding = -1;
|
|
|
|
} else {
|
|
|
|
winding = 1;
|
|
|
|
}
|
|
|
|
y_left = y_right = v0->y;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is basically the algorithm from graphics gems. The algorithm
|
|
|
|
// is cubic in the coordinates at one place. Since we use 64bit
|
|
|
|
// integers, this implies, that the allowed range for our coordinates
|
|
|
|
// is limited to 21 bits. With 5 bits behind the decimal, this
|
|
|
|
// implies that differences in coordaintes can range from 2*SHORT_MIN
|
|
|
|
// to 2*SHORT_MAX, giving us efficiently a coordinate system from
|
|
|
|
// SHORT_MIN to SHORT_MAX.
|
|
|
|
//
|
|
|
|
|
|
|
|
// WARNING: It's absolutely critical that the intersect() and isLeftOf() methods use
|
|
|
|
// exactly the same algorithm to calulate yi. It's also important to be sure the algorithms
|
|
|
|
// are transitive (ie. the conditions below are true for all input data):
|
|
|
|
//
|
|
|
|
// a.intersect(b) == b.intersect(a)
|
|
|
|
// a.isLeftOf(b) != b.isLeftOf(a)
|
|
|
|
//
|
|
|
|
// This is tricky to get right, so be very careful when changing anything in here!
|
|
|
|
|
|
|
|
static inline bool sameSign(qint64 a, qint64 b) {
|
|
|
|
return (((qint64) ((quint64) a ^ (quint64) b)) >= 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QTessellatorPrivate::Edge::intersect(const Edge &other, Q27Dot5 *y, bool *det_positive) const
|
|
|
|
{
|
|
|
|
qint64 a1 = v1->y - v0->y;
|
|
|
|
qint64 b1 = v0->x - v1->x;
|
|
|
|
|
|
|
|
qint64 a2 = other.v1->y - other.v0->y;
|
|
|
|
qint64 b2 = other.v0->x - other.v1->x;
|
|
|
|
|
|
|
|
qint64 det = a1 * b2 - a2 * b1;
|
|
|
|
if (det == 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
qint64 c1 = qint64(v1->x) * v0->y - qint64(v0->x) * v1->y;
|
|
|
|
|
|
|
|
qint64 r3 = a1 * other.v0->x + b1 * other.v0->y + c1;
|
|
|
|
qint64 r4 = a1 * other.v1->x + b1 * other.v1->y + c1;
|
|
|
|
|
|
|
|
// Check signs of r3 and r4. If both point 3 and point 4 lie on
|
|
|
|
// same side of line 1, the line segments do not intersect.
|
|
|
|
QDEBUG() << " " << r3 << r4;
|
|
|
|
if (r3 != 0 && r4 != 0 && sameSign( r3, r4 ))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
qint64 c2 = qint64(other.v1->x) * other.v0->y - qint64(other.v0->x) * other.v1->y;
|
|
|
|
|
|
|
|
qint64 r1 = a2 * v0->x + b2 * v0->y + c2;
|
|
|
|
qint64 r2 = a2 * v1->x + b2 * v1->y + c2;
|
|
|
|
|
|
|
|
// Check signs of r1 and r2. If both point 1 and point 2 lie
|
|
|
|
// on same side of second line segment, the line segments do not intersect.
|
|
|
|
QDEBUG() << " " << r1 << r2;
|
|
|
|
if (r1 != 0 && r2 != 0 && sameSign( r1, r2 ))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// The det/2 is to get rounding instead of truncating. It
|
|
|
|
// is added or subtracted to the numerator, depending upon the
|
|
|
|
// sign of the numerator.
|
|
|
|
qint64 offset = det < 0 ? -det : det;
|
|
|
|
offset >>= 1;
|
|
|
|
|
|
|
|
qint64 num = a2 * c1 - a1 * c2;
|
|
|
|
*y = ( num < 0 ? num - offset : num + offset ) / det;
|
|
|
|
|
|
|
|
*det_positive = (det > 0);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QTessellatorPrivate::Edge::isLeftOf(const Edge &other, Q27Dot5 y) const
|
|
|
|
{
|
|
|
|
// QDEBUG() << "isLeftOf" << edge << other.edge << y;
|
|
|
|
qint64 a1 = v1->y - v0->y;
|
|
|
|
qint64 b1 = v0->x - v1->x;
|
|
|
|
qint64 a2 = other.v1->y - other.v0->y;
|
|
|
|
qint64 b2 = other.v0->x - other.v1->x;
|
|
|
|
|
|
|
|
qint64 c2 = qint64(other.v1->x) * other.v0->y - qint64(other.v0->x) * other.v1->y;
|
|
|
|
|
|
|
|
qint64 det = a1 * b2 - a2 * b1;
|
|
|
|
if (det == 0) {
|
|
|
|
// lines are parallel. Only need to check side of one point
|
|
|
|
// fixed ordering for coincident edges
|
|
|
|
qint64 r1 = a2 * v0->x + b2 * v0->y + c2;
|
|
|
|
// QDEBUG() << "det = 0" << r1;
|
|
|
|
if (r1 == 0)
|
|
|
|
return edge < other.edge;
|
|
|
|
return (r1 < 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// not parallel, need to find the y coordinate of the intersection point
|
|
|
|
qint64 c1 = qint64(v1->x) * v0->y - qint64(v0->x) * v1->y;
|
|
|
|
|
|
|
|
qint64 offset = det < 0 ? -det : det;
|
|
|
|
offset >>= 1;
|
|
|
|
|
|
|
|
qint64 num = a2 * c1 - a1 * c2;
|
|
|
|
qint64 yi = ( num < 0 ? num - offset : num + offset ) / det;
|
|
|
|
// QDEBUG() << " num=" << num << "offset=" << offset << "det=" << det;
|
|
|
|
|
|
|
|
return ((yi > y) ^ (det < 0));
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline bool compareVertex(const QTessellatorPrivate::Vertex *p1,
|
|
|
|
const QTessellatorPrivate::Vertex *p2)
|
|
|
|
{
|
|
|
|
if (p1->y == p2->y) {
|
|
|
|
if (p1->x == p2->x)
|
|
|
|
return p1 < p2;
|
|
|
|
return p1->x < p2->x;
|
|
|
|
}
|
|
|
|
return p1->y < p2->y;
|
|
|
|
}
|
|
|
|
|
|
|
|
Q27Dot5 QTessellatorPrivate::Edge::positionAt(Q27Dot5 y) const
|
|
|
|
{
|
|
|
|
if (y == v0->y)
|
|
|
|
return v0->x;
|
|
|
|
else if (y == v1->y)
|
|
|
|
return v1->x;
|
|
|
|
|
|
|
|
qint64 d = v1->x - v0->x;
|
|
|
|
return (v0->x + d*(y - v0->y)/(v1->y-v0->y));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QTessellatorPrivate::EdgeSorter::operator() (const Edge *e1, const Edge *e2)
|
|
|
|
{
|
|
|
|
return e1->isLeftOf(*e2, y);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QTessellatorPrivate::Scanline::Scanline()
|
|
|
|
{
|
|
|
|
edges = 0;
|
|
|
|
edge_table = 0;
|
|
|
|
old = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QTessellatorPrivate::Scanline::init(int maxActiveEdges)
|
|
|
|
{
|
|
|
|
maxActiveEdges *= 2;
|
|
|
|
if (!edges || maxActiveEdges > default_alloc) {
|
|
|
|
max_edges = maxActiveEdges;
|
|
|
|
int s = qMax(maxActiveEdges + 1, default_alloc + 1);
|
|
|
|
edges = q_check_ptr((Edge **)realloc(edges, s*sizeof(Edge *)));
|
|
|
|
edge_table = q_check_ptr((Edge *)realloc(edge_table, s*sizeof(Edge)));
|
|
|
|
old = q_check_ptr((Edge **)realloc(old, s*sizeof(Edge *)));
|
|
|
|
}
|
|
|
|
size = 0;
|
|
|
|
old_size = 0;
|
|
|
|
first_unused = 0;
|
|
|
|
for (int i = 0; i < maxActiveEdges; ++i)
|
|
|
|
edge_table[i].edge = i+1;
|
|
|
|
edge_table[maxActiveEdges].edge = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QTessellatorPrivate::Scanline::done()
|
|
|
|
{
|
|
|
|
if (max_edges > default_alloc) {
|
|
|
|
free(edges);
|
|
|
|
free(old);
|
|
|
|
free(edge_table);
|
|
|
|
edges = 0;
|
|
|
|
old = 0;
|
|
|
|
edge_table = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QTessellatorPrivate::Scanline::~Scanline()
|
|
|
|
{
|
|
|
|
free(edges);
|
|
|
|
free(old);
|
|
|
|
free(edge_table);
|
|
|
|
}
|
|
|
|
|
|
|
|
int QTessellatorPrivate::Scanline::findEdgePosition(Q27Dot5 x, Q27Dot5 y) const
|
|
|
|
{
|
|
|
|
int min = 0;
|
|
|
|
int max = size - 1;
|
|
|
|
while (min < max) {
|
|
|
|
int pos = min + ((max - min + 1) >> 1);
|
|
|
|
Q27Dot5 ax = edges[pos]->positionAt(y);
|
|
|
|
if (ax > x) {
|
|
|
|
max = pos - 1;
|
|
|
|
} else {
|
|
|
|
min = pos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return min;
|
|
|
|
}
|
|
|
|
|
|
|
|
int QTessellatorPrivate::Scanline::findEdgePosition(const Edge &e) const
|
|
|
|
{
|
|
|
|
// qDebug() << ">> findEdgePosition";
|
|
|
|
int min = 0;
|
|
|
|
int max = size;
|
|
|
|
while (min < max) {
|
|
|
|
int pos = min + ((max - min) >> 1);
|
|
|
|
// qDebug() << " " << min << max << pos << edges[pos]->isLeftOf(e, e.y0);
|
|
|
|
if (edges[pos]->isLeftOf(e, e.v0->y)) {
|
|
|
|
min = pos + 1;
|
|
|
|
} else {
|
|
|
|
max = pos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// qDebug() << "<< findEdgePosition got" << min;
|
|
|
|
return min;
|
|
|
|
}
|
|
|
|
|
|
|
|
int QTessellatorPrivate::Scanline::findEdge(int edge) const
|
|
|
|
{
|
|
|
|
for (int i = 0; i < size; ++i) {
|
|
|
|
int item_edge = edges[i]->edge;
|
|
|
|
if (item_edge == edge)
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
//Q_ASSERT(false);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QTessellatorPrivate::Scanline::clearMarks()
|
|
|
|
{
|
|
|
|
for (int i = 0; i < size; ++i) {
|
|
|
|
edges[i]->mark = false;
|
|
|
|
edges[i]->intersect_left = false;
|
|
|
|
edges[i]->intersect_right = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QTessellatorPrivate::Scanline::prepareLine()
|
|
|
|
{
|
|
|
|
Edge **end = edges + size;
|
|
|
|
Edge **e = edges;
|
|
|
|
Edge **o = old;
|
|
|
|
while (e < end) {
|
|
|
|
*o = *e;
|
|
|
|
++o;
|
|
|
|
++e;
|
|
|
|
}
|
|
|
|
old_size = size;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QTessellatorPrivate::Scanline::lineDone()
|
|
|
|
{
|
|
|
|
Edge **end = old + old_size;
|
|
|
|
Edge **e = old;
|
|
|
|
while (e < end) {
|
|
|
|
if ((*e)->free) {
|
|
|
|
(*e)->edge = first_unused;
|
|
|
|
first_unused = (*e - edge_table);
|
|
|
|
}
|
|
|
|
++e;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QTessellatorPrivate::Scanline::insert(int pos, const Edge &e)
|
|
|
|
{
|
|
|
|
Edge *edge = edge_table + first_unused;
|
|
|
|
first_unused = edge->edge;
|
|
|
|
Q_ASSERT(first_unused != -1);
|
|
|
|
*edge = e;
|
|
|
|
memmove(edges + pos + 1, edges + pos, (size - pos)*sizeof(Edge *));
|
|
|
|
edges[pos] = edge;
|
|
|
|
++size;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QTessellatorPrivate::Scanline::removeAt(int pos)
|
|
|
|
{
|
|
|
|
Edge *e = edges[pos];
|
|
|
|
e->free = true;
|
|
|
|
--size;
|
|
|
|
memmove(edges + pos, edges + pos + 1, (size - pos)*sizeof(Edge *));
|
|
|
|
}
|
|
|
|
|
|
|
|
void QTessellatorPrivate::Scanline::markEdges(int pos1, int pos2)
|
|
|
|
{
|
|
|
|
if (pos2 < pos1)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (int i = pos1; i <= pos2; ++i)
|
|
|
|
edges[i]->mark = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QTessellatorPrivate::Vertices::Vertices()
|
|
|
|
{
|
|
|
|
storage = 0;
|
|
|
|
sorted = 0;
|
|
|
|
allocated = 0;
|
|
|
|
nPoints = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
QTessellatorPrivate::Vertices::~Vertices()
|
|
|
|
{
|
|
|
|
if (storage) {
|
|
|
|
free(storage);
|
|
|
|
free(sorted);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QTessellatorPrivate::Vertices::init(int maxVertices)
|
|
|
|
{
|
|
|
|
if (!storage || maxVertices > allocated) {
|
|
|
|
int size = qMax((int)default_alloc, maxVertices);
|
|
|
|
storage = q_check_ptr((Vertex *)realloc(storage, size*sizeof(Vertex)));
|
|
|
|
sorted = q_check_ptr((Vertex **)realloc(sorted, size*sizeof(Vertex *)));
|
|
|
|
allocated = maxVertices;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QTessellatorPrivate::Vertices::done()
|
|
|
|
{
|
|
|
|
if (allocated > default_alloc) {
|
|
|
|
free(storage);
|
|
|
|
free(sorted);
|
|
|
|
storage = 0;
|
|
|
|
sorted = 0;
|
|
|
|
allocated = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static inline void fillTrapezoid(Q27Dot5 y1, Q27Dot5 y2, int left, int right,
|
|
|
|
const QTessellatorPrivate::Vertices &vertices,
|
|
|
|
QTessellator::Trapezoid *trap)
|
|
|
|
{
|
|
|
|
trap->top = y1;
|
|
|
|
trap->bottom = y2;
|
|
|
|
const QTessellatorPrivate::Vertex *v = vertices[left];
|
|
|
|
trap->topLeft = v;
|
|
|
|
trap->bottomLeft = vertices.next(v);
|
|
|
|
if (trap->topLeft->y > trap->bottomLeft->y)
|
|
|
|
qSwap(trap->topLeft,trap->bottomLeft);
|
|
|
|
v = vertices[right];
|
|
|
|
trap->topRight = v;
|
|
|
|
trap->bottomRight = vertices.next(v);
|
|
|
|
if (trap->topRight->y > trap->bottomRight->y)
|
|
|
|
qSwap(trap->topRight, trap->bottomRight);
|
|
|
|
}
|
|
|
|
|
|
|
|
QRectF QTessellatorPrivate::collectAndSortVertices(const QPointF *points, int *maxActiveEdges)
|
|
|
|
{
|
|
|
|
*maxActiveEdges = 0;
|
|
|
|
Vertex *v = vertices.storage;
|
|
|
|
Vertex **vv = vertices.sorted;
|
|
|
|
|
|
|
|
qreal xmin(points[0].x());
|
|
|
|
qreal xmax(points[0].x());
|
|
|
|
qreal ymin(points[0].y());
|
|
|
|
qreal ymax(points[0].y());
|
|
|
|
|
|
|
|
// collect vertex data
|
|
|
|
Q27Dot5 y_prev = FloatToQ27Dot5(points[vertices.nPoints-1].y());
|
|
|
|
Q27Dot5 x_next = FloatToQ27Dot5(points[0].x());
|
|
|
|
Q27Dot5 y_next = FloatToQ27Dot5(points[0].y());
|
|
|
|
int j = 0;
|
|
|
|
int i = 0;
|
|
|
|
while (i < vertices.nPoints) {
|
|
|
|
Q27Dot5 y_curr = y_next;
|
|
|
|
|
|
|
|
*vv = v;
|
|
|
|
|
|
|
|
v->x = x_next;
|
|
|
|
v->y = y_next;
|
|
|
|
v->flags = 0;
|
|
|
|
|
|
|
|
next_point:
|
|
|
|
|
|
|
|
xmin = qMin(xmin, points[i+1].x());
|
|
|
|
xmax = qMax(xmax, points[i+1].x());
|
|
|
|
ymin = qMin(ymin, points[i+1].y());
|
|
|
|
ymax = qMax(ymax, points[i+1].y());
|
|
|
|
|
|
|
|
y_next = FloatToQ27Dot5(points[i+1].y());
|
|
|
|
x_next = FloatToQ27Dot5(points[i+1].x());
|
|
|
|
|
|
|
|
// skip vertices on top of each other
|
|
|
|
if (v->x == x_next && v->y == y_next) {
|
|
|
|
++i;
|
|
|
|
if (i < vertices.nPoints)
|
|
|
|
goto next_point;
|
|
|
|
Vertex *v0 = vertices.storage;
|
|
|
|
v0->flags &= ~(LineBeforeStarts|LineBeforeEnds|LineBeforeHorizontal);
|
|
|
|
if (y_prev < y_curr)
|
|
|
|
v0->flags |= LineBeforeEnds;
|
|
|
|
else if (y_prev > y_curr)
|
|
|
|
v0->flags |= LineBeforeStarts;
|
|
|
|
else
|
|
|
|
v0->flags |= LineBeforeHorizontal;
|
|
|
|
if ((v0->flags & (LineBeforeStarts|LineAfterStarts))
|
|
|
|
&& !(v0->flags & (LineAfterEnds|LineBeforeEnds)))
|
|
|
|
*maxActiveEdges += 2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (y_prev < y_curr)
|
|
|
|
v->flags |= LineBeforeEnds;
|
|
|
|
else if (y_prev > y_curr)
|
|
|
|
v->flags |= LineBeforeStarts;
|
|
|
|
else
|
|
|
|
v->flags |= LineBeforeHorizontal;
|
|
|
|
|
|
|
|
|
|
|
|
if (y_curr < y_next)
|
|
|
|
v->flags |= LineAfterStarts;
|
|
|
|
else if (y_curr > y_next)
|
|
|
|
v->flags |= LineAfterEnds;
|
|
|
|
else
|
|
|
|
v->flags |= LineAfterHorizontal;
|
|
|
|
// ### could probably get better limit by looping over sorted list and counting down on ending edges
|
|
|
|
if ((v->flags & (LineBeforeStarts|LineAfterStarts))
|
|
|
|
&& !(v->flags & (LineAfterEnds|LineBeforeEnds)))
|
|
|
|
*maxActiveEdges += 2;
|
|
|
|
y_prev = y_curr;
|
|
|
|
++v;
|
|
|
|
++vv;
|
|
|
|
++j;
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
vertices.nPoints = j;
|
|
|
|
|
|
|
|
QDEBUG() << "maxActiveEdges=" << *maxActiveEdges;
|
|
|
|
vv = vertices.sorted;
|
|
|
|
qSort(vv, vv + vertices.nPoints, compareVertex);
|
|
|
|
|
|
|
|
return QRectF(xmin, ymin, xmax-xmin, ymax-ymin);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct QCoincidingEdge {
|
|
|
|
QTessellatorPrivate::Vertex *start;
|
|
|
|
QTessellatorPrivate::Vertex *end;
|
|
|
|
bool used;
|
|
|
|
bool before;
|
|
|
|
|
|
|
|
inline bool operator<(const QCoincidingEdge &e2) const
|
|
|
|
{
|
|
|
|
return end->y == e2.end->y ? end->x < e2.end->x : end->y < e2.end->y;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
static void cancelEdges(QCoincidingEdge &e1, QCoincidingEdge &e2)
|
|
|
|
{
|
|
|
|
if (e1.before) {
|
|
|
|
e1.start->flags &= ~(LineBeforeStarts|LineBeforeHorizontal);
|
|
|
|
e1.end->flags &= ~(LineAfterEnds|LineAfterHorizontal);
|
|
|
|
} else {
|
|
|
|
e1.start->flags &= ~(LineAfterStarts|LineAfterHorizontal);
|
|
|
|
e1.end->flags &= ~(LineBeforeEnds|LineBeforeHorizontal);
|
|
|
|
}
|
|
|
|
if (e2.before) {
|
|
|
|
e2.start->flags &= ~(LineBeforeStarts|LineBeforeHorizontal);
|
|
|
|
e2.end->flags &= ~(LineAfterEnds|LineAfterHorizontal);
|
|
|
|
} else {
|
|
|
|
e2.start->flags &= ~(LineAfterStarts|LineAfterHorizontal);
|
|
|
|
e2.end->flags &= ~(LineBeforeEnds|LineBeforeHorizontal);
|
|
|
|
}
|
|
|
|
e1.used = e2.used = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QTessellatorPrivate::cancelCoincidingEdges()
|
|
|
|
{
|
|
|
|
Vertex **vv = vertices.sorted;
|
|
|
|
|
|
|
|
QCoincidingEdge *tl = 0;
|
|
|
|
int tlSize = 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < vertices.nPoints - 1; ++i) {
|
|
|
|
Vertex *v = vv[i];
|
|
|
|
int testListSize = 0;
|
|
|
|
while (i < vertices.nPoints - 1) {
|
|
|
|
Vertex *n = vv[i];
|
|
|
|
if (v->x != n->x || v->y != n->y)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (testListSize > tlSize - 2) {
|
|
|
|
tlSize = qMax(tlSize*2, 16);
|
|
|
|
tl = q_check_ptr((QCoincidingEdge *)realloc(tl, tlSize*sizeof(QCoincidingEdge)));
|
|
|
|
}
|
|
|
|
if (n->flags & (LineBeforeStarts|LineBeforeHorizontal)) {
|
|
|
|
tl[testListSize].start = n;
|
|
|
|
tl[testListSize].end = vertices.prev(n);
|
|
|
|
tl[testListSize].used = false;
|
|
|
|
tl[testListSize].before = true;
|
|
|
|
++testListSize;
|
|
|
|
}
|
|
|
|
if (n->flags & (LineAfterStarts|LineAfterHorizontal)) {
|
|
|
|
tl[testListSize].start = n;
|
|
|
|
tl[testListSize].end = vertices.next(n);
|
|
|
|
tl[testListSize].used = false;
|
|
|
|
tl[testListSize].before = false;
|
|
|
|
++testListSize;
|
|
|
|
}
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
if (!testListSize)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
qSort(tl, tl + testListSize);
|
|
|
|
|
|
|
|
for (int j = 0; j < testListSize; ++j) {
|
|
|
|
if (tl[j].used)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for (int k = j + 1; k < testListSize; ++k) {
|
|
|
|
if (tl[j].end->x != tl[k].end->x
|
|
|
|
|| tl[j].end->y != tl[k].end->y
|
|
|
|
|| tl[k].used)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (!winding || tl[j].before != tl[k].before) {
|
|
|
|
cancelEdges(tl[j], tl[k]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
++k;
|
|
|
|
}
|
|
|
|
++j;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free(tl);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void QTessellatorPrivate::emitEdges(QTessellator *tessellator)
|
|
|
|
{
|
|
|
|
//QDEBUG() << "TRAPS:";
|
|
|
|
if (!scanline.old_size)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// emit edges
|
|
|
|
if (winding) {
|
|
|
|
// winding fill rule
|
|
|
|
int w = 0;
|
|
|
|
|
|
|
|
scanline.old[0]->y_left = y;
|
|
|
|
|
|
|
|
for (int i = 0; i < scanline.old_size - 1; ++i) {
|
|
|
|
Edge *left = scanline.old[i];
|
|
|
|
Edge *right = scanline.old[i+1];
|
|
|
|
w += left->winding;
|
|
|
|
// qDebug() << "i=" << i << "edge->winding=" << left->winding << "winding=" << winding;
|
|
|
|
if (w == 0) {
|
|
|
|
left->y_right = y;
|
|
|
|
right->y_left = y;
|
|
|
|
} else if (!emit_clever || left->mark || right->mark) {
|
|
|
|
Q27Dot5 top = qMax(left->y_right, right->y_left);
|
|
|
|
if (top != y) {
|
|
|
|
QTessellator::Trapezoid trap;
|
|
|
|
fillTrapezoid(top, y, left->edge, right->edge, vertices, &trap);
|
|
|
|
tessellator->addTrap(trap);
|
|
|
|
// QDEBUG() << " top=" << Q27Dot5ToDouble(top) << "left=" << left->edge << "right=" << right->edge;
|
|
|
|
}
|
|
|
|
right->y_left = y;
|
|
|
|
left->y_right = y;
|
|
|
|
}
|
|
|
|
left->mark = false;
|
|
|
|
}
|
|
|
|
if (scanline.old[scanline.old_size - 1]->mark) {
|
|
|
|
scanline.old[scanline.old_size - 1]->y_right = y;
|
|
|
|
scanline.old[scanline.old_size - 1]->mark = false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// odd-even fill rule
|
|
|
|
for (int i = 0; i < scanline.old_size; i += 2) {
|
|
|
|
Edge *left = scanline.old[i];
|
|
|
|
Edge *right = scanline.old[i+1];
|
|
|
|
if (!emit_clever || left->mark || right->mark) {
|
|
|
|
Q27Dot5 top = qMax(left->y_right, right->y_left);
|
|
|
|
if (top != y) {
|
|
|
|
QTessellator::Trapezoid trap;
|
|
|
|
fillTrapezoid(top, y, left->edge, right->edge, vertices, &trap);
|
|
|
|
tessellator->addTrap(trap);
|
|
|
|
}
|
|
|
|
// QDEBUG() << " top=" << Q27Dot5ToDouble(top) << "left=" << left->edge << "right=" << right->edge;
|
|
|
|
left->y_left = y;
|
|
|
|
left->y_right = y;
|
|
|
|
right->y_left = y;
|
|
|
|
right->y_right = y;
|
|
|
|
left->mark = right->mark = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void QTessellatorPrivate::processIntersections()
|
|
|
|
{
|
|
|
|
QDEBUG() << "PROCESS INTERSECTIONS";
|
|
|
|
// process intersections
|
|
|
|
while (!intersections.isEmpty()) {
|
|
|
|
Intersections::iterator it = intersections.begin();
|
|
|
|
if (it.key().y != y)
|
|
|
|
break;
|
|
|
|
|
|
|
|
// swap edges
|
|
|
|
QDEBUG() << " swapping intersecting edges ";
|
|
|
|
int min = scanline.size;
|
|
|
|
int max = 0;
|
|
|
|
Q27Dot5 xmin = INT_MAX;
|
|
|
|
Q27Dot5 xmax = INT_MIN;
|
|
|
|
int num = 0;
|
|
|
|
while (1) {
|
|
|
|
const Intersection &i = it.key();
|
|
|
|
int next = it->next;
|
|
|
|
|
|
|
|
int edgePos = scanline.findEdge(i.edge);
|
|
|
|
if (edgePos >= 0) {
|
|
|
|
++num;
|
|
|
|
min = qMin(edgePos, min);
|
|
|
|
max = qMax(edgePos, max);
|
|
|
|
Edge *edge = scanline.edges[edgePos];
|
|
|
|
xmin = qMin(xmin, edge->positionAt(y));
|
|
|
|
xmax = qMax(xmax, edge->positionAt(y));
|
|
|
|
}
|
|
|
|
Intersection key;
|
|
|
|
key.y = y;
|
|
|
|
key.edge = next;
|
|
|
|
it = intersections.find(key);
|
|
|
|
intersections.remove(i);
|
|
|
|
if (it == intersections.end())
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (num < 2)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
Q_ASSERT(min != max);
|
|
|
|
QDEBUG() << "sorting between" << min << "and" << max << "xpos=" << xmin << xmax;
|
|
|
|
while (min > 0 && scanline.edges[min - 1]->positionAt(y) >= xmin) {
|
|
|
|
QDEBUG() << " adding edge on left";
|
|
|
|
--min;
|
|
|
|
}
|
|
|
|
while (max + 1 < scanline.size && scanline.edges[max + 1]->positionAt(y) <= xmax) {
|
|
|
|
QDEBUG() << " adding edge on right";
|
|
|
|
++max;
|
|
|
|
}
|
|
|
|
|
|
|
|
qSort(scanline.edges + min, scanline.edges + max + 1, EdgeSorter(y));
|
|
|
|
for (int i = min; i <= max; ++i) {
|
2019-06-22 13:20:54 +00:00
|
|
|
QDEBUG() << " " << scanline.edges[i]->edge << "at pos" << i;
|
2015-12-10 05:06:13 +02:00
|
|
|
Edge *edge = scanline.edges[i];
|
|
|
|
edge->intersect_left = true;
|
|
|
|
edge->intersect_right = true;
|
|
|
|
edge->mark = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QTessellatorPrivate::removeEdges()
|
|
|
|
{
|
|
|
|
int cv = currentVertex;
|
|
|
|
while (cv < vertices.nPoints) {
|
|
|
|
const Vertex *v = vertices.sorted[cv];
|
|
|
|
if (v->y > y)
|
|
|
|
break;
|
|
|
|
if (v->flags & LineBeforeEnds) {
|
|
|
|
QDEBUG() << " removing edge" << vertices.prevPos(v);
|
|
|
|
int pos = scanline.findEdge(vertices.prevPos(v));
|
|
|
|
if (pos == -1)
|
|
|
|
continue;
|
|
|
|
scanline.edges[pos]->mark = true;
|
|
|
|
if (pos > 0)
|
|
|
|
scanline.edges[pos - 1]->intersect_right = true;
|
|
|
|
if (pos < scanline.size - 1)
|
|
|
|
scanline.edges[pos + 1]->intersect_left = true;
|
|
|
|
scanline.removeAt(pos);
|
|
|
|
}
|
|
|
|
if (v->flags & LineAfterEnds) {
|
|
|
|
QDEBUG() << " removing edge" << vertices.position(v);
|
|
|
|
int pos = scanline.findEdge(vertices.position(v));
|
|
|
|
if (pos == -1)
|
|
|
|
continue;
|
|
|
|
scanline.edges[pos]->mark = true;
|
|
|
|
if (pos > 0)
|
|
|
|
scanline.edges[pos - 1]->intersect_right = true;
|
|
|
|
if (pos < scanline.size - 1)
|
|
|
|
scanline.edges[pos + 1]->intersect_left = true;
|
|
|
|
scanline.removeAt(pos);
|
|
|
|
}
|
|
|
|
++cv;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QTessellatorPrivate::addEdges()
|
|
|
|
{
|
|
|
|
while (currentVertex < vertices.nPoints) {
|
|
|
|
const Vertex *v = vertices.sorted[currentVertex];
|
|
|
|
if (v->y > y)
|
|
|
|
break;
|
|
|
|
if (v->flags & LineBeforeStarts) {
|
|
|
|
// add new edge
|
|
|
|
int start = vertices.prevPos(v);
|
|
|
|
Edge e(vertices, start);
|
|
|
|
int pos = scanline.findEdgePosition(e);
|
|
|
|
QDEBUG() << " adding edge" << start << "at position" << pos;
|
|
|
|
scanline.insert(pos, e);
|
|
|
|
if (!mark_clever || !(v->flags & LineAfterEnds)) {
|
|
|
|
if (pos > 0)
|
|
|
|
scanline.edges[pos - 1]->mark = true;
|
|
|
|
if (pos < scanline.size - 1)
|
|
|
|
scanline.edges[pos + 1]->mark = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (v->flags & LineAfterStarts) {
|
|
|
|
Edge e(vertices, vertices.position(v));
|
|
|
|
int pos = scanline.findEdgePosition(e);
|
|
|
|
QDEBUG() << " adding edge" << vertices.position(v) << "at position" << pos;
|
|
|
|
scanline.insert(pos, e);
|
|
|
|
if (!mark_clever || !(v->flags & LineBeforeEnds)) {
|
|
|
|
if (pos > 0)
|
|
|
|
scanline.edges[pos - 1]->mark = true;
|
|
|
|
if (pos < scanline.size - 1)
|
|
|
|
scanline.edges[pos + 1]->mark = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (v->flags & LineAfterHorizontal) {
|
|
|
|
int pos1 = scanline.findEdgePosition(v->x, v->y);
|
|
|
|
const Vertex *next = vertices.next(v);
|
|
|
|
Q_ASSERT(v->y == next->y);
|
|
|
|
int pos2 = scanline.findEdgePosition(next->x, next->y);
|
|
|
|
if (pos2 < pos1)
|
|
|
|
qSwap(pos1, pos2);
|
|
|
|
if (pos1 > 0)
|
|
|
|
--pos1;
|
|
|
|
if (pos2 == scanline.size)
|
|
|
|
--pos2;
|
|
|
|
//QDEBUG() << "marking horizontal edge from " << pos1 << "to" << pos2;
|
|
|
|
scanline.markEdges(pos1, pos2);
|
|
|
|
}
|
|
|
|
++currentVertex;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-06 18:00:28 +00:00
|
|
|
#ifdef DEBUG_TESSALLATOR
|
2015-12-10 05:06:13 +02:00
|
|
|
static void checkLinkChain(const QTessellatorPrivate::Intersections &intersections,
|
|
|
|
QTessellatorPrivate::Intersection i)
|
|
|
|
{
|
|
|
|
// qDebug() << " Link chain: ";
|
|
|
|
int end = i.edge;
|
|
|
|
while (1) {
|
|
|
|
QTessellatorPrivate::IntersectionLink l = intersections.value(i);
|
|
|
|
// qDebug() << " " << i.edge << "next=" << l.next << "prev=" << l.prev;
|
|
|
|
if (l.next == end)
|
|
|
|
break;
|
|
|
|
Q_ASSERT(l.next != -1);
|
|
|
|
Q_ASSERT(l.prev != -1);
|
|
|
|
|
|
|
|
QTessellatorPrivate::Intersection i2 = i;
|
|
|
|
i2.edge = l.next;
|
|
|
|
QTessellatorPrivate::IntersectionLink l2 = intersections.value(i2);
|
|
|
|
|
|
|
|
Q_ASSERT(l2.next != -1);
|
|
|
|
Q_ASSERT(l2.prev != -1);
|
|
|
|
Q_ASSERT(l.next == i2.edge);
|
|
|
|
Q_ASSERT(l2.prev == i.edge);
|
|
|
|
i = i2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
bool QTessellatorPrivate::edgeInChain(Intersection i, int edge)
|
|
|
|
{
|
|
|
|
int end = i.edge;
|
|
|
|
while (1) {
|
|
|
|
if (i.edge == edge)
|
|
|
|
return true;
|
|
|
|
IntersectionLink l = intersections.value(i);
|
|
|
|
if (l.next == end)
|
|
|
|
break;
|
|
|
|
Q_ASSERT(l.next != -1);
|
|
|
|
Q_ASSERT(l.prev != -1);
|
|
|
|
|
|
|
|
Intersection i2 = i;
|
|
|
|
i2.edge = l.next;
|
|
|
|
|
|
|
|
#ifndef QT_NO_DEBUG
|
|
|
|
IntersectionLink l2 = intersections.value(i2);
|
|
|
|
Q_ASSERT(l2.next != -1);
|
|
|
|
Q_ASSERT(l2.prev != -1);
|
|
|
|
Q_ASSERT(l.next == i2.edge);
|
|
|
|
Q_ASSERT(l2.prev == i.edge);
|
|
|
|
#endif
|
|
|
|
i = i2;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void QTessellatorPrivate::addIntersection(const Edge *e1, const Edge *e2)
|
|
|
|
{
|
|
|
|
const IntersectionLink emptyLink = {-1, -1};
|
|
|
|
|
|
|
|
int next = vertices.nextPos(vertices[e1->edge]);
|
|
|
|
if (e2->edge == next)
|
|
|
|
return;
|
|
|
|
int prev = vertices.prevPos(vertices[e1->edge]);
|
|
|
|
if (e2->edge == prev)
|
|
|
|
return;
|
|
|
|
|
|
|
|
Q27Dot5 yi;
|
|
|
|
bool det_positive;
|
|
|
|
bool isect = e1->intersect(*e2, &yi, &det_positive);
|
|
|
|
QDEBUG("checking edges %d and %d", e1->edge, e2->edge);
|
|
|
|
if (!isect) {
|
|
|
|
QDEBUG() << " no intersection";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// don't emit an intersection if it's at the start of a line segment or above us
|
|
|
|
if (yi <= y) {
|
|
|
|
if (!det_positive)
|
|
|
|
return;
|
|
|
|
QDEBUG() << " ----->>>>>> WRONG ORDER!";
|
|
|
|
yi = y;
|
|
|
|
}
|
|
|
|
QDEBUG() << " between edges " << e1->edge << "and" << e2->edge << "at point ("
|
|
|
|
<< Q27Dot5ToDouble(yi) << ')';
|
|
|
|
|
|
|
|
Intersection i1;
|
|
|
|
i1.y = yi;
|
|
|
|
i1.edge = e1->edge;
|
|
|
|
IntersectionLink link1 = intersections.value(i1, emptyLink);
|
|
|
|
Intersection i2;
|
|
|
|
i2.y = yi;
|
|
|
|
i2.edge = e2->edge;
|
|
|
|
IntersectionLink link2 = intersections.value(i2, emptyLink);
|
|
|
|
|
|
|
|
// new pair of edges
|
|
|
|
if (link1.next == -1 && link2.next == -1) {
|
|
|
|
link1.next = link1.prev = i2.edge;
|
|
|
|
link2.next = link2.prev = i1.edge;
|
|
|
|
} else if (link1.next == i2.edge || link1.prev == i2.edge
|
|
|
|
|| link2.next == i1.edge || link2.prev == i1.edge) {
|
2016-09-06 18:00:28 +00:00
|
|
|
#ifdef DEBUG_TESSALLATOR
|
2015-12-10 05:06:13 +02:00
|
|
|
checkLinkChain(intersections, i1);
|
|
|
|
checkLinkChain(intersections, i2);
|
|
|
|
Q_ASSERT(edgeInChain(i1, i2.edge));
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
} else if (link1.next == -1 || link2.next == -1) {
|
|
|
|
if (link2.next == -1) {
|
|
|
|
qSwap(i1, i2);
|
|
|
|
qSwap(link1, link2);
|
|
|
|
}
|
|
|
|
Q_ASSERT(link1.next == -1);
|
2016-09-06 18:00:28 +00:00
|
|
|
#ifdef DEBUG_TESSALLATOR
|
2015-12-10 05:06:13 +02:00
|
|
|
checkLinkChain(intersections, i2);
|
|
|
|
#endif
|
|
|
|
// only i2 in list
|
|
|
|
link1.next = i2.edge;
|
|
|
|
link1.prev = link2.prev;
|
|
|
|
link2.prev = i1.edge;
|
|
|
|
Intersection other;
|
|
|
|
other.y = yi;
|
|
|
|
other.edge = link1.prev;
|
|
|
|
IntersectionLink link = intersections.value(other, emptyLink);
|
|
|
|
Q_ASSERT(link.next == i2.edge);
|
|
|
|
Q_ASSERT(link.prev != -1);
|
|
|
|
link.next = i1.edge;
|
|
|
|
intersections.insert(other, link);
|
|
|
|
} else {
|
|
|
|
bool connected = edgeInChain(i1, i2.edge);
|
|
|
|
if (connected)
|
|
|
|
return;
|
2016-09-06 18:00:28 +00:00
|
|
|
#ifdef DEBUG_TESSALLATOR
|
2015-12-10 05:06:13 +02:00
|
|
|
checkLinkChain(intersections, i1);
|
|
|
|
checkLinkChain(intersections, i2);
|
|
|
|
#endif
|
|
|
|
// both already in some list. Have to make sure they are connected
|
|
|
|
// this can be done by cutting open the ring(s) after the two eges and
|
|
|
|
// connecting them again
|
|
|
|
Intersection other1;
|
|
|
|
other1.y = yi;
|
|
|
|
other1.edge = link1.next;
|
|
|
|
IntersectionLink linko1 = intersections.value(other1, emptyLink);
|
|
|
|
Intersection other2;
|
|
|
|
other2.y = yi;
|
|
|
|
other2.edge = link2.next;
|
|
|
|
IntersectionLink linko2 = intersections.value(other2, emptyLink);
|
|
|
|
|
|
|
|
linko1.prev = i2.edge;
|
|
|
|
link2.next = other1.edge;
|
|
|
|
|
|
|
|
linko2.prev = i1.edge;
|
|
|
|
link1.next = other2.edge;
|
|
|
|
intersections.insert(other1, linko1);
|
|
|
|
intersections.insert(other2, linko2);
|
|
|
|
}
|
|
|
|
intersections.insert(i1, link1);
|
|
|
|
intersections.insert(i2, link2);
|
2016-09-06 18:00:28 +00:00
|
|
|
#ifdef DEBUG_TESSALLATOR
|
2015-12-10 05:06:13 +02:00
|
|
|
checkLinkChain(intersections, i1);
|
|
|
|
checkLinkChain(intersections, i2);
|
|
|
|
Q_ASSERT(edgeInChain(i1, i2.edge));
|
|
|
|
#endif
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void QTessellatorPrivate::addIntersections()
|
|
|
|
{
|
|
|
|
if (scanline.size) {
|
|
|
|
QDEBUG() << "INTERSECTIONS";
|
|
|
|
// check marked edges for intersections
|
2016-09-06 18:00:28 +00:00
|
|
|
#ifdef DEBUG_TESSALLATOR
|
2015-12-10 05:06:13 +02:00
|
|
|
for (int i = 0; i < scanline.size; ++i) {
|
|
|
|
Edge *e = scanline.edges[i];
|
|
|
|
QDEBUG() << " " << i << e->edge << "isect=(" << e->intersect_left << e->intersect_right
|
|
|
|
<< ')';
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
for (int i = 0; i < scanline.size - 1; ++i) {
|
|
|
|
Edge *e1 = scanline.edges[i];
|
|
|
|
Edge *e2 = scanline.edges[i + 1];
|
|
|
|
// check for intersection
|
|
|
|
if (e1->intersect_right || e2->intersect_left)
|
|
|
|
addIntersection(e1, e2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#if 0
|
|
|
|
if (intersections.constBegin().key().y == y) {
|
|
|
|
QDEBUG() << "----------------> intersection on same line";
|
|
|
|
scanline.clearMarks();
|
|
|
|
scanline.processIntersections(y, &intersections);
|
|
|
|
goto redo;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QTessellator::QTessellator()
|
2019-06-22 13:20:54 +00:00
|
|
|
: d(new QTessellatorPrivate())
|
2015-12-10 05:06:13 +02:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
QTessellator::~QTessellator()
|
|
|
|
{
|
|
|
|
delete d;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QTessellator::setWinding(bool w)
|
|
|
|
{
|
|
|
|
d->winding = w;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QRectF QTessellator::tessellate(const QPointF *points, int nPoints)
|
|
|
|
{
|
|
|
|
Q_ASSERT(points[0] == points[nPoints-1]);
|
|
|
|
--nPoints;
|
|
|
|
|
2016-09-06 18:00:28 +00:00
|
|
|
#ifdef DEBUG_TESSALLATOR
|
2015-12-10 05:06:13 +02:00
|
|
|
QDEBUG()<< "POINTS:";
|
|
|
|
for (int i = 0; i < nPoints; ++i) {
|
|
|
|
QDEBUG() << points[i];
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// collect edges and calculate bounds
|
|
|
|
d->vertices.nPoints = nPoints;
|
|
|
|
d->vertices.init(nPoints);
|
|
|
|
|
|
|
|
int maxActiveEdges = 0;
|
|
|
|
QRectF br = d->collectAndSortVertices(points, &maxActiveEdges);
|
|
|
|
d->cancelCoincidingEdges();
|
|
|
|
|
2016-09-06 18:00:28 +00:00
|
|
|
#ifdef DEBUG_TESSALLATOR
|
2015-12-10 05:06:13 +02:00
|
|
|
QDEBUG() << "nPoints = " << nPoints << "using " << d->vertices.nPoints;
|
|
|
|
QDEBUG()<< "VERTICES:";
|
|
|
|
for (int i = 0; i < d->vertices.nPoints; ++i) {
|
|
|
|
QDEBUG() << " " << i << ": "
|
|
|
|
<< "point=" << d->vertices.position(d->vertices.sorted[i])
|
|
|
|
<< "flags=" << d->vertices.sorted[i]->flags
|
|
|
|
<< "pos=(" << Q27Dot5ToDouble(d->vertices.sorted[i]->x) << '/'
|
|
|
|
<< Q27Dot5ToDouble(d->vertices.sorted[i]->y) << ')';
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
d->scanline.init(maxActiveEdges);
|
|
|
|
d->y = INT_MIN/256;
|
|
|
|
d->currentVertex = 0;
|
|
|
|
|
|
|
|
while (d->currentVertex < d->vertices.nPoints) {
|
|
|
|
d->scanline.clearMarks();
|
|
|
|
|
|
|
|
d->y = d->vertices.sorted[d->currentVertex]->y;
|
|
|
|
if (!d->intersections.isEmpty())
|
|
|
|
d->y = qMin(d->y, d->intersections.constBegin().key().y);
|
|
|
|
|
|
|
|
QDEBUG()<< "===== SCANLINE: y =" << Q27Dot5ToDouble(d->y) << " =====";
|
|
|
|
|
|
|
|
d->scanline.prepareLine();
|
|
|
|
d->processIntersections();
|
|
|
|
d->removeEdges();
|
|
|
|
d->addEdges();
|
|
|
|
d->addIntersections();
|
|
|
|
d->emitEdges(this);
|
|
|
|
d->scanline.lineDone();
|
|
|
|
|
2016-09-06 18:00:28 +00:00
|
|
|
#ifdef DEBUG_TESSALLATOR
|
2015-12-10 05:06:13 +02:00
|
|
|
QDEBUG()<< "===== edges:";
|
|
|
|
for (int i = 0; i < d->scanline.size; ++i) {
|
|
|
|
QDEBUG() << " " << d->scanline.edges[i]->edge
|
|
|
|
<< "p0= (" << Q27Dot5ToDouble(d->scanline.edges[i]->v0->x)
|
|
|
|
<< '/' << Q27Dot5ToDouble(d->scanline.edges[i]->v0->y)
|
|
|
|
<< ") p1= (" << Q27Dot5ToDouble(d->scanline.edges[i]->v1->x)
|
|
|
|
<< '/' << Q27Dot5ToDouble(d->scanline.edges[i]->v1->y) << ')'
|
|
|
|
<< "x=" << Q27Dot5ToDouble(d->scanline.edges[i]->positionAt(d->y))
|
|
|
|
<< "isLeftOfNext="
|
|
|
|
<< ((i < d->scanline.size - 1)
|
|
|
|
? d->scanline.edges[i]->isLeftOf(*d->scanline.edges[i+1], d->y)
|
|
|
|
: true);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
d->scanline.done();
|
|
|
|
d->intersections.clear();
|
|
|
|
return br;
|
|
|
|
}
|
|
|
|
|
|
|
|
QT_END_NAMESPACE
|
|
|
|
|
2016-09-06 18:00:28 +00:00
|
|
|
#undef QDEBUG
|