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) { #ifdef DEBUG 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); #ifdef DEBUG 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; #ifdef DEBUG 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); #ifdef DEBUG checkLinkChain(intersections, i1); checkLinkChain(intersections, i2); Q_ASSERT(edgeInChain(i1, i2.edge)); #endif return; }
QRectF QTessellator::tessellate(const QPointF *points, int nPoints) { Q_ASSERT(points[0] == points[nPoints-1]); --nPoints; #ifdef DEBUG 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(); #ifdef DEBUG 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(); #ifdef DEBUG 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; }
void old_tesselate_polygon(QVector<XTrapezoid> *traps, const QPointF *pg, int pgSize, bool winding) { QVector<QEdge> edges; edges.reserve(128); qreal ymin(INT_MAX/256); qreal ymax(INT_MIN/256); //painter.begin(pg, pgSize); if (pg[0] != pg[pgSize-1]) qWarning() << Q_FUNC_INFO << "Malformed polygon (first and last points must be identical)"; // generate edge table // qDebug() << "POINTS:"; for (int x = 0; x < pgSize-1; ++x) { QEdge edge; QPointF p1(Q27Dot5ToDouble(FloatToQ27Dot5(pg[x].x())), Q27Dot5ToDouble(FloatToQ27Dot5(pg[x].y()))); QPointF p2(Q27Dot5ToDouble(FloatToQ27Dot5(pg[x+1].x())), Q27Dot5ToDouble(FloatToQ27Dot5(pg[x+1].y()))); // qDebug() << " " // << p1; edge.winding = p1.y() > p2.y() ? 1 : -1; if (edge.winding > 0) qSwap(p1, p2); edge.p1.x = XDoubleToFixed(p1.x()); edge.p1.y = XDoubleToFixed(p1.y()); edge.p2.x = XDoubleToFixed(p2.x()); edge.p2.y = XDoubleToFixed(p2.y()); edge.m = (p1.y() - p2.y()) / (p1.x() - p2.x()); // line derivative edge.b = p1.y() - edge.m * p1.x(); // intersection with y axis edge.m = edge.m != 0.0 ? 1.0 / edge.m : 0.0; // inverted derivative edges.append(edge); ymin = qMin(ymin, qreal(XFixedToDouble(edge.p1.y))); ymax = qMax(ymax, qreal(XFixedToDouble(edge.p2.y))); } QList<const QEdge *> et; // edge list for (int i = 0; i < edges.size(); ++i) et.append(&edges.at(i)); // sort edge table by min y value qSort(et.begin(), et.end(), compareEdges); // eliminate shared edges for (int i = 0; i < et.size(); ++i) { for (int k = i+1; k < et.size(); ++k) { const QEdge *edgeI = et.at(i); const QEdge *edgeK = et.at(k); if (edgeK->p1.y > edgeI->p1.y) break; if (edgeI->winding != edgeK->winding && isEqual(edgeI->p1, edgeK->p1) && isEqual(edgeI->p2, edgeK->p2) ) { et.removeAt(k); et.removeAt(i); --i; break; } } } if (ymax <= ymin) return; QList<const QEdge *> aet; // edges that intersects the current scanline // if (ymin < 0) // ymin = 0; // if (paintEventClipRegion) // don't scan more lines than we have to // ymax = paintEventClipRegion->boundingRect().height(); #ifdef QT_DEBUG_TESSELATOR qDebug("==> ymin = %f, ymax = %f", ymin, ymax); #endif // QT_DEBUG_TESSELATOR currentY = ymin; // used by the less than op for (qreal y = ymin; y < ymax;) { // fill active edge table with edges that intersect the current line for (int i = 0; i < et.size(); ++i) { const QEdge *edge = et.at(i); if (edge->p1.y > XDoubleToFixed(y)) break; aet.append(edge); et.removeAt(i); --i; } // remove processed edges from active edge table for (int i = 0; i < aet.size(); ++i) { if (aet.at(i)->p2.y <= XDoubleToFixed(y)) { aet.removeAt(i); --i; } } if (aet.size()%2 != 0) { #ifndef QT_NO_DEBUG qWarning("QX11PaintEngine: aet out of sync - this should not happen."); #endif return; } // done? if (!aet.size()) { if (!et.size()) { break; } else { y = currentY = XFixedToDouble(et.at(0)->p1.y); continue; } } // calculate the next y where we have to start a new set of trapezoids qreal next_y(INT_MAX/256); for (int i = 0; i < aet.size(); ++i) { const QEdge *edge = aet.at(i); if (XFixedToDouble(edge->p2.y) < next_y) next_y = XFixedToDouble(edge->p2.y); } if (et.size() && next_y > XFixedToDouble(et.at(0)->p1.y)) next_y = XFixedToDouble(et.at(0)->p1.y); int aetSize = aet.size(); for (int i = 0; i < aetSize; ++i) { for (int k = i+1; k < aetSize; ++k) { const QEdge *edgeI = aet.at(i); const QEdge *edgeK = aet.at(k); qreal m1 = edgeI->m; qreal b1 = edgeI->b; qreal m2 = edgeK->m; qreal b2 = edgeK->b; if (qAbs(m1 - m2) < 0.001) continue; // ### intersect is not calculated correctly when optimized with -O2 (gcc) volatile qreal intersect = 0; if (!qIsFinite(b1)) intersect = (1.f / m2) * XFixedToDouble(edgeI->p1.x) + b2; else if (!qIsFinite(b2)) intersect = (1.f / m1) * XFixedToDouble(edgeK->p1.x) + b1; else intersect = (b1*m1 - b2*m2) / (m1 - m2); if (intersect > y && intersect < next_y) next_y = intersect; } } XFixed yf, next_yf; yf = qrealToXFixed(y); next_yf = qrealToXFixed(next_y); if (yf == next_yf) { y = currentY = next_y; continue; } #ifdef QT_DEBUG_TESSELATOR qDebug("###> y = %f, next_y = %f, %d active edges", y, next_y, aet.size()); qDebug("===> edges"); dump_edges(et); qDebug("===> active edges"); dump_edges(aet); #endif // calc intersection points QVarLengthArray<QIntersectionPoint> isects(aet.size()+1); for (int i = 0; i < isects.size()-1; ++i) { const QEdge *edge = aet.at(i); isects[i].x = (edge->p1.x != edge->p2.x) ? ((y - edge->b)*edge->m) : XFixedToDouble(edge->p1.x); isects[i].edge = edge; } if (isects.size()%2 != 1) qFatal("%s: number of intersection points must be odd", Q_FUNC_INFO); // sort intersection points qSort(&isects[0], &isects[isects.size()-1], compareIntersections); // qDebug() << "INTERSECTION_POINTS:"; // for (int i = 0; i < isects.size(); ++i) // qDebug() << isects[i].edge << isects[i].x; if (winding) { // winding fill rule for (int i = 0; i < isects.size()-1;) { int winding = 0; const QEdge *left = isects[i].edge; const QEdge *right = 0; winding += isects[i].edge->winding; for (++i; i < isects.size()-1 && winding != 0; ++i) { winding += isects[i].edge->winding; right = isects[i].edge; } if (!left || !right) break; //painter.addTrapezoid(&toXTrapezoid(yf, next_yf, *left, *right)); traps->append(toXTrapezoid(yf, next_yf, *left, *right)); } } else { // odd-even fill rule for (int i = 0; i < isects.size()-2; i += 2) { //painter.addTrapezoid(&toXTrapezoid(yf, next_yf, *isects[i].edge, *isects[i+1].edge)); traps->append(toXTrapezoid(yf, next_yf, *isects[i].edge, *isects[i+1].edge)); } } y = currentY = next_y; } #ifdef QT_DEBUG_TESSELATOR qDebug("==> number of trapezoids: %d - edge table size: %d\n", traps->size(), et.size()); for (int i = 0; i < traps->size(); ++i) dump_trap(traps->at(i)); #endif // optimize by unifying trapezoids that share left/right lines // and have a common top/bottom edge // for (int i = 0; i < tps.size(); ++i) { // for (int k = i+1; k < tps.size(); ++k) { // if (i != k && tps.at(i).right == tps.at(k).right // && tps.at(i).left == tps.at(k).left // && (tps.at(i).top == tps.at(k).bottom // || tps.at(i).bottom == tps.at(k).top)) // { // tps[i].bottom = tps.at(k).bottom; // tps.removeAt(k); // i = 0; // break; // } // } // } //static int i = 0; //QImage img = painter.end(); //img.save(QString("res%1.png").arg(i++), "PNG"); }