Kstars

mosaictilesmanager.cpp
1 /*
2  SPDX-FileCopyrightText: 2021 Jasem Mutlaq <[email protected]>
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 
17 namespace Ekos
18 {
19 MosaicTilesManager::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 
34 MosaicTilesManager::~MosaicTilesManager()
35 {
36  qDeleteAll(tiles);
37 }
38 
39 
40 void MosaicTilesManager::setSkyCenter(SkyPoint center)
41 {
42  skyCenter = center;
43 }
44 
45 void 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 
54 void MosaicTilesManager::setGridDimensions(int width, int height)
55 {
56  m_HorizontalTiles = width;
57  m_VerticalTiles = height;
58 }
59 
60 void MosaicTilesManager::setSingleTileFOV(double fov_x, double fov_y)
61 {
62  fovW = fov_x;
63  fovH = fov_y;
64 }
65 
66 void MosaicTilesManager::setMosaicFOV(double mfov_x, double mfov_y)
67 {
68  mfovW = mfov_x;
69  mfovH = mfov_y;
70 }
71 
72 void MosaicTilesManager::setOverlap(double value)
73 {
74  overlap = (value < 0) ? 0 : (1 < value) ? 1 : value;
75 }
76 
77 
78 QSizeF 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 
92 void 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 
164 MosaicTilesManager::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 
174 QRectF 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;
185  QRectF rect = QRectF(-totalW / 2, -totalH / 2, totalW + xOverlapped, totalH + yOverlapped);
186  return rect;
187 }
188 
189 void MosaicTilesManager::mousePressEvent(QGraphicsSceneMouseEvent *event)
190 {
192  QPointF pos = event->screenPos();
193  m_LastPosition = pos;
194 }
195 
196 void MosaicTilesManager::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
197 {
199  QPointF delta = event->screenPos() - m_LastPosition;
200  emit newOffset(delta);
201 }
202 
203 void 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);
265  painter->drawText(oneRect, Qt::AlignHCenter | Qt::AlignVCenter, QString("%1\n%2")
266  .arg(tile->skyCenter.ra0().toHMSString())
267  .arg(tile->skyCenter.dec0().toDMSString()));
268  painter->drawText(oneRect, Qt::AlignHCenter | Qt::AlignBottom, QString("%1%2°")
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 
278 QPointF MosaicTilesManager::rotatePoint(QPointF pointToRotate, QPointF centerPoint, double paDegrees)
279 {
280  double angleInRadians = paDegrees * dms::DegToRad;
281  double cosTheta = cos(angleInRadians);
282  double sinTheta = sin(angleInRadians);
283 
284  QPointF rotation_point;
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 }
KDOCTOOLS_EXPORT QString transform(const QString &file, const QString &stylesheet, const QVector< const char * > &params=QVector< const char * >())
AlignRight
qreal height() const const
void setPen(const QColor &color)
void rotate(qreal angle)
Ekos is an advanced Astrophotography tool for Linux. It is based on a modular extensible framework to...
Definition: align.cpp:70
Stores dms coordinates for a point in the sky. for converting between coordinate systems.
Definition: skypoint.h:44
void drawRect(const QRectF &rectangle)
void setPointSize(int pointSize)
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:385
KCRASH_EXPORT void setFlags(KCrash::CrashFlags flags)
void drawText(const QPointF &position, const QString &text)
static KStars * Instance()
Definition: kstars.h:125
void setX(qreal x)
void setY(qreal y)
QTextStream & dec(QTextStream &stream)
const QBrush & brush() const const
void setBrush(const QBrush &brush)
virtual void mousePressEvent(QGraphicsSceneMouseEvent *event)
qreal x() const const
qreal y() const const
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
const QTransform & worldTransform() const const
const QFont & font() const const
void translate(const QPointF &offset)
QTextStream & center(QTextStream &stream)
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
void setFont(const QFont &font)
lightGray
qreal width() const const
void setWorldTransform(const QTransform &matrix, bool combine)
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Fri Aug 12 2022 04:00:55 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.