File indexing completed on 2024-03-24 15:17:43

0001 /*
0002     SPDX-FileCopyrightText: 2015 Jasem Mutlaq <mutlaqja@ikarustech.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #pragma once
0008 
0009 #include "noprecessindex.h"
0010 
0011 #include <memory>
0012 
0013 class TestArtificialHorizon;
0014 
0015 // An ArtificialHorizonEntity is a set of Azimuth & Altitude values defining
0016 // a series of connected line segments. Assuming ceiling is false (the default)
0017 // these lines define a horizon--coordinates indicating where the view is blocked
0018 // (below the line segments, lower in altitude) and where it is not blocked
0019 // (above the line segments, higher altitude values). If ceiling is true, then
0020 // this definition is flipped--the sky higher in altitude than the line segments
0021 // is considered blocked.
0022 class ArtificialHorizonEntity
0023 {
0024     public:
0025         ArtificialHorizonEntity() = default;
0026         ~ArtificialHorizonEntity();
0027 
0028         QString region() const;
0029         void setRegion(const QString &Region);
0030 
0031         bool enabled() const;
0032         void setEnabled(bool Enabled);
0033 
0034         bool ceiling() const;
0035         void setCeiling(bool value);
0036 
0037         void clearList();
0038         void setList(const std::shared_ptr<LineList> &list);
0039         std::shared_ptr<LineList> list() const;
0040 
0041         // Returns the altitude constraint for the azimuth angle (degrees).
0042         // constraintExists will be set to false if there is no constraint for the azimuth.
0043         double altitudeConstraint(double azimuthDegrees, bool *constraintExists) const;
0044 
0045     private:
0046         QString m_Region;
0047         bool m_Enabled { false };
0048         bool m_Ceiling { false };
0049         std::shared_ptr<LineList> m_List;
0050 };
0051 
0052 // ArtificialHorizon can contain several ArtificialHorizonEntities. That is,
0053 // it can have several sets of connected line segments. Assuming all the entities
0054 // are not ceilings, then the view is considered blocked below the highest line
0055 // segment that intersects a given azimuth. If none of the line segments cross
0056 // a given azimuth, then the view is not blocked at any altitude for that azimuth.
0057 // Similarly, if there are only "ceiling" horizon entities, then the view is blocked
0058 // at altitudes above the lowest ceiling. If there are a mix of ceilings and standard
0059 // entities, then for the given azimuth, at an altitude A, the view is blocked if
0060 // either the closest line below is a ceiling, or if the closest line above is a non-ceiling.
0061 class ArtificialHorizon
0062 {
0063     public:
0064         ArtificialHorizon() {}
0065         ~ArtificialHorizon();
0066 
0067         ArtificialHorizonEntity *findRegion(const QString &regionName);
0068         void addRegion(const QString &regionName, bool enabled, const std::shared_ptr<LineList> &list, bool ceiling);
0069         void removeRegion(const QString &regionName, bool lineOnly = false);
0070         bool enabled(int i) const;
0071         void load(const QList<ArtificialHorizonEntity *> &list);
0072 
0073         const QList<ArtificialHorizonEntity *> *horizonList() const
0074         {
0075             return &m_HorizonList;
0076         }
0077 
0078         // Returns true if one or more artificial horizons are enabled.
0079         bool altitudeConstraintsExist() const;
0080 
0081         // Returns true if the azimuth/altitude point is not blocked by the artificial horzon entities.
0082         bool isVisible(double azimuthDegrees, double altitudeDegrees, QString *reason = nullptr) const;
0083         // Like isVisible, but uses the cache if there are no ceiling constraints.
0084         bool isAltitudeOK(double azimuthDegrees, double altitudeDegrees, QString *reason) const;
0085 
0086         // returns the (highest) altitude constraint at the given azimuth.
0087         // If there are no constraints, then it returns -90.
0088         double altitudeConstraint(double azimuthDegrees) const;
0089 
0090         // Finds the nearest enabled constraint at the azimuth and above or below (not not exactly at)
0091         // the altitude given.
0092         const ArtificialHorizonEntity *getConstraintAbove(double azimuthDegrees, double altitudeDegrees,
0093                 const ArtificialHorizonEntity *ignore = nullptr) const;
0094         const ArtificialHorizonEntity *getConstraintBelow(double azimuthDegrees, double altitudeDegrees,
0095                 const ArtificialHorizonEntity *ignore = nullptr) const;
0096 
0097         // Draw the blocked areas on the skymap using the SkyPainter.
0098         // If painter is a nullptr, nothing is drawn.
0099         // If regious is not a nullpointer, all the polygon coordinates are placed
0100         // in the QList (for testing).
0101         void drawPolygons(SkyPainter *painter, QList<LineList> *regions = nullptr);
0102 
0103     private:
0104         // Removes a call to KStars::Instance() which is not necessary in testing.
0105         void setTesting()
0106         {
0107             testing = true;
0108         }
0109         void drawPolygons(int entity, SkyPainter *painter, QList<LineList> *regions = nullptr);
0110         void drawSampledPolygons(int entity, double az1, double alt1, double az2, double alt2,
0111                                  double sampling, SkyPainter *painter, QList<LineList> *regions);
0112         bool computePolygon(int entity, double az1, double alt1, double az2, double alt2,
0113                             double sampling, LineList *region);
0114 
0115         QList<ArtificialHorizonEntity *> m_HorizonList;
0116         bool testing { false };
0117 
0118         // Methods and data structure for precomputing altitudeConstraint(azimuth).
0119         // This way, we don't traverse the potentially horizon list each time
0120         // we query the horizon constraint.
0121         void precomputeConstraints() const;
0122         void resetPrecomputeConstraints() const;
0123         double precomputedConstraint(double azimuth) const;
0124         double altitudeConstraintInternal(double azimuthDegrees) const;
0125         mutable QVector<double> precomputedConstraints;
0126         bool noCeilingConstraints { true };
0127         void checkForCeilings();
0128         friend TestArtificialHorizon;
0129 };
0130 
0131 /**
0132  * @class ArtificialHorizon
0133  * Represents custom area from the horizon upwards which represent blocked views from the vantage point of the user.
0134  * Such blocked views could stem for example from tall trees or buildings. The user can define a series of line segments to
0135  * represent the blocked areas.
0136  *
0137  * @author Jasem Mutlaq
0138  * @version 0.1
0139  */
0140 class ArtificialHorizonComponent : public NoPrecessIndex
0141 {
0142     public:
0143         /**
0144          * @short Constructor
0145          *
0146          * @p parent pointer to the parent SkyComposite object
0147          * name is the name of the subclass
0148          */
0149         explicit ArtificialHorizonComponent(SkyComposite *parent);
0150 
0151         virtual ~ArtificialHorizonComponent() override;
0152 
0153         bool selected() override;
0154         void draw(SkyPainter *skyp) override;
0155 
0156         void setLivePreview(const std::shared_ptr<LineList> &preview)
0157         {
0158             livePreview = preview;
0159         }
0160         void setSelectedPreviewPoint(int index)
0161         {
0162             selectedPreviewPoint = index;
0163         }
0164         void addRegion(const QString &regionName, bool enabled, const std::shared_ptr<LineList> &list, bool ceiling);
0165         void removeRegion(const QString &regionName, bool lineOnly = false);
0166 
0167         const ArtificialHorizon &getHorizon()
0168         {
0169             return horizon;
0170         }
0171 
0172         bool load();
0173         void save();
0174 
0175     protected:
0176         void preDraw(SkyPainter *skyp) override;
0177 
0178     private:
0179         ArtificialHorizon horizon;
0180         std::shared_ptr<LineList> livePreview;
0181         int selectedPreviewPoint { -1 };
0182 
0183         friend class TestArtificialHorizon;
0184 };