File indexing completed on 2024-04-21 14:46:30

0001 /*
0002     SPDX-FileCopyrightText: 2011 Rafał Kułaga <rl.kulaga@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "legend.h"
0008 
0009 #include "colorscheme.h"
0010 #include "kstarsdata.h"
0011 #include "skymap.h"
0012 #include "skyqpainter.h"
0013 #include "Options.h"
0014 
0015 #include <QBrush>
0016 
0017 namespace
0018 {
0019 const int symbolSize      = 15;
0020 const int bRectWidth      = 100;
0021 const int bRectHeight     = 45;
0022 const int maxHScalePixels = 200;
0023 const int maxVScalePixels = 100;
0024 const int xSymbolSpacing  = 100;
0025 const int ySymbolSpacing  = 70;
0026 }
0027 
0028 Legend::Legend(LEGEND_ORIENTATION orientation, LEGEND_POSITION pos)
0029     : m_SkyMap(SkyMap::Instance()), m_Orientation(orientation),
0030       m_Position(pos), m_PositionFloating(QPoint(0, 0)), m_cScheme(KStarsData::Instance()->colorScheme()),
0031       m_SymbolSize(symbolSize), m_BRectWidth(bRectWidth), m_BRectHeight(bRectHeight),
0032       m_MaxHScalePixels(maxHScalePixels), m_MaxVScalePixels(maxVScalePixels), m_XSymbolSpacing(xSymbolSpacing),
0033       m_YSymbolSpacing(ySymbolSpacing)
0034 {
0035     m_BgColor = m_cScheme->colorNamed("SkyColor");
0036 }
0037 
0038 Legend::~Legend()
0039 {
0040     if (m_Painter != nullptr && m_DeletePainter)
0041     {
0042         delete m_Painter;
0043     }
0044 }
0045 
0046 QSize Legend::calculateSize()
0047 {
0048     int width  = 0;
0049     int height = 0;
0050 
0051     switch (m_Orientation)
0052     {
0053         case LO_HORIZONTAL:
0054         {
0055             switch (m_Type)
0056             {
0057                 case LT_SCALE_ONLY:
0058                 {
0059                     width  = 40 + m_MaxHScalePixels;
0060                     height = 60;
0061                     break;
0062                 }
0063 
0064                 case LT_MAGNITUDES_ONLY:
0065                 {
0066                     width  = 140;
0067                     height = 70;
0068                     break;
0069                 }
0070 
0071                 case LT_SYMBOLS_ONLY:
0072                 {
0073                     width  = 7 * m_XSymbolSpacing;
0074                     height = 20 + m_SymbolSize + m_BRectHeight;
0075                     break;
0076                 }
0077 
0078                 case LT_SCALE_MAGNITUDES:
0079                 {
0080                     width  = 160 + m_MaxHScalePixels;
0081                     height = 70;
0082                     break;
0083                 }
0084 
0085                 case LT_FULL:
0086                 {
0087                     width  = 7 * m_XSymbolSpacing;
0088                     height = 20 + m_SymbolSize + m_BRectHeight + 70;
0089                     break;
0090                 }
0091 
0092                 default:
0093                     break; // should never happen
0094             }
0095         }
0096 
0097         break;
0098 
0099         case LO_VERTICAL:
0100         {
0101             switch (m_Type)
0102             {
0103                 case LT_SCALE_ONLY:
0104                 {
0105                     width  = 120;
0106                     height = 40 + m_MaxVScalePixels;
0107                     break;
0108                 }
0109 
0110                 case LT_MAGNITUDES_ONLY:
0111                 {
0112                     width  = 140;
0113                     height = 70;
0114                     break;
0115                 }
0116 
0117                 case LT_SYMBOLS_ONLY:
0118                 {
0119                     width  = 120;
0120                     height = 7 * m_YSymbolSpacing;
0121                     break;
0122                 }
0123 
0124                 case LT_SCALE_MAGNITUDES:
0125                 {
0126                     width  = 120;
0127                     height = 100 + m_MaxVScalePixels;
0128                     break;
0129                 }
0130 
0131                 case LT_FULL:
0132                 {
0133                     width  = 120;
0134                     height = 100 + 7 * m_YSymbolSpacing + m_MaxVScalePixels;
0135                     break;
0136                 }
0137 
0138                 default:
0139                     break; // should never happen
0140             }
0141 
0142             break;
0143         }
0144 
0145         default:
0146         {
0147             return QSize();
0148         }
0149     }
0150 
0151     return QSize(width, height);
0152 }
0153 
0154 void Legend::paintLegend(QPaintDevice *pd)
0155 {
0156     if (m_Painter)
0157     {
0158         delete m_Painter;
0159     }
0160 
0161     m_Painter       = new SkyQPainter(m_SkyMap, pd);
0162     m_DeletePainter = true;
0163     m_Painter->begin();
0164 
0165     paintLegend(m_Painter);
0166 
0167     m_Painter->end();
0168 }
0169 
0170 void Legend::paintLegend(SkyQPainter *painter)
0171 {
0172     if (!m_DeletePainter)
0173     {
0174         m_Painter = painter;
0175     }
0176 
0177     if (m_Position != LP_FLOATING)
0178     {
0179         m_PositionFloating = positionToDeviceCoord(painter->device());
0180     }
0181 
0182     m_Painter->translate(m_PositionFloating.x(), m_PositionFloating.y());
0183 
0184     m_Painter->setFont(m_Font);
0185 
0186     QBrush backgroundBrush(m_BgColor, Qt::SolidPattern);
0187     QPen backgroundPen(m_cScheme->colorNamed("SNameColor"));
0188     backgroundPen.setStyle(Qt::SolidLine);
0189 
0190     // set brush & pen
0191     m_Painter->setBrush(backgroundBrush);
0192     m_Painter->setPen(backgroundPen);
0193 
0194     QSize size = calculateSize();
0195     if (m_DrawFrame)
0196     {
0197         m_Painter->drawRect(1, 1, size.width(), size.height());
0198     }
0199 
0200     else
0201     {
0202         QPen noLinePen;
0203         noLinePen.setStyle(Qt::NoPen);
0204 
0205         m_Painter->setPen(noLinePen);
0206         m_Painter->drawRect(1, 1, size.width(), size.height());
0207 
0208         m_Painter->setPen(backgroundPen);
0209     }
0210 
0211     switch (m_Orientation)
0212     {
0213         case LO_HORIZONTAL:
0214         {
0215             switch (m_Type)
0216             {
0217                 case LT_SCALE_ONLY:
0218                 {
0219                     paintScale(QPointF(20, 20));
0220                     break;
0221                 }
0222 
0223                 case LT_MAGNITUDES_ONLY:
0224                 {
0225                     paintMagnitudes(QPointF(20, 20));
0226                     break;
0227                 }
0228 
0229                 case LT_SYMBOLS_ONLY:
0230                 {
0231                     paintSymbols(QPointF(20, 20));
0232                     break;
0233                 }
0234 
0235                 case LT_SCALE_MAGNITUDES:
0236                 {
0237                     paintMagnitudes(QPointF(20, 20));
0238                     paintScale(QPointF(150, 20));
0239                     break;
0240                 }
0241 
0242                 case LT_FULL:
0243                 {
0244                     paintSymbols(QPointF(20, 20));
0245                     paintMagnitudes(QPointF(10, 40 + m_SymbolSize + m_BRectHeight));
0246                     paintScale(QPointF(200, 40 + m_SymbolSize + m_BRectHeight));
0247                     break;
0248                 }
0249 
0250                 default:
0251                     break; // should never happen
0252             }
0253 
0254             break;
0255         }
0256 
0257         case LO_VERTICAL:
0258         {
0259             switch (m_Type)
0260             {
0261                 case LT_SCALE_ONLY:
0262                 {
0263                     paintScale(QPointF(20, 20));
0264                     break;
0265                 }
0266 
0267                 case LT_MAGNITUDES_ONLY:
0268                 {
0269                     paintMagnitudes(QPointF(20, 20));
0270                     break;
0271                 }
0272 
0273                 case LT_SYMBOLS_ONLY:
0274                 {
0275                     paintSymbols(QPointF(20, 20));
0276                     break;
0277                 }
0278 
0279                 case LT_SCALE_MAGNITUDES:
0280                 {
0281                     paintMagnitudes(QPointF(7, 20));
0282                     paintScale(QPointF(20, 80));
0283                     break;
0284                 }
0285 
0286                 case LT_FULL:
0287                 {
0288                     paintSymbols(QPointF(30, 20));
0289                     paintMagnitudes(QPointF(7, 30 + 7 * m_YSymbolSpacing));
0290                     paintScale(QPointF(20, 90 + 7 * m_YSymbolSpacing));
0291                     break;
0292                 }
0293 
0294                 default:
0295                     break; // should never happen
0296             }
0297 
0298             break;
0299         }
0300 
0301         default:
0302             break; // should never happen
0303     }
0304 }
0305 
0306 void Legend::paintLegend(QPaintDevice *pd, LEGEND_TYPE type, LEGEND_POSITION pos)
0307 {
0308     LEGEND_TYPE prevType    = m_Type;
0309     LEGEND_POSITION prevPos = m_Position;
0310 
0311     m_Type     = type;
0312     m_Position = pos;
0313 
0314     paintLegend(pd);
0315 
0316     m_Type     = prevType;
0317     m_Position = prevPos;
0318 }
0319 
0320 void Legend::paintLegend(SkyQPainter *painter, LEGEND_TYPE type, LEGEND_POSITION pos)
0321 {
0322     LEGEND_TYPE prevType    = m_Type;
0323     LEGEND_POSITION prevPos = m_Position;
0324 
0325     m_Type     = type;
0326     m_Position = pos;
0327 
0328     paintLegend(painter);
0329 
0330     m_Type     = prevType;
0331     m_Position = prevPos;
0332 }
0333 
0334 void Legend::paintSymbols(QPointF pos)
0335 {
0336     qreal x = pos.x();
0337     qreal y = pos.y();
0338 
0339     x += 30;
0340 
0341     switch (m_Orientation)
0342     {
0343         case Legend::LO_HORIZONTAL:
0344         {
0345             // paint Open Cluster/Asterism symbol
0346             QString label1 = i18n("Open Cluster") + '\n' + i18n("Asterism");
0347             paintSymbol(QPointF(x, y), 3, 1, 0, label1);
0348             x += m_XSymbolSpacing;
0349 
0350             // paint Globular Cluster symbol
0351             paintSymbol(QPointF(x, y), 4, 1, 0, i18n("Globular Cluster"));
0352             x += m_XSymbolSpacing;
0353 
0354             // paint Gaseous Nebula/Dark Nebula symbol
0355             QString label3 = i18n("Gaseous Nebula") + '\n' + i18n("Dark Nebula");
0356             paintSymbol(QPointF(x, y), 5, 1, 0, label3);
0357             x += m_XSymbolSpacing;
0358 
0359             // paint Planetary Nebula symbol
0360             paintSymbol(QPointF(x, y), 6, 1, 0, i18n("Planetary Nebula"));
0361             x += m_XSymbolSpacing;
0362 
0363             // paint Supernova Remnant
0364             paintSymbol(QPointF(x, y), 7, 1, 0, i18n("Supernova Remnant"));
0365             x += m_XSymbolSpacing;
0366 
0367             // paint Galaxy/Quasar
0368             QString label6 = i18n("Galaxy") + '\n' + i18n("Quasar");
0369             paintSymbol(QPointF(x, y), 8, 0.5, 60, label6);
0370             x += m_XSymbolSpacing;
0371 
0372             // paint Galaxy Cluster
0373             paintSymbol(QPointF(x, y), 14, 1, 0, i18n("Galactic Cluster"));
0374 
0375             break;
0376         }
0377 
0378         case Legend::LO_VERTICAL:
0379         {
0380             // paint Open Cluster/Asterism symbol
0381             QString label1 = i18n("Open Cluster") + '\n' + i18n("Asterism");
0382             paintSymbol(QPointF(x, y), 3, 1, 0, label1);
0383             y += m_YSymbolSpacing;
0384 
0385             // paint Globular Cluster symbol
0386             paintSymbol(QPointF(x, y), 4, 1, 0, i18n("Globular Cluster"));
0387             y += m_YSymbolSpacing;
0388 
0389             // paint Gaseous Nebula/Dark Nebula symbol
0390             QString label3 = i18n("Gaseous Nebula") + '\n' + i18n("Dark Nebula");
0391             paintSymbol(QPointF(x, y), 5, 1, 0, label3);
0392             y += m_YSymbolSpacing;
0393 
0394             // paint Planetary Nebula symbol
0395             paintSymbol(QPointF(x, y), 6, 1, 0, i18n("Planetary Nebula"));
0396             y += m_YSymbolSpacing;
0397 
0398             // paint Supernova Remnant
0399             paintSymbol(QPointF(x, y), 7, 1, 0, i18n("Supernova Remnant"));
0400             y += m_YSymbolSpacing;
0401 
0402             // paint Galaxy/Quasar
0403             QString label6 = i18n("Galaxy") + '\n' + i18n("Quasar");
0404             paintSymbol(QPointF(x, y), 8, 0.5, 60, label6);
0405             y += m_YSymbolSpacing;
0406 
0407             // paint Galaxy Cluster
0408             paintSymbol(QPointF(x, y), 14, 1, 0, i18n("Galactic Cluster"));
0409 
0410             break;
0411         }
0412         default:
0413             return; // should never happen
0414     }
0415 }
0416 
0417 void Legend::paintSymbol(QPointF pos, int type, float e, float angle, QString label)
0418 {
0419     qreal x              = pos.x();
0420     qreal y              = pos.y();
0421     qreal bRectHalfWidth = (qreal)m_BRectWidth / 2;
0422 
0423     // paint symbol
0424     m_Painter->drawDeepSkySymbol(pos, type, m_SymbolSize, e, angle);
0425     QRectF bRect(QPoint(x - bRectHalfWidth, y + m_SymbolSize),
0426                  QPoint(x + bRectHalfWidth, y + m_SymbolSize + m_BRectHeight));
0427     //m_Painter->drawRect(bRect);
0428     // paint label
0429     m_Painter->drawText(bRect, label, QTextOption(Qt::AlignHCenter));
0430 }
0431 
0432 void Legend::paintMagnitudes(QPointF pos)
0433 {
0434     qreal x = pos.x();
0435     qreal y = pos.y();
0436 
0437     m_Painter->drawText(x, y, i18n("Star Magnitudes:"));
0438     y += 15;
0439 
0440     for (int i = 1; i <= 9; i += 2)
0441     {
0442         m_Painter->drawPointSource(QPointF(x + i * 10, y), m_Painter->starWidth(i));
0443         m_Painter->drawText(x + i * 10 - 4, y + 20, QString::number(i));
0444     }
0445 }
0446 
0447 void Legend::paintScale(QPointF pos)
0448 {
0449     qreal maxScalePixels;
0450 
0451     switch (m_Orientation)
0452     {
0453         case LO_HORIZONTAL:
0454         {
0455             maxScalePixels = m_MaxHScalePixels;
0456             break;
0457         }
0458 
0459         case LO_VERTICAL:
0460         {
0461             maxScalePixels = m_MaxVScalePixels;
0462             break;
0463         }
0464 
0465         default:
0466             return; // should never happen
0467     }
0468 
0469     qreal maxArcsec = maxScalePixels * 57.3 * 3600 / Options::zoomFactor();
0470 
0471     int deg    = 0;
0472     int arcmin = 0;
0473     int arcsec = 0;
0474 
0475     QString lab;
0476     if (maxArcsec >= 3600)
0477     {
0478         deg = maxArcsec / 3600;
0479         lab = QString::number(deg) + QString::fromWCharArray(L"\u00B0");
0480     }
0481 
0482     else if (maxArcsec >= 60)
0483     {
0484         arcmin = maxArcsec / 60;
0485         lab    = QString::number(arcmin) + '\'';
0486     }
0487 
0488     else
0489     {
0490         arcsec = maxArcsec;
0491         lab    = QString::number(arcsec) + "\"";
0492     }
0493 
0494     int actualArcsec = 3600 * deg + 60 * arcmin + arcsec;
0495 
0496     qreal size = actualArcsec * Options::zoomFactor() / 57.3 / 3600;
0497 
0498     qreal x = pos.x();
0499     qreal y = pos.y();
0500 
0501     switch (m_Orientation)
0502     {
0503         case LO_HORIZONTAL:
0504         {
0505             m_Painter->drawText(pos, i18n("Chart Scale:"));
0506             y += 15;
0507 
0508             m_Painter->drawLine(x, y, x + size, y);
0509             // paint line endings
0510             m_Painter->drawLine(x, y - 5, x, y + 5);
0511             m_Painter->drawLine(x + size, y - 5, x + size, y + 5);
0512 
0513             // paint scale text
0514             QRectF bRect(QPoint(x, y), QPoint(x + size, y + 20));
0515             m_Painter->drawText(bRect, lab, QTextOption(Qt::AlignHCenter));
0516 
0517             break;
0518         }
0519 
0520         case LO_VERTICAL:
0521         {
0522             m_Painter->drawText(pos, i18n("Chart Scale:"));
0523             y += 10;
0524             x += 40;
0525 
0526             m_Painter->drawLine(x, y, x, y + size);
0527             // paint line endings
0528             m_Painter->drawLine(x - 5, y, x + 5, y);
0529             m_Painter->drawLine(x - 5, y + size, x + 5, y + size);
0530 
0531             // paint scale text
0532             QRectF bRect(QPoint(x + 5, y), QPoint(x + 20, y + size));
0533             //m_Painter->drawRect(bRect);
0534             m_Painter->drawText(bRect, lab, QTextOption(Qt::AlignVCenter));
0535 
0536             break;
0537         }
0538 
0539         default:
0540             return; // should never happen
0541     }
0542 }
0543 
0544 QPoint Legend::positionToDeviceCoord(QPaintDevice *pd)
0545 {
0546     QSize legendSize = calculateSize();
0547 
0548     switch (m_Position)
0549     {
0550         case LP_UPPER_LEFT: // position: upper left corner
0551         {
0552             return QPoint(0, 0);
0553         }
0554 
0555         case LP_UPPER_RIGHT: // position: upper right corner
0556         {
0557             return QPoint(pd->width() - legendSize.width(), 0);
0558         }
0559 
0560         case LP_LOWER_LEFT: // position: lower left corner
0561         {
0562             return QPoint(0, pd->height() - legendSize.height());
0563         }
0564 
0565         case LP_LOWER_RIGHT: // position: lower right corner
0566         {
0567             return QPoint(pd->width() - legendSize.width(), pd->height() - legendSize.height());
0568         }
0569 
0570         default: // legend is floating
0571         {
0572             return QPoint();
0573         }
0574     }
0575 }
0576 
0577 Legend::Legend(const Legend &o)
0578     : m_Painter(nullptr), m_SkyMap(o.m_SkyMap), m_DeletePainter(o.m_DeletePainter), m_Type(o.m_Type),
0579       m_Orientation(o.m_Orientation), m_Position(o.m_Position), m_PositionFloating(o.m_PositionFloating),
0580       m_cScheme(o.m_cScheme), m_Font(o.m_Font), m_BgColor(o.m_BgColor), m_DrawFrame(o.m_DrawFrame),
0581       m_SymbolSize(o.m_SymbolSize), m_BRectWidth(o.m_BRectWidth), m_BRectHeight(o.m_BRectHeight),
0582       m_MaxHScalePixels(o.m_MaxHScalePixels), m_MaxVScalePixels(o.m_MaxVScalePixels),
0583       m_XSymbolSpacing(o.m_XSymbolSpacing), m_YSymbolSpacing(o.m_YSymbolSpacing)
0584 {
0585 }
0586 
0587 Legend& Legend::operator=(const Legend &o) noexcept
0588 {
0589     m_SkyMap = o.m_SkyMap;
0590     m_DeletePainter = o.m_DeletePainter;
0591     m_Type = o.m_Type;
0592     m_Orientation = o.m_Orientation;
0593     m_Position = o.m_Position;
0594     m_PositionFloating = o.m_PositionFloating;
0595     m_cScheme = o.m_cScheme;
0596     m_Font = o.m_Font;
0597     m_BgColor = o.m_BgColor;
0598     m_DrawFrame = o.m_DrawFrame;
0599     m_SymbolSize = o.m_SymbolSize;
0600     m_BRectWidth = o.m_BRectWidth;
0601     m_BRectHeight = o.m_BRectHeight;
0602     m_MaxHScalePixels = o.m_MaxHScalePixels;
0603     m_MaxVScalePixels = o.m_MaxVScalePixels;
0604     m_XSymbolSpacing = o.m_XSymbolSpacing;
0605     m_YSymbolSpacing = o.m_YSymbolSpacing;
0606     return *this;
0607 }