Kstars

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

KDE's Doxygen guidelines are available online.