Kstars

mosaictilesmanager.cpp
1/*
2 SPDX-FileCopyrightText: 2021 Jasem Mutlaq <mutlaqja@ikarustech.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include <QPainter>
8
9#include "mosaictilesmanager.h"
10
11#include "kstars.h"
12#include "kstarsdata.h"
13#include "ekos_scheduler_debug.h"
14
15#include <QGraphicsSceneMouseEvent>
16
17namespace Ekos
18{
19MosaicTilesManager::MosaicTilesManager(QWidget *parent) : QObject(parent), QGraphicsItem()
20{
21 brush.setStyle(Qt::NoBrush);
22 QColor lightGray(200, 200, 200, 100);
23 pen.setColor(lightGray);
24 pen.setWidth(1);
25
26 textBrush.setStyle(Qt::SolidPattern);
27 textPen.setColor(Qt::red);
28 textPen.setWidth(2);
29
31 setAcceptDrops(true);
32}
33
34MosaicTilesManager::~MosaicTilesManager()
35{
36 qDeleteAll(tiles);
37}
38
39
40void MosaicTilesManager::setSkyCenter(SkyPoint center)
41{
42 skyCenter = center;
43}
44
45void MosaicTilesManager::setPositionAngle(double positionAngle)
46{
47 pa = std::fmod(positionAngle * -1 + 360.0, 360.0);
48
49 // Rotate the whole mosaic around its local center
50 setTransformOriginPoint(QPointF());
51 setRotation(pa);
52}
53
54void MosaicTilesManager::setGridDimensions(int width, int height)
55{
56 m_HorizontalTiles = width;
57 m_VerticalTiles = height;
58}
59
60void MosaicTilesManager::setSingleTileFOV(double fov_x, double fov_y)
61{
62 fovW = fov_x;
63 fovH = fov_y;
64}
65
66void MosaicTilesManager::setMosaicFOV(double mfov_x, double mfov_y)
67{
68 mfovW = mfov_x;
69 mfovH = mfov_y;
70}
71
72void MosaicTilesManager::setOverlap(double value)
73{
74 overlap = (value < 0) ? 0 : (1 < value) ? 1 : value;
75}
76
77
78QSizeF MosaicTilesManager::adjustCoordinate(QPointF tileCoord)
79{
80 // Compute the declination of the tile row from the mosaic center
81 double const dec = skyCenter.dec0().Degrees() + tileCoord.y() / m_PixelScale.height();
82
83 // Adjust RA based on the shift in declination
84 QSizeF const toSpherical(
85 1 / (m_PixelScale.width() * cos(dec * dms::DegToRad)),
86 1 / (m_PixelScale.height()));
87
88 // Return the adjusted coordinates as a QSizeF in degrees
89 return QSizeF(tileCoord.x() * toSpherical.width(), tileCoord.y() * toSpherical.height());
90}
91
92void MosaicTilesManager::updateTiles(QPointF skymapCenter, bool s_shaped)
93{
94 prepareGeometryChange();
95 skymapCenter = QPointF(0, 0);
96
97 qDeleteAll(tiles);
98 tiles.clear();
99
100 // Sky map has objects moving from left to right, so configure the mosaic from right to left, column per column
101
102 // Offset is our tile size with an overlap removed
103 double const xOffset = fovW * (1 - overlap);
104 double const yOffset = fovH * (1 - overlap);
105
106 // We start at top right corner, (0,0) being the center of the tileset
107 double initX = +(fovW + xOffset * (m_HorizontalTiles - 1)) / 2.0 - fovW;
108 double initY = -(fovH + yOffset * (m_VerticalTiles - 1)) / 2.0;
109
110 double x = initX, y = initY;
111
112 qCDebug(KSTARS_EKOS_SCHEDULER) << "Mosaic Tile FovW" << fovW << "FovH" << fovH << "initX" << x << "initY" << y <<
113 "Offset X " << xOffset << " Y " << yOffset << " rotation " << getPA() << " reverseOdd " << s_shaped;
114
115 int index = 0;
116 for (int col = 0; col < m_HorizontalTiles; col++)
117 {
118 y = (s_shaped && (col % 2)) ? (y - yOffset) : initY;
119
120 for (int row = 0; row < m_VerticalTiles; row++)
121 {
122 OneTile *tile = new OneTile();
123
124 if (!tile)
125 continue;
126
127 tile->pos.setX(x);
128 tile->pos.setY(y);
129
130 tile->center.setX(tile->pos.x() + (fovW / 2.0));
131 tile->center.setY(tile->pos.y() + (fovH / 2.0));
132
133 // The location of the tile on the sky map refers to the center of the mosaic, and rotates with the mosaic itself
134 QPointF tileSkyLocation = skymapCenter - rotatePoint(tile->center, QPointF(), rotation());
135
136 // Compute the adjusted location in RA/DEC
137 QSizeF const tileSkyOffsetScaled = adjustCoordinate(tileSkyLocation);
138
139 tile->skyCenter.setRA0((skyCenter.ra0().Degrees() + tileSkyOffsetScaled.width()) / 15.0);
140 tile->skyCenter.setDec0(skyCenter.dec0().Degrees() + tileSkyOffsetScaled.height());
141 tile->skyCenter.apparentCoord(static_cast<long double>(J2000), KStars::Instance()->data()->ut().djd());
142
143 tile->rotation = tile->skyCenter.ra0().Degrees() - skyCenter.ra0().Degrees();
144
145 // Large rotations handled wrong by the algorithm - prefer doing multiple mosaics
146 if (abs(tile->rotation) <= 90.0)
147 {
148 tile->index = ++index;
149 tiles.append(tile);
150 }
151 else
152 {
153 delete tile;
154 tiles.append(nullptr);
155 }
156
157 y += (s_shaped && (col % 2)) ? -yOffset : +yOffset;
158 }
159
160 x -= xOffset;
161 }
162}
163
164MosaicTilesManager::OneTile *MosaicTilesManager::getTile(int row, int col)
165{
166 int offset = row * m_HorizontalTiles + col;
167
168 if (offset < 0 || offset >= tiles.size())
169 return nullptr;
170
171 return tiles[offset];
172}
173
174QRectF MosaicTilesManager::boundingRect() const
175{
176 const double xOffset = m_HorizontalTiles > 1 ? overlap : 0;
177 const double yOffset = m_VerticalTiles > 1 ? overlap : 0;
178 // QRectF rect = QRectF(0, 0, fovW + xOffset * qMax(1, (m_HorizontalTiles - 1)), fovH + yOffset * qMax(1,
179 // (m_VerticalTiles - 1)));
180 const double xOverlapped = fovW * xOffset * m_HorizontalTiles;
181 const double yOverlapped = fovH * yOffset * m_VerticalTiles;
182
183 const double totalW = fovW * m_HorizontalTiles;
184 const double totalH = fovH * m_VerticalTiles;
186 return rect;
187}
188
189void MosaicTilesManager::mousePressEvent(QGraphicsSceneMouseEvent *event)
190{
192 QPointF pos = event->screenPos();
193 m_LastPosition = pos;
194}
195
196void MosaicTilesManager::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
197{
199 QPointF delta = event->screenPos() - m_LastPosition;
200 emit newOffset(delta);
201}
202
203void MosaicTilesManager::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
204{
205 if (!tiles.size())
206 return;
207
208 QFont defaultFont = painter->font();
209 QRect const oneRect(-fovW / 2, -fovH / 2, fovW, fovH);
210
211 // HACK: all tiles should be QGraphicsItem instances so that texts would be scaled properly
212 double const fontScale = 1 / log(tiles.size() < 4 ? 4 : tiles.size());
213
214 // Draw a light background field first to help detect holes - reduce alpha as we are stacking tiles over this
215 painter->setBrush(QBrush(QColor(255, 0, 0, (200 * m_PainterAlpha) / 100), Qt::SolidPattern));
216 painter->setPen(QPen(painter->brush(), 2, Qt::PenStyle::DotLine));
217 painter->drawRect(QRectF(QPointF(-mfovW / 2, -mfovH / 2), QSizeF(mfovW, mfovH)));
218
219 // Fill tiles with a transparent brush to show overlaps
220 QBrush tileBrush(QColor(0, 255, 0, (200 * m_PainterAlpha) / 100), Qt::SolidPattern);
221
222 // Draw each tile, adjusted for rotation
223 for (int row = 0; row < m_VerticalTiles; row++)
224 {
225 for (int col = 0; col < m_HorizontalTiles; col++)
226 {
227 OneTile const * const tile = getTile(row, col);
228 if (tile)
229 {
230 QTransform const transform = painter->worldTransform();
231 painter->translate(tile->center);
232 painter->rotate(tile->rotation);
233
234 painter->setBrush(tileBrush);
235 painter->setPen(pen);
236
237 painter->drawRect(oneRect);
238
239 painter->setWorldTransform(transform);
240 }
241 }
242 }
243
244 // Overwrite with tile information
245 for (int row = 0; row < m_VerticalTiles; row++)
246 {
247 for (int col = 0; col < m_HorizontalTiles; col++)
248 {
249 OneTile const * const tile = getTile(row, col);
250 if (tile)
251 {
252 QTransform const transform = painter->worldTransform();
253 painter->translate(tile->center);
254 painter->rotate(tile->rotation);
255
256 painter->setBrush(textBrush);
257 painter->setPen(textPen);
258
259 defaultFont.setPointSize(50 * fontScale);
260 painter->setFont(defaultFont);
261 painter->drawText(oneRect, Qt::AlignRight | Qt::AlignTop, QString("%1.").arg(tile->index));
262
263 defaultFont.setPointSize(20 * fontScale);
264 painter->setFont(defaultFont);
266 .arg(tile->skyCenter.ra0().toHMSString())
267 .arg(tile->skyCenter.dec0().toDMSString()));
269 .arg(tile->rotation >= 0.01 ? '+' : tile->rotation <= -0.01 ? '-' : '~')
270 .arg(abs(tile->rotation), 5, 'f', 2));
271
272 painter->setWorldTransform(transform);
273 }
274 }
275 }
276}
277
278QPointF MosaicTilesManager::rotatePoint(QPointF pointToRotate, QPointF centerPoint, double paDegrees)
279{
281 double cosTheta = cos(angleInRadians);
282 double sinTheta = sin(angleInRadians);
283
285
286 rotation_point.setX((cosTheta * (pointToRotate.x() - centerPoint.x()) -
287 sinTheta * (pointToRotate.y() - centerPoint.y()) + centerPoint.x()));
288 rotation_point.setY((sinTheta * (pointToRotate.x() - centerPoint.x()) +
289 cosTheta * (pointToRotate.y() - centerPoint.y()) + centerPoint.y()));
290
291 return rotation_point;
292}
293}
static KStars * Instance()
Definition kstars.h:123
The sky coordinates of a point in the sky.
Definition skypoint.h:45
static constexpr double DegToRad
DegToRad is a const static member equal to the number of radians in one degree (dms::PI/180....
Definition dms.h:390
Ekos is an advanced Astrophotography tool for Linux.
Definition align.cpp:79
KCRASH_EXPORT void setFlags(KCrash::CrashFlags flags)
KDOCTOOLS_EXPORT QString transform(const QString &file, const QString &stylesheet, const QList< const char * > &params=QList< const char * >())
void setPointSize(int pointSize)
virtual void mousePressEvent(QGraphicsSceneMouseEvent *event)
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
const QBrush & brush() const const
void drawRect(const QRect &rectangle)
void drawText(const QPoint &position, const QString &text)
const QFont & font() const const
void rotate(qreal angle)
void setBrush(Qt::BrushStyle style)
void setFont(const QFont &font)
void setPen(Qt::PenStyle style)
void setWorldTransform(const QTransform &matrix, bool combine)
void translate(const QPoint &offset)
const QTransform & worldTransform() const const
qreal x() const const
qreal y() const const
QString arg(Args &&... args) const const
AlignRight
lightGray
QTextStream & center(QTextStream &stream)
QTextStream & dec(QTextStream &stream)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri May 17 2024 11:48:26 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.