File indexing completed on 2024-07-14 07:17:22

0001 /*
0002     SPDX-FileCopyrightText: 2007 James B. Bowlin <bowlin@mindspring.com>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #include "linelistlabel.h"
0007 
0008 #include "linelist.h"
0009 #include "Options.h"
0010 #ifndef KSTARS_LITE
0011 #include "skymap.h"
0012 #endif
0013 #include "skylabeler.h"
0014 #include "projections/projector.h"
0015 
0016 LineListLabel::LineListLabel(const QString &text) : m_text(text)
0017 {
0018     m_skyLabeler = SkyLabeler::Instance();
0019 
0020     // prevent a crash if drawGuideLabel() is called before reset()
0021     for (int i = 0; i < 4; i++)
0022     {
0023         m_labList[i]  = nullptr;
0024         m_labIndex[i] = 0;
0025     }
0026 }
0027 
0028 void LineListLabel::reset()
0029 {
0030     // These are the indices of the farthest left point, farthest right point,
0031     // etc.  The are data members so the drawLabels() routine can use them.
0032     // Zero indicates an index that was never set and is considered invalid
0033     // inside of drawLabels().
0034     for (int i = 0; i < 4; i++)
0035     {
0036         m_labList[i]  = nullptr;
0037         m_labIndex[i] = 0;
0038     }
0039 
0040     // These are used to keep track of the element that is farthest left,
0041     // farthest right, etc.
0042     m_farLeft  = 100000.0;
0043     m_farRight = 0.0;
0044     m_farTop   = 100000.0;
0045     m_farBot   = 0.0;
0046 
0047     m_skyLabeler->getMargins(m_text, &m_marginLeft, &m_marginRight, &m_marginTop, &m_marginBot);
0048 }
0049 
0050 void LineListLabel::updateLabelCandidates(qreal x, qreal y, LineList *lineList, int i)
0051 {
0052     if (i < 1)
0053         return;
0054 
0055     if (x < m_marginLeft || x > m_marginRight || y < m_marginTop || y > m_marginBot)
0056         return;
0057 
0058     if (x < m_farLeft)
0059     {
0060         m_labIndex[LeftCandidate] = i;
0061         m_labList[LeftCandidate]  = lineList;
0062         m_farLeft                 = x;
0063     }
0064     if (x > m_farRight)
0065     {
0066         m_labIndex[RightCandidate] = i;
0067         m_labList[RightCandidate]  = lineList;
0068         m_farRight                 = x;
0069     }
0070     if (y > m_farBot)
0071     {
0072         m_labIndex[BotCandidate] = i;
0073         m_labList[BotCandidate]  = lineList;
0074         m_farBot                 = x;
0075     }
0076     if (y < m_farTop)
0077     {
0078         m_labIndex[TopCandidate] = i;
0079         m_labList[TopCandidate]  = lineList;
0080         m_farTop                 = x;
0081     }
0082 }
0083 
0084 void LineListLabel::draw()
0085 {
0086 #ifndef KSTARS_LITE
0087     const Projector *proj = SkyMap::Instance()->projector();
0088 
0089     double comfyAngle = 40.0; // the first valid candidate with an angle
0090     // smaller than this gets displayed.  If you set
0091     // this to > 90. then the first valid candidate
0092     // will be displayed, regardless of angle.
0093 
0094     // We store info about the four candidate points in arrays to make several
0095     // of the steps easier, particularly choosing the valid candidate with the
0096     // smallest angle from the horizontal.
0097 
0098     int idx[4];                                   // index of candidate
0099     LineList *list[4];                            // LineList of candidate
0100     double a[4] = { 360.0, 360.0, 360.0, 360.0 }; // angle, default to large value
0101     QPointF o[4];                                 // candidate point
0102     bool okay[4] = { true, true, true, true };    // flag  candidate false if it
0103     // overlaps a previous label.
0104 
0105     // We no longer adjust the order but if we were to it would be here
0106     static int Order[4] = { LeftCandidate, BotCandidate, TopCandidate, LeftCandidate };
0107 
0108     for (int j = 0; j < 4; j++)
0109     {
0110         idx[j]  = m_labIndex[Order[j]];
0111         list[j] = m_labList[Order[j]];
0112     }
0113 
0114     // Make sure start with a valid candidate
0115     int first = 0;
0116     for (; first < 4; first++)
0117     {
0118         if (idx[first])
0119             break;
0120     }
0121 
0122     // return if there are no valid candidates
0123     if (first >= 4)
0124         return;
0125 
0126     // Try the points in order and print the label if we can draw it at
0127     // a comfortable angle for viewing;
0128     for (int j = first; j < 4; j++)
0129     {
0130         o[j] = angleAt(proj, list[j], idx[j], &a[j]);
0131 
0132         if (!idx[j] || !proj->checkVisibility(list[j]->at(idx[j]).get()))
0133         {
0134             okay[j] = false;
0135             continue;
0136         }
0137 
0138         if (fabs(a[j]) > comfyAngle)
0139             continue;
0140 
0141         if (m_skyLabeler->drawGuideLabel(o[j], m_text, a[j]))
0142             return;
0143 
0144         okay[j] = false;
0145     }
0146 
0147     //--- No angle was comfy so pick the one with the smallest angle ---
0148 
0149     // Index of the index/angle/point that gets displayed
0150     int best = first;
0151 
0152     // find first valid candidate that does not overlap existing labels
0153     for (; best < 4; best++)
0154     {
0155         if (idx[best] && okay[best])
0156             break;
0157     }
0158 
0159     // return if all candidates either overlap or are invalid
0160     if (best >= 4)
0161         return;
0162 
0163     // find the valid non-overlap candidate with the smallest angle
0164     for (int j = best + 1; j < 4; j++)
0165     {
0166         if (idx[j] && okay[j] && fabs(a[j]) < fabs(a[best]))
0167             best = j;
0168     }
0169 
0170     m_skyLabeler->drawGuideLabel(o[best], m_text, a[best]);
0171 #endif
0172 }
0173 
0174 QPointF LineListLabel::angleAt(const Projector *proj, LineList *list, int i, double *angle)
0175 {
0176     const SkyPoint *pThis = list->at(i).get();
0177     const SkyPoint *pLast = list->at(i-1).get();
0178 
0179     QPointF oThis = proj->toScreen(pThis);
0180     QPointF oLast = proj->toScreen(pLast);
0181 
0182     double sx = double(oThis.x() - oLast.x());
0183     double sy = double(oThis.y() - oLast.y());
0184 
0185     *angle = atan2(sy, sx) * 180.0 / dms::PI;
0186 
0187     // FIXME: use clamp in KSUtils
0188     // Never draw the label upside down
0189     if (*angle < -90.0)
0190         *angle += 180.0;
0191     if (*angle > 90.0)
0192         *angle -= 180.0;
0193 
0194     return oThis;
0195 }