File indexing completed on 2024-03-24 15:18:02

0001 /*
0002     SPDX-FileCopyrightText: 2007 Jason Harris <kstars@30doradus.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "avtplotwidget.h"
0008 
0009 #include "kstarsdata.h"
0010 #include "Options.h"
0011 
0012 #include <QWidget>
0013 #include <QMouseEvent>
0014 #include <QPainter>
0015 #include <QTime>
0016 #include <QLinearGradient>
0017 
0018 #include <KLocalizedString>
0019 #include <KPlotting/KPlotObject>
0020 #include <QDebug>
0021 
0022 AVTPlotWidget::AVTPlotWidget(QWidget *parent) : KPlotWidget(parent)
0023 {
0024     setAntialiasing(true);
0025 
0026     MousePoint = QPoint(-1, -1);
0027 }
0028 
0029 void AVTPlotWidget::mousePressEvent(QMouseEvent *e)
0030 {
0031     mouseMoveEvent(e);
0032 }
0033 
0034 void AVTPlotWidget::mouseDoubleClickEvent(QMouseEvent *)
0035 {
0036     MousePoint = QPoint(-1, -1);
0037     update();
0038 }
0039 
0040 void AVTPlotWidget::mouseMoveEvent(QMouseEvent *e)
0041 {
0042     QRect checkRect(leftPadding(), topPadding(), pixRect().width(), pixRect().height());
0043     int Xcursor = e->x();
0044     int Ycursor = e->y();
0045 
0046     if (!checkRect.contains(e->x(), e->y()))
0047     {
0048         if (e->x() < checkRect.left())
0049             Xcursor = checkRect.left();
0050         if (e->x() > checkRect.right())
0051             Xcursor = checkRect.right();
0052         if (e->y() < checkRect.top())
0053             Ycursor = checkRect.top();
0054         if (e->y() > checkRect.bottom())
0055             Ycursor = checkRect.bottom();
0056     }
0057 
0058     Xcursor -= leftPadding();
0059     Ycursor -= topPadding();
0060 
0061     MousePoint = QPoint(Xcursor, Ycursor);
0062     update();
0063 }
0064 
0065 void AVTPlotWidget::paintEvent(QPaintEvent *e)
0066 {
0067     Q_UNUSED(e)
0068 
0069     QPainter p;
0070 
0071     p.begin(this);
0072     p.setRenderHint(QPainter::Antialiasing, antialiasing());
0073     p.fillRect(rect(), backgroundColor());
0074     p.translate(leftPadding(), topPadding());
0075 
0076     setPixRect();
0077     p.setClipRect(pixRect());
0078     p.setClipping(true);
0079 
0080     int pW = pixRect().width();
0081     int pH = pixRect().height();
0082 
0083     QColor SkyColor(0, 100, 200);
0084     /*
0085     if (Options::darkAppColors())
0086         SkyColor = QColor(200, 0, 0); // use something red, visible through a red filter
0087         */
0088 
0089     // Draw gradient representing lunar interference in the sky
0090     if (MoonIllum > 0.01) // do this only if Moon illumination is reasonable so it's important
0091     {
0092         int moonrise = int(pW * (0.5 + MoonRise));
0093         int moonset  = int(pW * (MoonSet - 0.5));
0094         if (moonset < 0)
0095             moonset += pW;
0096         if (moonrise > pW)
0097             moonrise -= pW;
0098         int moonalpha = int(10 + MoonIllum * 130);
0099         int fadewidth =
0100             pW *
0101             0.01; // pW * fraction of day to fade the moon brightness over (0.01 corresponds to roughly 15 minutes, 0.007 to 10 minutes), both before and after actual set.
0102         QColor MoonColor(255, 255, 255, moonalpha);
0103 
0104         if (moonset < moonrise)
0105         {
0106             QLinearGradient grad =
0107                 QLinearGradient(QPointF(moonset - fadewidth, 0.0), QPointF(moonset + fadewidth, 0.0));
0108             grad.setColorAt(0, MoonColor);
0109             grad.setColorAt(1, Qt::transparent);
0110             p.fillRect(QRectF(0.0, 0.0, moonset + fadewidth, pH),
0111                        grad); // gradient should be padded until moonset - fadewidth (see QLinearGradient docs)
0112             grad.setStart(QPointF(moonrise + fadewidth, 0.0));
0113             grad.setFinalStop(QPointF(moonrise - fadewidth, 0.0));
0114             p.fillRect(QRectF(moonrise - fadewidth, 0.0, pW - moonrise + fadewidth, pH), grad);
0115         }
0116         else
0117         {
0118             p.fillRect(QRectF(moonrise + fadewidth, 0.0, moonset - moonrise - 2 * fadewidth, pH), MoonColor);
0119             QLinearGradient grad =
0120                 QLinearGradient(QPointF(moonrise + fadewidth, 0.0), QPointF(moonrise - fadewidth, 0.0));
0121             grad.setColorAt(0, MoonColor);
0122             grad.setColorAt(1, Qt::transparent);
0123             p.fillRect(QRectF(0.0, 0.0, moonrise + fadewidth, pH), grad);
0124             grad.setStart(QPointF(moonset - fadewidth, 0.0));
0125             grad.setFinalStop(QPointF(moonset + fadewidth, 0.0));
0126             p.fillRect(QRectF(moonset - fadewidth, 0.0, pW - moonset, pH), grad);
0127         }
0128     }
0129     //draw daytime sky if the Sun rises for the current date/location
0130     if (SunMaxAlt > -18.0)
0131     {
0132         //Display centered on midnight, so need to modulate dawn/dusk by 0.5
0133         int rise = int(pW * (0.5 + SunRise));
0134         int set  = int(pW * (SunSet - 0.5));
0135         int da   = int(pW * (0.5 + Dawn));
0136         int du   = int(pW * (Dusk - 0.5));
0137 
0138         if (SunMinAlt > 0.0)
0139         {
0140             // The sun never set and the sky is always blue
0141             p.fillRect(rect(), SkyColor);
0142         }
0143         else if (SunMaxAlt < 0.0 && SunMinAlt < -18.0)
0144         {
0145             // The sun never rise but the sky is not completely dark
0146             QLinearGradient grad = QLinearGradient(QPointF(0.0, 0.0), QPointF(du, 0.0));
0147 
0148             QColor gradStartColor = SkyColor;
0149             gradStartColor.setAlpha((1 - (SunMaxAlt / -18.0)) * 255);
0150 
0151             grad.setColorAt(0, gradStartColor);
0152             grad.setColorAt(1, Qt::transparent);
0153             p.fillRect(QRectF(0.0, 0.0, du, pH), grad);
0154             grad.setStart(QPointF(pW, 0.0));
0155             grad.setFinalStop(QPointF(da, 0.0));
0156             p.fillRect(QRectF(da, 0.0, pW, pH), grad);
0157         }
0158         else if (SunMaxAlt < 0.0 && SunMinAlt > -18.0)
0159         {
0160             // The sun never rise but the sky is NEVER completely dark
0161             QLinearGradient grad = QLinearGradient(QPointF(0.0, 0.0), QPointF(pW, 0.0));
0162 
0163             QColor gradStartEndColor = SkyColor;
0164             gradStartEndColor.setAlpha((1 - (SunMaxAlt / -18.0)) * 255);
0165             QColor gradMidColor = SkyColor;
0166             gradMidColor.setAlpha((1 - (SunMinAlt / -18.0)) * 255);
0167 
0168             grad.setColorAt(0, gradStartEndColor);
0169             grad.setColorAt(0.5, gradMidColor);
0170             grad.setColorAt(1, gradStartEndColor);
0171             p.fillRect(QRectF(0.0, 0.0, pW, pH), grad);
0172         }
0173         else if (Dawn < 0.0)
0174         {
0175             // The sun sets and rises but the sky is never completely dark
0176             p.fillRect(0, 0, set, int(0.5 * pH), SkyColor);
0177             p.fillRect(rise, 0, pW, int(0.5 * pH), SkyColor);
0178 
0179             QLinearGradient grad = QLinearGradient(QPointF(set, 0.0), QPointF(rise, 0.0));
0180 
0181             QColor gradMidColor = SkyColor;
0182             gradMidColor.setAlpha((1 - (SunMinAlt / -18.0)) * 255);
0183 
0184             grad.setColorAt(0, SkyColor);
0185             grad.setColorAt(0.5, gradMidColor);
0186             grad.setColorAt(1, SkyColor);
0187             p.fillRect(QRectF(set, 0.0, rise - set, pH), grad);
0188         }
0189         else
0190         {
0191             p.fillRect(0, 0, set, pH, SkyColor);
0192             p.fillRect(rise, 0, pW, pH, SkyColor);
0193 
0194             QLinearGradient grad = QLinearGradient(QPointF(set, 0.0), QPointF(du, 0.0));
0195             grad.setColorAt(0, SkyColor);
0196             grad.setColorAt(
0197                 1,
0198                 Qt::transparent); // FIXME?: The sky appears black well before the actual end of twilight if the gradient is too slow (eg: latitudes above arctic circle)
0199             p.fillRect(QRectF(set, 0.0, du - set, pH), grad);
0200 
0201             grad.setStart(QPointF(rise, 0.0));
0202             grad.setFinalStop(QPointF(da, 0.0));
0203             p.fillRect(QRectF(da, 0.0, rise - da, pH), grad);
0204         }
0205     }
0206 
0207     //draw ground
0208     p.fillRect(0, int(0.5 * pH), pW, int(0.5 * pH),
0209                KStarsData::Instance()->colorScheme()->colorNamed(
0210                    "HorzColor")); // asimha changed to use color from scheme. Formerly was QColor( "#002200" )
0211 
0212     foreach (KPlotObject *po, plotObjects())
0213     {
0214         po->draw(&p, this);
0215     }
0216 
0217     p.setClipping(false);
0218     drawAxes(&p);
0219 
0220     //Add vertical line indicating "now"
0221     QFont smallFont = p.font();
0222     smallFont.setPointSize(smallFont.pointSize()); // wat?
0223     if (geo)
0224     {
0225         QTime t = geo->UTtoLT(KStarsDateTime::currentDateTimeUtc())
0226                       .time(); // convert the current system clock time to the TZ corresponding to geo
0227         double x = 12.0 + t.hour() + t.minute() / 60.0 + t.second() / 3600.0;
0228         while (x > 24.0)
0229             x -= 24.0;
0230         int ix = int(x * pW / 24.0); //convert to screen pixel coords
0231         p.setPen(QPen(QBrush("white"), 2.0, Qt::DotLine));
0232         p.drawLine(ix, 0, ix, pH);
0233 
0234         //Label this vertical line with the current time
0235         p.save();
0236         p.setFont(smallFont);
0237         p.translate(ix + 10, pH - 20);
0238         p.rotate(-90);
0239         p.drawText(
0240             0, 0,
0241             QLocale().toString(t, QLocale::ShortFormat)); // short format necessary to avoid false time-zone labeling
0242         p.restore();
0243     }
0244 
0245     //Draw crosshairs at clicked position
0246     if (MousePoint.x() > 0)
0247     {
0248         p.setPen(QPen(QBrush("gold"), 1.0, Qt::SolidLine));
0249         p.drawLine(QLineF(MousePoint.x() + 0.5, 0.5, MousePoint.x() + 0.5, pixRect().height() - 0.5));
0250         p.drawLine(QLineF(0.5, MousePoint.y() + 0.5, pixRect().width() - 0.5, MousePoint.y() + 0.5));
0251 
0252         //Label each crosshair line (time and altitude)
0253         p.setFont(smallFont);
0254         double a = (pH - MousePoint.y()) * 180.0 / pH - 90.0;
0255         p.drawText(20, MousePoint.y() + 10, QString::number(a, 'f', 2) + QChar(176));
0256 
0257         double h = MousePoint.x() * 24.0 / pW - 12.0;
0258         if (h < 0.0)
0259             h += 24.0;
0260         QTime t = QTime(int(h), int(60. * (h - int(h))));
0261         p.save();
0262         p.translate(MousePoint.x() + 10, pH - 20);
0263         p.rotate(-90);
0264         p.drawText(
0265             0, 0,
0266             QLocale().toString(t, QLocale::ShortFormat)); // short format necessary to avoid false time-zone labeling
0267         p.restore();
0268     }
0269 
0270     p.end();
0271 }
0272 
0273 void AVTPlotWidget::setDawnDuskTimes(double da, double du)
0274 {
0275     Dawn = da;
0276     Dusk = du;
0277     update(); // fixme: should we always be calling update? It's probably cheap enough that we can.
0278 }
0279 
0280 void AVTPlotWidget::setMinMaxSunAlt(double min, double max)
0281 {
0282     SunMinAlt = min;
0283     SunMaxAlt = max;
0284     update();
0285 }
0286 
0287 void AVTPlotWidget::setSunRiseSetTimes(double sr, double ss)
0288 {
0289     SunRise = sr;
0290     SunSet  = ss;
0291     update();
0292 }
0293 
0294 void AVTPlotWidget::setMoonRiseSetTimes(double mr, double ms)
0295 {
0296     MoonRise = mr;
0297     MoonSet  = ms;
0298     update();
0299 }
0300 
0301 void AVTPlotWidget::setMoonIllum(double mi)
0302 {
0303     MoonIllum = mi;
0304     update();
0305 }