#include #include #include #include #include #include #include #include #include #include namespace scwx { namespace qt { namespace gl { namespace draw { static const std::string logPrefix_ = "scwx::qt::gl::draw::placefile_text"; static const auto logger_ = scwx::util::Logger::Create(logPrefix_); class PlacefileText::Impl { public: explicit Impl(const std::shared_ptr& context, const std::string& placefileName) : context_ {context}, placefileName_ {placefileName} { } ~Impl() {} void RenderTextDrawItem( const QMapLibre::CustomLayerRenderParameters& params, const std::shared_ptr& di); void RenderText(const QMapLibre::CustomLayerRenderParameters& params, const std::string& text, const std::string& hoverText, boost::gil::rgba8_pixel_t color, float x, float y); std::shared_ptr context_; std::string placefileName_; bool thresholded_ {false}; std::chrono::system_clock::time_point selectedTime_ {}; std::uint32_t textId_ {}; glm::vec2 mapScreenCoordLocation_ {}; float mapScale_ {1.0f}; float mapBearingCos_ {1.0f}; float mapBearingSin_ {0.0f}; float halfWidth_ {}; float halfHeight_ {}; std::string hoverText_ {}; units::length::nautical_miles mapDistance_ {}; std::mutex listMutex_ {}; std::vector> textList_ {}; std::vector> newList_ {}; std::vector> fonts_ {}; std::vector> newFonts_ {}; }; PlacefileText::PlacefileText(const std::shared_ptr& context, const std::string& placefileName) : DrawItem(context->gl()), p(std::make_unique(context, placefileName)) { } PlacefileText::~PlacefileText() = default; PlacefileText::PlacefileText(PlacefileText&&) noexcept = default; PlacefileText& PlacefileText::operator=(PlacefileText&&) noexcept = default; void PlacefileText::set_placefile_name(const std::string& placefileName) { p->placefileName_ = placefileName; } void PlacefileText::set_selected_time( std::chrono::system_clock::time_point selectedTime) { p->selectedTime_ = selectedTime; } void PlacefileText::set_thresholded(bool thresholded) { p->thresholded_ = thresholded; } void PlacefileText::Initialize() {} void PlacefileText::Render(const QMapLibre::CustomLayerRenderParameters& params) { std::unique_lock lock {p->listMutex_}; if (!p->textList_.empty()) { // Reset text ID per frame p->textId_ = 0; p->hoverText_.clear(); // Update map screen coordinate and scale information p->mapScreenCoordLocation_ = util::maplibre::LatLongToScreenCoordinate( {params.latitude, params.longitude}); p->mapScale_ = std::pow(2.0, params.zoom) * mbgl::util::tileSize_D / mbgl::util::DEGREES_MAX; p->mapBearingCos_ = cosf(params.bearing * common::kDegreesToRadians); p->mapBearingSin_ = sinf(params.bearing * common::kDegreesToRadians); p->halfWidth_ = params.width * 0.5f; p->halfHeight_ = params.height * 0.5f; p->mapDistance_ = util::maplibre::GetMapDistance(params); for (auto& di : p->textList_) { p->RenderTextDrawItem(params, di); } } } void PlacefileText::Impl::RenderTextDrawItem( const QMapLibre::CustomLayerRenderParameters& params, const std::shared_ptr& di) { // If no time has been selected, use the current time std::chrono::system_clock::time_point selectedTime = (selectedTime_ == std::chrono::system_clock::time_point {}) ? std::chrono::system_clock::now() : selectedTime_; const bool thresholdMet = mapDistance_ <= di->threshold_ || ((double)di->threshold_ < 0.0 && mapDistance_ >= -(di->threshold_)); if ((!thresholded_ || thresholdMet) && (di->startTime_ == std::chrono::system_clock::time_point {} || (di->startTime_ <= selectedTime && selectedTime < di->endTime_))) { const auto screenCoordinates = (util::maplibre::LatLongToScreenCoordinate( {di->latitude_, di->longitude_}) - mapScreenCoordLocation_) * mapScale_; // Rotate text according to map rotation float rotatedX = screenCoordinates.x; float rotatedY = screenCoordinates.y; if (params.bearing != 0.0) { rotatedX = screenCoordinates.x * mapBearingCos_ - screenCoordinates.y * mapBearingSin_; rotatedY = screenCoordinates.x * mapBearingSin_ + screenCoordinates.y * mapBearingCos_; } // Clamp font number to 0-8 std::size_t fontNumber = std::clamp(di->fontNumber_, 0, 8); // Set the font for the drop shadow and text ImGui::PushFont(fonts_[fontNumber]->font()); if (settings::TextSettings::Instance() .placefile_text_drop_shadow_enabled() .GetValue()) { // Draw a drop shadow 1 pixel to the lower right, in black, with the // original transparency level RenderText(params, di->text_, {}, boost::gil::rgba8_pixel_t {0, 0, 0, di->color_[3]}, rotatedX + di->x_ + halfWidth_ + 1.0f, rotatedY + di->y_ + halfHeight_ - 1.0f); } // Draw the text RenderText(params, di->text_, di->hoverText_, di->color_, rotatedX + di->x_ + halfWidth_, rotatedY + di->y_ + halfHeight_); // Reset the font ImGui::PopFont(); } } void PlacefileText::Impl::RenderText( const QMapLibre::CustomLayerRenderParameters& params, const std::string& text, const std::string& hoverText, boost::gil::rgba8_pixel_t color, float x, float y) { const std::string windowName { fmt::format("PlacefileText-{}-{}", placefileName_, ++textId_)}; // Convert screen to ImGui coordinates y = params.height - y; // Setup "window" to hold text ImGui::SetNextWindowPos( ImVec2 {x, y}, ImGuiCond_Always, ImVec2 {0.5f, 0.5f}); ImGui::Begin(windowName.c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoBackground); // Render text ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(color[0], color[1], color[2], color[3])); ImGui::TextUnformatted(text.c_str()); ImGui::PopStyleColor(); // Store hover text for mouse picking pass if (!hoverText.empty() && ImGui::IsItemHovered()) { hoverText_ = hoverText; } // End window ImGui::End(); } void PlacefileText::Deinitialize() { std::unique_lock lock {p->listMutex_}; // Clear the text list p->textList_.clear(); } bool PlacefileText::RunMousePicking( const QMapLibre::CustomLayerRenderParameters& /* params */, const QPointF& /* mouseLocalPos */, const QPointF& mouseGlobalPos, const glm::vec2& /* mouseCoords */, const common::Coordinate& /* mouseGeoCoords */, std::shared_ptr& /* eventHandler */) { bool itemPicked = false; // Create tooltip for hover text if (!p->hoverText_.empty()) { itemPicked = true; util::tooltip::Show(p->hoverText_, mouseGlobalPos); } return itemPicked; } void PlacefileText::StartText() { // Clear the new list p->newList_.clear(); } void PlacefileText::SetFonts( const boost::unordered_flat_map>& fonts) { auto defaultFont = manager::FontManager::Instance().GetImGuiFont( types::FontCategory::Default); // Valid font numbers are from 1 to 8, use 0 for the default font for (std::size_t i = 0; i <= 8; ++i) { auto it = (i > 0) ? fonts.find(i) : fonts.cend(); if (it != fonts.cend()) { p->newFonts_.push_back(it->second); } else { p->newFonts_.push_back(defaultFont); } } } void PlacefileText::AddText( const std::shared_ptr& di) { if (di != nullptr) { p->newList_.emplace_back(di); } } void PlacefileText::FinishText() { std::unique_lock lock {p->listMutex_}; // Swap text lists p->textList_.swap(p->newList_); p->fonts_.swap(p->newFonts_); // Clear the new list p->newList_.clear(); p->newFonts_.clear(); } } // namespace draw } // namespace gl } // namespace qt } // namespace scwx