Warning, file /education/kstars/kstars/skyqpainter.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2010 Henry de Valence <hdevalence@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "skyqpainter.h" 0008 0009 #include <QPointer> 0010 0011 #include "kstarsdata.h" 0012 #include "Options.h" 0013 #include "skymap.h" 0014 #include "projections/projector.h" 0015 #include "skycomponents/flagcomponent.h" 0016 #include "skycomponents/linelist.h" 0017 #include "skycomponents/linelistlabel.h" 0018 #include "skycomponents/satellitescomponent.h" 0019 #include "skycomponents/skiphashlist.h" 0020 #include "skycomponents/skymapcomposite.h" 0021 #include "skycomponents/solarsystemcomposite.h" 0022 #include "skycomponents/earthshadowcomponent.h" 0023 #include "skyobjects/skyobject.h" 0024 #include "skyobjects/constellationsart.h" 0025 #include "skyobjects/catalogobject.h" 0026 #include "skyobjects/ksasteroid.h" 0027 #include "skyobjects/kscomet.h" 0028 #include "skyobjects/kssun.h" 0029 #include "skyobjects/satellite.h" 0030 #include "skyobjects/supernova.h" 0031 #include "skyobjects/ksearthshadow.h" 0032 #ifdef HAVE_INDI 0033 #include "skyobjects/mosaictiles.h" 0034 #endif 0035 #include "hips/hipsrenderer.h" 0036 #include "terrain/terrainrenderer.h" 0037 0038 namespace 0039 { 0040 // Convert spectral class to numerical index. 0041 // If spectral class is invalid return index for white star (A class) 0042 int harvardToIndex(char c) 0043 { 0044 switch (c) 0045 { 0046 case 'o': 0047 case 'O': 0048 return 0; 0049 case 'b': 0050 case 'B': 0051 return 1; 0052 case 'a': 0053 case 'A': 0054 return 2; 0055 case 'f': 0056 case 'F': 0057 return 3; 0058 case 'g': 0059 case 'G': 0060 return 4; 0061 case 'k': 0062 case 'K': 0063 return 5; 0064 case 'm': 0065 case 'M': 0066 return 6; 0067 // For unknown spectral class assume A class (white star) 0068 default: 0069 return 2; 0070 } 0071 } 0072 0073 // Total number of sizes of stars. 0074 const int nStarSizes = 15; 0075 // Total number of spectral classes 0076 // N.B. Must be in sync with harvardToIndex 0077 const int nSPclasses = 7; 0078 0079 // Cache for star images. 0080 // 0081 // These pixmaps are never deallocated. Not really good... 0082 QPixmap *imageCache[nSPclasses][nStarSizes] = { { nullptr } }; 0083 0084 std::unique_ptr<QPixmap> visibleSatPixmap, invisibleSatPixmap; 0085 } // namespace 0086 0087 int SkyQPainter::starColorMode = 0; 0088 QColor SkyQPainter::m_starColor = QColor(); 0089 QMap<char, QColor> SkyQPainter::ColorMap = QMap<char, QColor>(); 0090 0091 void SkyQPainter::releaseImageCache() 0092 { 0093 for (char &color : ColorMap.keys()) 0094 { 0095 QPixmap **pmap = imageCache[harvardToIndex(color)]; 0096 0097 for (int size = 1; size < nStarSizes; size++) 0098 { 0099 if (pmap[size]) 0100 delete pmap[size]; 0101 0102 pmap[size] = nullptr; 0103 } 0104 } 0105 } 0106 0107 SkyQPainter::SkyQPainter(QPaintDevice *pd) : SkyPainter(), QPainter() 0108 { 0109 Q_ASSERT(pd); 0110 m_pd = pd; 0111 m_size = QSize(pd->width(), pd->height()); 0112 m_hipsRender = new HIPSRenderer(); 0113 } 0114 0115 SkyQPainter::SkyQPainter(QPaintDevice *pd, const QSize &size) : SkyPainter(), QPainter() 0116 { 0117 Q_ASSERT(pd); 0118 m_pd = pd; 0119 m_size = size; 0120 m_hipsRender = new HIPSRenderer(); 0121 } 0122 0123 SkyQPainter::SkyQPainter(QWidget *widget, QPaintDevice *pd) : SkyPainter(), QPainter() 0124 { 0125 Q_ASSERT(widget); 0126 // Set paint device pointer to pd or to the widget if pd = 0 0127 m_pd = (pd ? pd : widget); 0128 m_size = widget->size(); 0129 m_hipsRender = new HIPSRenderer(); 0130 } 0131 0132 SkyQPainter::~SkyQPainter() 0133 { 0134 delete (m_hipsRender); 0135 } 0136 0137 void SkyQPainter::begin() 0138 { 0139 QPainter::begin(m_pd); 0140 bool aa = !SkyMap::Instance()->isSlewing() && Options::useAntialias(); 0141 setRenderHint(QPainter::Antialiasing, aa); 0142 setRenderHint(QPainter::HighQualityAntialiasing, aa); 0143 m_proj = SkyMap::Instance()->projector(); 0144 } 0145 0146 void SkyQPainter::end() 0147 { 0148 QPainter::end(); 0149 } 0150 0151 void SkyQPainter::drawSkyBackground() 0152 { 0153 //FIXME use projector 0154 fillRect(0, 0, m_size.width(), m_size.height(), 0155 KStarsData::Instance()->colorScheme()->colorNamed("SkyColor")); 0156 } 0157 0158 void SkyQPainter::setPen(const QPen &pen) 0159 { 0160 QPainter::setPen(pen); 0161 } 0162 0163 void SkyQPainter::setBrush(const QBrush &brush) 0164 { 0165 QPainter::setBrush(brush); 0166 } 0167 0168 void SkyQPainter::initStarImages() 0169 { 0170 const int starColorIntensity = Options::starColorIntensity(); 0171 0172 ColorMap.clear(); 0173 switch (Options::starColorMode()) 0174 { 0175 case 1: // Red stars. 0176 m_starColor = Qt::red; 0177 break; 0178 case 2: // Black stars. 0179 m_starColor = Qt::black; 0180 break; 0181 case 3: // White stars 0182 m_starColor = Qt::white; 0183 break; 0184 case 0: // Real color 0185 default: // And use real color for everything else 0186 m_starColor = QColor(); 0187 ColorMap.insert('O', QColor::fromRgb(0, 0, 255)); 0188 ColorMap.insert('B', QColor::fromRgb(0, 200, 255)); 0189 ColorMap.insert('A', QColor::fromRgb(0, 255, 255)); 0190 ColorMap.insert('F', QColor::fromRgb(200, 255, 100)); 0191 ColorMap.insert('G', QColor::fromRgb(255, 255, 0)); 0192 ColorMap.insert('K', QColor::fromRgb(255, 100, 0)); 0193 ColorMap.insert('M', QColor::fromRgb(255, 0, 0)); 0194 break; 0195 } 0196 if (ColorMap.isEmpty()) 0197 { 0198 ColorMap.insert('O', m_starColor); 0199 ColorMap.insert('B', m_starColor); 0200 ColorMap.insert('A', m_starColor); 0201 ColorMap.insert('F', m_starColor); 0202 ColorMap.insert('G', m_starColor); 0203 ColorMap.insert('K', m_starColor); 0204 ColorMap.insert('M', m_starColor); 0205 } 0206 0207 for (char &color : ColorMap.keys()) 0208 { 0209 QPixmap BigImage(15, 15); 0210 BigImage.fill(Qt::transparent); 0211 0212 QPainter p; 0213 p.begin(&BigImage); 0214 0215 if (Options::starColorMode() == 0) 0216 { 0217 qreal h, s, v, a; 0218 p.setRenderHint(QPainter::Antialiasing, false); 0219 QColor starColor = ColorMap[color]; 0220 starColor.getHsvF(&h, &s, &v, &a); 0221 for (int i = 0; i < 8; i++) 0222 { 0223 for (int j = 0; j < 8; j++) 0224 { 0225 qreal x = i - 7; 0226 qreal y = j - 7; 0227 qreal dist = sqrt(x * x + y * y) / 7.0; 0228 starColor.setHsvF( 0229 h, 0230 qMin(qreal(1), 0231 dist < (10 - starColorIntensity) / 10.0 ? 0 : dist), 0232 v, 0233 qMax(qreal(0), 0234 dist < (10 - starColorIntensity) / 20.0 ? 1 : 1 - dist)); 0235 p.setPen(starColor); 0236 p.drawPoint(i, j); 0237 p.drawPoint(14 - i, j); 0238 p.drawPoint(i, 14 - j); 0239 p.drawPoint(14 - i, 14 - j); 0240 } 0241 } 0242 } 0243 else 0244 { 0245 p.setRenderHint(QPainter::Antialiasing, true); 0246 p.setPen(QPen(ColorMap[color], 2.0)); 0247 p.setBrush(p.pen().color()); 0248 p.drawEllipse(QRectF(2, 2, 10, 10)); 0249 } 0250 p.end(); 0251 0252 // Cache array slice 0253 QPixmap **pmap = imageCache[harvardToIndex(color)]; 0254 0255 for (int size = 1; size < nStarSizes; size++) 0256 { 0257 if (!pmap[size]) 0258 pmap[size] = new QPixmap(); 0259 *pmap[size] = BigImage.scaled(size, size, Qt::KeepAspectRatio, 0260 Qt::SmoothTransformation); 0261 } 0262 } 0263 starColorMode = Options::starColorMode(); 0264 0265 if (!visibleSatPixmap.get()) 0266 visibleSatPixmap.reset(new QPixmap(":/icons/kstars_satellites_visible.svg")); 0267 if (!invisibleSatPixmap.get()) 0268 invisibleSatPixmap.reset(new QPixmap(":/icons/kstars_satellites_invisible.svg")); 0269 } 0270 0271 void SkyQPainter::drawSkyLine(SkyPoint *a, SkyPoint *b) 0272 { 0273 bool aVisible, bVisible; 0274 QPointF aScreen = m_proj->toScreen(a, true, &aVisible); 0275 QPointF bScreen = m_proj->toScreen(b, true, &bVisible); 0276 0277 drawLine(aScreen, bScreen); 0278 0279 //THREE CASES: 0280 // if (aVisible && bVisible) 0281 // { 0282 // //Both a,b visible, so paint the line normally: 0283 // drawLine(aScreen, bScreen); 0284 // } 0285 // else if (aVisible) 0286 // { 0287 // //a is visible but b isn't: 0288 // drawLine(aScreen, m_proj->clipLine(a, b)); 0289 // } 0290 // else if (bVisible) 0291 // { 0292 // //b is visible but a isn't: 0293 // drawLine(bScreen, m_proj->clipLine(b, a)); 0294 // } //FIXME: what if both are offscreen but the line isn't? 0295 } 0296 0297 void SkyQPainter::drawSkyPolyline(LineList *list, SkipHashList *skipList, 0298 LineListLabel *label) 0299 { 0300 SkyList *points = list->points(); 0301 bool isVisible, isVisibleLast; 0302 0303 if (points->size() == 0) 0304 return; 0305 QPointF oLast = m_proj->toScreen(points->first().get(), true, &isVisibleLast); 0306 // & with the result of checkVisibility to clip away things below horizon 0307 isVisibleLast &= m_proj->checkVisibility(points->first().get()); 0308 QPointF oThis, oThis2; 0309 0310 for (int j = 1; j < points->size(); j++) 0311 { 0312 SkyPoint *pThis = points->at(j).get(); 0313 0314 oThis2 = oThis = m_proj->toScreen(pThis, true, &isVisible); 0315 // & with the result of checkVisibility to clip away things below horizon 0316 isVisible &= m_proj->checkVisibility(pThis); 0317 bool doSkip = false; 0318 if (skipList) 0319 { 0320 doSkip = skipList->skip(j); 0321 } 0322 0323 bool pointsVisible = false; 0324 //Temporary solution to avoid random lines in Gnomonic projection and draw lines up to horizon 0325 if (SkyMap::Instance()->projector()->type() == Projector::Gnomonic) 0326 { 0327 if (isVisible && isVisibleLast) 0328 pointsVisible = true; 0329 } 0330 else 0331 { 0332 if (isVisible || isVisibleLast) 0333 pointsVisible = true; 0334 } 0335 0336 if (!doSkip) 0337 { 0338 if (pointsVisible) 0339 { 0340 drawLine(oLast, oThis); 0341 if (label) 0342 label->updateLabelCandidates(oThis.x(), oThis.y(), list, j); 0343 } 0344 } 0345 0346 oLast = oThis2; 0347 isVisibleLast = isVisible; 0348 } 0349 } 0350 0351 void SkyQPainter::drawSkyPolygon(LineList *list, bool forceClip) 0352 { 0353 bool isVisible = false, isVisibleLast; 0354 SkyList *points = list->points(); 0355 QPolygonF polygon; 0356 0357 if (forceClip == false) 0358 { 0359 for (const auto &point : *points) 0360 { 0361 polygon << m_proj->toScreen(point.get(), false, &isVisibleLast); 0362 isVisible |= isVisibleLast; 0363 } 0364 0365 // If 1+ points are visible, draw it 0366 if (polygon.size() && isVisible) 0367 drawPolygon(polygon); 0368 0369 return; 0370 } 0371 0372 SkyPoint *pLast = points->last().get(); 0373 QPointF oLast = m_proj->toScreen(pLast, true, &isVisibleLast); 0374 // & with the result of checkVisibility to clip away things below horizon 0375 isVisibleLast &= m_proj->checkVisibility(pLast); 0376 0377 for (const auto &point : *points) 0378 { 0379 SkyPoint *pThis = point.get(); 0380 QPointF oThis = m_proj->toScreen(pThis, true, &isVisible); 0381 // & with the result of checkVisibility to clip away things below horizon 0382 isVisible &= m_proj->checkVisibility(pThis); 0383 0384 if (isVisible && isVisibleLast) 0385 { 0386 polygon << oThis; 0387 } 0388 else if (isVisibleLast) 0389 { 0390 QPointF oMid = m_proj->clipLine(pLast, pThis); 0391 polygon << oMid; 0392 } 0393 else if (isVisible) 0394 { 0395 QPointF oMid = m_proj->clipLine(pThis, pLast); 0396 polygon << oMid; 0397 polygon << oThis; 0398 } 0399 0400 pLast = pThis; 0401 oLast = oThis; 0402 isVisibleLast = isVisible; 0403 } 0404 0405 if (polygon.size()) 0406 drawPolygon(polygon); 0407 } 0408 0409 bool SkyQPainter::drawPlanet(KSPlanetBase *planet) 0410 { 0411 if (!m_proj->checkVisibility(planet)) 0412 return false; 0413 0414 bool visible = false; 0415 QPointF pos = m_proj->toScreen(planet, true, &visible); 0416 if (!visible || !m_proj->onScreen(pos)) 0417 return false; 0418 0419 float fakeStarSize = (10.0 + log10(Options::zoomFactor()) - log10(MINZOOM)) * 0420 (10 - planet->mag()) / 10; 0421 if (fakeStarSize > 15.0) 0422 fakeStarSize = 15.0; 0423 0424 double size = planet->angSize() * dms::PI * Options::zoomFactor() / 10800.0; 0425 if (size < fakeStarSize && planet->name() != i18n("Sun") && 0426 planet->name() != i18n("Moon")) 0427 { 0428 // Draw them as bright stars of appropriate color instead of images 0429 char spType; 0430 //FIXME: do these need i18n? 0431 if (planet->name() == i18n("Mars")) 0432 { 0433 spType = 'K'; 0434 } 0435 else if (planet->name() == i18n("Jupiter") || planet->name() == i18n("Mercury") || 0436 planet->name() == i18n("Saturn")) 0437 { 0438 spType = 'F'; 0439 } 0440 else 0441 { 0442 spType = 'B'; 0443 } 0444 drawPointSource(pos, fakeStarSize, spType); 0445 } 0446 else 0447 { 0448 float sizemin = 1.0; 0449 if (planet->name() == i18n("Sun") || planet->name() == i18n("Moon")) 0450 sizemin = 8.0; 0451 0452 if (size < sizemin) 0453 size = sizemin; 0454 0455 if (Options::showPlanetImages() && !planet->image().isNull()) 0456 { 0457 //Because Saturn has rings, we inflate its image size by a factor 2.5 0458 if (planet->name() == "Saturn") 0459 size = int(2.5 * size); 0460 // Scale size exponentially so it is visible at large zooms 0461 else if (planet->name() == "Pluto") 0462 size = int(size * exp(1.5 * size)); 0463 0464 save(); 0465 translate(pos); 0466 rotate(m_proj->findPA(planet, pos.x(), pos.y())); 0467 drawImage(QRectF(-0.5 * size, -0.5 * size, size, size), planet->image()); 0468 restore(); 0469 } 0470 else //Otherwise, draw a simple circle. 0471 { 0472 drawEllipse(pos, size * .5, size * .5); 0473 } 0474 } 0475 return true; 0476 } 0477 0478 bool SkyQPainter::drawEarthShadow(KSEarthShadow *shadow) 0479 { 0480 if (!m_proj->checkVisibility(shadow)) 0481 return false; 0482 0483 bool visible = false; 0484 QPointF pos = m_proj->toScreen(shadow, true, &visible); 0485 0486 if (!visible) 0487 return false; 0488 0489 double umbra_size = 0490 shadow->getUmbraAngSize() * dms::PI * Options::zoomFactor() / 10800.0; 0491 double penumbra_size = 0492 shadow->getPenumbraAngSize() * dms::PI * Options::zoomFactor() / 10800.0; 0493 0494 save(); 0495 setBrush(QBrush(QColor(255, 96, 38, 128))); 0496 drawEllipse(pos, umbra_size, umbra_size); 0497 setBrush(QBrush(QColor(255, 96, 38, 90))); 0498 drawEllipse(pos, penumbra_size, penumbra_size); 0499 restore(); 0500 0501 return true; 0502 } 0503 0504 bool SkyQPainter::drawComet(KSComet *com) 0505 { 0506 if (!m_proj->checkVisibility(com)) 0507 return false; 0508 0509 double size = 0510 com->angSize() * dms::PI * Options::zoomFactor() / 10800.0 / 2; // Radius 0511 if (size < 1) 0512 size = 1; 0513 0514 bool visible = false; 0515 QPointF pos = m_proj->toScreen(com, true, &visible); 0516 0517 // Draw the coma. FIXME: Another Check?? 0518 if (visible && m_proj->onScreen(pos)) 0519 { 0520 // Draw the comet. 0521 drawEllipse(pos, size, size); 0522 0523 double comaLength = 0524 (com->getComaAngSize().arcmin() * dms::PI * Options::zoomFactor() / 10800.0); 0525 0526 // If coma is visible and long enough. 0527 if (Options::showCometComas() && comaLength > size) 0528 { 0529 KSSun *sun = 0530 KStarsData::Instance()->skyComposite()->solarSystemComposite()->sun(); 0531 0532 // Find the angle to the sun. 0533 double comaAngle = m_proj->findPA(sun, pos.x(), pos.y()); 0534 0535 const QVector<QPoint> coma = { QPoint(pos.x() - size, pos.y()), 0536 QPoint(pos.x() + size, pos.y()), 0537 QPoint(pos.x(), pos.y() + comaLength) 0538 }; 0539 0540 QPolygon comaPoly(coma); 0541 0542 comaPoly = 0543 QTransform() 0544 .translate(pos.x(), pos.y()) 0545 .rotate( 0546 comaAngle) // Already + 180 Deg, because rotated from south, not north. 0547 .translate(-pos.x(), -pos.y()) 0548 .map(comaPoly); 0549 0550 save(); 0551 0552 // Nice fade for the Coma. 0553 QLinearGradient linearGrad(pos, comaPoly.point(2)); 0554 linearGrad.setColorAt(0, QColor("white")); 0555 linearGrad.setColorAt(size / comaLength, QColor("white")); 0556 linearGrad.setColorAt(0.9, QColor("transparent")); 0557 setBrush(linearGrad); 0558 0559 // Render Coma. 0560 drawConvexPolygon(comaPoly); 0561 restore(); 0562 } 0563 0564 return true; 0565 } 0566 else 0567 { 0568 return false; 0569 } 0570 } 0571 0572 bool SkyQPainter::drawPointSource(const SkyPoint *loc, float mag, char sp) 0573 { 0574 //Check if it's even visible before doing anything 0575 if (!m_proj->checkVisibility(loc)) 0576 return false; 0577 0578 bool visible = false; 0579 QPointF pos = m_proj->toScreen(loc, true, &visible); 0580 if (visible && 0581 m_proj->onScreen( 0582 pos)) // FIXME: onScreen here should use canvas size rather than SkyMap size, especially while printing in portrait mode! 0583 { 0584 drawPointSource(pos, starWidth(mag), sp); 0585 return true; 0586 } 0587 else 0588 { 0589 return false; 0590 } 0591 } 0592 0593 void SkyQPainter::drawPointSource(const QPointF &pos, float size, char sp) 0594 { 0595 int isize = qMin(static_cast<int>(size), 14); 0596 if (!m_vectorStars || starColorMode == 0) 0597 { 0598 // Draw stars as bitmaps, either because we were asked to, or because we're painting real colors 0599 QPixmap *im = imageCache[harvardToIndex(sp)][isize]; 0600 float offset = 0.5 * im->width(); 0601 drawPixmap(QPointF(pos.x() - offset, pos.y() - offset), *im); 0602 } 0603 else 0604 { 0605 // Draw stars as vectors, for better printing / SVG export etc. 0606 if (starColorMode != 4) 0607 { 0608 setPen(m_starColor); 0609 setBrush(m_starColor); 0610 } 0611 else 0612 { 0613 // Note: This is not efficient, but we use vector stars only when plotting SVG, not when drawing the skymap, so speed is not very important. 0614 QColor c = ColorMap.value(sp, Qt::white); 0615 setPen(c); 0616 setBrush(c); 0617 } 0618 0619 // Be consistent with old raster representation 0620 if (size > 14) 0621 size = 14; 0622 if (size >= 2) 0623 drawEllipse(pos.x() - 0.5 * size, pos.y() - 0.5 * size, int(size), int(size)); 0624 else if (size >= 1) 0625 drawPoint(pos.x(), pos.y()); 0626 } 0627 } 0628 0629 bool SkyQPainter::drawConstellationArtImage(ConstellationsArt *obj) 0630 { 0631 double zoom = Options::zoomFactor(); 0632 0633 bool visible = false; 0634 obj->EquatorialToHorizontal(KStarsData::Instance()->lst(), 0635 KStarsData::Instance()->geo()->lat()); 0636 QPointF constellationmidpoint = m_proj->toScreen(obj, true, &visible); 0637 0638 if (!visible || !m_proj->onScreen(constellationmidpoint)) 0639 return false; 0640 0641 //qDebug() << Q_FUNC_INFO << "o->pa() " << obj->pa(); 0642 float positionangle = 0643 m_proj->findPA(obj, constellationmidpoint.x(), constellationmidpoint.y()); 0644 //qDebug() << Q_FUNC_INFO << " final PA " << positionangle; 0645 0646 float w = obj->getWidth() * 60 * dms::PI * zoom / 10800; 0647 float h = obj->getHeight() * 60 * dms::PI * zoom / 10800; 0648 0649 save(); 0650 0651 setRenderHint(QPainter::SmoothPixmapTransform); 0652 0653 translate(constellationmidpoint); 0654 rotate(positionangle); 0655 setOpacity(0.7); 0656 drawImage(QRectF(-0.5 * w, -0.5 * h, w, h), obj->image()); 0657 setOpacity(1); 0658 0659 setRenderHint(QPainter::SmoothPixmapTransform, false); 0660 restore(); 0661 return true; 0662 } 0663 0664 #ifdef HAVE_INDI 0665 bool SkyQPainter::drawMosaicPanel(MosaicTiles *obj) 0666 { 0667 bool visible = false; 0668 obj->EquatorialToHorizontal(KStarsData::Instance()->lst(), 0669 KStarsData::Instance()->geo()->lat()); 0670 QPointF tileMid = m_proj->toScreen(obj, true, &visible); 0671 0672 if (!visible || !m_proj->onScreen(tileMid) || !obj->isValid()) 0673 return false; 0674 0675 //double northRotation = m_proj->findNorthPA(obj, tileMid.x(), tileMid.y()) 0676 0677 // convert 0 to +180 EAST, and 0 to -180 WEST to 0 to 360 CCW 0678 auto PA = (obj->positionAngle() < 0) ? obj->positionAngle() + 360 : obj->positionAngle(); 0679 auto finalPA = m_proj->findNorthPA(obj, tileMid.x(), tileMid.y()) - PA; 0680 0681 save(); 0682 translate(tileMid.toPoint()); 0683 rotate(finalPA); 0684 obj->draw(this); 0685 restore(); 0686 0687 return true; 0688 } 0689 #endif 0690 0691 bool SkyQPainter::drawHips(bool useCache) 0692 { 0693 int w = viewport().width(); 0694 int h = viewport().height(); 0695 0696 if (useCache && m_HiPSImage) 0697 { 0698 drawImage(viewport(), *m_HiPSImage.data()); 0699 return true; 0700 } 0701 else 0702 { 0703 m_HiPSImage.reset(new QImage(w, h, QImage::Format_ARGB32_Premultiplied)); 0704 bool rendered = m_hipsRender->render(w, h, m_HiPSImage.data(), m_proj); 0705 if (rendered) 0706 drawImage(viewport(), *m_HiPSImage.data()); 0707 return rendered; 0708 } 0709 } 0710 0711 bool SkyQPainter::drawTerrain(bool useCache) 0712 { 0713 // TODO 0714 Q_UNUSED(useCache); 0715 int w = viewport().width(); 0716 int h = viewport().height(); 0717 QImage *terrainImage = new QImage(w, h, QImage::Format_ARGB32_Premultiplied); 0718 TerrainRenderer *renderer = TerrainRenderer::Instance(); 0719 bool rendered = renderer->render(w, h, terrainImage, m_proj); 0720 if (rendered) 0721 drawImage(viewport(), *terrainImage); 0722 0723 delete (terrainImage); 0724 return rendered; 0725 } 0726 0727 void SkyQPainter::drawCatalogObjectImage(const QPointF &pos, const CatalogObject &obj, 0728 float positionAngle) 0729 { 0730 const auto &image = obj.image(); 0731 0732 if (!image.first) 0733 return; 0734 0735 double zoom = Options::zoomFactor(); 0736 double w = obj.a() * dms::PI * zoom / 10800.0; 0737 double h = obj.e() * w; 0738 0739 save(); 0740 translate(pos); 0741 rotate(positionAngle); 0742 drawImage(QRectF(-0.5 * w, -0.5 * h, w, h), image.second); 0743 restore(); 0744 } 0745 0746 bool SkyQPainter::drawCatalogObject(const CatalogObject &obj) 0747 { 0748 if (!m_proj->checkVisibility(&obj)) 0749 return false; 0750 0751 bool visible = false; 0752 QPointF pos = m_proj->toScreen(&obj, true, &visible); 0753 if (!visible || !m_proj->onScreen(pos)) 0754 return false; 0755 0756 // if size is 0.0 set it to 1.0, this are normally stars (type 0 and 1) 0757 // if we use size 0.0 the star wouldn't be drawn 0758 float majorAxis = obj.a(); 0759 if (majorAxis == 0.0) 0760 { 0761 majorAxis = 1.0; 0762 } 0763 0764 float size = majorAxis * dms::PI * Options::zoomFactor() / 10800.0; 0765 0766 const auto positionAngle = 0767 m_proj->findNorthPA(&obj, pos.x(), pos.y()) - obj.pa() + 90; 0768 0769 // Draw image 0770 if (Options::showInlineImages() && Options::zoomFactor() > 5. * MINZOOM && 0771 !Options::showHIPS()) 0772 drawCatalogObjectImage(pos, obj, positionAngle); 0773 0774 // Draw Symbol 0775 drawDeepSkySymbol(pos, obj.type(), size, obj.e(), positionAngle); 0776 0777 return true; 0778 } 0779 0780 void SkyQPainter::drawDeepSkySymbol(const QPointF &pos, int type, float size, float e, 0781 float positionAngle) 0782 { 0783 float x = pos.x(); 0784 float y = pos.y(); 0785 float zoom = Options::zoomFactor(); 0786 0787 int isize = int(size); 0788 0789 float dx1 = -0.5 * size; 0790 float dx2 = 0.5 * size; 0791 float dy1 = -1.0 * e * size / 2.; 0792 float dy2 = e * size / 2.; 0793 float x1 = x + dx1; 0794 float x2 = x + dx2; 0795 float y1 = y + dy1; 0796 float y2 = y + dy2; 0797 0798 float dxa = -size / 4.; 0799 float dxb = size / 4.; 0800 float dya = -1.0 * e * size / 4.; 0801 float dyb = e * size / 4.; 0802 float xa = x + dxa; 0803 float xb = x + dxb; 0804 float ya = y + dya; 0805 float yb = y + dyb; 0806 0807 QString color; 0808 0809 float psize; 0810 0811 QBrush tempBrush; 0812 0813 std::function<void(float, float, float, float)> lambdaDrawEllipse; 0814 std::function<void(float, float, float, float)> lambdaDrawLine; 0815 std::function<void(float, float, float, float)> lambdaDrawCross; 0816 0817 if (Options::useAntialias()) 0818 { 0819 lambdaDrawEllipse = [this](float x, float y, float width, float height) 0820 { 0821 drawEllipse(QRectF(x, y, width, height)); 0822 }; 0823 lambdaDrawLine = [this](float x1, float y1, float x2, float y2) 0824 { 0825 drawLine(QLineF(x1, y1, x2, y2)); 0826 }; 0827 lambdaDrawCross = [this](float centerX, float centerY, float sizeX, float sizeY) 0828 { 0829 drawLine( 0830 QLineF(centerX - sizeX / 2., centerY, centerX + sizeX / 2., centerY)); 0831 drawLine( 0832 QLineF(centerX, centerY - sizeY / 2., centerX, centerY + sizeY / 2.)); 0833 }; 0834 } 0835 else 0836 { 0837 lambdaDrawEllipse = [this](float x, float y, float width, float height) 0838 { 0839 drawEllipse(QRect(x, y, width, height)); 0840 }; 0841 lambdaDrawLine = [this](float x1, float y1, float x2, float y2) 0842 { 0843 drawLine(QLine(x1, y1, x2, y2)); 0844 }; 0845 lambdaDrawCross = [this](float centerX, float centerY, float sizeX, float sizeY) 0846 { 0847 drawLine(QLine(centerX - sizeX / 2., centerY, centerX + sizeX / 2., centerY)); 0848 drawLine(QLine(centerX, centerY - sizeY / 2., centerX, centerY + sizeY / 2.)); 0849 }; 0850 } 0851 0852 switch ((SkyObject::TYPE)type) 0853 { 0854 case SkyObject::STAR: 0855 case SkyObject::CATALOG_STAR: //catalog star 0856 //Some NGC/IC objects are stars...changed their type to 1 (was double star) 0857 if (size < 2.) 0858 size = 2.; 0859 lambdaDrawEllipse(x - size / 2., y - size / 2., size, size); 0860 break; 0861 case SkyObject::PLANET: //Planet 0862 break; 0863 case SkyObject::OPEN_CLUSTER: //Open cluster; draw circle of points 0864 case SkyObject::ASTERISM: // Asterism 0865 { 0866 tempBrush = brush(); 0867 color = pen().color().name(); 0868 setBrush(pen().color()); 0869 psize = 2.; 0870 if (size > 50.) 0871 psize *= 2.; 0872 if (size > 100.) 0873 psize *= 2.; 0874 auto putDot = [psize, &lambdaDrawEllipse](float x, float y) 0875 { 0876 lambdaDrawEllipse(x - psize / 2., y - psize / 2., psize, psize); 0877 }; 0878 putDot(xa, y1); 0879 putDot(xb, y1); 0880 putDot(xa, y2); 0881 putDot(xb, y2); 0882 putDot(x1, ya); 0883 putDot(x1, yb); 0884 putDot(x2, ya); 0885 putDot(x2, yb); 0886 setBrush(tempBrush); 0887 break; 0888 } 0889 case SkyObject::GLOBULAR_CLUSTER: //Globular Cluster 0890 if (size < 2.) 0891 size = 2.; 0892 save(); 0893 translate(x, y); 0894 color = pen().color().name(); 0895 rotate(positionAngle); //rotate the coordinate system 0896 lambdaDrawEllipse(dx1, dy1, size, e * size); 0897 lambdaDrawCross(0, 0, size, e * size); 0898 restore(); //reset coordinate system 0899 break; 0900 0901 case SkyObject::GASEOUS_NEBULA: //Gaseous Nebula 0902 case SkyObject::DARK_NEBULA: // Dark Nebula 0903 save(); 0904 translate(x, y); 0905 rotate(positionAngle); //rotate the coordinate system 0906 color = pen().color().name(); 0907 lambdaDrawLine(dx1, dy1, dx2, dy1); 0908 lambdaDrawLine(dx2, dy1, dx2, dy2); 0909 lambdaDrawLine(dx2, dy2, dx1, dy2); 0910 lambdaDrawLine(dx1, dy2, dx1, dy1); 0911 restore(); //reset coordinate system 0912 break; 0913 case SkyObject::PLANETARY_NEBULA: //Planetary Nebula 0914 if (size < 2.) 0915 size = 2.; 0916 save(); 0917 translate(x, y); 0918 rotate(positionAngle); //rotate the coordinate system 0919 color = pen().color().name(); 0920 lambdaDrawEllipse(dx1, dy1, size, e * size); 0921 lambdaDrawLine(0., dy1, 0., dy1 - e * size / 2.); 0922 lambdaDrawLine(0., dy2, 0., dy2 + e * size / 2.); 0923 lambdaDrawLine(dx1, 0., dx1 - size / 2., 0.); 0924 lambdaDrawLine(dx2, 0., dx2 + size / 2., 0.); 0925 restore(); //reset coordinate system 0926 break; 0927 case SkyObject::SUPERNOVA_REMNANT: //Supernova remnant // FIXME: Why is SNR drawn different from a gaseous nebula? 0928 save(); 0929 translate(x, y); 0930 rotate(positionAngle); //rotate the coordinate system 0931 color = pen().color().name(); 0932 lambdaDrawLine(0., dy1, dx2, 0.); 0933 lambdaDrawLine(dx2, 0., 0., dy2); 0934 lambdaDrawLine(0., dy2, dx1, 0.); 0935 lambdaDrawLine(dx1, 0., 0., dy1); 0936 restore(); //reset coordinate system 0937 break; 0938 case SkyObject::GALAXY: //Galaxy 0939 case SkyObject::QUASAR: // Quasar 0940 color = pen().color().name(); 0941 if (size < 1. && zoom > 20 * MINZOOM) 0942 size = 3.; //force ellipse above zoomFactor 20 0943 if (size < 1. && zoom > 5 * MINZOOM) 0944 size = 1.; //force points above zoomFactor 5 0945 if (size > 2.) 0946 { 0947 save(); 0948 translate(x, y); 0949 rotate(positionAngle); //rotate the coordinate system 0950 lambdaDrawEllipse(dx1, dy1, size, e * size); 0951 restore(); //reset coordinate system 0952 } 0953 else if (size > 0.) 0954 { 0955 drawPoint(QPointF(x, y)); 0956 } 0957 break; 0958 case SkyObject::GALAXY_CLUSTER: // Galaxy cluster - draw a dashed circle 0959 { 0960 tempBrush = brush(); 0961 setBrush(QBrush()); 0962 color = pen().color().name(); 0963 save(); 0964 translate(x, y); 0965 rotate(positionAngle); //rotate the coordinate system 0966 QPen newPen = pen(); 0967 newPen.setStyle(Qt::DashLine); 0968 setPen(newPen); 0969 lambdaDrawEllipse(dx1, dy1, size, e * size); 0970 restore(); 0971 setBrush(tempBrush); 0972 break; 0973 } 0974 default 0975 : // Unknown object or something we don't know how to draw. Just draw an ellipse with a ?-mark 0976 color = pen().color().name(); 0977 if (size < 1. && zoom > 20 * MINZOOM) 0978 size = 3.; //force ellipse above zoomFactor 20 0979 if (size < 1. && zoom > 5 * MINZOOM) 0980 size = 1.; //force points above zoomFactor 5 0981 if (size > 2.) 0982 { 0983 save(); 0984 QFont f = font(); 0985 const QString qMark = " ? "; 0986 0987 #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0) 0988 double scaleFactor = 0.8 * size / fontMetrics().horizontalAdvance(qMark); 0989 #else 0990 double scaleFactor = 0.8 * size / fontMetrics().width(qMark); 0991 #endif 0992 0993 f.setPointSizeF(f.pointSizeF() * scaleFactor); 0994 setFont(f); 0995 translate(x, y); 0996 rotate(positionAngle); //rotate the coordinate system 0997 lambdaDrawEllipse(dx1, dy1, size, e * size); 0998 if (Options::useAntialias()) 0999 drawText(QRectF(dx1, dy1, size, e * size), Qt::AlignCenter, qMark); 1000 else 1001 { 1002 int idx1 = int(dx1); 1003 int idy1 = int(dy1); 1004 drawText(QRect(idx1, idy1, isize, int(e * size)), Qt::AlignCenter, 1005 qMark); 1006 } 1007 restore(); //reset coordinate system (and font?) 1008 } 1009 else if (size > 0.) 1010 { 1011 if (Options::useAntialias()) 1012 drawPoint(QPointF(x, y)); 1013 else 1014 drawPoint(QPoint(x, y)); 1015 } 1016 } 1017 } 1018 1019 void SkyQPainter::drawObservingList(const QList<SkyObject *> &obs) 1020 { 1021 foreach (SkyObject *obj, obs) 1022 { 1023 bool visible = false; 1024 QPointF o = m_proj->toScreen(obj, true, &visible); 1025 if (!visible || !m_proj->onScreen(o)) 1026 continue; 1027 1028 float size = 20.; 1029 float x1 = o.x() - 0.5 * size; 1030 float y1 = o.y() - 0.5 * size; 1031 drawArc(QRectF(x1, y1, size, size), -60 * 16, 120 * 16); 1032 drawArc(QRectF(x1, y1, size, size), 120 * 16, 120 * 16); 1033 } 1034 } 1035 1036 void SkyQPainter::drawFlags() 1037 { 1038 KStarsData *data = KStarsData::Instance(); 1039 std::shared_ptr<SkyPoint> point; 1040 QImage image; 1041 bool visible = false; 1042 QPointF pos; 1043 1044 for (int i = 0; i < data->skyComposite()->flags()->size(); i++) 1045 { 1046 point = data->skyComposite()->flags()->pointList().at(i); 1047 image = data->skyComposite()->flags()->image(i); 1048 1049 // Set Horizontal coordinates 1050 point->EquatorialToHorizontal(data->lst(), data->geo()->lat()); 1051 1052 // Get flag position on screen 1053 pos = m_proj->toScreen(point.get(), true, &visible); 1054 1055 // Return if flag is not visible 1056 if (!visible || !m_proj->onScreen(pos)) 1057 continue; 1058 1059 // Draw flag image 1060 drawImage(pos.x() - 0.5 * image.width(), pos.y() - 0.5 * image.height(), image); 1061 1062 // Draw flag label 1063 setPen(data->skyComposite()->flags()->labelColor(i)); 1064 setFont(QFont("Helvetica", 10, QFont::Bold)); 1065 drawText(pos.x() + 10, pos.y() - 10, data->skyComposite()->flags()->label(i)); 1066 } 1067 } 1068 1069 void SkyQPainter::drawHorizon(bool filled, SkyPoint *labelPoint, bool *drawLabel) 1070 { 1071 QVector<Eigen::Vector2f> ground = m_proj->groundPoly(labelPoint, drawLabel); 1072 if (ground.size()) 1073 { 1074 QPolygonF groundPoly(ground.size()); 1075 for (int i = 0; i < ground.size(); ++i) 1076 groundPoly[i] = KSUtils::vecToPoint(ground[i]); 1077 if (filled) 1078 drawPolygon(groundPoly); 1079 else 1080 { 1081 groundPoly.append(groundPoly.first()); 1082 drawPolyline(groundPoly); 1083 } 1084 } 1085 } 1086 1087 bool SkyQPainter::drawSatellite(Satellite *sat) 1088 { 1089 if (!m_proj->checkVisibility(sat)) 1090 return false; 1091 1092 QPointF pos; 1093 bool visible = false; 1094 1095 //sat->HorizontalToEquatorial( data->lst(), data->geo()->lat() ); 1096 1097 pos = m_proj->toScreen(sat, true, &visible); 1098 1099 if (!visible || !m_proj->onScreen(pos)) 1100 return false; 1101 1102 if (Options::drawSatellitesLikeStars()) 1103 { 1104 drawPointSource(pos, 3.5, 'B'); 1105 } 1106 else 1107 { 1108 if (sat->isVisible()) 1109 drawPixmap(QPoint(pos.x() - 15, pos.y() - 11), *visibleSatPixmap); 1110 else 1111 drawPixmap(QPoint(pos.x() - 15, pos.y() - 11), *invisibleSatPixmap); 1112 1113 //drawPixmap(pos, *genericSatPixmap); 1114 /*drawLine( QPoint( pos.x() - 0.5, pos.y() - 0.5 ), QPoint( pos.x() + 0.5, pos.y() - 0.5 ) ); 1115 drawLine( QPoint( pos.x() + 0.5, pos.y() - 0.5 ), QPoint( pos.x() + 0.5, pos.y() + 0.5 ) ); 1116 drawLine( QPoint( pos.x() + 0.5, pos.y() + 0.5 ), QPoint( pos.x() - 0.5, pos.y() + 0.5 ) ); 1117 drawLine( QPoint( pos.x() - 0.5, pos.y() + 0.5 ), QPoint( pos.x() - 0.5, pos.y() - 0.5 ) );*/ 1118 } 1119 1120 return true; 1121 1122 //if ( Options::showSatellitesLabels() ) 1123 //data->skyComposite()->satellites()->drawLabel( sat, pos ); 1124 } 1125 1126 bool SkyQPainter::drawSupernova(Supernova *sup) 1127 { 1128 KStarsData *data = KStarsData::Instance(); 1129 if (!m_proj->checkVisibility(sup)) 1130 { 1131 return false; 1132 } 1133 1134 bool visible = false; 1135 QPointF pos = m_proj->toScreen(sup, true, &visible); 1136 //qDebug()<<"sup->ra() = "<<(sup->ra()).toHMSString()<<"sup->dec() = "<<sup->dec().toDMSString(); 1137 //qDebug()<<"pos = "<<pos<<"m_proj->onScreen(pos) = "<<m_proj->onScreen(pos); 1138 if (!visible || !m_proj->onScreen(pos)) 1139 return false; 1140 1141 setPen(data->colorScheme()->colorNamed("SupernovaColor")); 1142 //qDebug()<<"Here"; 1143 drawLine(QPoint(pos.x() - 2.0, pos.y()), QPoint(pos.x() + 2.0, pos.y())); 1144 drawLine(QPoint(pos.x(), pos.y() - 2.0), QPoint(pos.x(), pos.y() + 2.0)); 1145 return true; 1146 }