File indexing completed on 2024-04-14 14:10:44

0001 /*
0002     SPDX-FileCopyrightText: 2015-2017 Pavel Mraz
0003 
0004     SPDX-FileCopyrightText: 2017 Jasem Mutlaq
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "hipsrenderer.h"
0010 
0011 #include "colorscheme.h"
0012 #include "kstars_debug.h"
0013 #include "Options.h"
0014 #include "skymap.h"
0015 #include "skyqpainter.h"
0016 #include "projections/projector.h"
0017 
0018 HIPSRenderer::HIPSRenderer()
0019 {
0020     m_scanRender.reset(new ScanRender());
0021     m_HEALpix.reset(new HEALPix());
0022 }
0023 
0024 bool HIPSRenderer::render(uint16_t w, uint16_t h, QImage *hipsImage, const Projector *m_proj)
0025 {
0026     gridColor = KStarsData::Instance()->colorScheme()->colorNamed("HIPSGridColor").name();
0027 
0028     m_projector = m_proj;
0029 
0030     int level = 1;
0031 
0032     // Min FOV in Degrees
0033     double minfov = 58.5;
0034     double fov    = m_proj->fov() * w / h;
0035 
0036     // Find suitable level for current FOV
0037     while( level < HIPSManager::Instance()->getCurrentOrder() && fov < minfov)
0038     {
0039         minfov /= 2;
0040         level++;
0041     }
0042 
0043     // We need this in case of offline storage missing a few levels.
0044     level = HIPSManager::Instance()->getUsableLevel(level);
0045 
0046     m_renderedMap.clear();
0047     m_rendered = 0;
0048     m_blocks = 0;
0049     m_size = 0;
0050 
0051     SkyPoint center = SkyMap::Instance()->getCenterPoint();
0052     //center.deprecess(KStarsData::Instance()->updateNum());
0053     center.catalogueCoord(KStarsData::Instance()->updateNum()->julianDay());
0054 
0055     double ra = center.ra0().radians();
0056     double de = center.dec0().radians();
0057 
0058     if (std::isnan(ra) || std::isnan(de))
0059     {
0060         qCWarning(KSTARS) << "NAN Center, HiPS draw canceled.";
0061         return false;
0062     }
0063 
0064     bool allSky;
0065 
0066     if (level < 3)
0067     {
0068         allSky = true;
0069         level = 3;
0070     }
0071     else
0072     {
0073         allSky = false;
0074     }
0075 
0076     int centerPix = m_HEALpix->getPix(level, ra, de);
0077 
0078     SkyPoint cornerSkyCoords[4];
0079     QPointF tileLine[2];
0080     m_HEALpix->getCornerPoints(level, centerPix, cornerSkyCoords);
0081 
0082     //qCDebug(KSTARS) << "#" << i+1 << "RA0" << cornerSkyCoords[i].ra0().toHMSString();
0083     //qCDebug(KSTARS) << "#" << i+1 << "DE0" << cornerSkyCoords[i].dec0().toHMSString();
0084 
0085     //qCDebug(KSTARS) << "#" << i+1 << "X" << tileLine[i].x();
0086     //qCDebug(KSTARS) << "#" << i+1 << "Y" << tileLine[i].y();
0087 
0088     for (int i = 0; i < 2; i++)
0089         tileLine[i] = m_projector->toScreen(&cornerSkyCoords[i]);
0090 
0091     int size = std::sqrt(std::pow(tileLine[0].x() - tileLine[1].x(), 2) + std::pow(tileLine[0].y() - tileLine[1].y(), 2));
0092     if (size < 0)
0093         size = HIPSManager::Instance()->getCurrentTileWidth();
0094 
0095     bool old = m_scanRender->isBilinearInterpolationEnabled();
0096     m_scanRender->setBilinearInterpolationEnabled(Options::hIPSBiLinearInterpolation()
0097             && (size >= HIPSManager::Instance()->getCurrentTileWidth() || allSky));
0098 
0099     renderRec(allSky, level, centerPix, hipsImage);
0100 
0101     m_scanRender->setBilinearInterpolationEnabled(old);
0102 
0103     return true;
0104 }
0105 
0106 void HIPSRenderer::renderRec(bool allsky, int level, int pix, QImage *pDest)
0107 {
0108     if (m_renderedMap.contains(pix))
0109     {
0110         return;
0111     }
0112 
0113     if (renderPix(allsky, level, pix, pDest))
0114     {
0115         m_renderedMap.insert(pix);
0116         int dirs[8];
0117         int nside = 1 << level;
0118 
0119         m_HEALpix->neighbours(nside, pix, dirs);
0120 
0121         renderRec(allsky, level, dirs[0], pDest);
0122         renderRec(allsky, level, dirs[2], pDest);
0123         renderRec(allsky, level, dirs[4], pDest);
0124         renderRec(allsky, level, dirs[6], pDest);
0125     }
0126 }
0127 
0128 bool HIPSRenderer::renderPix(bool allsky, int level, int pix, QImage *pDest)
0129 {
0130     SkyPoint cornerSkyCoords[4];
0131     QPointF cornerScreenCoords[4];
0132     bool freeImage = false;
0133 
0134     m_HEALpix->getCornerPoints(level, pix, cornerSkyCoords);
0135     bool isVisible = false;
0136 
0137     for (int i = 0; i < 4; i++)
0138     {
0139         cornerScreenCoords[i] = m_projector->toScreen(&cornerSkyCoords[i]);
0140         isVisible |= m_projector->checkVisibility(&cornerSkyCoords[i]);
0141     }
0142 
0143     //if (SKPLANECheckFrustumToPolygon(trfGetFrustum(), pts, 4))
0144     // Is the right way to do this?
0145 
0146     if (isVisible)
0147     {
0148         m_blocks++;
0149 
0150         /*for (int i = 0; i < 4; i++)
0151         {
0152           trfProjectPointNoCheck(&pts[i]);
0153         } */
0154 
0155         QImage *image = HIPSManager::Instance()->getPix(allsky, level, pix, freeImage);
0156 
0157         if (image)
0158         {
0159             m_rendered++;
0160 
0161             m_size += image->sizeInBytes();
0162 
0163             // UV Mapping to apply image unto the destination image
0164             // 4x4 = 16 points are mapped from the source image unto the destination image.
0165             // Starting from each grandchild pixel, each pix polygon is mapped accordingly.
0166             // For example, pixel 357 will have 4 child pixels, each of them will have 4 childs pixels and so
0167             // on. Each healpix pixel appears roughly as a diamond on the sky map.
0168             // The corners points for HealPIX moves from NORTH -> EAST -> SOUTH -> WEST
0169             // Hence first point is 0.25, 0.25 in UV coordinate system.
0170             // Depending on the selected algorithm, the mapping will either utilize nearest neighbour
0171             // or bilinear interpolation.
0172             QPointF uv[16][4] = {{QPointF(.25, .25), QPointF(0.25, 0), QPointF(0, .0), QPointF(0, .25)},
0173                 {QPointF(.25, .5), QPointF(0.25, 0.25), QPointF(0, .25), QPointF(0, .5)},
0174                 {QPointF(.5, .25), QPointF(0.5, 0), QPointF(.25, .0), QPointF(.25, .25)},
0175                 {QPointF(.5, .5), QPointF(0.5, 0.25), QPointF(.25, .25), QPointF(.25, .5)},
0176 
0177                 {QPointF(.25, .75), QPointF(0.25, 0.5), QPointF(0, 0.5), QPointF(0, .75)},
0178                 {QPointF(.25, 1), QPointF(0.25, 0.75), QPointF(0, .75), QPointF(0, 1)},
0179                 {QPointF(.5, .75), QPointF(0.5, 0.5), QPointF(.25, .5), QPointF(.25, .75)},
0180                 {QPointF(.5, 1), QPointF(0.5, 0.75), QPointF(.25, .75), QPointF(.25, 1)},
0181 
0182                 {QPointF(.75, .25), QPointF(0.75, 0), QPointF(0.5, .0), QPointF(0.5, .25)},
0183                 {QPointF(.75, .5), QPointF(0.75, 0.25), QPointF(0.5, .25), QPointF(0.5, .5)},
0184                 {QPointF(1, .25), QPointF(1, 0), QPointF(.75, .0), QPointF(.75, .25)},
0185                 {QPointF(1, .5), QPointF(1, 0.25), QPointF(.75, .25), QPointF(.75, .5)},
0186 
0187                 {QPointF(.75, .75), QPointF(0.75, 0.5), QPointF(0.5, .5), QPointF(0.5, .75)},
0188                 {QPointF(.75, 1), QPointF(0.75, 0.75), QPointF(0.5, .75), QPointF(0.5, 1)},
0189                 {QPointF(1, .75), QPointF(1, 0.5), QPointF(.75, .5), QPointF(.75, .75)},
0190                 {QPointF(1, 1), QPointF(1, 0.75), QPointF(.75, .75), QPointF(.75, 1)},
0191             };
0192 
0193             int childPixelID[4];
0194 
0195             // Find all the 4 children of the current pixel
0196             m_HEALpix->getPixChilds(pix, childPixelID);
0197 
0198             int j = 0;
0199             for (int id : childPixelID)
0200             {
0201                 int grandChildPixelID[4];
0202                 // Find the children of this child (i.e. grand child)
0203                 // Then we have 4x4 pixels under the primary pixel
0204                 // The image is interpolated and rendered over these pixels
0205                 // coordinate to minimize any distortions due to the projection
0206                 // system.
0207                 m_HEALpix->getPixChilds(id, grandChildPixelID);
0208 
0209                 QPointF fineScreenCoords[4];
0210 
0211                 for (int id2 : grandChildPixelID)
0212                 {
0213                     SkyPoint fineSkyPoints[4];
0214                     m_HEALpix->getCornerPoints(level + 2, id2, fineSkyPoints);
0215 
0216                     for (int i = 0; i < 4; i++)
0217                         fineScreenCoords[i] = m_projector->toScreen(&fineSkyPoints[i]);
0218                     m_scanRender->renderPolygon(3, fineScreenCoords, pDest, image, uv[j]);
0219                     j++;
0220                 }
0221             }
0222 
0223             if (freeImage)
0224             {
0225                 delete image;
0226             }
0227         }
0228 
0229         if (Options::hIPSShowGrid())
0230         {
0231             QPainter p(pDest);
0232             p.setRenderHint(QPainter::Antialiasing);
0233             p.setPen(gridColor);
0234 
0235             p.drawLine(cornerScreenCoords[0].x(), cornerScreenCoords[0].y(), cornerScreenCoords[1].x(), cornerScreenCoords[1].y());
0236             p.drawLine(cornerScreenCoords[1].x(), cornerScreenCoords[1].y(), cornerScreenCoords[2].x(), cornerScreenCoords[2].y());
0237             p.drawLine(cornerScreenCoords[2].x(), cornerScreenCoords[2].y(), cornerScreenCoords[3].x(), cornerScreenCoords[3].y());
0238             p.drawLine(cornerScreenCoords[3].x(), cornerScreenCoords[3].y(), cornerScreenCoords[0].x(), cornerScreenCoords[0].y());
0239             p.drawText((cornerScreenCoords[0].x() + cornerScreenCoords[1].x() + cornerScreenCoords[2].x() + cornerScreenCoords[3].x()) /
0240                        4,
0241                        (cornerScreenCoords[0].y() + cornerScreenCoords[1].y() + cornerScreenCoords[2].y() + cornerScreenCoords[3].y()) / 4,
0242                        QString::number(pix) + " / " + QString::number(level));
0243         }
0244 
0245         return true;
0246     }
0247 
0248     return false;
0249 }