Kstars

avtplotwidget.cpp
1/*
2 SPDX-FileCopyrightText: 2007 Jason Harris <kstars@30doradus.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "avtplotwidget.h"
8
9#include "kstarsdata.h"
10#include "Options.h"
11
12#include <QWidget>
13#include <QMouseEvent>
14#include <QPainter>
15#include <QTime>
16#include <QLinearGradient>
17
18#include <KLocalizedString>
19#include <kplotobject.h>
20#include <kplotpoint.h>
21#include <QDebug>
22
23#include "kplotaxis.h"
24#include "ksalmanac.h"
25
26AVTPlotWidget::AVTPlotWidget(QWidget *parent) : KPlotWidget(parent)
27{
28 setAntialiasing(true);
29
30 MousePoint = QPoint(-1, -1);
31}
32
37
39{
40 MousePoint = QPoint(-1, -1);
41 update();
42}
43
45{
46 QRect checkRect(leftPadding(), topPadding(), pixRect().width(), pixRect().height());
47 int Xcursor = e->x();
48 int Ycursor = e->y();
49
50 if (!checkRect.contains(e->x(), e->y()))
51 {
52 if (e->x() < checkRect.left())
53 Xcursor = checkRect.left();
54 if (e->x() > checkRect.right())
55 Xcursor = checkRect.right();
56 if (e->y() < checkRect.top())
57 Ycursor = checkRect.top();
58 if (e->y() > checkRect.bottom())
59 Ycursor = checkRect.bottom();
60 }
61
62 Xcursor -= leftPadding();
63 Ycursor -= topPadding();
64
65 MousePoint = QPoint(Xcursor, Ycursor);
66 update();
67}
68
69// All the int coordinates (rise, set) need to be converted from hours relative to midnight
70// into graph coordinates before calling this.
71void drawMoon(QPainter &p, int rise, int set, int fade, const QColor &color, int width, int height, double leftPadding)
72{
73 QBrush brush(color, Qt::Dense5Pattern);
74 QBrush dimmerBrush(color, Qt::Dense6Pattern);
75 QBrush dimmestBrush(color, Qt::Dense7Pattern);
76 QRectF r;
77 if (set < rise)
78 {
79 if (set + fade >= leftPadding && set - fade < leftPadding + width)
80 {
81 r = QRectF(leftPadding, 0.0, (set - fade) - leftPadding, height);
82 p.fillRect(r, brush);
83 r = QRectF(set - fade, 0.0, fade, height);
84 p.fillRect(r, dimmerBrush);
85 r = QRectF(set, 0.0, fade, height);
86 p.fillRect(r, dimmestBrush);
87 }
88 if (rise + fade >= leftPadding && rise - fade < leftPadding + width)
89 {
90 r = QRectF(rise - fade, 0.0, fade, height);
91 p.fillRect(r, dimmestBrush);
92 r = QRectF(rise, 0.0, fade, height);
93 p.fillRect(r, dimmerBrush);
94
95 // Since set < rise, we draw to the end of the box
96 r = QRectF(rise + fade, 0.0, width, height);
97 p.fillRect(r, brush);
98 }
99 }
100 else
101 {
102 r = QRectF(rise - fade, 0.0, fade, height);
103 p.fillRect(r, dimmestBrush);
104 r = QRectF(rise, 0.0, fade, height);
105 p.fillRect(r, dimmerBrush);
106 r = QRectF(rise + fade, 0.0, (set - rise) - 2 * fade, height);
107 p.fillRect(r, brush);
108 r = QRectF(set - fade, 0.0, fade, height);
109 p.fillRect(r, dimmerBrush);
110 r = QRectF(set, 0.0, fade, height);
111 p.fillRect(r, dimmestBrush);
112 }
113}
114
115// All the int coordinates (rise, set, da, du) need to be converted from hours relative to midnight
116// into graph coordinates before calling this.
117void drawSun(QPainter &p, int rise, int set, double minAlt, double maxAlt, int da, int du, bool noDawn,
118 const QColor &color, int width, int height)
119{
120 if (maxAlt < 0.0 && minAlt < -18.0)
121 {
122 // The sun never rise but the sky is not completely dark
123 QLinearGradient grad = QLinearGradient(QPointF(0.0, 0.0), QPointF(du, 0.0));
124
125 QColor gradStartColor = color;
126 gradStartColor.setAlpha((1 - (maxAlt / -18.0)) * 255);
127
128 grad.setColorAt(0, gradStartColor);
130 p.fillRect(QRectF(0.0, 0.0, du, height), grad);
131 grad.setStart(QPointF(width, 0.0));
132 grad.setFinalStop(QPointF(da, 0.0));
133 p.fillRect(QRectF(da, 0.0, width, height), grad);
134 }
135 else if (maxAlt < 0.0 && minAlt > -18.0)
136 {
137 // The sun never rise but the sky is NEVER completely dark
138 QLinearGradient grad = QLinearGradient(QPointF(0.0, 0.0), QPointF(width, 0.0));
139
140 QColor gradStartEndColor = color;
141 gradStartEndColor.setAlpha((1 - (maxAlt / -18.0)) * 255);
142 QColor gradMidColor = color;
143 gradMidColor.setAlpha((1 - (minAlt / -18.0)) * 255);
144
145 grad.setColorAt(0, gradStartEndColor);
146 grad.setColorAt(0.5, gradMidColor);
147 grad.setColorAt(1, gradStartEndColor);
148 p.fillRect(QRectF(0.0, 0.0, width, height), grad);
149 }
150 else if (noDawn)
151 {
152 // The sun sets and rises but the sky is never completely dark
153 p.fillRect(0, 0, set, int(0.5 * height), color);
154 p.fillRect(rise, 0, width, int(0.5 * height), color);
155
156 QLinearGradient grad = QLinearGradient(QPointF(set, 0.0), QPointF(rise, 0.0));
157
158 QColor gradMidColor = color;
159 gradMidColor.setAlpha((1 - (minAlt / -18.0)) * 255);
160
161 grad.setColorAt(0, color);
162 grad.setColorAt(0.5, gradMidColor);
163 grad.setColorAt(1, color);
164 p.fillRect(QRectF(set, 0.0, rise - set, height), grad);
165 }
166 else
167 {
168 if (set > 0)
169 p.fillRect(0, 0, set, height, color);
170 if (rise < width)
171 p.fillRect(rise, 0, width, height, color);
172
173 QLinearGradient grad = QLinearGradient(QPointF(set, 0.0), QPointF(du, 0.0));
174 grad.setColorAt(0, color);
176 p.fillRect(QRectF(set, 0.0, du - set, height), grad);
177
178 grad.setStart(QPointF(rise, 0.0));
179 grad.setFinalStop(QPointF(da, 0.0));
180 p.fillRect(QRectF(da, 0.0, rise - da, height), grad);
181 }
182}
183
184// This legacy code always plotted from noon to noon (24 hours starting at noon).
185// To generalize this code, we still compute noon-to-noon coords, but then convert
186// them to more general plot coordinates where the plot length isn't 24 hours and
187// the plot doesn't begin at noon.
188int AVTPlotWidget::convertCoords(double xCoord)
189{
190 const double plotWidth = pixRect().width();
191 const double pixelsPerHour = plotWidth / plotDuration;
192 const double newPosition = pixelsPerHour * ((xCoord * 24.0 / plotWidth) - noonOffset);
193 return newPosition;
194}
195
196namespace
197{
198double findYValue(const KPlotObject *po, double x)
199{
200 const auto points = po->points();
201 const int size = points.size();
202 if (size == 0) return 0;
203 if (x < points[0]->x()) return points[0]->y();
204 if (x > points[size - 1]->x()) return points[size - 1]->y();
205 for (int i = 0; i < size - 1; ++i)
206 {
207 const double ix = points[i]->x();
208 const double iy = points[i]->y();
209 const double nextIx = points[i + 1]->x();
210 const double nextIy = points[i + 1]->y();
211 if (x == ix) return iy;
212 if (x == nextIx) return nextIy;
213 if (x > ix && x < nextIx)
214 return iy + (nextIy - iy) * (x - ix) / (nextIx - ix);
215 }
216 return points[size - 1]->y();
217}
218} // namespace
219
221{
222 Q_UNUSED(e)
223
224 QPainter p;
225
226 p.begin(this);
230
231 setPixRect();
232 p.setClipRect(pixRect());
233 p.setClipping(true);
234
235 int pW = pixRect().width();
236 int pH = pixRect().height();
237
238 QColor SkyColor(0, 100, 200);
239 /*
240 if (Options::darkAppColors())
241 SkyColor = QColor(200, 0, 0); // use something red, visible through a red filter
242 */
243
244 // Draw gradient representing lunar interference in the sky
245 if (MoonIllum > 0.01) // do this only if Moon illumination is reasonable so it's important
246 {
247 double moonrise = pW * (0.5 + MoonRise);
248 double moonset = pW * (MoonSet - 0.5);
249 if (moonset < 0)
250 moonset += pW;
251 if (moonrise > pW)
252 moonrise -= pW;
253 moonrise = convertCoords(moonrise);
254 moonset = convertCoords(moonset);
255
256 if (moonrise > pW)
257 {
258 const double pixelsPerHour = pW * 1.0 / plotDuration;
259 moonrise -= 24 * pixelsPerHour;
260 }
261 const int mooncolor = int(10 + MoonIllum * 130);
262 const QColor MoonColor(mooncolor, mooncolor, mooncolor);
263 int fadewidth =
264 pW *
265 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.
266
267 drawMoon(p, int(moonrise), int(moonset), fadewidth, MoonColor, pW, pH, leftPadding());
268
269 }
270 //draw daytime sky if the Sun rises for the current date/location
271 if (SunMaxAlt > -18.0)
272 {
273 // Initially compute centered on midnight, so modulate dawn/dusk by 0.5
274 // Then convert to general coordinates.
275 int rise = convertCoords(pW * (0.5 + SunRise));
276 int set = convertCoords(pW * (SunSet - 0.5));
277 int dawn = convertCoords(pW * (0.5 + Dawn));
278 double dusk = int(pW * (Dusk - 0.5));
279 if (dusk < 0) dusk = pW + dusk;
280 dusk = convertCoords(dusk);
281
282 if (SunMinAlt > 0.0)
283 {
284 // The sun never set and the sky is always blue
285 p.fillRect(rect(), SkyColor);
286 }
287 else drawSun(p, rise, set, SunMinAlt, SunMaxAlt, dawn, int(dusk), Dawn < 0.0, SkyColor, pW, pH);
288 }
289
290 //draw ground
291 if (altitudeAxisMin < 0)
292 {
293 const int groundYValue = pH + altitudeAxisMin * pH / (altitudeAxisMax - altitudeAxisMin);
294 p.fillRect(0, groundYValue, pW, groundYValue,
295 KStarsData::Instance()->colorScheme()->colorNamed(
296 "HorzColor")); // asimha changed to use color from scheme. Formerly was QColor( "#002200" )
297 }
298
299 foreach (KPlotObject *po, plotObjects())
300 {
301 po->draw(&p, this);
302 }
303
304 p.setClipping(false);
305 drawAxes(&p);
306
307 //Add vertical line indicating "now"
308 QFont smallFont = p.font();
309 smallFont.setPointSize(smallFont.pointSize()); // wat?
310 if (geo)
311 {
313 .time(); // convert the current system clock time to the TZ corresponding to geo
314 double x = 12.0 + t.hour() + t.minute() / 60.0 + t.second() / 3600.0;
315 while (x > 24.0)
316 x -= 24.0;
317 double ix = x * pW / 24.0; //convert to screen pixel coords
318 ix = convertCoords(ix);
319 p.setPen(QPen(QBrush("white"), 2.0, Qt::DotLine));
320 p.drawLine(int(ix), 0, ix, pH);
321
322 //Label this vertical line with the current time
323 p.save();
324 p.setFont(smallFont);
325 p.translate(int(ix) + 10, pH - 20);
326 p.rotate(-90);
327 p.drawText(
328 0, 0,
329 QLocale().toString(t, QLocale::ShortFormat)); // short format necessary to avoid false time-zone labeling
330 p.restore();
331 }
332
333 //Draw crosshairs at clicked position
334 if (MousePoint.x() > 0)
335 {
336 p.setPen(QPen(QBrush("gold"), 1.0, Qt::SolidLine));
337 p.drawLine(QLineF(MousePoint.x() + 0.5, 0.5, MousePoint.x() + 0.5, pixRect().height() - 0.5));
338
339 //Label each crosshair line (time and altitude)
340 p.setFont(smallFont);
341
342 double h = (MousePoint.x() * plotDuration) / pW - (12.0 - noonOffset);
343 double a = 0;
344 if (plotObjects().size() > 0)
345 a = findYValue(plotObjects()[0], h);
346 p.drawText(15, 15, QString::number(a, 'f', 1) + QChar(176));
347
348 if (h < 0.0)
349 h += 24.0;
350 QTime t = QTime(int(h), int(60. * (h - int(h))));
351 p.save();
352 p.translate(MousePoint.x() + 10, pH - 20);
353 p.rotate(-90);
354 p.drawText(
355 0, 0,
356 QLocale().toString(t, QLocale::ShortFormat)); // short format necessary to avoid false time-zone labeling
357 p.restore();
358 }
359
360 p.end();
361}
362
363void AVTPlotWidget::setDawnDuskTimes(double da, double du)
364{
365 Dawn = da;
366 Dusk = du;
367 update(); // fixme: should we always be calling update? It's probably cheap enough that we can.
368}
369
370void AVTPlotWidget::setMinMaxSunAlt(double min, double max)
371{
372 SunMinAlt = min;
373 SunMaxAlt = max;
374 update();
375}
376
377void AVTPlotWidget::setSunRiseSetTimes(double sr, double ss)
378{
379 SunRise = sr;
380 SunSet = ss;
381 update();
382}
383
384void AVTPlotWidget::setMoonRiseSetTimes(double mr, double ms)
385{
386 MoonRise = mr;
387 MoonSet = ms;
388 update();
389}
390
392{
393 MoonIllum = mi;
394 update();
395}
396
397void AVTPlotWidget::setPlotExtent(double offset, double duration)
398{
399 noonOffset = offset;
400 plotDuration = duration;
401}
402
404 const QVector<double> &times, const QVector<double> &alts, bool overlay)
405{
407 if (overlay)
408 {
409 QPen pen;
410 pen.setWidth(5);
411 pen.setColor(Qt::green);
412 po->setLinePen(pen);
413 }
414 else
415 {
416 setLimits(times[0], times.last(), altitudeAxisMin, altitudeAxisMax);
417 setSecondaryLimits(times[0], times.last(), altitudeAxisMin, altitudeAxisMax);
421 setGeoLocation(geo);
422
423 setSunRiseSetTimes(ksal->getSunRise(), ksal->getSunSet());
424 setDawnDuskTimes(ksal->getDawnAstronomicalTwilight(), ksal->getDuskAstronomicalTwilight());
425 setMinMaxSunAlt(ksal->getSunMinAlt(), ksal->getSunMaxAlt());
426 setMoonRiseSetTimes(ksal->getMoonRise(), ksal->getMoonSet());
427 setMoonIllum(ksal->getMoonIllum());
428
429 const double noonOffset = times[0] - -12;
430 const double plotDuration = times.last() - times[0];
431 setPlotExtent(noonOffset, plotDuration);
433 }
434
435 for (int i = 0; i < times.size(); ++i)
436 po->addPoint(times[i], alts[i]);
437 addPlotObject(po);
438
439 update();
440}
441
442void AVTPlotWidget::setAltitudeAxis(double min, double max)
443{
444 if (min < max)
445 {
446 altitudeAxisMin = min;
447 altitudeAxisMax = max;
448 }
449}
450
void plot(const GeoLocation *geo, KSAlmanac *ksal, const QVector< double > &times, const QVector< double > &alts, bool overlay)
Higher level method to plot.
void setGeoLocation(const GeoLocation *geo_)
Set the GeoLocation.
void setSunRiseSetTimes(double sr, double ss)
Set the fractional positions of the Sunrise and Sunset positions, in units where last midnight was 0....
void setMoonRiseSetTimes(double mr, double ms)
Set the fractional positions of moonrise and moon set in units where last midnight was 0....
void paintEvent(QPaintEvent *e) override
Redraw the plot.
void setPlotExtent(double noonOffset, double plotDuration)
This is needed when not plotting from noon to noon.
void mouseDoubleClickEvent(QMouseEvent *e) override
Reset the MousePoint to a null value, to erase the crosshairs.
void setMoonIllum(double mi)
Set the moon illumination.
void mousePressEvent(QMouseEvent *e) override
Simply calls mouseMoveEvent().
void mouseMoveEvent(QMouseEvent *e) override
Handle mouse move events.
void setAltitudeAxis(double min, double max)
Sets the Y-axis min and max values.
Contains all relevant information for specifying a location on Earth: City Name, State/Province name,...
Definition geolocation.h:28
void setTickLabelFormat(char format='g', int fieldWidth=0, int precision=-1)
void setTickLabelsShown(bool b)
void draw(QPainter *p, KPlotWidget *pw)
void addPoint(const QPointF &p, const QString &label=QString(), double barWidth=0.0)
void setLinePen(const QPen &p)
QList< KPlotPoint * > points() const
QList< KPlotObject * > plotObjects() const
void setPixRect()
void setSecondaryLimits(double x1, double x2, double y1, double y2)
int leftPadding() const
int topPadding() const
KPlotAxis * axis(Axis type)
virtual void drawAxes(QPainter *p)
void setLimits(double x1, double x2, double y1, double y2)
void addPlotObject(KPlotObject *object)
QRect pixRect() const
QColor backgroundColor() const
bool antialiasing() const
void removeAllPlotObjects()
A class that implements methods to find sun rise, sun set, twilight begin / end times,...
Definition ksalmanac.h:27
double getSunMaxAlt() const
These functions return the max and min altitude of the sun during the course of the day in degrees.
Definition ksalmanac.h:75
double getMoonIllum() const
Definition ksalmanac.h:86
double getSunRise() const
All the functions returns the fraction of the day given by getDate() as their return value.
Definition ksalmanac.h:65
static KStarsDateTime currentDateTimeUtc()
void setAlpha(int alpha)
int pointSize() const const
void setPointSize(int pointSize)
void setColorAt(qreal position, const QColor &color)
void setFinalStop(const QPointF &stop)
void setStart(const QPointF &start)
T & last()
qsizetype size() const const
int x() const const
int y() const const
bool begin(QPaintDevice *device)
void drawLine(const QLine &line)
void drawText(const QPoint &position, const QString &text)
bool end()
void fillRect(const QRect &rectangle, QGradient::Preset preset)
const QFont & font() const const
void restore()
void rotate(qreal angle)
void save()
void setClipRect(const QRect &rectangle, Qt::ClipOperation operation)
void setClipping(bool enable)
void setFont(const QFont &font)
void setPen(Qt::PenStyle style)
void setRenderHint(RenderHint hint, bool on)
void translate(const QPoint &offset)
void setColor(const QColor &color)
void setWidth(int width)
int x() const const
int bottom() const const
bool contains(const QPoint &point, bool proper) const const
int height() const const
int left() const const
int right() const const
int top() const const
int width() const const
QString number(double n, char format, int precision)
Dense5Pattern
transparent
int hour() const const
int minute() const const
int second() const const
void update()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:16:42 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.