Kstars

skyqpainter.cpp
1/*
2 SPDX-FileCopyrightText: 2010 Henry de Valence <hdevalence@gmail.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "skyqpainter.h"
8
9#include <QPointer>
10
11#include "kstarsdata.h"
12#include "kstars.h"
13#include "Options.h"
14#include "skymap.h"
15#include "projections/projector.h"
16#include "skycomponents/flagcomponent.h"
17#include "skycomponents/linelist.h"
18#include "skycomponents/linelistlabel.h"
19#include "skycomponents/satellitescomponent.h"
20#include "skycomponents/skiphashlist.h"
21#include "skycomponents/skymapcomposite.h"
22#include "skycomponents/solarsystemcomposite.h"
23#include "skycomponents/earthshadowcomponent.h"
24#include "skycomponents/imageoverlaycomponent.h"
25#include "skyobjects/skyobject.h"
26#include "skyobjects/constellationsart.h"
27#include "skyobjects/catalogobject.h"
28#include "skyobjects/ksasteroid.h"
29#include "skyobjects/kscomet.h"
30#include "skyobjects/kssun.h"
31#include "skyobjects/satellite.h"
32#include "skyobjects/supernova.h"
33#include "skyobjects/ksearthshadow.h"
34#ifdef HAVE_INDI
35#include "skyobjects/mosaictiles.h"
36#endif
37#include "hips/hipsrenderer.h"
38#include "terrain/terrainrenderer.h"
39#include <QElapsedTimer>
40#include "auxiliary/rectangleoverlap.h"
41
42namespace
43{
44// Convert spectral class to numerical index.
45// If spectral class is invalid return index for white star (A class)
46int harvardToIndex(char c)
47{
48 switch (c)
49 {
50 case 'o':
51 case 'O':
52 return 0;
53 case 'b':
54 case 'B':
55 return 1;
56 case 'a':
57 case 'A':
58 return 2;
59 case 'f':
60 case 'F':
61 return 3;
62 case 'g':
63 case 'G':
64 return 4;
65 case 'k':
66 case 'K':
67 return 5;
68 case 'm':
69 case 'M':
70 return 6;
71 // For unknown spectral class assume A class (white star)
72 default:
73 return 2;
74 }
75}
76
77// Total number of sizes of stars.
78const int nStarSizes = 15;
79// Total number of spectral classes
80// N.B. Must be in sync with harvardToIndex
81const int nSPclasses = 7;
82
83// Cache for star images.
84//
85// These pixmaps are never deallocated. Not really good...
86QPixmap *imageCache[nSPclasses][nStarSizes] = { { nullptr } };
87
88std::unique_ptr<QPixmap> visibleSatPixmap, invisibleSatPixmap;
89} // namespace
90
91int SkyQPainter::starColorMode = 0;
92QColor SkyQPainter::m_starColor = QColor();
93QMap<char, QColor> SkyQPainter::ColorMap = QMap<char, QColor>();
94
96{
97 for (char &color : ColorMap.keys())
98 {
99 QPixmap **pmap = imageCache[harvardToIndex(color)];
100
101 for (int size = 1; size < nStarSizes; size++)
102 {
103 if (pmap[size])
104 delete pmap[size];
105
106 pmap[size] = nullptr;
107 }
108 }
109}
110
112{
113 Q_ASSERT(pd);
114 m_pd = pd;
115 m_size = QSize(pd->width(), pd->height());
116 m_hipsRender = new HIPSRenderer();
117}
118
120{
121 Q_ASSERT(pd);
122 m_pd = pd;
123 m_size = size;
124 m_hipsRender = new HIPSRenderer();
125}
126
128{
129 Q_ASSERT(widget);
130 // Set paint device pointer to pd or to the widget if pd = 0
131 m_pd = (pd ? pd : widget);
132 m_size = widget->size();
133 m_hipsRender = new HIPSRenderer();
134}
135
136SkyQPainter::~SkyQPainter()
137{
138 delete (m_hipsRender);
139}
140
142{
143 QPainter::begin(m_pd);
144 bool aa = !SkyMap::Instance()->isSlewing() && Options::useAntialias();
146 setRenderHint(QPainter::HighQualityAntialiasing, aa);
147 m_proj = SkyMap::Instance()->projector();
148}
149
151{
153}
154
156{
157 //FIXME use projector
158 fillRect(0, 0, m_size.width(), m_size.height(),
159 KStarsData::Instance()->colorScheme()->colorNamed("SkyColor"));
160}
161
162void SkyQPainter::setPen(const QPen &pen)
163{
165}
166
168{
170}
171
173{
174 const int starColorIntensity = Options::starColorIntensity();
175
176 ColorMap.clear();
177 switch (Options::starColorMode())
178 {
179 case 1: // Red stars.
180 m_starColor = Qt::red;
181 break;
182 case 2: // Black stars.
183 m_starColor = Qt::black;
184 break;
185 case 3: // White stars
186 m_starColor = Qt::white;
187 break;
188 case 0: // Real color
189 default: // And use real color for everything else
190 m_starColor = QColor();
191 ColorMap.insert('O', QColor::fromRgb(0, 0, 255));
192 ColorMap.insert('B', QColor::fromRgb(0, 200, 255));
193 ColorMap.insert('A', QColor::fromRgb(0, 255, 255));
194 ColorMap.insert('F', QColor::fromRgb(200, 255, 100));
195 ColorMap.insert('G', QColor::fromRgb(255, 255, 0));
196 ColorMap.insert('K', QColor::fromRgb(255, 100, 0));
197 ColorMap.insert('M', QColor::fromRgb(255, 0, 0));
198 break;
199 }
200 if (ColorMap.isEmpty())
201 {
202 ColorMap.insert('O', m_starColor);
203 ColorMap.insert('B', m_starColor);
204 ColorMap.insert('A', m_starColor);
205 ColorMap.insert('F', m_starColor);
206 ColorMap.insert('G', m_starColor);
207 ColorMap.insert('K', m_starColor);
208 ColorMap.insert('M', m_starColor);
209 }
210
211 for (char &color : ColorMap.keys())
212 {
213 QPixmap BigImage(15, 15);
215
216 QPainter p;
217 p.begin(&BigImage);
218
219 if (Options::starColorMode() == 0)
220 {
221 qreal h, s, v, a;
223 QColor starColor = ColorMap[color];
224 starColor.getHsvF(&h, &s, &v, &a);
225 for (int i = 0; i < 8; i++)
226 {
227 for (int j = 0; j < 8; j++)
228 {
229 qreal x = i - 7;
230 qreal y = j - 7;
231 qreal dist = sqrt(x * x + y * y) / 7.0;
232 starColor.setHsvF(
233 h,
234 qMin(qreal(1),
235 dist < (10 - starColorIntensity) / 10.0 ? 0 : dist),
236 v,
237 qMax(qreal(0),
238 dist < (10 - starColorIntensity) / 20.0 ? 1 : 1 - dist));
239 p.setPen(starColor);
240 p.drawPoint(i, j);
241 p.drawPoint(14 - i, j);
242 p.drawPoint(i, 14 - j);
243 p.drawPoint(14 - i, 14 - j);
244 }
245 }
246 }
247 else
248 {
250 p.setPen(QPen(ColorMap[color], 2.0));
251 p.setBrush(p.pen().color());
252 p.drawEllipse(QRectF(2, 2, 10, 10));
253 }
254 p.end();
255
256 // Cache array slice
257 QPixmap **pmap = imageCache[harvardToIndex(color)];
258
259 for (int size = 1; size < nStarSizes; size++)
260 {
261 if (!pmap[size])
262 pmap[size] = new QPixmap();
263 *pmap[size] = BigImage.scaled(size, size, Qt::KeepAspectRatio,
265 }
266 }
267 starColorMode = Options::starColorMode();
268
269 if (!visibleSatPixmap.get())
270 visibleSatPixmap.reset(new QPixmap(":/icons/kstars_satellites_visible.svg"));
271 if (!invisibleSatPixmap.get())
272 invisibleSatPixmap.reset(new QPixmap(":/icons/kstars_satellites_invisible.svg"));
273}
274
276{
277 bool aVisible, bVisible;
278 QPointF aScreen = m_proj->toScreen(a, true, &aVisible);
279 QPointF bScreen = m_proj->toScreen(b, true, &bVisible);
280
282
283 //THREE CASES:
284 // if (aVisible && bVisible)
285 // {
286 // //Both a,b visible, so paint the line normally:
287 // drawLine(aScreen, bScreen);
288 // }
289 // else if (aVisible)
290 // {
291 // //a is visible but b isn't:
292 // drawLine(aScreen, m_proj->clipLine(a, b));
293 // }
294 // else if (bVisible)
295 // {
296 // //b is visible but a isn't:
297 // drawLine(bScreen, m_proj->clipLine(b, a));
298 // } //FIXME: what if both are offscreen but the line isn't?
299}
300
301void SkyQPainter::drawSkyPolyline(LineList *list, SkipHashList *skipList,
302 LineListLabel *label)
303{
304 SkyList *points = list->points();
305 bool isVisible, isVisibleLast;
306
307 if (points->size() == 0)
308 return;
309 QPointF oLast = m_proj->toScreen(points->first().get(), true, &isVisibleLast);
310 // & with the result of checkVisibility to clip away things below horizon
311 isVisibleLast &= m_proj->checkVisibility(points->first().get());
313
314 for (int j = 1; j < points->size(); j++)
315 {
316 SkyPoint *pThis = points->at(j).get();
317
318 oThis2 = oThis = m_proj->toScreen(pThis, true, &isVisible);
319 // & with the result of checkVisibility to clip away things below horizon
320 isVisible &= m_proj->checkVisibility(pThis);
321 bool doSkip = false;
322 if (skipList)
323 {
324 doSkip = skipList->skip(j);
325 }
326
327 bool pointsVisible = false;
328 //Temporary solution to avoid random lines in Gnomonic projection and draw lines up to horizon
329 if (SkyMap::Instance()->projector()->type() == Projector::Gnomonic)
330 {
331 if (isVisible && isVisibleLast)
332 pointsVisible = true;
333 }
334 else
335 {
336 if (isVisible || isVisibleLast)
337 pointsVisible = true;
338 }
339
340 if (!doSkip)
341 {
342 if (pointsVisible)
343 {
345 if (label)
346 label->updateLabelCandidates(oThis.x(), oThis.y(), list, j);
347 }
348 }
349
350 oLast = oThis2;
351 isVisibleLast = isVisible;
352 }
353}
354
356{
357 bool isVisible = false, isVisibleLast;
358 SkyList *points = list->points();
359 QPolygonF polygon;
360
361 if (forceClip == false)
362 {
363 for (const auto &point : *points)
364 {
365 polygon << m_proj->toScreen(point.get(), false, &isVisibleLast);
366 isVisible |= isVisibleLast;
367 }
368
369 // If 1+ points are visible, draw it
370 if (polygon.size() && isVisible)
371 drawPolygon(polygon);
372
373 return;
374 }
375
376 SkyPoint *pLast = points->last().get();
377 QPointF oLast = m_proj->toScreen(pLast, true, &isVisibleLast);
378 // & with the result of checkVisibility to clip away things below horizon
379 isVisibleLast &= m_proj->checkVisibility(pLast);
380
381 for (const auto &point : *points)
382 {
383 SkyPoint *pThis = point.get();
384 QPointF oThis = m_proj->toScreen(pThis, true, &isVisible);
385 // & with the result of checkVisibility to clip away things below horizon
386 isVisible &= m_proj->checkVisibility(pThis);
387
388 if (isVisible && isVisibleLast)
389 {
390 polygon << oThis;
391 }
392 else if (isVisibleLast)
393 {
394 QPointF oMid = m_proj->clipLine(pLast, pThis);
395 polygon << oMid;
396 }
397 else if (isVisible)
398 {
399 QPointF oMid = m_proj->clipLine(pThis, pLast);
400 polygon << oMid;
401 polygon << oThis;
402 }
403
404 pLast = pThis;
405 oLast = oThis;
406 isVisibleLast = isVisible;
407 }
408
409 if (polygon.size())
410 drawPolygon(polygon);
411}
412
414{
415 if (!m_proj->checkVisibility(planet))
416 return false;
417
418 bool visible = false;
419 QPointF pos = m_proj->toScreen(planet, true, &visible);
420 if (!visible || !m_proj->onScreen(pos))
421 return false;
422
423 float fakeStarSize = (10.0 + log10(Options::zoomFactor()) - log10(MINZOOM)) *
424 (10 - planet->mag()) / 10;
425 if (fakeStarSize > 15.0)
426 fakeStarSize = 15.0;
427
428 double size = planet->angSize() * dms::PI * Options::zoomFactor() / 10800.0;
429 if (size < fakeStarSize && planet->name() != i18n("Sun") &&
430 planet->name() != i18n("Moon"))
431 {
432 // Draw them as bright stars of appropriate color instead of images
433 char spType;
434 //FIXME: do these need i18n?
435 if (planet->name() == i18n("Mars"))
436 {
437 spType = 'K';
438 }
439 else if (planet->name() == i18n("Jupiter") || planet->name() == i18n("Mercury") ||
440 planet->name() == i18n("Saturn"))
441 {
442 spType = 'F';
443 }
444 else
445 {
446 spType = 'B';
447 }
448 drawPointSource(pos, fakeStarSize, spType);
449 }
450 else
451 {
452 float sizemin = 1.0;
453 if (planet->name() == i18n("Sun") || planet->name() == i18n("Moon"))
454 sizemin = 8.0;
455 if (planet->name() == i18n("Sun")) size = size * Options::sunScale();
456 if (planet->name() == i18n("Moon")) size = size * Options::moonScale();
457
458 if (size < sizemin)
459 size = sizemin;
460
461 if (Options::showPlanetImages() && !planet->image().isNull())
462 {
463 //Because Saturn has rings, we inflate its image size by a factor 2.5
464 if (planet->name() == "Saturn")
465 size = int(2.5 * size);
466 // Scale size exponentially so it is visible at large zooms
467 else if (planet->name() == "Pluto")
468 size = int(size * exp(1.5 * size));
469
470 save();
471 translate(pos);
472 rotate(m_proj->findPA(planet, pos.x(), pos.y()));
473 drawImage(QRectF(-0.5 * size, -0.5 * size, size, size), planet->image());
474 restore();
475 }
476 else //Otherwise, draw a simple circle.
477 {
478 drawEllipse(pos, size * .5, size * .5);
479 }
480 }
481 return true;
482}
483
485{
486 if (!m_proj->checkVisibility(shadow))
487 return false;
488
489 bool visible = false;
490 QPointF pos = m_proj->toScreen(shadow, true, &visible);
491
492 if (!visible)
493 return false;
494
495 double umbra_size =
496 shadow->getUmbraAngSize() * dms::PI * Options::zoomFactor() / 10800.0;
497 double penumbra_size =
498 shadow->getPenumbraAngSize() * dms::PI * Options::zoomFactor() / 10800.0;
499
500 save();
501 setBrush(QBrush(QColor(255, 96, 38, 128)));
503 setBrush(QBrush(QColor(255, 96, 38, 90)));
505 restore();
506
507 return true;
508}
509
511{
512 if (!m_proj->checkVisibility(com))
513 return false;
514
515 double size =
516 com->angSize() * dms::PI * Options::zoomFactor() / 10800.0 / 2; // Radius
517 if (size < 1)
518 size = 1;
519
520 bool visible = false;
521 QPointF pos = m_proj->toScreen(com, true, &visible);
522
523 // Draw the coma. FIXME: Another Check??
524 if (visible && m_proj->onScreen(pos))
525 {
526 // Draw the comet.
527 drawEllipse(pos, size, size);
528
529 double comaLength =
530 (com->getComaAngSize().arcmin() * dms::PI * Options::zoomFactor() / 10800.0);
531
532 // If coma is visible and long enough.
533 if (Options::showCometComas() && comaLength > size)
534 {
535 KSSun *sun =
536 KStarsData::Instance()->skyComposite()->solarSystemComposite()->sun();
537
538 // Find the angle to the sun.
539 double comaAngle = m_proj->findPA(sun, pos.x(), pos.y());
540
541 const QVector<QPoint> coma = { QPoint(pos.x() - size, pos.y()),
542 QPoint(pos.x() + size, pos.y()),
543 QPoint(pos.x(), pos.y() + comaLength)
544 };
545
547
548 comaPoly =
549 QTransform()
550 .translate(pos.x(), pos.y())
551 .rotate(
552 comaAngle) // Already + 180 Deg, because rotated from south, not north.
553 .translate(-pos.x(), -pos.y())
554 .map(comaPoly);
555
556 save();
557
558 // Nice fade for the Coma.
559 QLinearGradient linearGrad(pos, comaPoly.point(2));
560 linearGrad.setColorAt(0, QColor("white"));
561 linearGrad.setColorAt(size / comaLength, QColor("white"));
562 linearGrad.setColorAt(0.9, QColor("transparent"));
564
565 // Render Coma.
567 restore();
568 }
569
570 return true;
571 }
572 else
573 {
574 return false;
575 }
576}
577
579{
580 if (!m_proj->checkVisibility(ast))
581 {
582 return false;
583 }
584
585 bool visible = false;
586 QPointF pos = m_proj->toScreen(ast, true, &visible);
587
588 if (visible && m_proj->onScreen(pos))
589 {
590 KStarsData *data = KStarsData::Instance();
591
592 setPen(data->colorScheme()->colorNamed("AsteroidColor"));
593 drawLine(QPoint(pos.x() - 1.0, pos.y()), QPoint(pos.x() + 1.0, pos.y()));
594 drawLine(QPoint(pos.x(), pos.y() - 1.0), QPoint(pos.x(), pos.y() + 1.0));
595
596 return true;
597 }
598
599 return false;
600}
601
602bool SkyQPainter::drawPointSource(const SkyPoint *loc, float mag, char sp)
603{
604 //Check if it's even visible before doing anything
605 if (!m_proj->checkVisibility(loc))
606 return false;
607
608 bool visible = false;
609 QPointF pos = m_proj->toScreen(loc, true, &visible);
610 // FIXME: onScreen here should use canvas size rather than SkyMap size, especially while printing in portrait mode!
611 if (visible && m_proj->onScreen(pos))
612 {
613 drawPointSource(pos, starWidth(mag), sp);
614 return true;
615 }
616 else
617 {
618 return false;
619 }
620}
621
622void SkyQPainter::drawPointSource(const QPointF &pos, float size, char sp)
623{
624 int isize = qMin(static_cast<int>(size), 14);
625 if (!m_vectorStars || starColorMode == 0)
626 {
627 // Draw stars as bitmaps, either because we were asked to, or because we're painting real colors
628 QPixmap *im = imageCache[harvardToIndex(sp)][isize];
629 float offset = 0.5 * im->width();
630 drawPixmap(QPointF(pos.x() - offset, pos.y() - offset), *im);
631 }
632 else
633 {
634 // Draw stars as vectors, for better printing / SVG export etc.
635 if (starColorMode != 4)
636 {
637 setPen(m_starColor);
638 setBrush(m_starColor);
639 }
640 else
641 {
642 // 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.
643 QColor c = ColorMap.value(sp, Qt::white);
644 setPen(c);
645 setBrush(c);
646 }
647
648 // Be consistent with old raster representation
649 if (size > 14)
650 size = 14;
651 if (size >= 2)
652 drawEllipse(pos.x() - 0.5 * size, pos.y() - 0.5 * size, int(size), int(size));
653 else if (size >= 1)
654 drawPoint(pos.x(), pos.y());
655 }
656}
657
659{
660 double zoom = Options::zoomFactor();
661
662 bool visible = false;
663 obj->EquatorialToHorizontal(KStarsData::Instance()->lst(),
664 KStarsData::Instance()->geo()->lat());
665 QPointF constellationmidpoint = m_proj->toScreen(obj, true, &visible);
666
667 if (!visible || !m_proj->onScreen(constellationmidpoint))
668 return false;
669
670 //qDebug() << Q_FUNC_INFO << "o->pa() " << obj->pa();
671 float positionangle =
672 m_proj->findPA(obj, constellationmidpoint.x(), constellationmidpoint.y());
673 //qDebug() << Q_FUNC_INFO << " final PA " << positionangle;
674
675 float w = obj->getWidth() * 60 * dms::PI * zoom / 10800;
676 float h = obj->getHeight() * 60 * dms::PI * zoom / 10800;
677
678 save();
679
681
684 if (m_proj->viewParams().mirror)
685 {
686 scale(-1., 1.);
687 }
688 setOpacity(0.7);
689 drawImage(QRectF(-0.5 * w, -0.5 * h, w, h), obj->image());
690 setOpacity(1);
691
693 restore();
694 return true;
695}
696
697#ifdef HAVE_INDI
698bool SkyQPainter::drawMosaicPanel(MosaicTiles *obj)
699{
700 bool visible = false;
701 obj->EquatorialToHorizontal(KStarsData::Instance()->lst(),
702 KStarsData::Instance()->geo()->lat());
703 QPointF tileMid = m_proj->toScreen(obj, true, &visible);
704
705 if (!visible || !m_proj->onScreen(tileMid) || !obj->isValid())
706 return false;
707
708 //double northRotation = m_proj->findNorthPA(obj, tileMid.x(), tileMid.y())
709
710 // convert 0 to +180 EAST, and 0 to -180 WEST to 0 to 360 CCW
711 const auto mirror = m_proj->viewParams().mirror;
712 auto PA = (obj->positionAngle() < 0) ? obj->positionAngle() + 360 : obj->positionAngle();
713 auto finalPA = m_proj->findNorthPA(obj, tileMid.x(), tileMid.y()) - (mirror ? -PA : PA);
714
715 save();
716 translate(tileMid.toPoint());
718 obj->draw(this);
719 restore();
720
721 return true;
722}
723#endif
724
726{
727 int w = viewport().width();
728 int h = viewport().height();
729
730 if (useCache && m_HiPSImage)
731 {
732 drawImage(viewport(), *m_HiPSImage.data());
733 return true;
734 }
735 else
736 {
737 m_HiPSImage.reset(new QImage(w, h, QImage::Format_ARGB32_Premultiplied));
738 bool rendered = m_hipsRender->render(w, h, m_HiPSImage.data(), m_proj);
739 if (rendered)
740 drawImage(viewport(), *m_HiPSImage.data());
741 return rendered;
742 }
743}
744
746{
747 // TODO
749 int w = viewport().width();
750 int h = viewport().height();
752 TerrainRenderer *renderer = TerrainRenderer::Instance();
753 bool rendered = renderer->render(w, h, terrainImage, m_proj);
754 if (rendered)
756
757 delete (terrainImage);
758 return rendered;
759}
760
762{
764 if (!Options::showImageOverlays())
765 return false;
766
767 constexpr int minDisplayDimension = 5;
768
769 // Convert the RA/DEC from j2000 to jNow and add in az/alt computations.
770 auto localTime = KStarsData::Instance()->geo()->UTtoLT(KStarsData::Instance()->clock()->utc());
771
772 const ViewParams view = m_proj->viewParams();
773 const double vw = view.width, vh = view.height;
774 RectangleOverlap overlap(QPointF(vw / 2.0, vh / 2.0), vw, vh);
775
776 // QElapsedTimer drawTimer;
777 // drawTimer.restart();
778 int numDrawn = 0;
779 for (const ImageOverlay &o : *imageOverlays)
780 {
781 if (o.m_Status != ImageOverlay::AVAILABLE || o.m_Img.get() == nullptr)
782 continue;
783
784 double orientation = o.m_Orientation, ra = o.m_RA, dec = o.m_DEC, scale = o.m_ArcsecPerPixel;
785 const int origWidth = o.m_Width, origHeight = o.m_Height;
786
787 // Not sure why I have to do this, suspect it's related to the East-to-the-right thing.
788 // Note that I have mirrored the image above, so east-to-the-right=false is now east-to-the-right=true
789 // BTW, solver gave me -66, but astrometry.net gave me 246.1 East of North
790 orientation += 180;
791
792 const dms raDms(ra), decDms(dec);
794 coord.apparentCoord(static_cast<long double>(J2000), KStars::Instance()->data()->ut().djd());
795 coord.EquatorialToHorizontal(KStarsData::Instance()->lst(), KStarsData::Instance()->geo()->lat());
796
797 // Find if the object is not visible, or if it is very small.
798 const double a = origWidth * scale / 60.0; // This is the width of the image in arcmin--not the major axis
799 const double zoom = Options::zoomFactor();
800 // W & h are the actual pixel width and height (as doubles) though
801 // the projection size might be smaller.
802 const double w = a * dms::PI * zoom / 10800.0;
803 const double h = w * origHeight / origWidth;
804 const double maxDimension = std::max(w, h);
806 continue;
807
808 bool visible;
809 QPointF pos = m_proj->toScreen(&coord, true, &visible);
810 if (!visible || isnan(pos.x()) || isnan(pos.y()))
811 continue;
812
813 const auto PA = (orientation < 0) ? orientation + 360 : orientation;
814 const auto mirror = m_proj->viewParams().mirror;
815 const auto finalPA = m_proj->findNorthPA(&coord, pos.x(), pos.y()) - (mirror ? -PA : PA);
816 if (!overlap.intersects(pos, w, h, finalPA))
817 continue;
818
819 save();
820 translate(pos);
822 if (mirror)
823 {
824 this->scale(-1., 1.);
825 }
826 drawImage(QRectF(-0.5 * w, -0.5 * h, w, h), *(o.m_Img.get()));
827 numDrawn++;
828 restore();
829 }
830 // fprintf(stderr, "DrawTimer: %lldms for %d images\n", drawTimer.elapsed(), numDrawn);
831 return true;
832}
833
834void SkyQPainter::drawCatalogObjectImage(const QPointF &pos, const CatalogObject &obj,
835 float positionAngle)
836{
837 const auto &image = obj.image();
838
839 if (!image.first)
840 return;
841
842 double zoom = Options::zoomFactor();
843 double w = obj.a() * dms::PI * zoom / 10800.0;
844 double h = obj.e() * w;
845
846 save();
847 translate(pos);
848 rotate(positionAngle);
849 drawImage(QRectF(-0.5 * w, -0.5 * h, w, h), image.second);
850 restore();
851}
852
854{
855 if (!m_proj->checkVisibility(&obj))
856 return false;
857
858 bool visible = false;
859 QPointF pos = m_proj->toScreen(&obj, true, &visible);
860 if (!visible || !m_proj->onScreen(pos))
861 return false;
862
863 // if size is 0.0 set it to 1.0, this are normally stars (type 0 and 1)
864 // if we use size 0.0 the star wouldn't be drawn
865 float majorAxis = obj.a();
866 if (majorAxis == 0.0)
867 {
868 majorAxis = 1.0;
869 }
870
871 float size = majorAxis * dms::PI * Options::zoomFactor() / 10800.0;
872
873 const auto mirror = m_proj->viewParams().mirror;
874 const auto positionAngle = m_proj->findNorthPA(&obj, pos.x(), pos.y())
875 - (mirror ? -obj.pa() : obj.pa()) // FIXME: We seem to have two different conventions for PA sign in KStars!
876 + 90.;
877
878 // Draw image
879 if (Options::showInlineImages() && Options::zoomFactor() > 5. * MINZOOM &&
880 !Options::showHIPS())
881 drawCatalogObjectImage(pos, obj, positionAngle);
882
883 // Draw Symbol
884 drawDeepSkySymbol(pos, obj.type(), size, obj.e(), positionAngle);
885
886 return true;
887}
888
889void SkyQPainter::drawDeepSkySymbol(const QPointF &pos, int type, float size, float e,
890 float positionAngle)
891{
892 float x = pos.x();
893 float y = pos.y();
894 float zoom = Options::zoomFactor();
895
896 int isize = int(size);
897
898 float dx1 = -0.5 * size;
899 float dx2 = 0.5 * size;
900 float dy1 = -1.0 * e * size / 2.;
901 float dy2 = e * size / 2.;
902 float x1 = x + dx1;
903 float x2 = x + dx2;
904 float y1 = y + dy1;
905 float y2 = y + dy2;
906
907 float dxa = -size / 4.;
908 float dxb = size / 4.;
909 float dya = -1.0 * e * size / 4.;
910 float dyb = e * size / 4.;
911 float xa = x + dxa;
912 float xb = x + dxb;
913 float ya = y + dya;
914 float yb = y + dyb;
915
916 QString color;
917
918 float psize;
919
921
922 std::function<void(float, float, float, float)> lambdaDrawEllipse;
923 std::function<void(float, float, float, float)> lambdaDrawLine;
924 std::function<void(float, float, float, float)> lambdaDrawCross;
925
926 if (Options::useAntialias())
927 {
928 lambdaDrawEllipse = [this](float x, float y, float width, float height)
929 {
930 drawEllipse(QRectF(x, y, width, height));
931 };
932 lambdaDrawLine = [this](float x1, float y1, float x2, float y2)
933 {
934 drawLine(QLineF(x1, y1, x2, y2));
935 };
936 lambdaDrawCross = [this](float centerX, float centerY, float sizeX, float sizeY)
937 {
938 drawLine(
939 QLineF(centerX - sizeX / 2., centerY, centerX + sizeX / 2., centerY));
940 drawLine(
941 QLineF(centerX, centerY - sizeY / 2., centerX, centerY + sizeY / 2.));
942 };
943 }
944 else
945 {
946 lambdaDrawEllipse = [this](float x, float y, float width, float height)
947 {
948 drawEllipse(QRect(x, y, width, height));
949 };
950 lambdaDrawLine = [this](float x1, float y1, float x2, float y2)
951 {
952 drawLine(QLine(x1, y1, x2, y2));
953 };
954 lambdaDrawCross = [this](float centerX, float centerY, float sizeX, float sizeY)
955 {
956 drawLine(QLine(centerX - sizeX / 2., centerY, centerX + sizeX / 2., centerY));
957 drawLine(QLine(centerX, centerY - sizeY / 2., centerX, centerY + sizeY / 2.));
958 };
959 }
960
961 switch ((SkyObject::TYPE)type)
962 {
963 case SkyObject::STAR:
964 case SkyObject::CATALOG_STAR: //catalog star
965 //Some NGC/IC objects are stars...changed their type to 1 (was double star)
966 if (size < 2.)
967 size = 2.;
968 lambdaDrawEllipse(x - size / 2., y - size / 2., size, size);
969 break;
970 case SkyObject::PLANET: //Planet
971 break;
972 case SkyObject::OPEN_CLUSTER: //Open cluster; draw circle of points
973 case SkyObject::ASTERISM: // Asterism
974 {
975 tempBrush = brush();
976 color = pen().color().name();
977 setBrush(pen().color());
978 psize = 2.;
979 if (size > 50.)
980 psize *= 2.;
981 if (size > 100.)
982 psize *= 2.;
983 auto putDot = [psize, &lambdaDrawEllipse](float x, float y)
984 {
985 lambdaDrawEllipse(x - psize / 2., y - psize / 2., psize, psize);
986 };
987 putDot(xa, y1);
988 putDot(xb, y1);
989 putDot(xa, y2);
990 putDot(xb, y2);
991 putDot(x1, ya);
992 putDot(x1, yb);
993 putDot(x2, ya);
994 putDot(x2, yb);
996 break;
997 }
998 case SkyObject::GLOBULAR_CLUSTER: //Globular Cluster
999 if (size < 2.)
1000 size = 2.;
1001 save();
1002 translate(x, y);
1003 color = pen().color().name();
1004 rotate(positionAngle); //rotate the coordinate system
1005 lambdaDrawEllipse(dx1, dy1, size, e * size);
1006 lambdaDrawCross(0, 0, size, e * size);
1007 restore(); //reset coordinate system
1008 break;
1009
1010 case SkyObject::GASEOUS_NEBULA: //Gaseous Nebula
1011 case SkyObject::DARK_NEBULA: // Dark Nebula
1012 save();
1013 translate(x, y);
1014 rotate(positionAngle); //rotate the coordinate system
1015 color = pen().color().name();
1016 lambdaDrawLine(dx1, dy1, dx2, dy1);
1017 lambdaDrawLine(dx2, dy1, dx2, dy2);
1018 lambdaDrawLine(dx2, dy2, dx1, dy2);
1019 lambdaDrawLine(dx1, dy2, dx1, dy1);
1020 restore(); //reset coordinate system
1021 break;
1022 case SkyObject::PLANETARY_NEBULA: //Planetary Nebula
1023 if (size < 2.)
1024 size = 2.;
1025 save();
1026 translate(x, y);
1027 rotate(positionAngle); //rotate the coordinate system
1028 color = pen().color().name();
1029 lambdaDrawEllipse(dx1, dy1, size, e * size);
1030 lambdaDrawLine(0., dy1, 0., dy1 - e * size / 2.);
1031 lambdaDrawLine(0., dy2, 0., dy2 + e * size / 2.);
1032 lambdaDrawLine(dx1, 0., dx1 - size / 2., 0.);
1033 lambdaDrawLine(dx2, 0., dx2 + size / 2., 0.);
1034 restore(); //reset coordinate system
1035 break;
1036 case SkyObject::SUPERNOVA_REMNANT: //Supernova remnant // FIXME: Why is SNR drawn different from a gaseous nebula?
1037 save();
1038 translate(x, y);
1039 rotate(positionAngle); //rotate the coordinate system
1040 color = pen().color().name();
1041 lambdaDrawLine(0., dy1, dx2, 0.);
1042 lambdaDrawLine(dx2, 0., 0., dy2);
1043 lambdaDrawLine(0., dy2, dx1, 0.);
1044 lambdaDrawLine(dx1, 0., 0., dy1);
1045 restore(); //reset coordinate system
1046 break;
1047 case SkyObject::GALAXY: //Galaxy
1048 case SkyObject::QUASAR: // Quasar
1049 color = pen().color().name();
1050 if (size < 1. && zoom > 20 * MINZOOM)
1051 size = 3.; //force ellipse above zoomFactor 20
1052 if (size < 1. && zoom > 5 * MINZOOM)
1053 size = 1.; //force points above zoomFactor 5
1054 if (size > 2.)
1055 {
1056 save();
1057 translate(x, y);
1058 rotate(positionAngle); //rotate the coordinate system
1059 lambdaDrawEllipse(dx1, dy1, size, e * size);
1060 restore(); //reset coordinate system
1061 }
1062 else if (size > 0.)
1063 {
1064 drawPoint(QPointF(x, y));
1065 }
1066 break;
1067 case SkyObject::GALAXY_CLUSTER: // Galaxy cluster - draw a dashed circle
1068 {
1069 tempBrush = brush();
1070 setBrush(QBrush());
1071 color = pen().color().name();
1072 save();
1073 translate(x, y);
1074 rotate(positionAngle); //rotate the coordinate system
1075 QPen newPen = pen();
1076 newPen.setStyle(Qt::DashLine);
1077 setPen(newPen);
1078 lambdaDrawEllipse(dx1, dy1, size, e * size);
1079 restore();
1081 break;
1082 }
1083 default
1084: // Unknown object or something we don't know how to draw. Just draw an ellipse with a ?-mark
1085 color = pen().color().name();
1086 if (size < 1. && zoom > 20 * MINZOOM)
1087 size = 3.; //force ellipse above zoomFactor 20
1088 if (size < 1. && zoom > 5 * MINZOOM)
1089 size = 1.; //force points above zoomFactor 5
1090 if (size > 2.)
1091 {
1092 save();
1093 QFont f = font();
1094 const QString qMark = " ? ";
1095
1096#if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
1097 double scaleFactor = 0.8 * size / fontMetrics().horizontalAdvance(qMark);
1098#else
1099 double scaleFactor = 0.8 * size / fontMetrics().width(qMark);
1100#endif
1101
1102 f.setPointSizeF(f.pointSizeF() * scaleFactor);
1103 setFont(f);
1104 translate(x, y);
1105 rotate(positionAngle); //rotate the coordinate system
1106 lambdaDrawEllipse(dx1, dy1, size, e * size);
1107 if (Options::useAntialias())
1108 drawText(QRectF(dx1, dy1, size, e * size), Qt::AlignCenter, qMark);
1109 else
1110 {
1111 int idx1 = int(dx1);
1112 int idy1 = int(dy1);
1113 drawText(QRect(idx1, idy1, isize, int(e * size)), Qt::AlignCenter,
1114 qMark);
1115 }
1116 restore(); //reset coordinate system (and font?)
1117 }
1118 else if (size > 0.)
1119 {
1120 if (Options::useAntialias())
1121 drawPoint(QPointF(x, y));
1122 else
1123 drawPoint(QPoint(x, y));
1124 }
1125 }
1126}
1127
1129{
1130 foreach (SkyObject *obj, obs)
1131 {
1132 bool visible = false;
1133 QPointF o = m_proj->toScreen(obj, true, &visible);
1134 if (!visible || !m_proj->onScreen(o))
1135 continue;
1136
1137 float size = 20.;
1138 float x1 = o.x() - 0.5 * size;
1139 float y1 = o.y() - 0.5 * size;
1140 drawArc(QRectF(x1, y1, size, size), -60 * 16, 120 * 16);
1141 drawArc(QRectF(x1, y1, size, size), 120 * 16, 120 * 16);
1142 }
1143}
1144
1146{
1147 KStarsData *data = KStarsData::Instance();
1148 std::shared_ptr<SkyPoint> point;
1149 QImage image;
1150 bool visible = false;
1151 QPointF pos;
1152
1153 for (int i = 0; i < data->skyComposite()->flags()->size(); i++)
1154 {
1155 point = data->skyComposite()->flags()->pointList().at(i);
1156 image = data->skyComposite()->flags()->image(i);
1157
1158 // Set Horizontal coordinates
1159 point->EquatorialToHorizontal(data->lst(), data->geo()->lat());
1160
1161 // Get flag position on screen
1162 pos = m_proj->toScreen(point.get(), true, &visible);
1163
1164 // Return if flag is not visible
1165 if (!visible || !m_proj->onScreen(pos))
1166 continue;
1167
1168 // Draw flag image
1169 drawImage(pos.x() - 0.5 * image.width(), pos.y() - 0.5 * image.height(), image);
1170
1171 // Draw flag label
1172 setPen(data->skyComposite()->flags()->labelColor(i));
1173 setFont(QFont("Helvetica", 10, QFont::Bold));
1174 drawText(pos.x() + 10, pos.y() - 10, data->skyComposite()->flags()->label(i));
1175 }
1176}
1177
1178void SkyQPainter::drawHorizon(bool filled, SkyPoint *labelPoint, bool *drawLabel)
1179{
1180 QVector<Eigen::Vector2f> ground = m_proj->groundPoly(labelPoint, drawLabel);
1181 if (ground.size())
1182 {
1183 QPolygonF groundPoly(ground.size());
1184 for (int i = 0; i < ground.size(); ++i)
1185 groundPoly[i] = KSUtils::vecToPoint(ground[i]);
1186 if (filled)
1187 drawPolygon(groundPoly);
1188 else
1189 {
1190 groundPoly.append(groundPoly.first());
1191 drawPolyline(groundPoly);
1192 }
1193 }
1194}
1195
1197{
1198 if (!m_proj->checkVisibility(sat))
1199 return false;
1200
1201 QPointF pos;
1202 bool visible = false;
1203
1204 //sat->HorizontalToEquatorial( data->lst(), data->geo()->lat() );
1205
1206 pos = m_proj->toScreen(sat, true, &visible);
1207
1208 if (!visible || !m_proj->onScreen(pos))
1209 return false;
1210
1211 if (Options::drawSatellitesLikeStars())
1212 {
1213 drawPointSource(pos, 3.5, 'B');
1214 }
1215 else
1216 {
1217 if (sat->isVisible())
1218 drawPixmap(QPoint(pos.x() - 15, pos.y() - 11), *visibleSatPixmap);
1219 else
1220 drawPixmap(QPoint(pos.x() - 15, pos.y() - 11), *invisibleSatPixmap);
1221
1222 //drawPixmap(pos, *genericSatPixmap);
1223 /*drawLine( QPoint( pos.x() - 0.5, pos.y() - 0.5 ), QPoint( pos.x() + 0.5, pos.y() - 0.5 ) );
1224 drawLine( QPoint( pos.x() + 0.5, pos.y() - 0.5 ), QPoint( pos.x() + 0.5, pos.y() + 0.5 ) );
1225 drawLine( QPoint( pos.x() + 0.5, pos.y() + 0.5 ), QPoint( pos.x() - 0.5, pos.y() + 0.5 ) );
1226 drawLine( QPoint( pos.x() - 0.5, pos.y() + 0.5 ), QPoint( pos.x() - 0.5, pos.y() - 0.5 ) );*/
1227 }
1228
1229 return true;
1230
1231 //if ( Options::showSatellitesLabels() )
1232 //data->skyComposite()->satellites()->drawLabel( sat, pos );
1233}
1234
1236{
1237 KStarsData *data = KStarsData::Instance();
1238 if (!m_proj->checkVisibility(sup))
1239 {
1240 return false;
1241 }
1242
1243 bool visible = false;
1244 QPointF pos = m_proj->toScreen(sup, true, &visible);
1245 //qDebug()<<"sup->ra() = "<<(sup->ra()).toHMSString()<<"sup->dec() = "<<sup->dec().toDMSString();
1246 //qDebug()<<"pos = "<<pos<<"m_proj->onScreen(pos) = "<<m_proj->onScreen(pos);
1247 if (!visible || !m_proj->onScreen(pos))
1248 return false;
1249
1250 setPen(data->colorScheme()->colorNamed("SupernovaColor"));
1251 //qDebug()<<"Here";
1252 drawLine(QPoint(pos.x() - 2.0, pos.y()), QPoint(pos.x() + 2.0, pos.y()));
1253 drawLine(QPoint(pos.x(), pos.y() - 2.0), QPoint(pos.x(), pos.y() + 2.0));
1254 return true;
1255}
A simple container object to hold the minimum information for a Deep Sky Object to be drawn on the sk...
float e() const
std::pair< bool, const QImage & > image() const
Get the image for this object.
float a() const
double pa() const override
QColor colorNamed(const QString &name) const
Retrieve a color by name.
Information about a ConstellationsArt object.
const QImage & image()
QColor labelColor(int index)
Get label color.
QString label(int index)
Get label.
QImage image(int index)
Get image.
const CachingDms * lat() const
Definition geolocation.h:70
A subclass of KSPlanetBase that implements asteroids.
Definition ksasteroid.h:42
A subclass of KSPlanetBase that implements comets.
Definition kscomet.h:44
A class that manages the calculation of the earths shadow (in moon distance) as a 'virtual' skyobject...
double getPenumbraAngSize() const
double getUmbraAngSize() const
A subclass of TrailObject that provides additional information needed for most solar system objects.
const QImage & image() const
double angSize() const
Child class of KSPlanetBase; encapsulates information about the Sun.
Definition kssun.h:24
KStarsData is the backbone of KStars.
Definition kstarsdata.h:72
CachingDms * lst()
Definition kstarsdata.h:224
ColorScheme * colorScheme()
Definition kstarsdata.h:172
GeoLocation * geo()
Definition kstarsdata.h:230
SkyMapComposite * skyComposite()
Definition kstarsdata.h:166
static KStars * Instance()
Definition kstars.h:123
A simple data container used by LineListIndex.
Definition linelist.h:25
This class checks if two rectangles overlap.
bool intersects(const QPointF &center, int width, int height, double rotationDegrees=0.0) const
Check if the input rectangle overlaps the reference rectangle.
Represents an artificial satellites.
Definition satellite.h:23
bool isVisible()
Provides all necessary information about an object in the sky: its coordinates, name(s),...
Definition skyobject.h:42
virtual QString name(void) const
Definition skyobject.h:145
int type(void) const
Definition skyobject.h:188
float mag() const
Definition skyobject.h:206
TYPE
The type classification of the SkyObject.
Definition skyobject.h:112
Draws things on the sky, without regard to backend.
Definition skypainter.h:40
float starWidth(float mag) const
Get the width of a star of magnitude mag.
The sky coordinates of a point in the sky.
Definition skypoint.h:45
void EquatorialToHorizontal(const CachingDms *LST, const CachingDms *lat)
Determine the (Altitude, Azimuth) coordinates of the SkyPoint from its (RA, Dec) coordinates,...
Definition skypoint.cpp:77
bool drawAsteroid(KSAsteroid *ast) override
Draw an asteroid in the sky.
bool drawComet(KSComet *com) override
Draw a comet in the sky.
void end() override
End and finalize painting.
SkyQPainter(QPaintDevice *pd, const QSize &canvasSize)
Creates a SkyQPainter with the given QPaintDevice and uses the dimensions of the paint device as canv...
bool drawPlanet(KSPlanetBase *planet) override
Draw a planet.
bool drawPointSource(const SkyPoint *loc, float mag, char sp='A') override
Draw a point source (e.g., a star).
void drawSkyLine(SkyPoint *a, SkyPoint *b) override
Draw a line between points in the sky.
bool drawImageOverlay(const QList< ImageOverlay > *imageOverlays, bool useCache=false) override
drawImageOverlay Draws a user-supplied image onto the skymap
void setBrush(const QBrush &brush) override
Set the brush of the painter.
void drawSkyPolyline(LineList *list, SkipHashList *skipList=nullptr, LineListLabel *label=nullptr) override
Draw a polyline in the sky.
bool drawTerrain(bool useCache=false) override
drawTerrain Draw the Terrain
void drawSkyPolygon(LineList *list, bool forceClip=true) override
Draw a polygon in the sky.
void drawSkyBackground() override
Draw the sky background.
bool drawCatalogObject(const CatalogObject &obj) override
Draw a deep sky object (loaded from the new implementation)
void drawObservingList(const QList< SkyObject * > &obs) override
Draw the symbols for the observing list.
static void initStarImages()
Recalculates the star pixmaps.
static void releaseImageCache()
Release the image cache.
void setPen(const QPen &pen) override
Set the pen of the painter.
bool drawEarthShadow(KSEarthShadow *shadow) override
Draw the earths shadow on the moon (red-ish)
bool drawConstellationArtImage(ConstellationsArt *obj) override
Draw a ConstellationsArt object.
void drawFlags() override
Draw flags.
bool drawSupernova(Supernova *sup) override
Draw a Supernova.
bool drawHips(bool useCache=false) override
drawMosaicPanel Draws mosaic panel in planning or operation mode.
bool drawSatellite(Satellite *sat) override
Draw a satellite.
void begin() override
Begin painting.
Represents the supernova object.
Definition supernova.h:34
This is just a container that holds information needed to do projections.
Definition projector.h:37
An angle, stored as degrees, but expressible in many ways.
Definition dms.h:38
static constexpr double PI
PI is a const static member; it's public so that it can be used anywhere, as long as dms....
Definition dms.h:385
QString i18n(const char *text, const TYPE &arg...)
QColor fromRgb(QRgb rgb)
int horizontalAdvance(QChar ch) const const
Format_ARGB32_Premultiplied
int height() const const
int width() const const
const_reference at(qsizetype i) const const
qsizetype size() const const
bool begin(QPaintDevice *device)
const QBrush & brush() const const
void drawArc(const QRect &rectangle, int startAngle, int spanAngle)
void drawConvexPolygon(const QPoint *points, int pointCount)
void drawEllipse(const QPoint &center, int rx, int ry)
void drawImage(const QPoint &point, const QImage &image)
void drawLine(const QLine &line)
void drawPixmap(const QPoint &point, const QPixmap &pixmap)
void drawPoint(const QPoint &position)
void drawPolygon(const QPoint *points, int pointCount, Qt::FillRule fillRule)
void drawPolyline(const QPoint *points, int pointCount)
void drawText(const QPoint &position, const QString &text)
bool end()
void fillRect(const QRect &rectangle, QGradient::Preset preset)
const QFont & font() const const
QFontMetrics fontMetrics() const const
const QPen & pen() const const
void restore()
void rotate(qreal angle)
void save()
void scale(qreal sx, qreal sy)
void setBrush(Qt::BrushStyle style)
void setFont(const QFont &font)
void setOpacity(qreal opacity)
void setPen(Qt::PenStyle style)
void setRenderHint(RenderHint hint, bool on)
void translate(const QPoint &offset)
QRect viewport() const const
qreal x() const const
qreal y() const const
int height() const const
int width() const const
T * data() const const
void reset(T *other)
int height() const const
int width() const const
AlignCenter
KeepAspectRatio
DashLine
SmoothTransformation
QLine map(const QLine &l) const const
QTransform & rotate(qreal a, Qt::Axis axis)
QTransform & translate(qreal dx, qreal dy)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:19:04 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.