File indexing completed on 2024-04-21 03:44:55

0001 /*
0002     SPDX-FileCopyrightText: 2003 Jason Harris <jharris@30doradus.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "planetviewer.h"
0008 
0009 #include "ksfilereader.h"
0010 #include "ksnumbers.h"
0011 #include "kstarsdata.h"
0012 #include "ksutils.h"
0013 #include "skyobjects/ksplanet.h"
0014 #include "skyobjects/ksplanetbase.h"
0015 #include "widgets/timespinbox.h"
0016 
0017 #include <KLocalizedString>
0018 #include <KPlotting/KPlotAxis>
0019 #include <KPlotting/KPlotObject>
0020 #include <KPlotting/KPlotPoint>
0021 #include <KPlotting/KPlotWidget>
0022 
0023 #include <QFile>
0024 #include <QKeyEvent>
0025 #include <QVBoxLayout>
0026 
0027 #include <cmath>
0028 
0029 // Qt version calming
0030 #include <qtskipemptyparts.h>
0031 
0032 PlanetViewerUI::PlanetViewerUI(QWidget *p) : QFrame(p)
0033 {
0034     setupUi(this);
0035 }
0036 
0037 PlanetViewer::PlanetViewer(QWidget *parent) : QDialog(parent), scale(1.0), isClockRunning(false), tmr(this)
0038 {
0039 #ifdef Q_OS_OSX
0040     setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint);
0041 #endif
0042     KStarsData *data = KStarsData::Instance();
0043     pw               = new PlanetViewerUI(this);
0044 
0045     QVBoxLayout *mainLayout = new QVBoxLayout;
0046 
0047     mainLayout->addWidget(pw);
0048     setLayout(mainLayout);
0049 
0050     setWindowTitle(i18nc("@title:window", "Solar System Viewer"));
0051     //setMainWidget( pw );
0052     //setButtons( QDialog::Close );
0053     setModal(false);
0054 
0055     pw->map->setLimits(-48.0, 48.0, -48.0, 48.0);
0056     pw->map->axis(KPlotWidget::BottomAxis)
0057     ->setLabel(i18nc("axis label for x-coordinate of solar system viewer.  AU means astronomical unit.",
0058                      "X-position (AU)"));
0059     pw->map->axis(KPlotWidget::LeftAxis)
0060     ->setLabel(i18nc("axis label for y-coordinate of solar system viewer.  AU means astronomical unit.",
0061                      "Y-position (AU)"));
0062 
0063     pw->TimeStep->setDaysOnly(true);
0064     pw->TimeStep->tsbox()->setValue(1); //start with 1-day timestep
0065 
0066     pw->RunButton->setIcon(QIcon::fromTheme("arrow-right"));
0067     pw->ZoomInButton->setIcon(QIcon::fromTheme("zoom-in"));
0068     pw->ZoomOutButton->setIcon(QIcon::fromTheme("zoom-out"));
0069     pw->DateBox->setDate(data->lt().date());
0070 
0071     resize(500, 500);
0072     pw->map->QWidget::setFocus(); //give keyboard focus to the plot widget for key and mouse events
0073 
0074     setCenterPlanet(QString());
0075 
0076     PlanetList.append(KSPlanetBase::createPlanet(KSPlanetBase::MERCURY));
0077     PlanetList.append(KSPlanetBase::createPlanet(KSPlanetBase::VENUS));
0078     PlanetList.append(new KSPlanet(i18n("Earth")));
0079     PlanetList.append(KSPlanetBase::createPlanet(KSPlanetBase::MARS));
0080     PlanetList.append(KSPlanetBase::createPlanet(KSPlanetBase::JUPITER));
0081     PlanetList.append(KSPlanetBase::createPlanet(KSPlanetBase::SATURN));
0082     PlanetList.append(KSPlanetBase::createPlanet(KSPlanetBase::URANUS));
0083     PlanetList.append(KSPlanetBase::createPlanet(KSPlanetBase::NEPTUNE));
0084     //PlanetList.append( KSPlanetBase::createPlanet( KSPlanetBase::PLUTO ) );
0085 
0086     ut = data->ut();
0087     KSNumbers num(ut.djd());
0088 
0089     for (int i = 0; i < PlanetList.count(); ++i)
0090     {
0091         PlanetList[i]->findPosition(&num, nullptr, nullptr); // nullptr args: don't need geocent. coords.
0092         LastUpdate[i] = int(ut.date().toJulianDay());
0093     }
0094 
0095     //The planets' update intervals are 0.25% of one period:
0096     UpdateInterval[0] = 0;
0097     UpdateInterval[1] = 0;
0098     UpdateInterval[2] = 0;
0099     UpdateInterval[3] = 1;
0100     UpdateInterval[4] = 5;
0101     UpdateInterval[5] = 13;
0102     UpdateInterval[6] = 38;
0103     UpdateInterval[7] = 75;
0104     //UpdateInterval[8] = 113;
0105 
0106     QTimer::singleShot(0, this, SLOT(initPlotObjects()));
0107 
0108     connect(&tmr, SIGNAL(timeout()), SLOT(tick()));
0109     connect(pw->TimeStep, SIGNAL(scaleChanged(float)), SLOT(setTimeScale(float)));
0110     connect(pw->RunButton, SIGNAL(clicked()), SLOT(slotRunClock()));
0111     connect(pw->ZoomInButton, SIGNAL(clicked()), pw->map, SLOT(slotZoomIn()));
0112     connect(pw->ZoomOutButton, SIGNAL(clicked()), pw->map, SLOT(slotZoomOut()));
0113     connect(pw->DateBox, SIGNAL(dateChanged(QDate)), SLOT(slotChangeDate()));
0114     connect(pw->TodayButton, SIGNAL(clicked()), SLOT(slotToday()));
0115     connect(this, SIGNAL(closeClicked()), SLOT(slotCloseWindow()));
0116 }
0117 
0118 QString PlanetViewer::planetName(uint i) const
0119 {
0120     return PlanetList[i]->name();
0121 }
0122 
0123 void PlanetViewer::tick()
0124 {
0125     //Update the time/date
0126     ut.setDJD(ut.djd() + scale * 0.1);
0127     pw->DateBox->setDate(ut.date());
0128 
0129     updatePlanets();
0130 }
0131 
0132 void PlanetViewer::setTimeScale(float f)
0133 {
0134     scale = f / 86400.; //convert seconds to days
0135 }
0136 
0137 void PlanetViewer::slotRunClock()
0138 {
0139     isClockRunning = !isClockRunning;
0140 
0141     if (isClockRunning)
0142     {
0143         pw->RunButton->setIcon(
0144             QIcon::fromTheme("media-playback-pause"));
0145         tmr.start(100);
0146         //      pw->DateBox->setEnabled( false );
0147     }
0148     else
0149     {
0150         pw->RunButton->setIcon(QIcon::fromTheme("arrow-right"));
0151         tmr.stop();
0152         //      pw->DateBox->setEnabled( true );
0153     }
0154 }
0155 
0156 void PlanetViewer::slotChangeDate()
0157 {
0158     ut.setDate(pw->DateBox->date());
0159     updatePlanets();
0160 }
0161 
0162 void PlanetViewer::slotCloseWindow()
0163 {
0164     //Stop the clock if it's running
0165     if (isClockRunning)
0166     {
0167         tmr.stop();
0168         isClockRunning = false;
0169         pw->RunButton->setIcon(QIcon::fromTheme("arrow-right"));
0170     }
0171 }
0172 
0173 void PlanetViewer::updatePlanets()
0174 {
0175     KSNumbers num(ut.djd());
0176     bool changed(false);
0177 
0178     //Check each planet to see if it needs to be updated
0179     for (int i = 0; i < PlanetList.count(); ++i)
0180     {
0181         if (abs(int(ut.date().toJulianDay()) - LastUpdate[i]) > UpdateInterval[i])
0182         {
0183             KSPlanetBase *p = PlanetList[i];
0184             p->findPosition(&num);
0185 
0186             double s, c, s2, c2;
0187             p->helEcLong().SinCos(s, c);
0188             p->helEcLat().SinCos(s2, c2);
0189             QList<KPlotPoint *> points = planet[i]->points();
0190             points.at(0)->setX(p->rsun() * c * c2);
0191             points.at(0)->setY(p->rsun() * s * c2);
0192 
0193             if (centerPlanet() == p->name())
0194             {
0195                 QRectF dataRect = pw->map->dataRect();
0196                 double xc       = (dataRect.right() + dataRect.left()) * 0.5;
0197                 double yc       = (dataRect.bottom() + dataRect.top()) * 0.5;
0198                 double dx       = points.at(0)->x() - xc;
0199                 double dy       = points.at(0)->y() - yc;
0200                 pw->map->setLimits(dataRect.x() + dx, dataRect.right() + dx, dataRect.y() + dy, dataRect.bottom() + dy);
0201             }
0202 
0203             LastUpdate[i] = int(ut.date().toJulianDay());
0204             changed       = true;
0205         }
0206     }
0207 
0208     if (changed)
0209         pw->map->update();
0210 }
0211 
0212 void PlanetViewer::slotToday()
0213 {
0214     pw->DateBox->setDate(KStarsData::Instance()->lt().date());
0215 }
0216 
0217 void PlanetViewer::paintEvent(QPaintEvent *)
0218 {
0219     pw->map->update();
0220 }
0221 
0222 void PlanetViewer::initPlotObjects()
0223 {
0224     // Planets
0225     ksun = new KPlotObject(Qt::yellow, KPlotObject::Points, 12, KPlotObject::Circle);
0226     ksun->addPoint(0.0, 0.0);
0227     pw->map->addPlotObject(ksun);
0228 
0229     //Read in the orbit curves
0230     for (int i = 0; i < PlanetList.count(); ++i)
0231     {
0232         KSPlanetBase *p    = PlanetList[i];
0233         KPlotObject *orbit = new KPlotObject(Qt::white, KPlotObject::Lines, 1.0);
0234 
0235         QFile orbitFile;
0236         QString orbitFileName =
0237             (p->isMajorPlanet() ? (dynamic_cast<KSPlanet *>(p))->untranslatedName().toLower() : p->name().toLower()) + ".orbit";
0238         if (KSUtils::openDataFile(orbitFile, orbitFileName))
0239         {
0240             KSFileReader fileReader(orbitFile); // close file is included
0241             double x, y;
0242             while (fileReader.hasMoreLines())
0243             {
0244                 QString line       = fileReader.readLine();
0245                 QStringList fields = line.split(' ', Qt::SkipEmptyParts);
0246                 if (fields.size() == 3)
0247                 {
0248                     x = fields[0].toDouble();
0249                     y = fields[1].toDouble();
0250                     orbit->addPoint(x, y);
0251                 }
0252             }
0253         }
0254 
0255         pw->map->addPlotObject(orbit);
0256     }
0257 
0258     for (int i = 0; i < PlanetList.count(); ++i)
0259     {
0260         KSPlanetBase *p = PlanetList[i];
0261         planet[i]       = new KPlotObject(p->color(), KPlotObject::Points, 6, KPlotObject::Circle);
0262 
0263         double s, c;
0264         p->helEcLong().SinCos(s, c);
0265 
0266         planet[i]->addPoint(p->rsun() * c, p->rsun() * s, p->translatedName());
0267         pw->map->addPlotObject(planet[i]);
0268     }
0269 
0270     update();
0271 }
0272 
0273 void PlanetViewer::keyPressEvent(QKeyEvent *e)
0274 {
0275     if (e->key() == Qt::Key_Escape)
0276         close();
0277     else
0278         e->ignore();
0279 }