File indexing completed on 2024-04-21 03:43:59
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 }