bool pointIntersectsBufferedLine(const GeometryCoordinate& p, const GeometryCoordinates& line, const float radius) { const float radiusSquared = radius * radius; if (line.size() == 1) return util::distSqr<float>(p, line.at(0)) < radiusSquared; if (line.size() == 0) return false; for (auto i = line.begin() + 1; i != line.end(); i++) { // Find line segments that have a distance <= radius^2 to p // In that case, we treat the line as "containing point p". auto& v = *(i - 1); auto& w = *i; if (distToSegmentSquared(p, v, w) < radiusSquared) return true; } return false; }
optional<PlacedGlyph> placeGlyphAlongLine(const float offsetX, const float lineOffsetX, const float lineOffsetY, const bool flip, const Point<float>& projectedAnchorPoint, const Point<float>& tileAnchorPoint, const uint16_t anchorSegment, const GeometryCoordinates& line, const std::vector<float>& tileDistances, const mat4& labelPlaneMatrix, const bool returnTileDistance) { const float combinedOffsetX = flip ? offsetX - lineOffsetX : offsetX + lineOffsetX; int16_t dir = combinedOffsetX > 0 ? 1 : -1; float angle = 0.0; if (flip) { // The label needs to be flipped to keep text upright. // Iterate in the reverse direction. dir *= -1; angle = M_PI; } if (dir < 0) angle += M_PI; int32_t currentIndex = dir > 0 ? anchorSegment : anchorSegment + 1; const int32_t initialIndex = currentIndex; Point<float> current = projectedAnchorPoint; Point<float> prev = projectedAnchorPoint; float distanceToPrev = 0.0; float currentSegmentDistance = 0.0; const float absOffsetX = std::abs(combinedOffsetX); while (distanceToPrev + currentSegmentDistance <= absOffsetX) { currentIndex += dir; // offset does not fit on the projected line if (currentIndex < 0 || currentIndex >= static_cast<int32_t>(line.size())) { return {}; } prev = current; PointAndCameraDistance projection = project(convertPoint<float>(line.at(currentIndex)), labelPlaneMatrix); if (projection.second > 0) { current = projection.first; } else { // The vertex is behind the plane of the camera, so we can't project it // Instead, we'll create a vertex along the line that's far enough to include the glyph const Point<float> previousTilePoint = distanceToPrev == 0 ? tileAnchorPoint : convertPoint<float>(line.at(currentIndex - dir)); const Point<float> currentTilePoint = convertPoint<float>(line.at(currentIndex)); current = projectTruncatedLineSegment(previousTilePoint, currentTilePoint, prev, absOffsetX - distanceToPrev + 1, labelPlaneMatrix); } distanceToPrev += currentSegmentDistance; currentSegmentDistance = util::dist<float>(prev, current); } // The point is on the current segment. Interpolate to find it. const float segmentInterpolationT = (absOffsetX - distanceToPrev) / currentSegmentDistance; const Point<float> prevToCurrent = current - prev; Point<float> p = (prevToCurrent * segmentInterpolationT) + prev; // offset the point from the line to text-offset and icon-offset p += util::perp(prevToCurrent) * static_cast<float>(lineOffsetY * dir / util::mag(prevToCurrent)); const float segmentAngle = angle + std::atan2(current.y - prev.y, current.x - prev.x); return {{ p, segmentAngle, returnTileDistance ? TileDistance( (currentIndex - dir) == initialIndex ? 0 : tileDistances[currentIndex - dir], absOffsetX - distanceToPrev ) : optional<TileDistance>() }}; }