1 /* 2 SPDX-FileCopyrightText: 2010 Henry de Valence <hdevalence@gmail.com> 3 4 SPDX-License-Identifier: GPL-2.0-or-later 5 */ 6 7 #pragma once 8 9 #ifdef KSTARS_LITE 10 #include "skymaplite.h" 11 #else 12 #include "skymap.h" 13 #endif 14 #include "skyobjects/skypoint.h" 15 16 #if __GNUC__ > 5 17 #pragma GCC diagnostic push 18 #pragma GCC diagnostic ignored "-Wignored-attributes" 19 #endif 20 #if __GNUC__ > 6 21 #pragma GCC diagnostic ignored "-Wint-in-bool-context" 22 #endif 23 #include <Eigen/Core> 24 #if __GNUC__ > 5 25 #pragma GCC diagnostic pop 26 #endif 27 28 #include <QPointF> 29 30 #include <cstddef> 31 #include <cmath> 32 33 class KStarsData; 34 35 /** This is just a container that holds information needed to do projections. */ 36 class ViewParams 37 { 38 public: 39 float width, height; 40 float zoomFactor; 41 bool useRefraction; 42 bool useAltAz; 43 bool fillGround; ///<If the ground is filled, then points below horizon are invisible 44 SkyPoint *focus; ViewParams()45 ViewParams() : width(0), height(0), zoomFactor(0), 46 useRefraction(false), useAltAz(false), fillGround(false), 47 focus(nullptr) {} 48 }; 49 50 /** 51 * @class Projector 52 * 53 * The Projector class is the primary class that serves as an interface to handle projections. 54 */ 55 class Projector 56 { 57 Q_GADGET 58 public: 59 /** 60 * Constructor. 61 * 62 * @param p the ViewParams for this projection 63 */ 64 explicit Projector(const ViewParams &p); 65 66 virtual ~Projector() = default; 67 68 /** Update cached values for projector */ 69 void setViewParams(const ViewParams &p); viewParams()70 ViewParams viewParams() const 71 { 72 return m_vp; 73 } 74 75 enum Projection 76 { 77 Lambert, 78 AzimuthalEquidistant, 79 Orthographic, 80 Equirectangular, 81 Stereographic, 82 Gnomonic, 83 UnknownProjection 84 }; 85 Q_ENUM(Projection) 86 87 /** Return the type of this projection */ 88 Q_INVOKABLE virtual Projection type() const = 0; 89 90 /** Return the FOV of this projection */ 91 double fov() const; 92 93 /** 94 * Check if the current point on screen is a valid point on the sky. This is needed 95 * to avoid a crash of the program if the user clicks on a point outside the sky (the 96 * corners of the sky map at the lowest zoom level are the invalid points). 97 * @param p the screen pixel position 98 */ 99 virtual bool unusablePoint(const QPointF &p) const; 100 101 /** 102 * Given the coordinates of the SkyPoint argument, determine the 103 * pixel coordinates in the SkyMap. 104 * 105 * Since most of the projections used by KStars are very similar, 106 * if this function were to be reimplemented in each projection subclass 107 * we would end up changing maybe 5 or 6 lines out of 150. 108 * Instead, we have a default implementation that uses the projectionK 109 * and projectionL functions to take care of the differences between 110 * e.g. Orthographic and Stereographic. There is also the cosMaxFieldAngle 111 * function, which is used for testing whether a point is on the visible 112 * part of the projection, and the radius function which gives the radius of 113 * the projection in screen coordinates. 114 * 115 * While this seems ugly, it is less ugly than duplicating 150 loc to change 5. 116 * 117 * @return Eigen::Vector2f containing screen pixel x, y coordinates of SkyPoint. 118 * @param o pointer to the SkyPoint for which to calculate x, y coordinates. 119 * @param oRefract true = use Options::useRefraction() value. 120 * false = do not use refraction. This argument is only needed 121 * for the Horizon, which should never be refracted. 122 * @param onVisibleHemisphere pointer to a bool to indicate whether the point is 123 * on the visible part of the Celestial Sphere. 124 */ 125 virtual Eigen::Vector2f toScreenVec(const SkyPoint *o, bool oRefract = true, 126 bool *onVisibleHemisphere = nullptr) const; 127 128 /** 129 * This is exactly the same as toScreenVec but it returns a QPointF. 130 * It just calls toScreenVec and converts the result. 131 * @see toScreenVec() 132 */ 133 QPointF toScreen(const SkyPoint *o, bool oRefract = true, bool *onVisibleHemisphere = nullptr) const; 134 135 /** 136 * @short Determine RA, Dec coordinates of the pixel at (dx, dy), which are the 137 * screen pixel coordinate offsets from the center of the Sky pixmap. 138 * @param p the screen pixel position to convert 139 * @param LST pointer to the local sidereal time, as a dms object. 140 * @param lat pointer to the current geographic laitude, as a dms object 141 * @param onlyAltAz the returned SkyPoint's RA & DEC are not computed, only Alt/Az. 142 */ 143 virtual SkyPoint fromScreen(const QPointF &p, dms *LST, const dms *lat, bool onlyAltAz = false) const; 144 145 /** 146 * ASSUMES *p1 did not clip but *p2 did. Returns the QPointF on the line 147 * between *p1 and *p2 that just clips. 148 */ 149 QPointF clipLine(SkyPoint *p1, SkyPoint *p2) const; 150 151 /** 152 * ASSUMES *p1 did not clip but *p2 did. Returns the Eigen::Vector2f on the line 153 * between *p1 and *p2 that just clips. 154 */ 155 Eigen::Vector2f clipLineVec(SkyPoint *p1, SkyPoint *p2) const; 156 157 /** Check whether the projected point is on-screen */ 158 bool onScreen(const QPointF &p) const; 159 bool onScreen(const Eigen::Vector2f &p) const; 160 161 /** 162 * @short Determine if the skypoint p is likely to be visible in the display window. 163 * 164 * checkVisibility() is an optimization function. It determines whether an object 165 * appears within the bounds of the skymap window, and therefore should be drawn. 166 * The idea is to save time by skipping objects which are off-screen, so it is 167 * absolutely essential that checkVisibility() is significantly faster than 168 * the computations required to draw the object to the screen. 169 * 170 * If the ground is to be filled, the function first checks whether the point is 171 * below the horizon, because they will be covered by the ground anyways. 172 * Importantly, it does not call the expensive EquatorialToHorizontal function. 173 * This means that the horizontal coordinates MUST BE CORRECT! The vast majority 174 * of points are already synchronized, so recomputing the horizontal coordinates is 175 * a waste. 176 * 177 * The function then checks the difference between the Declination/Altitude 178 * coordinate of the Focus position, and that of the point p. If the absolute 179 * value of this difference is larger than fov, then the function returns false. 180 * For most configurations of the sky map window, this simple check is enough to 181 * exclude a large number of objects. 182 * 183 * Next, it determines if one of the poles of the current Coordinate System 184 * (Equatorial or Horizontal) is currently inside the sky map window. This is 185 * stored in the member variable 'bool SkyMap::isPoleVisible, and is set by the 186 * function SkyMap::setMapGeometry(), which is called by SkyMap::paintEvent(). 187 * If a Pole is visible, then it will return true immediately. The idea is that 188 * when a pole is on-screen it is computationally expensive to determine whether 189 * a particular position is on-screen or not: for many valid Dec/Alt values, *all* 190 * values of RA/Az will indeed be onscreen, but for other valid Dec/Alt values, 191 * only *most* RA/Az values are onscreen. It is cheaper to simply accept all 192 * "horizontal" RA/Az values, since we have already determined that they are 193 * on-screen in the "vertical" Dec/Alt coordinate. 194 * 195 * Finally, if no Pole is onscreen, it checks the difference between the Focus 196 * position's RA/Az coordinate and that of the point p. If the absolute value of 197 * this difference is larger than XMax, the function returns false. Otherwise, 198 * it returns true. 199 * 200 * @param p pointer to the skypoint to be checked. 201 * @return true if the point p was found to be inside the Sky map window. 202 * @see SkyMap::setMapGeometry() 203 * @see SkyMap::fov() 204 * @note If you are creating skypoints using equatorial coordinates, then 205 * YOU MUST CALL EQUATORIALTOHORIZONTAL BEFORE THIS FUNCTION! 206 */ 207 bool checkVisibility(const SkyPoint *p) const; 208 209 /** 210 * Determine the on-screen position angle of a SkyPont with recept with NCP. 211 * This is the object's sky position angle (w.r.t. North). 212 * of "North" at the position of the object (w.r.t. the screen Y-axis). 213 * The latter is determined by constructing a test point with the same RA but 214 * a slightly increased Dec as the object, and calculating the angle w.r.t. the 215 * Y-axis of the line connecting the object to its test point. 216 */ 217 double findNorthPA(const SkyPoint *o, float x, float y) const; 218 219 /** 220 * Determine the on-screen position angle of a SkyObject. This is the sum 221 * of the object's sky position angle (w.r.t. North), and the position angle 222 * of "North" at the position of the object (w.r.t. the screen Y-axis). 223 * The latter is determined by constructing a test point with the same RA but 224 * a slightly increased Dec as the object, and calculating the angle w.r.t. the 225 * Y-axis of the line connecting the object to its test point. 226 */ 227 double findPA(const SkyObject *o, float x, float y) const; 228 229 /** 230 * Get the ground polygon 231 * @param labelpoint This point will be set to something suitable for attaching a label 232 * @param drawLabel this tells whether to draw a label. 233 * @return the ground polygon 234 */ 235 virtual QVector<Eigen::Vector2f> groundPoly(SkyPoint *labelpoint = nullptr, bool *drawLabel = nullptr) const; 236 237 /** 238 * @brief updateClipPoly calculate the clipping polygen given the current FOV. 239 */ 240 virtual void updateClipPoly(); 241 242 /** 243 * @return the clipping polygen covering the visible sky area. Anything outside this polygon is 244 * clipped by QPainter. 245 */ 246 virtual QPolygonF clipPoly() const; 247 248 protected: 249 /** 250 * Get the radius of this projection's sky circle. 251 * @return the radius in radians 252 */ radius()253 virtual double radius() const 254 { 255 return 2 * M_PI; 256 } 257 258 /** 259 * This function handles some of the projection-specific code. 260 * @see toScreen() 261 */ projectionK(double x)262 virtual double projectionK(double x) const 263 { 264 return x; 265 } 266 267 /** 268 * This function handles some of the projection-specific code. 269 * @see toScreen() 270 */ projectionL(double x)271 virtual double projectionL(double x) const 272 { 273 return x; 274 } 275 276 /** 277 * This function returns the cosine of the maximum field angle, i.e., the maximum angular 278 * distance from the focus for which a point should be projected. Default is 0, i.e., 279 * 90 degrees. 280 */ cosMaxFieldAngle()281 virtual double cosMaxFieldAngle() const 282 { 283 return 0; 284 } 285 286 /** 287 * Helper function for drawing ground. 288 * @return the point with Alt = 0, az = @p az 289 */ 290 static SkyPoint pointAt(double az); 291 292 KStarsData *m_data { nullptr }; 293 ViewParams m_vp; 294 double m_sinY0 { 0 }; 295 double m_cosY0 { 0 }; 296 double m_fov { 0 }; 297 QPolygonF m_clipPolygon; 298 299 private: 300 //Used by CheckVisibility 301 double m_xrange { 0 }; 302 bool m_isPoleVisible { false }; 303 }; 304