From c01f2e328aac229596ecb33a904bdfb311d2f2df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Du=C5=A1an=20Poizl?= Date: Mon, 26 May 2025 15:50:37 +0200 Subject: [PATCH] Add sky grid painting --- imageinfodata.cpp | 14 ++++- imageinfodata.h | 2 + imagewidget.cpp | 138 ++++++++++++++++++++++++++++++++++++++++++++++ imagewidget.h | 6 ++ 4 files changed, 159 insertions(+), 1 deletion(-) diff --git a/imageinfodata.cpp b/imageinfodata.cpp index 68a7654..5a43d6c 100644 --- a/imageinfodata.cpp +++ b/imageinfodata.cpp @@ -266,6 +266,16 @@ QString SkyPoint::toString() const return QString("RA: %1 DEC: %2° %3' %4\"").arg(t.toString("HH'h' mm'm' ss's'")).arg(deg, 2, 'f', 0, '0').arg(min, 2, 'f', 0, '0').arg(sec, 2, 'f', 0, '0'); } +QString SkyPoint::RAString() const +{ + return toHMS(ra / 15); +} + +QString SkyPoint::DECString() const +{ + return toDMS(dec); +} + double SkyPoint::fromHMS(const QString &hms) { double deg = fromDMS(hms); @@ -308,11 +318,13 @@ QString SkyPoint::toHMS(double decHour) QString SkyPoint::toDMS(double deg) { + int sign = deg < 0.0 ? -1 : 1; + deg *= sign; double d,m,s,md; md = std::modf(deg, &d) * 60.0; s = std::modf(md, &m) * 60.0; - return QString("%1˚ %2' %3\"").arg((int)d, 2, 10, QChar('0')).arg((int)m, 2, 10, QChar('0')).arg((int)s, 2, 10, QChar('0')); + return QString("%1˚ %2' %3\"").arg((int)d * sign, 2, 10, QChar('0')).arg((int)m, 2, 10, QChar('0')).arg((int)s, 2, 10, QChar('0')); } SkyPointScale ImageInfoData::getCenterRaDec() const diff --git a/imageinfodata.h b/imageinfodata.h index 77a1759..0d46db4 100644 --- a/imageinfodata.h +++ b/imageinfodata.h @@ -36,6 +36,8 @@ public: double RAHour() const { return ra / 15.0; } double DEC() const { return dec; } QString toString() const; + QString RAString() const; + QString DECString() const; static double fromHMS(const QString &hms); static double fromDMS(const QString &dms); static QString toHMS(double decHour); diff --git a/imagewidget.cpp b/imagewidget.cpp index 99e48c6..83bf95a 100644 --- a/imagewidget.cpp +++ b/imagewidget.cpp @@ -26,6 +26,8 @@ struct RawImageType QOpenGLTexture::PixelType dataType; }; +void prepareGrid(WCSDataT *wcs, uint32_t w, uint32_t h, QPainterPath &grid, QPainterPath &text); + RawImageType getRawImageType(const RawImage *img) { RawImageType type; @@ -180,6 +182,10 @@ void ImageWidgetGL::setImage(std::shared_ptr image, int index) void ImageWidgetGL::setWCS(std::shared_ptr wcs) { m_wcs = wcs; + m_grid.clear(); + m_text.clear(); + if(m_drawGrid) + prepareGrid(m_wcs.get(), m_imgWidth, m_imgHeight, m_grid, m_text); } void ImageWidgetGL::zoom(int zoom, const QPointF &mousePos) @@ -496,6 +502,119 @@ void swPaint(std::shared_ptr &rawImage, float dx, float dy, float scal painter.drawImage(0, 0, img); } +void prepareGrid(WCSDataT *wcs, uint32_t w, uint32_t h, QPainterPath &grid, QPainterPath &text) +{ + if(!wcs)return; + + double minRa, maxRa, minDec, maxDec, crVal1, crVal2; + wcs->calculateBounds(minRa, maxRa, minDec, maxDec, crVal1, crVal2); + + double raStep = 15; + double decStep = 15; + double raRange = maxRa - minRa; + double decRange = maxDec - minDec; + const QVector raSteps = {15, 5, 2.5, 1.25, 0.25, 20/240.0, 10/240.0, 5/240.0, 1/240.0}; + const QVector decSteps = {20, 10, 5, 2, 1, 20/60.0, 10/60.0, 5/60.0, 2/60.0, 1/60.0, 20/3600.0, 10/3600.0, 5/3600.0, 2/3600.0, 1/3600.0}; + + for(double ra : raSteps) + { + if(ra * 5 <= raRange) + { + raStep = ra; + break; + } + } + + for(double dec : decSteps) + { + if(dec * 5 <= decRange) + { + decStep = dec; + break; + } + } + + minRa -= std::fmod(minRa, raStep); + minDec -= std::fmod(minDec, decStep); + if(minRa < 0)minRa -= raStep; + if(minDec < 0)minDec -= decStep; + + QRectF clip(0, 0, w, h); + + maxRa += raStep; + maxDec += decStep; + for(double ra = minRa; ra <= maxRa; ra += raStep) + { + QPointF p; + wcs->worldToPixel(SkyPoint(ra, minDec), p); + grid.moveTo(p); + for(double dec = minDec + decStep * 0.02; dec <= maxDec; dec += decStep * 0.02) + { + wcs->worldToPixel(SkyPoint(ra, dec), p); + grid.lineTo(p); + } + } + + for(double dec = minDec; dec <= maxDec; dec += decStep) + { + QPointF p; + wcs->worldToPixel(SkyPoint(minRa, dec), p); + grid.moveTo(p); + for(double ra = minRa + raStep * 0.02; ra <= maxRa; ra += raStep * 0.02) + { + wcs->worldToPixel(SkyPoint(ra, dec), p); + grid.lineTo(p); + } + } + + SkyPoint sp1, sp2,orig; + wcs->pixelToWorld(QPointF(-1, -1), orig); + sp1 = orig; + QFont font("Sans", 12); + for(uint32_t x = 0; x < w; x++) + { + QPointF p(x, 0); + if(!wcs->pixelToWorld(p, sp2)) + break; + + if(static_cast(sp1.RA() / raStep) != static_cast(sp2.RA() / raStep)) + text.addText(p + QPointF(0, 14), font, std::abs(sp1.RA()) > std::abs(sp2.RA()) ? sp1.RAString() : sp2.RAString()); + + if(static_cast(sp1.DEC() / decStep) != static_cast(sp2.DEC() / decStep)) + text.addText(p + QPointF(0, 14), font, std::abs(sp1.DEC()) > std::abs(sp2.DEC()) ? sp1.DECString() : sp2.DECString()); + + sp1 = sp2; + } + + sp1 = orig; + for(uint32_t y = 0; y < h; y++) + { + QPointF p(0, y); + if(!wcs->pixelToWorld(p, sp2)) + break; + + if(static_cast(sp1.RA() / raStep) != static_cast(sp2.RA() / raStep)) + text.addText(p + QPointF(2, 0), font, std::abs(sp1.RA()) > std::abs(sp2.RA()) ? sp1.RAString() : sp2.RAString()); + + if(static_cast(sp1.DEC() / decStep) != static_cast(sp2.DEC() / decStep)) + text.addText(p + QPointF(2, 0), font, std::abs(sp1.DEC()) > std::abs(sp2.DEC()) ? sp1.DECString() : sp2.DECString()); + + sp1 = sp2; + } +} + +void ImageWidgetGL::drawGrid(bool enable) +{ + if(m_grid.elementCount() == 0) + prepareGrid(m_wcs.get(), m_imgWidth, m_imgHeight, m_grid, m_text); + + if(enable != m_drawGrid) + { + m_drawGrid = enable; + update(); + } +} + void ImageWidgetGL::paintGL() { float dx = m_dx; @@ -613,6 +732,25 @@ void ImageWidgetGL::paintGL() m_program->setUniformValue("colormapIdx", m_colormapIdx); f->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); m_vao->release(); + + if(m_drawGrid) + { + QPainter painter(this); + painter.setRenderHint(QPainter::Antialiasing); + painter.setRenderHint(QPainter::TextAntialiasing); + + painter.setPen(QPen(Qt::yellow, 1.0 / m_scale)); + QTransform tran; + tran.translate(-std::floor(dx), -std::floor(dy)); + tran.scale(m_scale, m_scale); + painter.setTransform(tran); + painter.setClipRect(0, 0, m_imgWidth, m_imgHeight); + painter.drawPath(m_grid); + painter.setPen(Qt::NoPen); + painter.setBrush(Qt::yellow); + painter.drawPath(m_text); + } + } } diff --git a/imagewidget.h b/imagewidget.h index 12e8fc7..c33bf25 100644 --- a/imagewidget.h +++ b/imagewidget.h @@ -8,6 +8,7 @@ #include #include #include +#include #include "database.h" #include "rawimage.h" #include "imageinfodata.h" @@ -37,6 +38,7 @@ public: virtual QImage renderToImage() = 0; virtual void thumbnailLoaded(const Image *image) = 0; virtual void showThumbnail(bool enable) = 0; + virtual void drawGrid(bool enable) = 0; static QImage loadColormap(); }; @@ -70,6 +72,8 @@ class ImageWidgetGL : public QOpenGLWidget, public ImageWidget GLuint m_debayerTex = 0; std::shared_ptr m_rawImage; std::shared_ptr m_wcs; + QPainterPath m_grid; + QPainterPath m_text; int m_width, m_height; int m_imgWidth = -1, m_imgHeight = -1; int m_currentImg = 0; @@ -87,6 +91,7 @@ class ImageWidgetGL : public QOpenGLWidget, public ImageWidget bool m_selecting = false; bool m_sizesDirty = false; bool m_srgb = false; + bool m_drawGrid = false; int m_thumbnailCount = 0; int m_maxTextureSize = 0; int m_maxArrayLayers = 0; @@ -116,6 +121,7 @@ public: QImage renderToImage() override; void thumbnailLoaded(const Image *image) override; void showThumbnail(bool enable) override; + void drawGrid(bool enable) override; protected: void paintGL() override; void resizeGL(int w, int h) override;