1 /*
2     SPDX-FileCopyrightText: 2015 Jasem Mutlaq <mutlaqja@ikarustech.com>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #pragma once
8 
9 #include "noprecessindex.h"
10 
11 #include <memory>
12 
13 class TestArtificialHorizon;
14 
15 // An ArtificialHorizonEntity is a set of Azimuth & Altitude values defining
16 // a series of connected line segments. Assuming ceiling is false (the default)
17 // these lines define a horizon--coordinates indicating where the view is blocked
18 // (below the line segments, lower in altitude) and where it is not blocked
19 // (above the line segments, higher altitude values). If ceiling is true, then
20 // this definition is flipped--the sky higher in altitude than the line segments
21 // is considered blocked.
22 class ArtificialHorizonEntity
23 {
24     public:
25         ArtificialHorizonEntity() = default;
26         ~ArtificialHorizonEntity();
27 
28         QString region() const;
29         void setRegion(const QString &Region);
30 
31         bool enabled() const;
32         void setEnabled(bool Enabled);
33 
34         bool ceiling() const;
35         void setCeiling(bool value);
36 
37         void clearList();
38         void setList(const std::shared_ptr<LineList> &list);
39         std::shared_ptr<LineList> list() const;
40 
41         // Returns the altitude constraint for the azimuth angle (degrees).
42         // constraintExists will be set to false if there is no constraint for the azimuth.
43         double altitudeConstraint(double azimuthDegrees, bool *constraintExists) const;
44 
45     private:
46         QString m_Region;
47         bool m_Enabled { false };
48         bool m_Ceiling { false };
49         std::shared_ptr<LineList> m_List;
50 };
51 
52 // ArtificialHorizon can contain several ArtificialHorizonEntities. That is,
53 // it can have several sets of connected line segments. Assuming all the entities
54 // are not ceilings, then the view is considered blocked below the highest line
55 // segment that intersects a given azimuth. If none of the line segments cross
56 // a given azimuth, then the view is not blocked at any altitude for that azimuth.
57 // Similarly, if there are only "ceiling" horizon entities, then the view is blocked
58 // at altitudes above the lowest ceiling. If there are a mix of ceilings and standard
59 // entities, then for the given azimuth, at an altitude A, the view is blocked if
60 // either the closest line below is a ceiling, or if the closest line above is a non-ceiling.
61 class ArtificialHorizon
62 {
63     public:
ArtificialHorizon()64         ArtificialHorizon() {}
65         ~ArtificialHorizon();
66 
67         ArtificialHorizonEntity *findRegion(const QString &regionName);
68         void addRegion(const QString &regionName, bool enabled, const std::shared_ptr<LineList> &list, bool ceiling);
69         void removeRegion(const QString &regionName, bool lineOnly = false);
70         bool enabled(int i) const;
71         void load(const QList<ArtificialHorizonEntity *> &list);
72 
horizonList()73         const QList<ArtificialHorizonEntity *> *horizonList() const
74         {
75             return &m_HorizonList;
76         }
77 
78         // Returns true if one or more artificial horizons are enabled.
79         bool altitudeConstraintsExist() const;
80 
81         // Returns true if the azimuth/altitude point is not blocked by the artificial horzon entities.
82         bool isVisible(double azimuthDegrees, double altitudeDegrees) const;
83 
84         // returns the (highest) altitude constraint at the given azimuth.
85         // If there are no constraints, then it returns -90.
86         double altitudeConstraint(double azimuthDegrees) const;
87 
88         // Finds the nearest enabled constraint at the azimuth and above or below (not not exactly at)
89         // the altitude given.
90         const ArtificialHorizonEntity *getConstraintAbove(double azimuthDegrees, double altitudeDegrees,
91                 const ArtificialHorizonEntity *ignore = nullptr) const;
92         const ArtificialHorizonEntity *getConstraintBelow(double azimuthDegrees, double altitudeDegrees,
93                 const ArtificialHorizonEntity *ignore = nullptr) const;
94 
95         // Draw the blocked areas on the skymap using the SkyPainter.
96         // If painter is a nullptr, nothing is drawn.
97         // If regious is not a nullpointer, all the polygon coordinates are placed
98         // in the QList (for testing).
99         void drawPolygons(SkyPainter *painter, QList<LineList> *regions = nullptr);
100 
101     private:
102         // Removes a call to KStars::Instance() which is not necessary in testing.
setTesting()103         void setTesting()
104         {
105             testing = true;
106         }
107         void drawPolygons(int entity, SkyPainter *painter, QList<LineList> *regions = nullptr);
108         void drawSampledPolygons(int entity, double az1, double alt1, double az2, double alt2,
109                                  double sampling, SkyPainter *painter, QList<LineList> *regions);
110         bool computePolygon(int entity, double az1, double alt1, double az2, double alt2,
111                             LineList *region);
112 
113         QList<ArtificialHorizonEntity *> m_HorizonList;
114         bool testing { false };
115 
116         friend TestArtificialHorizon;
117 };
118 
119 /**
120  * @class ArtificialHorizon
121  * Represents custom area from the horizon upwards which represent blocked views from the vantage point of the user.
122  * Such blocked views could stem for example from tall trees or buildings. The user can define a series of line segments to
123  * represent the blocked areas.
124  *
125  * @author Jasem Mutlaq
126  * @version 0.1
127  */
128 class ArtificialHorizonComponent : public NoPrecessIndex
129 {
130     public:
131         /**
132          * @short Constructor
133          *
134          * @p parent pointer to the parent SkyComposite object
135          * name is the name of the subclass
136          */
137         explicit ArtificialHorizonComponent(SkyComposite *parent);
138 
139         virtual ~ArtificialHorizonComponent() override;
140 
141         bool selected() override;
142         void draw(SkyPainter *skyp) override;
143 
setLivePreview(const std::shared_ptr<LineList> & preview)144         void setLivePreview(const std::shared_ptr<LineList> &preview)
145         {
146             livePreview = preview;
147         }
setSelectedPreviewPoint(int index)148         void setSelectedPreviewPoint(int index)
149         {
150             selectedPreviewPoint = index;
151         }
152         void addRegion(const QString &regionName, bool enabled, const std::shared_ptr<LineList> &list, bool ceiling);
153         void removeRegion(const QString &regionName, bool lineOnly = false);
154 
getHorizon()155         const ArtificialHorizon &getHorizon()
156         {
157             return horizon;
158         }
159 
160         bool load();
161         void save();
162 
163     protected:
164         void preDraw(SkyPainter *skyp) override;
165 
166     private:
167         ArtificialHorizon horizon;
168         std::shared_ptr<LineList> livePreview;
169         int selectedPreviewPoint { -1 };
170 
171         friend class TestArtificialHorizon;
172 };
173