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