1 /*
2     SPDX-FileCopyrightText: 2015-2017 Pavel Mraz
3 
4     SPDX-FileCopyrightText: 2017 Jasem Mutlaq
5 
6     SPDX-License-Identifier: GPL-2.0-or-later
7 */
8 
9 #include "hipsrenderer.h"
10 
11 #include "colorscheme.h"
12 #include "kstars_debug.h"
13 #include "Options.h"
14 #include "skymap.h"
15 #include "skyqpainter.h"
16 #include "projections/projector.h"
17 
HIPSRenderer()18 HIPSRenderer::HIPSRenderer()
19 {
20     m_scanRender.reset(new ScanRender());
21     m_HEALpix.reset(new HEALPix());
22 }
23 
render(uint16_t w,uint16_t h,QImage * hipsImage,const Projector * m_proj)24 bool HIPSRenderer::render(uint16_t w, uint16_t h, QImage *hipsImage, const Projector *m_proj)
25 {
26   gridColor = KStarsData::Instance()->colorScheme()->colorNamed("HIPSGridColor").name();
27 
28   m_projector = m_proj;
29 
30   int level = 1;
31 
32   // Min FOV in Degrees
33   double minfov = 58.5;
34   double fov    = m_proj->fov() * w / (double) h;
35 
36   // Find suitable level for current FOV
37   while( level < HIPSManager::Instance()->getCurrentOrder() && fov < minfov)
38   {
39       minfov /= 2;
40       level++;
41   }
42 
43   m_renderedMap.clear();
44   m_rendered = 0;
45   m_blocks = 0;
46   m_size = 0;
47 
48   SkyPoint center = SkyMap::Instance()->getCenterPoint();
49   //center.deprecess(KStarsData::Instance()->updateNum());
50   center.catalogueCoord(KStarsData::Instance()->updateNum()->julianDay());
51 
52   double ra = center.ra0().radians();
53   double de = center.dec0().radians();
54 
55   if (std::isnan(ra) || std::isnan(de))
56   {
57       qCWarning(KSTARS) << "NAN Center, HiPS draw canceled.";
58       return false;
59   }
60 
61   bool allSky;
62 
63   if (level < 3)
64   {
65     allSky = true;
66     level = 3;
67   }
68   else
69   {
70     allSky = false;
71   }
72 
73   int centerPix = m_HEALpix->getPix(level, ra, de);
74 
75   SkyPoint cornerSkyCoords[4];
76   QPointF tileLine[2];
77   m_HEALpix->getCornerPoints(level, centerPix, cornerSkyCoords);
78 
79   //qCDebug(KSTARS) << "#" << i+1 << "RA0" << cornerSkyCoords[i].ra0().toHMSString();
80   //qCDebug(KSTARS) << "#" << i+1 << "DE0" << cornerSkyCoords[i].dec0().toHMSString();
81 
82   //qCDebug(KSTARS) << "#" << i+1 << "X" << tileLine[i].x();
83   //qCDebug(KSTARS) << "#" << i+1 << "Y" << tileLine[i].y();
84 
85   for (int i=0; i < 2; i++)
86       tileLine[i] = m_projector->toScreen(&cornerSkyCoords[i]);
87 
88   int size = std::sqrt(std::pow(tileLine[0].x()-tileLine[1].x(), 2) + std::pow(tileLine[0].y()-tileLine[1].y(), 2));
89   if (size < 0)
90       size = HIPSManager::Instance()->getCurrentTileWidth();
91 
92   bool old = m_scanRender->isBilinearInterpolationEnabled();
93   m_scanRender->setBilinearInterpolationEnabled(Options::hIPSBiLinearInterpolation() && (size >= HIPSManager::Instance()->getCurrentTileWidth() || allSky));
94 
95   renderRec(allSky, level, centerPix, hipsImage);
96 
97   m_scanRender->setBilinearInterpolationEnabled(old);
98 
99   return true;
100 }
101 
renderRec(bool allsky,int level,int pix,QImage * pDest)102 void HIPSRenderer::renderRec(bool allsky, int level, int pix, QImage *pDest)
103 {
104   if (m_renderedMap.contains(pix))
105   {
106     return;
107   }
108 
109   if (renderPix(allsky, level, pix, pDest))
110   {
111     m_renderedMap.insert(pix);
112     int dirs[8];
113     int nside = 1 << level;
114 
115     m_HEALpix->neighbours(nside, pix, dirs);
116 
117     renderRec(allsky, level, dirs[0], pDest);
118     renderRec(allsky, level, dirs[2], pDest);
119     renderRec(allsky, level, dirs[4], pDest);
120     renderRec(allsky, level, dirs[6], pDest);
121   }
122 }
123 
renderPix(bool allsky,int level,int pix,QImage * pDest)124 bool HIPSRenderer::renderPix(bool allsky, int level, int pix, QImage *pDest)
125 {
126   SkyPoint cornerSkyCoords[4];
127   QPointF cornerScreenCoords[4];
128   bool freeImage = false;
129 
130   m_HEALpix->getCornerPoints(level, pix, cornerSkyCoords);
131   bool isVisible = false;
132 
133   for (int i=0; i < 4; i++)
134   {
135       cornerScreenCoords[i] = m_projector->toScreen(&cornerSkyCoords[i]);
136       isVisible |= m_projector->checkVisibility(&cornerSkyCoords[i]);
137   }
138 
139   //if (SKPLANECheckFrustumToPolygon(trfGetFrustum(), pts, 4))
140   // Is the right way to do this?
141 
142   if (isVisible)
143   {
144     m_blocks++;
145 
146     /*for (int i = 0; i < 4; i++)
147     {
148       trfProjectPointNoCheck(&pts[i]);
149     } */
150 
151     QImage *image = HIPSManager::Instance()->getPix(allsky, level, pix, freeImage);
152 
153     if (image)
154     {
155       m_rendered++;
156 
157       #if QT_VERSION >= QT_VERSION_CHECK(5,10,0)
158       m_size += image->sizeInBytes();
159       #else
160       m_size += image->byteCount();
161       #endif
162 
163       // UV Mapping to apply image unto the destination image
164       // 4x4 = 16 points are mapped from the source image unto the destination image.
165       // Starting from each grandchild pixel, each pix polygon is mapped accordingly.
166       // For example, pixel 357 will have 4 child pixels, each of them will have 4 childs pixels and so
167       // on. Each healpix pixel appears roughly as a diamond on the sky map.
168       // The corners points for HealPIX moves from NORTH -> EAST -> SOUTH -> WEST
169       // Hence first point is 0.25, 0.25 in UV coordinate system.
170       // Depending on the selected algorithm, the mapping will either utilize nearest neighbour
171       // or bilinear interpolation.
172       QPointF uv[16][4] = {{QPointF(.25, .25), QPointF(0.25, 0), QPointF(0, .0),QPointF(0, .25)},
173                            {QPointF(.25, .5), QPointF(0.25, 0.25), QPointF(0, .25),QPointF(0, .5)},
174                            {QPointF(.5, .25), QPointF(0.5, 0), QPointF(.25, .0),QPointF(.25, .25)},
175                            {QPointF(.5, .5), QPointF(0.5, 0.25), QPointF(.25, .25),QPointF(.25, .5)},
176 
177                            {QPointF(.25, .75), QPointF(0.25, 0.5), QPointF(0, 0.5), QPointF(0, .75)},
178                            {QPointF(.25, 1), QPointF(0.25, 0.75), QPointF(0, .75),QPointF(0, 1)},
179                            {QPointF(.5, .75), QPointF(0.5, 0.5), QPointF(.25, .5),QPointF(.25, .75)},
180                            {QPointF(.5, 1), QPointF(0.5, 0.75), QPointF(.25, .75),QPointF(.25, 1)},
181 
182                            {QPointF(.75, .25), QPointF(0.75, 0), QPointF(0.5, .0),QPointF(0.5, .25)},
183                            {QPointF(.75, .5), QPointF(0.75, 0.25), QPointF(0.5, .25),QPointF(0.5, .5)},
184                            {QPointF(1, .25), QPointF(1, 0), QPointF(.75, .0),QPointF(.75, .25)},
185                            {QPointF(1, .5), QPointF(1, 0.25), QPointF(.75, .25),QPointF(.75, .5)},
186 
187                            {QPointF(.75, .75), QPointF(0.75, 0.5), QPointF(0.5, .5),QPointF(0.5, .75)},
188                            {QPointF(.75, 1), QPointF(0.75, 0.75), QPointF(0.5, .75),QPointF(0.5, 1)},
189                            {QPointF(1, .75), QPointF(1, 0.5), QPointF(.75, .5),QPointF(.75, .75)},
190                            {QPointF(1, 1), QPointF(1, 0.75), QPointF(.75, .75),QPointF(.75, 1)},
191                           };
192 
193       int childPixelID[4];
194 
195       // Find all the 4 children of the current pixel
196       m_HEALpix->getPixChilds(pix, childPixelID);
197 
198       int j = 0;
199       for (int id : childPixelID)
200       {
201         int grandChildPixelID[4];
202         // Find the children of this child (i.e. grand child)
203         // Then we have 4x4 pixels under the primary pixel
204         // The image is interpolated and rendered over these pixels
205         // coordinate to minimize any distortions due to the projection
206         // system.
207         m_HEALpix->getPixChilds(id, grandChildPixelID);
208 
209         QPointF fineScreenCoords[4];
210 
211         for (int id2 : grandChildPixelID)
212         {
213           SkyPoint fineSkyPoints[4];
214           m_HEALpix->getCornerPoints(level + 2, id2, fineSkyPoints);
215 
216           for (int i = 0; i < 4; i++)
217               fineScreenCoords[i] = m_projector->toScreen(&fineSkyPoints[i]);
218           m_scanRender->renderPolygon(3, fineScreenCoords, pDest, image, uv[j]);
219           j++;
220         }
221       }
222 
223       if (freeImage)
224       {
225         delete image;
226       }
227     }
228 
229     if (Options::hIPSShowGrid())
230     {
231       QPainter p(pDest);
232       p.setRenderHint(QPainter::Antialiasing);
233       p.setPen(gridColor);
234 
235       p.drawLine(cornerScreenCoords[0].x(), cornerScreenCoords[0].y(), cornerScreenCoords[1].x(), cornerScreenCoords[1].y());
236       p.drawLine(cornerScreenCoords[1].x(), cornerScreenCoords[1].y(), cornerScreenCoords[2].x(), cornerScreenCoords[2].y());
237       p.drawLine(cornerScreenCoords[2].x(), cornerScreenCoords[2].y(), cornerScreenCoords[3].x(), cornerScreenCoords[3].y());
238       p.drawLine(cornerScreenCoords[3].x(), cornerScreenCoords[3].y(), cornerScreenCoords[0].x(), cornerScreenCoords[0].y());
239       p.drawText((cornerScreenCoords[0].x() + cornerScreenCoords[1].x() + cornerScreenCoords[2].x() + cornerScreenCoords[3].x()) / 4,
240                          (cornerScreenCoords[0].y() + cornerScreenCoords[1].y() + cornerScreenCoords[2].y() + cornerScreenCoords[3].y()) / 4, QString::number(pix) + " / " + QString::number(level));
241     }
242 
243     return true;
244   }
245 
246   return false;
247 }
248