From aaff1c0bdb09de5072f697add8f0020c0376d1bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20T=C5=AFma?= Date: Mon, 9 Oct 2023 21:59:05 +0200 Subject: [PATCH] Fixed/improved path label layout algorithm --- src/textitem.cpp | 4 +- src/textpathitem.cpp | 209 +++++++++++++++++++++++++------------------ 2 files changed, 122 insertions(+), 91 deletions(-) diff --git a/src/textitem.cpp b/src/textitem.cpp index d6fe3ed..2e8ab25 100644 --- a/src/textitem.cpp +++ b/src/textitem.cpp @@ -21,14 +21,14 @@ qreal TextItem::avgCharWidth() const ratio = 1.0; // Greek & Cyrilic else if (cp >= 0x03FF && cp <= 0x04FF) { - ratio = (_font.capitalization() == QFont::AllUppercase) ? 0.80 : 0.73; + ratio = (_font.capitalization() == QFont::AllUppercase) ? 0.80 : 0.70; if (_font.bold()) ratio *= 1.1; if (_font.italic()) ratio *= 0.9; // The rest (Latin scripts, Arabic, ...) } else { - ratio = (_font.capitalization() == QFont::AllUppercase) ? 0.75 : 0.63; + ratio = (_font.capitalization() == QFont::AllUppercase) ? 0.75 : 0.60; if (_font.bold()) ratio *= 1.1; if (_font.italic()) diff --git a/src/textpathitem.cpp b/src/textpathitem.cpp index e83624b..e1d7d74 100644 --- a/src/textpathitem.cpp +++ b/src/textpathitem.cpp @@ -9,23 +9,20 @@ #define INTERSECTS intersects #endif // QT 5.15 -static bool intersection(const QLineF &line, const QRectF &rect, - QPointF *p) +static void swap(const QLineF &line, QPointF *p1, QPointF *p2) { - if (line.INTERSECTS(QLineF(rect.topLeft(), rect.topRight()), p) - == QLineF::BoundedIntersection) - return true; - if (line.INTERSECTS(QLineF(rect.topLeft(), rect.bottomLeft()), p) - == QLineF::BoundedIntersection) - return true; - if (line.INTERSECTS(QLineF(rect.bottomRight(), rect.bottomLeft()), p) - == QLineF::BoundedIntersection) - return true; - if (line.INTERSECTS(QLineF(rect.bottomRight(), rect.topRight()), p) - == QLineF::BoundedIntersection) - return true; - return false; + QPointF lp1(line.p1()); + QPointF lp2(line.p2()); + + if ((lp1.rx() < lp2.rx() && p1->rx() > p2->rx()) + || (lp1.ry() < lp2.ry() && p1->ry() > p2->ry()) + || (lp1.rx() > lp2.rx() && p1->rx() < p2->rx()) + || (lp1.ry() > lp2.ry() && p1->ry() < p2->ry())) { + QPointF tmp(*p2); + *p2 = *p1; + *p1 = tmp; + } } static bool intersection(const QLineF &line, const QRectF &rect, QPointF *p1, @@ -38,20 +35,26 @@ static bool intersection(const QLineF &line, const QRectF &rect, QPointF *p1, p = p2; if (line.INTERSECTS(QLineF(rect.topLeft(), rect.bottomLeft()), p) == QLineF::BoundedIntersection) { - if (p == p2) + if (p == p2) { + swap(line, p1, p2); return true; + } p = p2; } if (line.INTERSECTS(QLineF(rect.bottomRight(), rect.bottomLeft()), p) == QLineF::BoundedIntersection) { - if (p == p2) + if (p == p2) { + swap(line, p1, p2); return true; + } p = p2; } if (line.INTERSECTS(QLineF(rect.bottomRight(), rect.topRight()), p) == QLineF::BoundedIntersection) { - if (p == p2) + if (p == p2) { + swap(line, p1, p2); return true; + } } Q_ASSERT(p != p2); @@ -59,22 +62,42 @@ static bool intersection(const QLineF &line, const QRectF &rect, QPointF *p1, return false; } -static QPainterPath subpath(const QList &lines, int start, int end, +static bool intersection(const QLineF &line, const QRectF &rect, QPointF *p) +{ + if (line.INTERSECTS(QLineF(rect.topLeft(), rect.topRight()), p) + == QLineF::BoundedIntersection) + return true; + if (line.INTERSECTS(QLineF(rect.topLeft(), rect.bottomLeft()), p) + == QLineF::BoundedIntersection) + return true; + if (line.INTERSECTS(QLineF(rect.bottomRight(), rect.bottomLeft()), p) + == QLineF::BoundedIntersection) + return true; + if (line.INTERSECTS(QLineF(rect.bottomRight(), rect.topRight()), p) + == QLineF::BoundedIntersection) + return true; + + return false; +} + +static QPainterPath subpath(const QPolygonF &path, int start, int end, qreal cut) { qreal ss = 0, es = 0; int si = start, ei = end; - for (int i = start; i <= end; i++) { - qreal len = lines.at(i).length(); + for (int i = start; i < end; i++) { + QLineF l(path.at(i), path.at(i+1)); + qreal len = l.length(); if (ss + len < cut / 2) { ss += len; si++; } else break; } - for (int i = end; i >= start; i--) { - qreal len = lines.at(i).length(); + for (int i = end; i > start; i--) { + QLineF l(path.at(i), path.at(i-1)); + qreal len = l.length(); if (es + len < cut / 2) { es += len; ei--; @@ -82,98 +105,104 @@ static QPainterPath subpath(const QList &lines, int start, int end, break; } - QLineF sl(lines.at(si).p2(), lines.at(si).p1()); + QLineF sl(path.at(si+1), path.at(si)); sl.setLength(sl.length() - (cut / 2 - ss)); - QLineF el(lines.at(ei)); + QLineF el(path.at(ei-1), path.at(ei)); el.setLength(el.length() - (cut / 2 - es)); QPainterPath p(sl.p2()); - for (int i = si; i <= ei; i++) - p.lineTo(lines.at(i).p2()); - p.setElementPositionAt(p.elementCount() - 1, el.p2().x(), el.p2().y()); + for (int i = si + 1; i < ei; i++) + p.lineTo(path.at(i)); + p.lineTo(el.p2()); return p; } -static QList lineString(const QPainterPath &path, - const QRectF &boundingRect) +static QList polyLines(const QPainterPath &path, const QRectF &rect) { - QList lines; - int start = -1, end = -1; + QList lines; + QPolygonF line; + bool lastIn = rect.contains(path.elementAt(0)); + for (int i = 1; i < path.elementCount(); i++) { + if (rect.contains(path.elementAt(i))) { + if (lastIn) { + if (line.isEmpty()) + line.append(path.elementAt(i-1)); + line.append(path.elementAt(i)); + } else { + QPointF p; + QLineF l(path.elementAt(i-1), path.elementAt(i)); - for (int i = 0; i < path.elementCount(); i++) { - if (boundingRect.contains(path.elementAt(i))) { - start = i; - break; - } - } - for (int i = path.elementCount() - 1; i >= 0; i--) { - if (boundingRect.contains(path.elementAt(i))) { - end = i; - break; - } - } - - if (start < 0) { - QPointF p1, p2; - - for (int i = 1; i < path.elementCount(); i++) { - QLineF l(path.elementAt(i-1), path.elementAt(i)); - if (intersection(l, boundingRect, &p1, &p2)) { - lines.append(QLineF(p1, p2)); - break; + if (intersection(l, rect, &p)) + line.append(p); + line.append(path.elementAt(i)); } - } - } else { - QPointF p; - if (start > 0) { - QLineF l(path.elementAt(start-1), path.elementAt(start)); - if (intersection(l, boundingRect, &p)) - lines.append(QLineF(p, path.elementAt(start))); - } - for (int i = start + 1; i <= end; i++) - lines.append(QLineF(path.elementAt(i-1), path.elementAt(i))); - if (end < path.elementCount() - 1) { - QLineF l(path.elementAt(end), path.elementAt(end+1)); - if (intersection(l, boundingRect, &p)) - lines.append(QLineF(path.elementAt(end), p)); + lastIn = true; + } else { + QLineF l(path.elementAt(i-1), path.elementAt(i)); + + if (lastIn) { + QPointF p; + if (intersection(l, rect, &p)) + line.append(p); + lines.append(line); + line.clear(); + } else { + QPointF p1, p2; + if (intersection(l, rect, &p1, &p2)) { + line.append(p1); + line.append(p2); + lines.append(line); + line.clear(); + } + } + + lastIn = false; } } + if (!line.isEmpty()) + lines.append(line); + return lines; } static QPainterPath textPath(const QPainterPath &path, qreal textWidth, qreal maxAngle, qreal charWidth, const QRectF &tileRect) { - QList lines(lineString(path, tileRect)); - if (lines.isEmpty()) + if (path.isEmpty()) return QPainterPath(); - qreal length = 0; - qreal angle = lines.first().angle(); - int last = 0; + + QList lines(polyLines(path, tileRect)); for (int i = 0; i < lines.size(); i++) { - qreal sl = lines.at(i).length(); - qreal a = lines.at(i).angle(); + const QPolygonF &pl = lines.at(i); + qreal angle, length = 0; + int last = 0; - if (!tileRect.contains(lines.at(i).p2()) || sl < charWidth - || qAbs(angle - a) > maxAngle) { - if (length > textWidth) - return subpath(lines, last, i - 1, length - textWidth); - last = i; - length = 0; - } else - length += sl; + for (int j = 1; j < pl.size(); j ++) { + QLineF l(pl.at(j-1), pl.at(j)); + qreal sl = l.length(); + qreal a = l.angle(); - angle = a; + if ((sl < charWidth) || (j > 1 && qAbs(angle - a) > maxAngle)) { + if (length > textWidth) + return subpath(pl, last, j - 1, length - textWidth); + last = j; + length = 0; + } else + length += sl; + + angle = a; + } + + if (length > textWidth) + return subpath(pl, last, pl.size() - 1, length - textWidth); } - return (length > textWidth) - ? subpath(lines, last, lines.size() - 1, length - textWidth) - : QPainterPath(); + return QPainterPath(); } static bool reverse(const QPainterPath &path) @@ -208,9 +237,6 @@ TextPathItem::TextPathItem(const QString &text, const QPainterPath &path, void TextPathItem::paint(QPainter *painter) const { - //painter->setPen(Qt::red); - //painter->drawPath(_shape); - QFontMetrics fm(font()); int textWidth = fm.boundingRect(text()).width(); @@ -259,4 +285,9 @@ void TextPathItem::paint(QPainter *painter) const int width = fm.horizontalAdvance(text().at(i)); percent += ((qreal)width / (qreal)textWidth) * factor; } + + //painter->setBrush(Qt::NoBrush); + //painter->setPen(Qt::red); + //painter->setRenderHint(QPainter::Antialiasing, false); + //painter->drawPath(_shape); }