diff --git a/src/style.cpp b/src/style.cpp index 6f97c9c..e7a9b27 100644 --- a/src/style.cpp +++ b/src/style.cpp @@ -269,8 +269,8 @@ bool Style::Layer::Paint::antialias(Layer::Type type) const } Style::Layer::Layout::Layout(const QJsonObject &json) - : _textSize(16), _textMaxWidth(10), _textMaxAngle(45), _symbolSpacing(250), - _lineCap(Qt::FlatCap), _lineJoin(Qt::MiterJoin), _capitalize(false) + : _textSize(16), _textMaxWidth(10), _textMaxAngle(45), _lineCap(Qt::FlatCap), + _lineJoin(Qt::MiterJoin), _capitalize(false) { if (!(json.contains("text-field") && json["text-field"].isString())) return; @@ -298,11 +298,6 @@ Style::Layer::Layout::Layout(const QJsonObject &json) else if (json.contains("text-max-angle") && json["text-max-angle"].isDouble()) _textMaxWidth = json["text-max-angle"].toDouble(); - if (json.contains("symbol-spacing") && json["symbol-spacing"].isObject()) - _symbolSpacing = json["symbol-spacing"].toObject(); - else if (json.contains("symbol-spacing") && json["symbol-spacing"].isDouble()) - _symbolSpacing = json["symbol-spacing"].toDouble(); - if (json.contains("text-transform") && json["text-transform"].isString()) _capitalize = json["text-transform"].toString() == "uppercase"; @@ -422,8 +417,7 @@ void Style::Layer::drawSymbol(int zoom, const QPainterPath &path, tile.text().addLabel(tt, path.elementAt(0), font, pen, _layout.maxTextWidth(zoom)); else - tile.text().addLabel(tt, path, font, pen, - _layout.maxTextAngle(zoom), _layout.symbolSpacing(zoom)); + tile.text().addLabel(tt, path, font, pen, _layout.maxTextAngle(zoom)); } bool Style::load(const QString &fileName) diff --git a/src/style.h b/src/style.h index 3aecdd8..2ddf200 100644 --- a/src/style.h +++ b/src/style.h @@ -79,8 +79,8 @@ private: class Layout { public: Layout() : _textSize(16), _textMaxWidth(10), _textMaxAngle(45), - _symbolSpacing(250), _lineCap(Qt::FlatCap), - _lineJoin(Qt::MiterJoin), _capitalize(false) {} + _lineCap(Qt::FlatCap), _lineJoin(Qt::MiterJoin), + _capitalize(false) {} Layout(const QJsonObject &json); bool capitalize() const {return _capitalize;} @@ -88,8 +88,6 @@ private: {return _textMaxWidth.value(zoom);} qreal maxTextAngle(int zoom) const {return _textMaxAngle.value(zoom);} - qreal symbolSpacing(int zoom) const - {return _symbolSpacing.value(zoom);} const QString &field() const {return _textField;} const QStringList &keys() const {return _keys;} QFont font(int zoom) const; @@ -102,7 +100,6 @@ private: FunctionF _textSize; FunctionF _textMaxWidth; FunctionF _textMaxAngle; - FunctionF _symbolSpacing; Qt::PenCapStyle _lineCap; Qt::PenJoinStyle _lineJoin; bool _capitalize; diff --git a/src/text.cpp b/src/text.cpp index 55216f9..80b0b64 100644 --- a/src/text.cpp +++ b/src/text.cpp @@ -6,61 +6,123 @@ #include "textpathitem.h" -static QPainterPath subpath(const QPainterPath &path, int start, int end, +static QPointF intersection(const QLineF &line, const QRectF &rect) +{ + QPointF p; + if (line.intersect(QLineF(rect.topLeft(), rect.topRight()), &p) + == QLineF::BoundedIntersection) + return p; + if (line.intersect(QLineF(rect.topLeft(), rect.bottomLeft()), &p) + == QLineF::BoundedIntersection) + return p; + if (line.intersect(QLineF(rect.bottomRight(), rect.bottomLeft()), &p) + == QLineF::BoundedIntersection) + return p; + if (line.intersect(QLineF(rect.bottomRight(), rect.topRight()), &p) + == QLineF::BoundedIntersection) + return p; + + return rect.center(); +} + +static QPainterPath subpath(const QList &lines, int start, int end, qreal cut) { - QPainterPath p(path.elementAt(start)); + qreal ss = 0, es = 0; + int si = start, ei = end; - for (int i = start + 1; i < end; i++) - p.lineTo(path.elementAt(i)); + for (int i = start; i <= end; i++) { + qreal len = lines.at(i).length(); + if (ss + len < cut / 2) { + ss += len; + si++; + } else + break; + } + for (int i = end; i >= start; i--) { + qreal len = lines.at(i).length(); + if (es + len < cut / 2) { + es += len; + ei--; + } else + break; + } - QLineF l(path.elementAt(end - 1), path.elementAt(end)); - l.setLength(l.length() - cut); - p.lineTo(l.p2()); + QLineF sl(lines.at(si).p2(), lines.at(si).p1()); + sl.setLength(sl.length() - (cut / 2 - ss)); + QLineF el(lines.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()); return p; } -static QList segments(const QPainterPath &path, qreal textLength, - qreal maxAngle, qreal charWidth, int symbolSpacing, const QRectF &tileRect) +static QList lineString(const QPainterPath &path, + const QRectF &boundingRect) { - QList list; - int start = 0; - qreal length = 0; - qreal angle = -1; - qreal dist = symbolSpacing; + QList lines; + int start = 0, end = path.elementCount() - 1; - for (int i = 1; i < path.elementCount(); i++) { - QLineF l(path.elementAt(i-1), path.elementAt(i)); - qreal sl = l.length(); - - if (dist < symbolSpacing) { - dist += sl; - start++; - continue; - } - - qreal a = l.angle(); - if (angle < 0) - angle = a; - - if (!tileRect.contains(path.elementAt(i)) - || sl < charWidth || qAbs(angle - a) > maxAngle - || length > textLength) { - if (length > textLength) { - list.append(subpath(path, start, i - 1, length - textLength - 1)); - dist = 0; - } + 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) { + QLineF l(path.elementAt(start-1), path.elementAt(start)); + QPointF p(intersection(l, boundingRect)); + if (p != boundingRect.center()) + 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)); + QPointF p(intersection(l, boundingRect)); + if (p != boundingRect.center()) + lines.append(QLineF(path.elementAt(end), p)); + } + + return lines; +} + +static QPainterPath textPath(const QPainterPath &path, qreal textWidth, + qreal maxAngle, qreal charWidth, const QRectF &tileRect) +{ + QList lines(lineString(path, tileRect)); + qreal length = 0; + qreal angle = lines.first().angle(); + int last = 0; + + for (int i = 0; i < lines.size(); i++) { + qreal sl = lines.at(i).length(); + qreal a = lines.at(i).angle(); + + 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; - dist += sl; angle = a; } - return list; + return QPainterPath(); } static bool reverse(const QPainterPath &path) @@ -93,9 +155,9 @@ void Text::addLabel(const QString &text, const QPointF &pos, const QFont &font, } void Text::addLabel(const QString &text, const QPainterPath &path, - const QFont &font, const QPen &pen, qreal maxAngle, qreal symbolSpacing) + const QFont &font, const QPen &pen, qreal maxAngle) { - if (path.elementCount() < 2 || !path.elementAt(0).isMoveTo()) + if (path.isEmpty()) return; if (text.isEmpty()) return; @@ -106,22 +168,22 @@ void Text::addLabel(const QString &text, const QPainterPath &path, if (textWidth > path.length()) return; - QList list(segments(path, textWidth, maxAngle, - fm.averageCharWidth(), symbolSpacing, sceneRect())); - for (int i = 0; i < list.size(); i++) { - const QPainterPath &segment = list.at(i); - TextPathItem *pi = new TextPathItem(text, reverse(segment) - ? segment.toReversed() : segment, font); - addItem(pi); - if (!sceneRect().contains(pi->sceneBoundingRect())) { - delete pi; - continue; - } + QPainterPath tp(textPath(path, textWidth, maxAngle, fm.averageCharWidth(), + sceneRect())); + if (tp.isEmpty()) + return; - pi->setPen(pen); - - QList ci = collidingItems(pi); - for (int j = 0; j < ci.size(); j++) - ci[j]->setVisible(false); + TextPathItem *pi = new TextPathItem(text, reverse(tp) ? tp.toReversed() + : tp, font); + addItem(pi); + if (!sceneRect().contains(pi->sceneBoundingRect())) { + delete pi; + return; } + + pi->setPen(pen); + + QList ci = collidingItems(pi); + for (int j = 0; j < ci.size(); j++) + ci[j]->setVisible(false); } diff --git a/src/text.h b/src/text.h index 894044f..0649290 100644 --- a/src/text.h +++ b/src/text.h @@ -12,7 +12,7 @@ public: void addLabel(const QString &text, const QPointF &pos, const QFont &font, const QPen &pen, qreal maxTextWidth); void addLabel(const QString &text, const QPainterPath &path, - const QFont &font, const QPen &pen, qreal maxAngle, qreal symbolSpacing); + const QFont &font, const QPen &pen, qreal maxAngle); }; #endif // TEXT_H diff --git a/src/textitem.cpp b/src/textitem.cpp index 4ee7656..cb1dbd1 100644 --- a/src/textitem.cpp +++ b/src/textitem.cpp @@ -26,4 +26,7 @@ void TextItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, painter->setFont(_font); painter->setPen(_pen); painter->drawText(_boundingRect, FLAGS, _text); + + //painter->setPen(Qt::red); + //painter->drawRect(_boundingRect); } diff --git a/src/textpathitem.cpp b/src/textpathitem.cpp index ba7f11d..e0e2342 100644 --- a/src/textpathitem.cpp +++ b/src/textpathitem.cpp @@ -22,15 +22,13 @@ void TextPathItem::paint(QPainter *painter, QFontMetrics fm(_font); int textWidth = fm.width(_text); - qreal factor = (textWidth) / _path.length(); + qreal factor = (textWidth) / qMax(_path.length(), (qreal)textWidth); qreal percent = (1.0 - factor) / 2.0; painter->setFont(_font); painter->setPen(_pen); for (int i = 0; i < _text.size(); i++) { - Q_ASSERT(percent <= 1.0); - QPointF point = _path.pointAtPercent(percent); qreal angle = _path.angleAtPercent(percent); @@ -42,4 +40,7 @@ void TextPathItem::paint(QPainter *painter, int width = fm.charWidth(_text, i); percent += ((qreal)width / (qreal)textWidth) * factor; } + + //painter->setPen(Qt::red); + //painter->drawPath(_shape); }