File indexing completed on 2024-03-24 03:47:34
0001 /* 0002 SPDX-FileCopyrightText: 2016 Akarsh Simha <akarsh@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "exporteyepieceview.h" 0008 0009 #include "dms.h" 0010 #include "eyepiecefield.h" 0011 #include "kstarsdata.h" 0012 #include "Options.h" 0013 #include "skypoint.h" 0014 0015 #include <QComboBox> 0016 #include <QDialogButtonBox> 0017 #include <QFileDialog> 0018 #include <QHBoxLayout> 0019 #include <QLabel> 0020 #include <QPainter> 0021 #include <QPixmap> 0022 #include <QVBoxLayout> 0023 #include <QWidget> 0024 0025 ExportEyepieceView::ExportEyepieceView(const SkyPoint *_sp, const KStarsDateTime &dt, const QPixmap *renderImage, 0026 const QPixmap *renderChart, QWidget *parent) 0027 : QDialog(parent), m_dt(dt) 0028 { 0029 #ifdef Q_OS_OSX 0030 setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); 0031 #endif 0032 m_sp.reset(new SkyPoint(*_sp)); // Work on a copy. 0033 0034 Q_ASSERT(renderChart); 0035 m_renderChart.reset(new QPixmap(*renderChart)); 0036 0037 if (renderImage != nullptr) 0038 m_renderImage.reset(new QPixmap(*renderImage)); 0039 0040 setWindowTitle(i18nc("@title:window", "Export eyepiece view")); 0041 0042 QWidget *mainWidget = new QWidget(this); 0043 QVBoxLayout *mainLayout = new QVBoxLayout; 0044 mainLayout->addWidget(mainWidget); 0045 setLayout(mainLayout); 0046 0047 QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Save | QDialogButtonBox::Cancel); 0048 mainLayout->addWidget(buttonBox); 0049 connect(buttonBox, SIGNAL(rejected()), this, SLOT(slotCloseDialog())); 0050 connect(buttonBox, SIGNAL(accepted()), this, SLOT(slotSaveImage())); 0051 0052 QVBoxLayout *rows = new QVBoxLayout; 0053 mainWidget->setLayout(rows); 0054 0055 QLabel *tickInfoLabel = new QLabel(i18n("Overlay orientation vs. time ticks: "), this); 0056 m_tickConfigCombo = new QComboBox(this); 0057 m_tickConfigCombo->addItem(i18n("None")); 0058 m_tickConfigCombo->addItem(i18n("Towards Zenith")); 0059 m_tickConfigCombo->addItem(i18n("Dobsonian View")); 0060 0061 QHBoxLayout *optionsLayout = new QHBoxLayout; 0062 optionsLayout->addWidget(tickInfoLabel); 0063 optionsLayout->addWidget(m_tickConfigCombo); 0064 optionsLayout->addStretch(); 0065 rows->addLayout(optionsLayout); 0066 0067 m_tickWarningLabel = new QLabel(this); 0068 rows->addWidget(m_tickWarningLabel); 0069 0070 m_outputDisplay = new QLabel; 0071 m_outputDisplay->setBackgroundRole(QPalette::Base); 0072 m_outputDisplay->setScaledContents(false); 0073 m_outputDisplay->setMinimumWidth(400); 0074 m_outputDisplay->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); 0075 rows->addWidget(m_outputDisplay); 0076 0077 connect(m_tickConfigCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(slotOverlayTicks(int))); 0078 connect(m_tickConfigCombo, SIGNAL(activated(int)), this, SLOT(slotOverlayTicks(int))); 0079 0080 render(); 0081 show(); 0082 } 0083 0084 void ExportEyepieceView::slotOverlayTicks(int tickConfig) 0085 { 0086 m_tickConfig = tickConfig; 0087 if (tickConfig == 0) 0088 m_tickWarningLabel->setText(QString()); 0089 else if (tickConfig == 1) 0090 m_tickWarningLabel->setText(i18n("Note: This overlay makes sense only if the view was generated in alt/az mode " 0091 "with a preset such as Refractor or Vanilla")); 0092 else if (tickConfig == 2) 0093 m_tickWarningLabel->setText(i18n("Note: This overlay makes sense only if the view was generated in alt/az " 0094 "mode with a preset such as Dobsonian")); 0095 render(); 0096 } 0097 0098 void ExportEyepieceView::render() 0099 { 0100 float baseWidth = m_renderChart->width(); 0101 float baseHeight = m_renderChart->height(); 0102 0103 if (m_renderImage.get() != nullptr) 0104 m_output = QImage(int(baseWidth * 2.25), int(baseHeight), 0105 QImage::Format_ARGB32); // 0.25 * baseWidth gap between the images 0106 else 0107 m_output = QImage(int(baseWidth), int(baseHeight), QImage::Format_ARGB32); 0108 0109 m_output.fill(Qt::white); 0110 QPainter op(&m_output); 0111 op.drawPixmap(QPointF(0, 0), *m_renderChart); 0112 if (m_renderImage.get() != nullptr) 0113 op.drawPixmap(QPointF(baseWidth * 1.25, 0), *m_renderImage); 0114 0115 if (m_tickConfig != 0 && Options::useAltAz()) // FIXME: this is very skymap-state-heavy for my happiness --asimha 0116 { 0117 // we must draw ticks 0118 QImage tickOverlay(baseWidth, baseHeight, QImage::Format_ARGB32); 0119 tickOverlay.fill(Qt::transparent); 0120 0121 QPainter p(&tickOverlay); 0122 p.setPen(Qt::red); // FIXME: Determine color depending on situation, or make it configurable 0123 double rEnd = 0.85 * (baseWidth / 2.); 0124 double rStart = 0.8 * (baseWidth / 2.); 0125 QFont font; 0126 font.setPixelSize((rEnd - rStart)); 0127 p.setFont(font); 0128 0129 GeoLocation *geo = KStarsData::Instance()->geo(); 0130 double alt0 = m_sp->alt().Degrees(); // altitude when hour = 0, i.e. at m_dt (see below). 0131 dms northAngle0 = EyepieceField::findNorthAngle(m_sp.get(), geo->lat()); 0132 0133 for (float hour = -3.5; hour <= 3.5; hour += 0.5) 0134 { 0135 dms rotation; // rotation 0136 0137 // FIXME: Code duplication : code duplicated from EyepieceField. This should really be a member of SkyPoint or something. 0138 SkyPoint sp = SkyPoint::timeTransformed(m_sp.get(), m_dt, geo, hour); 0139 double alt = sp.alt().Degrees(); 0140 dms northAngle = EyepieceField::findNorthAngle(&sp, geo->lat()); 0141 0142 rotation = (northAngle - northAngle0); 0143 if (m_tickConfig == 2) 0144 { 0145 // Dobsonian: add additional CW rotation by altitude, but compensate for the fact that we've already rotated by alt0 0146 rotation = rotation - dms((alt - alt0)); 0147 } 0148 rotation = rotation.reduce(); 0149 p.save(); 0150 p.translate(baseWidth / 2.0, baseHeight / 2.0); 0151 p.rotate(-(rotation.Degrees() + 90.0)); 0152 p.drawLine(QPointF(rStart, 0), QPointF(rEnd, 0)); 0153 QTime ct = geo->UTtoLT(m_dt.addSecs(3600.0 * hour)).time(); 0154 p.drawText(QPointF(rEnd + 0.01 * baseWidth, 0), QString::asprintf("%02d:%02d", ct.hour(), ct.minute())); 0155 p.restore(); 0156 } 0157 p.end(); 0158 op.drawImage(QPointF(0, 0), tickOverlay); 0159 if (m_renderImage.get() != nullptr) 0160 { 0161 op.drawImage(QPointF(baseWidth * 1.25, 0), tickOverlay); 0162 } 0163 } 0164 op.end(); 0165 0166 m_outputDisplay->setPixmap((QPixmap::fromImage(m_output)) 0167 .scaled(m_outputDisplay->width(), m_outputDisplay->height(), Qt::KeepAspectRatio, 0168 Qt::SmoothTransformation)); 0169 } 0170 0171 void ExportEyepieceView::slotSaveImage() 0172 { 0173 // does nothing at the moment. TODO: Implement. 0174 QString fileName = QFileDialog::getSaveFileName(this, i18nc("@title:window", "Save Image as"), QString(), 0175 i18n("Image files (*.png *.jpg *.xpm *.bmp *.gif)")); 0176 if (!fileName.isEmpty()) 0177 { 0178 m_output.save(fileName); 0179 slotCloseDialog(); 0180 } 0181 } 0182 0183 void ExportEyepieceView::slotCloseDialog() 0184 { 0185 hide(); 0186 deleteLater(); 0187 }