supercell-wx/scwx-qt/source/scwx/qt/gl/draw/linked_vectors.cpp
2025-07-10 21:56:17 -05:00

357 lines
11 KiB
C++

#include <scwx/qt/gl/draw/linked_vectors.hpp>
#include <scwx/qt/gl/draw/geo_lines.hpp>
#include <scwx/qt/util/geographic_lib.hpp>
#include <scwx/wsr88d/rpg/linked_vector_packet.hpp>
#include <boost/iterator/zip_iterator.hpp>
namespace scwx
{
namespace qt
{
namespace gl
{
namespace draw
{
static const std::string logPrefix_ = "scwx::qt::gl::draw::linked_vectors";
static const boost::gil::rgba32f_pixel_t kBlack {0.0f, 0.0f, 0.0f, 1.0f};
struct LinkedVectorDrawItem
{
LinkedVectorDrawItem(
const common::Coordinate& center,
const std::shared_ptr<const wsr88d::rpg::LinkedVectorPacket>&
vectorPacket)
{
coordinates_.push_back(util::GeographicLib::GetCoordinate(
center, vectorPacket->start_i_km(), vectorPacket->start_j_km()));
const auto endI = vectorPacket->end_i_km();
const auto endJ = vectorPacket->end_j_km();
std::for_each(
boost::make_zip_iterator(
boost::make_tuple(endI.begin(), endJ.begin())),
boost::make_zip_iterator(boost::make_tuple(endI.end(), endJ.end())),
[this,
&center](const boost::tuple<units::length::kilometers<double>,
units::length::kilometers<double>>& p)
{
coordinates_.push_back(util::GeographicLib::GetCoordinate(
center, p.get<0>(), p.get<1>()));
});
}
std::vector<std::shared_ptr<GeoLineDrawItem>> borderDrawItems_ {};
std::vector<std::shared_ptr<GeoLineDrawItem>> lineDrawItems_ {};
std::vector<common::Coordinate> coordinates_ {};
boost::gil::rgba32f_pixel_t modulate_ {1.0f, 1.0f, 1.0f, 1.0f};
float width_ {5.0f};
bool visible_ {true};
std::string hoverText_ {};
bool ticksEnabled_ {false};
units::length::nautical_miles<double> tickRadius_ {1.0};
units::length::nautical_miles<double> tickRadiusIncrement_ {0.0};
};
class LinkedVectors::Impl
{
public:
explicit Impl(std::shared_ptr<GlContext> context) :
context_ {context}, geoLines_ {std::make_shared<GeoLines>(context)}
{
}
~Impl() {}
std::shared_ptr<GlContext> context_;
bool borderEnabled_ {true};
bool visible_ {true};
std::vector<std::shared_ptr<LinkedVectorDrawItem>> vectorList_ {};
std::shared_ptr<GeoLines> geoLines_;
};
LinkedVectors::LinkedVectors(std::shared_ptr<GlContext> context) :
DrawItem(), p(std::make_unique<Impl>(context))
{
}
LinkedVectors::~LinkedVectors() = default;
LinkedVectors::LinkedVectors(LinkedVectors&&) noexcept = default;
LinkedVectors& LinkedVectors::operator=(LinkedVectors&&) noexcept = default;
void LinkedVectors::set_selected_time(
std::chrono::system_clock::time_point selectedTime)
{
p->geoLines_->set_selected_time(selectedTime);
}
void LinkedVectors::set_thresholded(bool thresholded)
{
p->geoLines_->set_thresholded(thresholded);
}
void LinkedVectors::Initialize()
{
p->geoLines_->Initialize();
}
void LinkedVectors::Render(const QMapLibre::CustomLayerRenderParameters& params)
{
if (!p->visible_)
{
return;
}
p->geoLines_->Render(params);
}
void LinkedVectors::Deinitialize()
{
p->geoLines_->Deinitialize();
}
void LinkedVectors::SetBorderEnabled(bool enabled)
{
p->borderEnabled_ = enabled;
}
void LinkedVectors::SetVisible(bool visible)
{
p->visible_ = visible;
}
void LinkedVectors::StartVectors()
{
// Start a new set of geo lines
p->geoLines_->StartLines();
p->vectorList_.clear();
}
std::shared_ptr<LinkedVectorDrawItem> LinkedVectors::AddVector(
const common::Coordinate& center,
const std::shared_ptr<const wsr88d::rpg::LinkedVectorPacket>& vectorPacket)
{
return p->vectorList_.emplace_back(
std::make_shared<LinkedVectorDrawItem>(center, vectorPacket));
}
void LinkedVectors::SetVectorModulate(
const std::shared_ptr<LinkedVectorDrawItem>& di,
boost::gil::rgba8_pixel_t modulate)
{
di->modulate_ = {modulate[0] / 255.0f,
modulate[1] / 255.0f,
modulate[2] / 255.0f,
modulate[3] / 255.0f};
}
void LinkedVectors::SetVectorModulate(
const std::shared_ptr<LinkedVectorDrawItem>& di,
boost::gil::rgba32f_pixel_t modulate)
{
di->modulate_ = modulate;
}
void LinkedVectors::SetVectorWidth(
const std::shared_ptr<LinkedVectorDrawItem>& di, float width)
{
di->width_ = width;
}
void LinkedVectors::SetVectorVisible(
const std::shared_ptr<LinkedVectorDrawItem>& di, bool visible)
{
di->visible_ = visible;
}
void LinkedVectors::SetVectorHoverText(
const std::shared_ptr<LinkedVectorDrawItem>& di, const std::string& text)
{
di->hoverText_ = text;
}
void LinkedVectors::SetVectorTicksEnabled(
const std::shared_ptr<LinkedVectorDrawItem>& di, bool enabled)
{
di->ticksEnabled_ = enabled;
}
void LinkedVectors::SetVectorTickRadius(
const std::shared_ptr<LinkedVectorDrawItem>& di,
units::length::meters<double> radius)
{
di->tickRadius_ = radius;
}
void LinkedVectors::SetVectorTickRadiusIncrement(
const std::shared_ptr<LinkedVectorDrawItem>& di,
units::length::meters<double> radiusIncrement)
{
di->tickRadiusIncrement_ = radiusIncrement;
}
void LinkedVectors::FinishVectors()
{
// Generate borders
if (p->borderEnabled_)
{
for (auto& di : p->vectorList_)
{
auto tickRadius = di->tickRadius_;
for (std::size_t i = 0; i < di->coordinates_.size() - 1; ++i)
{
auto borderLine = p->geoLines_->AddLine();
const common::Coordinate& coordinate1 = di->coordinates_[i];
const common::Coordinate& coordinate2 = di->coordinates_[i + 1];
const double& latitude1 = coordinate1.latitude_;
const double& longitude1 = coordinate1.longitude_;
const double& latitude2 = coordinate2.latitude_;
const double& longitude2 = coordinate2.longitude_;
p->geoLines_->SetLineLocation(
borderLine, latitude1, longitude1, latitude2, longitude2);
p->geoLines_->SetLineModulate(borderLine, kBlack);
p->geoLines_->SetLineWidth(borderLine, di->width_ + 2.0f);
p->geoLines_->SetLineVisible(borderLine, di->visible_);
p->geoLines_->SetLineHoverText(borderLine, di->hoverText_);
di->borderDrawItems_.emplace_back(std::move(borderLine));
if (di->ticksEnabled_)
{
auto angle = util::GeographicLib::GetAngle(
latitude1, longitude1, latitude2, longitude2);
auto angle1 = angle + units::angle::degrees<double>(90.0);
auto angle2 = angle - units::angle::degrees<double>(90.0);
auto tickCoord1 = util::GeographicLib::GetCoordinate(
coordinate2, angle1, tickRadius);
auto tickCoord2 = util::GeographicLib::GetCoordinate(
coordinate2, angle2, tickRadius);
const double& tickLat1 = tickCoord1.latitude_;
const double& tickLon1 = tickCoord1.longitude_;
const double& tickLat2 = tickCoord2.latitude_;
const double& tickLon2 = tickCoord2.longitude_;
auto tickBorderLine = p->geoLines_->AddLine();
p->geoLines_->SetLineLocation(
tickBorderLine, tickLat1, tickLon1, tickLat2, tickLon2);
p->geoLines_->SetLineModulate(tickBorderLine, kBlack);
p->geoLines_->SetLineWidth(tickBorderLine, di->width_ + 2.0f);
p->geoLines_->SetLineVisible(tickBorderLine, di->visible_);
p->geoLines_->SetLineHoverText(tickBorderLine, di->hoverText_);
tickRadius += di->tickRadiusIncrement_;
}
}
}
}
// Generate geo lines
for (auto& di : p->vectorList_)
{
auto tickRadius = di->tickRadius_;
for (std::size_t i = 0; i < di->coordinates_.size() - 1; ++i)
{
auto geoLine = p->geoLines_->AddLine();
const common::Coordinate& coordinate1 = di->coordinates_[i];
const common::Coordinate& coordinate2 = di->coordinates_[i + 1];
const double& latitude1 = coordinate1.latitude_;
const double& longitude1 = coordinate1.longitude_;
const double& latitude2 = coordinate2.latitude_;
const double& longitude2 = coordinate2.longitude_;
p->geoLines_->SetLineLocation(
geoLine, latitude1, longitude1, latitude2, longitude2);
p->geoLines_->SetLineModulate(geoLine, di->modulate_);
p->geoLines_->SetLineWidth(geoLine, di->width_);
p->geoLines_->SetLineVisible(geoLine, di->visible_);
// If the border is not enabled, this line must have hover text instead
if (!p->borderEnabled_)
{
p->geoLines_->SetLineHoverText(geoLine, di->hoverText_);
}
di->lineDrawItems_.emplace_back(std::move(geoLine));
if (di->ticksEnabled_)
{
auto angle = util::GeographicLib::GetAngle(
latitude1, longitude1, latitude2, longitude2);
auto angle1 = angle + units::angle::degrees<double>(90.0);
auto angle2 = angle - units::angle::degrees<double>(90.0);
auto tickCoord1 = util::GeographicLib::GetCoordinate(
coordinate2, angle1, tickRadius);
auto tickCoord2 = util::GeographicLib::GetCoordinate(
coordinate2, angle2, tickRadius);
const double& tickLat1 = tickCoord1.latitude_;
const double& tickLon1 = tickCoord1.longitude_;
const double& tickLat2 = tickCoord2.latitude_;
const double& tickLon2 = tickCoord2.longitude_;
auto tickGeoLine = p->geoLines_->AddLine();
p->geoLines_->SetLineLocation(
tickGeoLine, tickLat1, tickLon1, tickLat2, tickLon2);
p->geoLines_->SetLineModulate(tickGeoLine, di->modulate_);
p->geoLines_->SetLineWidth(tickGeoLine, di->width_);
p->geoLines_->SetLineVisible(tickGeoLine, di->visible_);
// If the border is not enabled, this line must have hover text
if (!p->borderEnabled_)
{
p->geoLines_->SetLineHoverText(tickGeoLine, di->hoverText_);
}
tickRadius += di->tickRadiusIncrement_;
}
}
}
// Finish geo lines
p->geoLines_->FinishLines();
}
bool LinkedVectors::RunMousePicking(
const QMapLibre::CustomLayerRenderParameters& params,
const QPointF& mouseLocalPos,
const QPointF& mouseGlobalPos,
const glm::vec2& mouseCoords,
const common::Coordinate& mouseGeoCoords,
std::shared_ptr<types::EventHandler>& eventHandler)
{
return p->geoLines_->RunMousePicking(params,
mouseLocalPos,
mouseGlobalPos,
mouseCoords,
mouseGeoCoords,
eventHandler);
}
} // namespace draw
} // namespace gl
} // namespace qt
} // namespace scwx