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 
572 bool SkyQPainter::drawPointSource(const SkyPoint *loc, float mag, char sp)
573 {
574  //Check if it's even visible before doing anything
575  if (!m_proj->checkVisibility(loc))
576  return false;
577 
578  bool visible = false;
579  QPointF pos = m_proj->toScreen(loc, true, &visible);
580  if (visible &&
581  m_proj->onScreen(
582  pos)) // FIXME: onScreen here should use canvas size rather than SkyMap size, especially while printing in portrait mode!
583  {
584  drawPointSource(pos, starWidth(mag), sp);
585  return true;
586  }
587  else
588  {
589  return false;
590  }
591 }
592 
593 void SkyQPainter::drawPointSource(const QPointF &pos, float size, char sp)
594 {
595  int isize = qMin(static_cast<int>(size), 14);
596  if (!m_vectorStars || starColorMode == 0)
597  {
598  // Draw stars as bitmaps, either because we were asked to, or because we're painting real colors
599  QPixmap *im = imageCache[harvardToIndex(sp)][isize];
600  float offset = 0.5 * im->width();
601  drawPixmap(QPointF(pos.x() - offset, pos.y() - offset), *im);
602  }
603  else
604  {
605  // Draw stars as vectors, for better printing / SVG export etc.
606  if (starColorMode != 4)
607  {
608  setPen(m_starColor);
609  setBrush(m_starColor);
610  }
611  else
612  {
613  // 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.
614  QColor c = ColorMap.value(sp, Qt::white);
615  setPen(c);
616  setBrush(c);
617  }
618 
619  // Be consistent with old raster representation
620  if (size > 14)
621  size = 14;
622  if (size >= 2)
623  drawEllipse(pos.x() - 0.5 * size, pos.y() - 0.5 * size, int(size), int(size));
624  else if (size >= 1)
625  drawPoint(pos.x(), pos.y());
626  }
627 }
628 
630 {
631  double zoom = Options::zoomFactor();
632 
633  bool visible = false;
634  obj->EquatorialToHorizontal(KStarsData::Instance()->lst(),
635  KStarsData::Instance()->geo()->lat());
636  QPointF constellationmidpoint = m_proj->toScreen(obj, true, &visible);
637 
638  if (!visible || !m_proj->onScreen(constellationmidpoint))
639  return false;
640 
641  //qDebug() << Q_FUNC_INFO << "o->pa() " << obj->pa();
642  float positionangle =
643  m_proj->findPA(obj, constellationmidpoint.x(), constellationmidpoint.y());
644  //qDebug() << Q_FUNC_INFO << " final PA " << positionangle;
645 
646  float w = obj->getWidth() * 60 * dms::PI * zoom / 10800;
647  float h = obj->getHeight() * 60 * dms::PI * zoom / 10800;
648 
649  save();
650 
652 
653  translate(constellationmidpoint);
654  rotate(positionangle);
655  setOpacity(0.7);
656  drawImage(QRectF(-0.5 * w, -0.5 * h, w, h), obj->image());
657  setOpacity(1);
658 
660  restore();
661  return true;
662 }
663 
664 #ifdef HAVE_INDI
665 bool SkyQPainter::drawMosaicPanel(MosaicTiles *obj)
666 {
667  bool visible = false;
668  obj->EquatorialToHorizontal(KStarsData::Instance()->lst(),
669  KStarsData::Instance()->geo()->lat());
670  QPointF tileMid = m_proj->toScreen(obj, true, &visible);
671 
672  if (!visible || !m_proj->onScreen(tileMid) || !obj->isValid())
673  return false;
674 
675  //double northRotation = m_proj->findNorthPA(obj, tileMid.x(), tileMid.y())
676 
677  // convert 0 to +180 EAST, and 0 to -180 WEST to 0 to 360 CCW
678  auto PA = (obj->positionAngle() < 0) ? obj->positionAngle() + 360 : obj->positionAngle();
679  auto finalPA = m_proj->findNorthPA(obj, tileMid.x(), tileMid.y()) - PA;
680 
681  save();
682  translate(tileMid.toPoint());
683  rotate(finalPA);
684  obj->draw(this);
685  restore();
686 
687  return true;
688 }
689 #endif
690 
691 bool SkyQPainter::drawHips(bool useCache)
692 {
693  int w = viewport().width();
694  int h = viewport().height();
695 
696  if (useCache && m_HiPSImage)
697  {
698  drawImage(viewport(), *m_HiPSImage.data());
699  return true;
700  }
701  else
702  {
703  m_HiPSImage.reset(new QImage(w, h, QImage::Format_ARGB32_Premultiplied));
704  bool rendered = m_hipsRender->render(w, h, m_HiPSImage.data(), m_proj);
705  if (rendered)
706  drawImage(viewport(), *m_HiPSImage.data());
707  return rendered;
708  }
709 }
710 
711 bool SkyQPainter::drawTerrain(bool useCache)
712 {
713  // TODO
714  Q_UNUSED(useCache);
715  int w = viewport().width();
716  int h = viewport().height();
717  QImage *terrainImage = new QImage(w, h, QImage::Format_ARGB32_Premultiplied);
718  TerrainRenderer *renderer = TerrainRenderer::Instance();
719  bool rendered = renderer->render(w, h, terrainImage, m_proj);
720  if (rendered)
721  drawImage(viewport(), *terrainImage);
722 
723  delete (terrainImage);
724  return rendered;
725 }
726 
727 void SkyQPainter::drawCatalogObjectImage(const QPointF &pos, const CatalogObject &obj,
728  float positionAngle)
729 {
730  const auto &image = obj.image();
731 
732  if (!image.first)
733  return;
734 
735  double zoom = Options::zoomFactor();
736  double w = obj.a() * dms::PI * zoom / 10800.0;
737  double h = obj.e() * w;
738 
739  save();
740  translate(pos);
741  rotate(positionAngle);
742  drawImage(QRectF(-0.5 * w, -0.5 * h, w, h), image.second);
743  restore();
744 }
745 
747 {
748  if (!m_proj->checkVisibility(&obj))
749  return false;
750 
751  bool visible = false;
752  QPointF pos = m_proj->toScreen(&obj, true, &visible);
753  if (!visible || !m_proj->onScreen(pos))
754  return false;
755 
756  // if size is 0.0 set it to 1.0, this are normally stars (type 0 and 1)
757  // if we use size 0.0 the star wouldn't be drawn
758  float majorAxis = obj.a();
759  if (majorAxis == 0.0)
760  {
761  majorAxis = 1.0;
762  }
763 
764  float size = majorAxis * dms::PI * Options::zoomFactor() / 10800.0;
765 
766  const auto positionAngle =
767  m_proj->findNorthPA(&obj, pos.x(), pos.y()) - obj.pa() + 90;
768 
769  // Draw image
770  if (Options::showInlineImages() && Options::zoomFactor() > 5. * MINZOOM &&
771  !Options::showHIPS())
772  drawCatalogObjectImage(pos, obj, positionAngle);
773 
774  // Draw Symbol
775  drawDeepSkySymbol(pos, obj.type(), size, obj.e(), positionAngle);
776 
777  return true;
778 }
779 
780 void SkyQPainter::drawDeepSkySymbol(const QPointF &pos, int type, float size, float e,
781  float positionAngle)
782 {
783  float x = pos.x();
784  float y = pos.y();
785  float zoom = Options::zoomFactor();
786 
787  int isize = int(size);
788 
789  float dx1 = -0.5 * size;
790  float dx2 = 0.5 * size;
791  float dy1 = -1.0 * e * size / 2.;
792  float dy2 = e * size / 2.;
793  float x1 = x + dx1;
794  float x2 = x + dx2;
795  float y1 = y + dy1;
796  float y2 = y + dy2;
797 
798  float dxa = -size / 4.;
799  float dxb = size / 4.;
800  float dya = -1.0 * e * size / 4.;
801  float dyb = e * size / 4.;
802  float xa = x + dxa;
803  float xb = x + dxb;
804  float ya = y + dya;
805  float yb = y + dyb;
806 
807  QString color;
808 
809  float psize;
810 
811  QBrush tempBrush;
812 
813  std::function<void(float, float, float, float)> lambdaDrawEllipse;
814  std::function<void(float, float, float, float)> lambdaDrawLine;
815  std::function<void(float, float, float, float)> lambdaDrawCross;
816 
817  if (Options::useAntialias())
818  {
819  lambdaDrawEllipse = [this](float x, float y, float width, float height)
820  {
821  drawEllipse(QRectF(x, y, width, height));
822  };
823  lambdaDrawLine = [this](float x1, float y1, float x2, float y2)
824  {
825  drawLine(QLineF(x1, y1, x2, y2));
826  };
827  lambdaDrawCross = [this](float centerX, float centerY, float sizeX, float sizeY)
828  {
829  drawLine(
830  QLineF(centerX - sizeX / 2., centerY, centerX + sizeX / 2., centerY));
831  drawLine(
832  QLineF(centerX, centerY - sizeY / 2., centerX, centerY + sizeY / 2.));
833  };
834  }
835  else
836  {
837  lambdaDrawEllipse = [this](float x, float y, float width, float height)
838  {
839  drawEllipse(QRect(x, y, width, height));
840  };
841  lambdaDrawLine = [this](float x1, float y1, float x2, float y2)
842  {
843  drawLine(QLine(x1, y1, x2, y2));
844  };
845  lambdaDrawCross = [this](float centerX, float centerY, float sizeX, float sizeY)
846  {
847  drawLine(QLine(centerX - sizeX / 2., centerY, centerX + sizeX / 2., centerY));
848  drawLine(QLine(centerX, centerY - sizeY / 2., centerX, centerY + sizeY / 2.));
849  };
850  }
851 
852  switch ((SkyObject::TYPE)type)
853  {
854  case SkyObject::STAR:
855  case SkyObject::CATALOG_STAR: //catalog star
856  //Some NGC/IC objects are stars...changed their type to 1 (was double star)
857  if (size < 2.)
858  size = 2.;
859  lambdaDrawEllipse(x - size / 2., y - size / 2., size, size);
860  break;
861  case SkyObject::PLANET: //Planet
862  break;
863  case SkyObject::OPEN_CLUSTER: //Open cluster; draw circle of points
864  case SkyObject::ASTERISM: // Asterism
865  {
866  tempBrush = brush();
867  color = pen().color().name();
868  setBrush(pen().color());
869  psize = 2.;
870  if (size > 50.)
871  psize *= 2.;
872  if (size > 100.)
873  psize *= 2.;
874  auto putDot = [psize, &lambdaDrawEllipse](float x, float y)
875  {
876  lambdaDrawEllipse(x - psize / 2., y - psize / 2., psize, psize);
877  };
878  putDot(xa, y1);
879  putDot(xb, y1);
880  putDot(xa, y2);
881  putDot(xb, y2);
882  putDot(x1, ya);
883  putDot(x1, yb);
884  putDot(x2, ya);
885  putDot(x2, yb);
886  setBrush(tempBrush);
887  break;
888  }
889  case SkyObject::GLOBULAR_CLUSTER: //Globular Cluster
890  if (size < 2.)
891  size = 2.;
892  save();
893  translate(x, y);
894  color = pen().color().name();
895  rotate(positionAngle); //rotate the coordinate system
896  lambdaDrawEllipse(dx1, dy1, size, e * size);
897  lambdaDrawCross(0, 0, size, e * size);
898  restore(); //reset coordinate system
899  break;
900 
901  case SkyObject::GASEOUS_NEBULA: //Gaseous Nebula
902  case SkyObject::DARK_NEBULA: // Dark Nebula
903  save();
904  translate(x, y);
905  rotate(positionAngle); //rotate the coordinate system
906  color = pen().color().name();
907  lambdaDrawLine(dx1, dy1, dx2, dy1);
908  lambdaDrawLine(dx2, dy1, dx2, dy2);
909  lambdaDrawLine(dx2, dy2, dx1, dy2);
910  lambdaDrawLine(dx1, dy2, dx1, dy1);
911  restore(); //reset coordinate system
912  break;
913  case SkyObject::PLANETARY_NEBULA: //Planetary Nebula
914  if (size < 2.)
915  size = 2.;
916  save();
917  translate(x, y);
918  rotate(positionAngle); //rotate the coordinate system
919  color = pen().color().name();
920  lambdaDrawEllipse(dx1, dy1, size, e * size);
921  lambdaDrawLine(0., dy1, 0., dy1 - e * size / 2.);
922  lambdaDrawLine(0., dy2, 0., dy2 + e * size / 2.);
923  lambdaDrawLine(dx1, 0., dx1 - size / 2., 0.);
924  lambdaDrawLine(dx2, 0., dx2 + size / 2., 0.);
925  restore(); //reset coordinate system
926  break;
927  case SkyObject::SUPERNOVA_REMNANT: //Supernova remnant // FIXME: Why is SNR drawn different from a gaseous nebula?
928  save();
929  translate(x, y);
930  rotate(positionAngle); //rotate the coordinate system
931  color = pen().color().name();
932  lambdaDrawLine(0., dy1, dx2, 0.);
933  lambdaDrawLine(dx2, 0., 0., dy2);
934  lambdaDrawLine(0., dy2, dx1, 0.);
935  lambdaDrawLine(dx1, 0., 0., dy1);
936  restore(); //reset coordinate system
937  break;
938  case SkyObject::GALAXY: //Galaxy
939  case SkyObject::QUASAR: // Quasar
940  color = pen().color().name();
941  if (size < 1. && zoom > 20 * MINZOOM)
942  size = 3.; //force ellipse above zoomFactor 20
943  if (size < 1. && zoom > 5 * MINZOOM)
944  size = 1.; //force points above zoomFactor 5
945  if (size > 2.)
946  {
947  save();
948  translate(x, y);
949  rotate(positionAngle); //rotate the coordinate system
950  lambdaDrawEllipse(dx1, dy1, size, e * size);
951  restore(); //reset coordinate system
952  }
953  else if (size > 0.)
954  {
955  drawPoint(QPointF(x, y));
956  }
957  break;
958  case SkyObject::GALAXY_CLUSTER: // Galaxy cluster - draw a dashed circle
959  {
960  tempBrush = brush();
961  setBrush(QBrush());
962  color = pen().color().name();
963  save();
964  translate(x, y);
965  rotate(positionAngle); //rotate the coordinate system
966  QPen newPen = pen();
967  newPen.setStyle(Qt::DashLine);
968  setPen(newPen);
969  lambdaDrawEllipse(dx1, dy1, size, e * size);
970  restore();
971  setBrush(tempBrush);
972  break;
973  }
974  default
975 : // Unknown object or something we don't know how to draw. Just draw an ellipse with a ?-mark
976  color = pen().color().name();
977  if (size < 1. && zoom > 20 * MINZOOM)
978  size = 3.; //force ellipse above zoomFactor 20
979  if (size < 1. && zoom > 5 * MINZOOM)
980  size = 1.; //force points above zoomFactor 5
981  if (size > 2.)
982  {
983  save();
984  QFont f = font();
985  const QString qMark = " ? ";
986 
987 #if QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)
988  double scaleFactor = 0.8 * size / fontMetrics().horizontalAdvance(qMark);
989 #else
990  double scaleFactor = 0.8 * size / fontMetrics().width(qMark);
991 #endif
992 
993  f.setPointSizeF(f.pointSizeF() * scaleFactor);
994  setFont(f);
995  translate(x, y);
996  rotate(positionAngle); //rotate the coordinate system
997  lambdaDrawEllipse(dx1, dy1, size, e * size);
998  if (Options::useAntialias())
999  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 
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 
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 
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 
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 }
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:380
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:265
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
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:37
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:270
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
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:299
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:86
A simple container object to hold the minimum information for a Deeb 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-2022 The KDE developers.
Generated on Mon Aug 8 2022 04:13:26 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.