1#include <mbgl/text/get_anchors.hpp>
2#include <mbgl/text/check_max_angle.hpp>
3#include <mbgl/util/constants.hpp>
4#include <mbgl/util/interpolate.hpp>
5
6#include <cassert>
7#include <cmath>
8
9namespace mbgl {
10
11float getAngleWindowSize(const float textLeft, const float textRight, const float glyphSize, const float boxScale) {
12 return (textLeft - textRight) != 0.0f ?
13 3.0f / 5.0f * glyphSize * boxScale :
14 0;
15}
16
17float getLineLength(const GeometryCoordinates& line) {
18 float lineLength = 0;
19 for (auto it = line.begin(), end = line.end() - 1; it != end; it++) {
20 lineLength += util::dist<float>(a: *(it), b: *(it + 1));
21 }
22 return lineLength;
23}
24
25static Anchors resample(const GeometryCoordinates& line,
26 const float offset,
27 const float spacing,
28 const float angleWindowSize,
29 const float maxAngle,
30 const float labelLength,
31 const bool continuedLine,
32 const bool placeAtMiddle) {
33 const float halfLabelLength = labelLength / 2.0f;
34 const float lineLength = getLineLength(line);
35
36 float distance = 0;
37 float markedDistance = offset - spacing;
38
39 Anchors anchors;
40
41 assert(spacing > 0.0);
42
43 int i = 0;
44 for (auto it = line.begin(), end = line.end() - 1; it != end; it++, i++) {
45 const GeometryCoordinate& a = *(it);
46 const GeometryCoordinate& b = *(it + 1);
47
48 const auto segmentDist = util::dist<float>(a, b);
49 const float angle = util::angle_to(a: b, b: a);
50
51 while (markedDistance + spacing < distance + segmentDist) {
52 markedDistance += spacing;
53
54 float t = (markedDistance - distance) / segmentDist,
55 x = util::interpolate(a: float(a.x), b: float(b.x), t),
56 y = util::interpolate(a: float(a.y), b: float(b.y), t);
57
58 // Check that the point is within the tile boundaries and that
59 // the label would fit before the beginning and end of the line
60 // if placed at this point.
61 if (x >= 0 && x < util::EXTENT && y >= 0 && y < util::EXTENT &&
62 markedDistance - halfLabelLength >= 0.0f &&
63 markedDistance + halfLabelLength <= lineLength) {
64 Anchor anchor(::round(x: x), ::round(x: y), angle, 0.5f, i);
65
66 if (!angleWindowSize || checkMaxAngle(line, anchor, labelLength, windowSize: angleWindowSize, maxAngle)) {
67 anchors.push_back(x: anchor);
68 }
69 }
70 }
71
72 distance += segmentDist;
73 }
74
75 if (!placeAtMiddle && anchors.empty() && !continuedLine) {
76 // The first attempt at finding anchors at which labels can be placed failed.
77 // Try again, but this time just try placing one anchor at the middle of the line.
78 // This has the most effect for short lines in overscaled tiles, since the
79 // initial offset used in overscaled tiles is calculated to align labels with positions in
80 // parent tiles instead of placing the label as close to the beginning as possible.
81 anchors = resample(line, offset: distance / 2, spacing, angleWindowSize, maxAngle, labelLength, continuedLine, placeAtMiddle: true);
82 }
83
84 return anchors;
85}
86
87Anchors getAnchors(const GeometryCoordinates& line,
88 float spacing,
89 const float maxAngle,
90 const float textLeft,
91 const float textRight,
92 const float iconLeft,
93 const float iconRight,
94 const float glyphSize,
95 const float boxScale,
96 const float overscaling) {
97 if (line.empty()) {
98 return {};
99 }
100
101 // Resample a line to get anchor points for labels and check that each
102 // potential label passes text-max-angle check and has enough froom to fit
103 // on the line.
104
105 const float angleWindowSize = getAngleWindowSize(textLeft, textRight, glyphSize, boxScale);
106
107 const float shapedLabelLength = fmax(x: textRight - textLeft, y: iconRight - iconLeft);
108 const float labelLength = shapedLabelLength * boxScale;
109
110 // Is the line continued from outside the tile boundary?
111 const bool continuedLine = (line[0].x == 0 || line[0].x == util::EXTENT || line[0].y == 0 || line[0].y == util::EXTENT);
112
113 // Is the label long, relative to the spacing?
114 // If so, adjust the spacing so there is always a minimum space of `spacing / 4` between label edges.
115 if (spacing - labelLength < spacing / 4) {
116 spacing = labelLength + spacing / 4;
117 }
118
119 // Offset the first anchor by:
120 // Either half the label length plus a fixed extra offset if the line is not continued
121 // Or half the spacing if the line is continued.
122
123 // For non-continued lines, add a bit of fixed extra offset to avoid collisions at T intersections.
124 const float fixedExtraOffset = glyphSize * 2;
125
126 const float offset = !continuedLine ?
127 std::fmod(x: (shapedLabelLength / 2 + fixedExtraOffset) * boxScale * overscaling, y: spacing) :
128 std::fmod(x: spacing / 2 * overscaling, y: spacing);
129
130 return resample(line, offset, spacing, angleWindowSize, maxAngle, labelLength, continuedLine, placeAtMiddle: false);
131}
132
133optional<Anchor> getCenterAnchor(const GeometryCoordinates& line,
134 const float maxAngle,
135 const float textLeft,
136 const float textRight,
137 const float iconLeft,
138 const float iconRight,
139 const float glyphSize,
140 const float boxScale) {
141 if (line.empty()) {
142 return {};
143 }
144
145 const float angleWindowSize = getAngleWindowSize(textLeft, textRight, glyphSize, boxScale);
146 const float labelLength = fmax(x: textRight - textLeft, y: iconRight - iconLeft) * boxScale;
147
148 float prevDistance = 0;
149 const float centerDistance = getLineLength(line) / 2;
150
151 int i = 0;
152 for (auto it = line.begin(), end = line.end() - 1; it != end; it++, i++) {
153 const GeometryCoordinate& a = *(it);
154 const GeometryCoordinate& b = *(it + 1);
155
156 const auto segmentDistance = util::dist<float>(a, b);
157
158 if (prevDistance + segmentDistance > centerDistance) {
159 // The center is on this segment
160 float t = (centerDistance - prevDistance) / segmentDistance,
161 x = util::interpolate(a: float(a.x), b: float(b.x), t),
162 y = util::interpolate(a: float(a.y), b: float(b.y), t);
163
164 Anchor anchor(::round(x: x), ::round(x: y), util::angle_to(a: b, b: a), 0.5f, i);
165
166 if (!angleWindowSize || checkMaxAngle(line, anchor, labelLength, windowSize: angleWindowSize, maxAngle)) {
167 return anchor;
168 }
169 }
170
171 prevDistance += segmentDistance;
172 }
173 return {};
174}
175
176} // namespace mbgl
177

source code of qtlocation/src/3rdparty/mapbox-gl-native/src/mbgl/text/get_anchors.cpp