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

0001 /*
0002     SPDX-FileCopyrightText: 2005 Thomas Kabelmann <thomas.kabelmann@gmx.de>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "starcomponent.h"
0008 
0009 #include "binfilehelper.h"
0010 #include "deepstarcomponent.h"
0011 #include "highpmstarlist.h"
0012 #ifndef KSTARS_LITE
0013 #include "kstars.h"
0014 #endif
0015 #include "kstarsdata.h"
0016 #include "kstarssplash.h"
0017 #include "Options.h"
0018 #include "skylabeler.h"
0019 #include "skymap.h"
0020 #include "skymesh.h"
0021 #ifndef KSTARS_LITE
0022 #include "skyqpainter.h"
0023 #endif
0024 #include "htmesh/MeshIterator.h"
0025 #include "projections/projector.h"
0026 
0027 #include "kstars_debug.h"
0028 
0029 #include <qplatformdefs.h>
0030 
0031 #ifdef _WIN32
0032 #include <windows.h>
0033 #endif
0034 
0035 #if defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
0036 #include <sys/endian.h>
0037 #define bswap_16(x) bswap16(x)
0038 #define bswap_32(x) bswap32(x)
0039 #else
0040 #include "byteorder.h"
0041 #endif
0042 
0043 // Qt version calming
0044 #include <qtskipemptyparts.h>
0045 
0046 StarComponent *StarComponent::pinstance = nullptr;
0047 
0048 StarComponent::StarComponent(SkyComposite *parent)
0049     : ListComponent(parent), m_reindexNum(J2000)
0050 {
0051     m_skyMesh          = SkyMesh::Instance();
0052     m_StarBlockFactory = StarBlockFactory::Instance();
0053 
0054     m_starIndex.reset(new StarIndex());
0055     for (int i = 0; i < m_skyMesh->size(); i++)
0056         m_starIndex->append(new StarList());
0057     m_highPMStars.append(new HighPMStarList(840.0));
0058     m_highPMStars.append(new HighPMStarList(304.0));
0059     m_reindexInterval = StarObject::reindexInterval(304.0);
0060 
0061     for (int i = 0; i <= MAX_LINENUMBER_MAG; i++)
0062         m_labelList[i] = new LabelList;
0063 
0064     // Actually load data
0065     emitProgressText(i18n("Loading stars"));
0066 
0067     loadStaticData();
0068     // Load any deep star catalogs that are available
0069     loadDeepStarCatalogs();
0070 
0071     // The following works but can cause crashes sometimes
0072     //QtConcurrent::run(this, &StarComponent::loadDeepStarCatalogs);
0073 
0074     //In KStars Lite star images are initialized in SkyMapLite
0075 #ifndef KSTARS_LITE
0076     SkyQPainter::initStarImages();
0077 #endif
0078 }
0079 
0080 StarComponent::~StarComponent()
0081 {
0082     qDeleteAll(*m_starIndex);
0083     m_starIndex->clear();
0084     qDeleteAll(m_DeepStarComponents);
0085     m_DeepStarComponents.clear();
0086     qDeleteAll(m_highPMStars);
0087     m_highPMStars.clear();
0088     for (int i = 0; i <= MAX_LINENUMBER_MAG; i++)
0089         delete m_labelList[i];
0090 }
0091 
0092 StarComponent *StarComponent::Create(SkyComposite *parent)
0093 {
0094     delete pinstance;
0095     pinstance = new StarComponent(parent);
0096     return pinstance;
0097 }
0098 
0099 bool StarComponent::selected()
0100 {
0101     return Options::showStars();
0102 }
0103 
0104 bool StarComponent::addDeepStarCatalogIfExists(const QString &fileName, float trigMag, bool staticstars)
0105 {
0106     if (BinFileHelper::testFileExists(fileName))
0107     {
0108         m_DeepStarComponents.append(new DeepStarComponent(parent(), fileName, trigMag, staticstars));
0109         return true;
0110     }
0111     return false;
0112 }
0113 
0114 int StarComponent::loadDeepStarCatalogs()
0115 {
0116     // Look for the basic unnamed star catalog to mag 8.0
0117     if (!addDeepStarCatalogIfExists("unnamedstars.dat", -5.0, true))
0118         return 0;
0119 
0120     // Look for the Tycho-2 add-on with 2.5 million stars to mag 12.5
0121     if (!addDeepStarCatalogIfExists("tycho2.dat", 8.0) && !addDeepStarCatalogIfExists("deepstars.dat", 8.0))
0122         return 1;
0123 
0124     // Look for the USNO NOMAD 1e8 star catalog add-on with stars to mag 16
0125     if (!addDeepStarCatalogIfExists("USNO-NOMAD-1e8.dat", 11.0))
0126         return 2;
0127 
0128     return 3;
0129 }
0130 
0131 //This function is empty for a reason; we override the normal
0132 //update function in favor of JiT updates for stars.
0133 void StarComponent::update(KSNumbers *)
0134 {
0135 }
0136 
0137 // We use the update hook to re-index all the stars when the date has changed by
0138 // more than 150 years.
0139 
0140 bool StarComponent::reindex(KSNumbers *num)
0141 {
0142     if (!num)
0143         return false;
0144 
0145     // for large time steps we re-index all points
0146     if (fabs(num->julianCenturies() - m_reindexNum.julianCenturies()) > m_reindexInterval)
0147     {
0148         reindexAll(num);
0149         return true;
0150     }
0151 
0152     bool highPM = true;
0153 
0154     // otherwise we just re-index fast movers as needed
0155     for (auto &star : m_highPMStars)
0156         highPM &= !(star->reindex(num, m_starIndex.get()));
0157 
0158     return !(highPM);
0159 }
0160 
0161 void StarComponent::reindexAll(KSNumbers *num)
0162 {
0163 #if 0
0164     if (0 && !m_reindexSplash)
0165     {
0166         m_reindexSplash = new KStarsSplash(i18n("Please wait while re-indexing stars..."));
0167         QObject::connect(KStarsData::Instance(), SIGNAL(progressText(QString)), m_reindexSplash,
0168                          SLOT(setMessage(QString)));
0169 
0170         m_reindexSplash->show();
0171         m_reindexSplash->raise();
0172         return;
0173     }
0174 #endif
0175 
0176     qCInfo(KSTARS) << "Re-indexing Stars to year" << 2000.0 + num->julianCenturies() * 100.0;
0177 
0178     m_reindexNum = KSNumbers(*num);
0179     m_skyMesh->setKSNumbers(num);
0180 
0181     // clear out the old index
0182     for (auto &item : *m_starIndex)
0183     {
0184         item->clear();
0185     }
0186 
0187     // re-populate it from the objectList
0188     for (auto &object : m_ObjectList)
0189     {
0190         StarObject *star = dynamic_cast<StarObject *>(object);
0191         Trixel trixel    = m_skyMesh->indexStar(star);
0192 
0193         m_starIndex->at(trixel)->append(star);
0194     }
0195 
0196     // Let everyone else know we have re-indexed to num
0197     for (auto &star : m_highPMStars)
0198     {
0199         star->setIndexTime(num);
0200     }
0201 
0202     //delete m_reindexSplash;
0203     //m_reindexSplash = 0;
0204 }
0205 
0206 float StarComponent::faintMagnitude() const
0207 {
0208     float faintmag = m_FaintMagnitude;
0209 
0210     for (auto &component : m_DeepStarComponents)
0211     {
0212         if (faintmag < component->faintMagnitude())
0213             faintmag = component->faintMagnitude();
0214     }
0215     return faintmag;
0216 }
0217 
0218 float StarComponent::zoomMagnitudeLimit()
0219 {
0220     //adjust maglimit for ZoomLevel
0221     double lgmin = log10(MINZOOM);
0222     double lgz   = log10(Options::zoomFactor());
0223 
0224     // Old formula:
0225     //    float maglim = ( 2.000 + 2.444 * Options::memUsage() / 10.0 ) * ( lgz - lgmin ) + Options::magLimitDrawStarZoomOut();
0226 
0227     /*
0228      Explanation for the following formula:
0229      --------------------------------------
0230      Estimates from a sample of 125000 stars shows that, magnitude
0231      limit vs. number of stars follows the formula:
0232        nStars = 10^(.45 * maglim + .95)
0233      (A better formula is available here: https://www.aa.quae.nl/en/antwoorden/magnituden.html
0234       which we do not implement for simplicity)
0235      We want to keep the star density on screen a constant. This is directly proportional to the number of stars
0236      and directly proportional to the area on screen. The area is in turn inversely proportional to the square
0237      of the zoom factor ( zoomFactor / MINZOOM ). This means that (taking logarithms):
0238        0.45 * maglim + 0.95 - 2 * log( ZoomFactor ) - log( Star Density ) - log( Some proportionality constant )
0239      hence the formula. We've gathered together all the constants and set it to 3.5, so as to set the minimum
0240      possible value of maglim to 3.5
0241     */
0242 
0243     //    float maglim = 4.444 * ( lgz - lgmin ) + 2.222 * log10( Options::starDensity() ) + 3.5;
0244 
0245     // Reducing the slope w.r.t zoom factor to avoid the extremely fast increase in star density with zoom
0246     // that 4.444 gives us (although that is what the derivation gives us)
0247 
0248     return 3.5 + 3.7 * (lgz - lgmin) + 2.222 * log10(static_cast<float>(Options::starDensity()));
0249 }
0250 
0251 void StarComponent::draw(SkyPainter *skyp)
0252 {
0253 #ifndef KSTARS_LITE
0254     if (!selected())
0255         return;
0256 
0257     SkyMap *map           = SkyMap::Instance();
0258     const Projector *proj = map->projector();
0259     KStarsData *data      = KStarsData::Instance();
0260     UpdateID updateID     = data->updateID();
0261 
0262     bool checkSlewing = (map->isSlewing() && Options::hideOnSlew());
0263     m_hideLabels      = checkSlewing || !(Options::showStarMagnitudes() || Options::showStarNames());
0264 
0265     //shortcuts to inform whether to draw different objects
0266     bool hideFaintStars = checkSlewing && Options::hideStars();
0267     double hideStarsMag = Options::magLimitHideStar();
0268     reindex(data->updateNum());
0269 
0270     double lgmin = log10(MINZOOM);
0271     double lgmax = log10(MAXZOOM);
0272     double lgz   = log10(Options::zoomFactor());
0273 
0274     double maglim;
0275     m_zoomMagLimit = maglim = zoomMagnitudeLimit();
0276 
0277     double labelMagLim = Options::starLabelDensity() / 5.0;
0278     labelMagLim += (12.0 - labelMagLim) * (lgz - lgmin) / (lgmax - lgmin);
0279     if (labelMagLim > 8.0)
0280         labelMagLim = 8.0;
0281 
0282     //Calculate sizeMagLim
0283     // Old formula:
0284     //    float sizeMagLim = ( 2.000 + 2.444 * Options::memUsage() / 10.0 ) * ( lgz - lgmin ) + 5.8;
0285 
0286     // Using the maglim to compute the sizes of stars reduces
0287     // discernability between brighter and fainter stars at high zoom
0288     // levels. To fix that, we use an "arbitrary" constant in place of
0289     // the variable star density.
0290     // Not using this formula now.
0291     //    float sizeMagLim = 4.444 * ( lgz - lgmin ) + 5.0;
0292 
0293     float sizeMagLim = zoomMagnitudeLimit();
0294     if (sizeMagLim > faintMagnitude() * (1 - 1.5 / 16))
0295         sizeMagLim = faintMagnitude() * (1 - 1.5 / 16);
0296     skyp->setSizeMagLimit(sizeMagLim);
0297 
0298     //Loop for drawing star images
0299 
0300     MeshIterator region(m_skyMesh, DRAW_BUF);
0301     magLim = maglim;
0302 
0303     // If we are hiding faint stars, then maglim is really the brighter of hideStarsMag and maglim
0304     if (hideFaintStars && maglim > hideStarsMag)
0305         maglim = hideStarsMag;
0306 
0307     m_StarBlockFactory->drawID = m_skyMesh->drawID();
0308 
0309     int nTrixels = 0;
0310 
0311     while (region.hasNext())
0312     {
0313         ++nTrixels;
0314         Trixel currentRegion = region.next();
0315         StarList *starList   = m_starIndex->at(currentRegion);
0316 
0317         for (auto &star : *starList)
0318         {
0319             if (!star)
0320                 continue;
0321 
0322             float mag = star->mag();
0323 
0324             // break loop if maglim is reached
0325             if (mag > maglim)
0326                 break;
0327 
0328             if (star->updateID != updateID)
0329                 star->JITupdate();
0330 
0331             bool drawn = skyp->drawPointSource(star, mag, star->spchar());
0332 
0333             //FIXME_SKYPAINTER: find a better way to do this.
0334             if (drawn && !(m_hideLabels || mag > labelMagLim))
0335                 addLabel(proj->toScreen(star), star);
0336         }
0337     }
0338 
0339     // Draw focusStar if not null
0340     if (focusStar)
0341     {
0342         if (focusStar->updateID != updateID)
0343             focusStar->JITupdate();
0344         float mag = focusStar->mag();
0345         skyp->drawPointSource(focusStar, mag, focusStar->spchar());
0346     }
0347 
0348     // Now draw each of our DeepStarComponents
0349     for (auto &component : m_DeepStarComponents)
0350     {
0351         component->draw(skyp);
0352     }
0353 #else
0354     Q_UNUSED(skyp)
0355 #endif
0356 }
0357 
0358 void StarComponent::addLabel(const QPointF &p, StarObject *star)
0359 {
0360     int idx = int(star->mag() * 10.0);
0361     if (idx < 0)
0362         idx = 0;
0363     if (idx > MAX_LINENUMBER_MAG)
0364         idx = MAX_LINENUMBER_MAG;
0365     m_labelList[idx]->append(SkyLabel(p, star));
0366 }
0367 
0368 void StarComponent::drawLabels()
0369 {
0370     if (m_hideLabels)
0371         return;
0372 
0373     SkyLabeler *labeler = SkyLabeler::Instance();
0374     labeler->setPen(QColor(KStarsData::Instance()->colorScheme()->colorNamed("SNameColor")));
0375 
0376     int max = int(m_zoomMagLimit * 10.0);
0377     if (max < 0)
0378         max = 0;
0379     if (max > MAX_LINENUMBER_MAG)
0380         max = MAX_LINENUMBER_MAG;
0381 
0382     for (int i = 0; i <= max; i++)
0383     {
0384         LabelList *list = m_labelList[i];
0385 
0386         for (const auto &item : *list)
0387         {
0388             labeler->drawNameLabel(item.obj, item.o);
0389         }
0390         list->clear();
0391     }
0392 }
0393 
0394 bool StarComponent::loadStaticData()
0395 {
0396     // We break from Qt / KDE API and use traditional file handling here, to obtain speed.
0397     // We also avoid C++ constructors for the same reason.
0398     FILE *dataFile, *nameFile;
0399     bool swapBytes = false, named = false, gnamed = false;
0400     BinFileHelper dataReader, nameReader;
0401     QString name, gname, visibleName;
0402     StarObject *star;
0403 
0404     if (starsLoaded)
0405         return true;
0406 
0407     // prepare to index stars to this date
0408     m_skyMesh->setKSNumbers(&m_reindexNum);
0409 
0410     /* Open the data files */
0411     // TODO: Maybe we don't want to hardcode the filename?
0412     if ((dataFile = dataReader.openFile("namedstars.dat")) == nullptr)
0413     {
0414         qCWarning(KSTARS) << "Could not open data file namedstars.dat";
0415         return false;
0416     }
0417 
0418     if (!(nameFile = nameReader.openFile("starnames.dat")))
0419     {
0420         qCWarning(KSTARS) << "Could not open data file starnames.dat";
0421         return false;
0422     }
0423 
0424     if (!dataReader.readHeader())
0425     {
0426         qCWarning(KSTARS) << "Error reading namedstars.dat header : " << dataReader.getErrorNumber() << " : "
0427                           << dataReader.getError();
0428         return false;
0429     }
0430 
0431     if (!nameReader.readHeader())
0432     {
0433         qCWarning(KSTARS) << "Error reading starnames.dat header : " << nameReader.getErrorNumber() << " : "
0434                           << nameReader.getError();
0435         return false;
0436     }
0437     //KDE_fseek(nameFile, nameReader.getDataOffset(), SEEK_SET);
0438     QT_FSEEK(nameFile, nameReader.getDataOffset(), SEEK_SET);
0439     swapBytes = dataReader.getByteSwap();
0440 
0441     long int nstars = 0;
0442 
0443     //KDE_fseek(dataFile, dataReader.getDataOffset(), SEEK_SET);
0444     QT_FSEEK(dataFile, dataReader.getDataOffset(), SEEK_SET);
0445 
0446     qint16 faintmag;
0447     quint8 htm_level;
0448     quint16 t_MSpT;
0449     int ret = 0;
0450 
0451     // Faint Magnitude
0452     ret = fread(&faintmag, 2, 1, dataFile);
0453     if (swapBytes)
0454         faintmag = bswap_16(faintmag);
0455 
0456     // HTM Level
0457     ret = fread(&htm_level, 1, 1, dataFile);
0458 
0459     // Unused
0460     {
0461         int rc = fread(&t_MSpT, 2, 1, dataFile);
0462         Q_UNUSED(rc)
0463     }
0464 
0465     if (faintmag / 100.0 > m_FaintMagnitude)
0466         m_FaintMagnitude = faintmag / 100.0;
0467 
0468     if (htm_level != m_skyMesh->level())
0469         qCWarning(KSTARS)
0470                 << "HTM Level in shallow star data file and HTM Level in m_skyMesh do not match. EXPECT TROUBLE"
0471                 ;
0472     for (int i = 0; i < m_skyMesh->size(); ++i)
0473     {
0474         Trixel trixel = i; // = ( ( i >= 256 ) ? ( i - 256 ) : ( i + 256 ) );
0475         for (unsigned long j = 0; j < static_cast<unsigned long>(dataReader.getRecordCount(i)); ++j)
0476         {
0477             if (1 != fread(&stardata, sizeof(StarData), 1, dataFile))
0478             {
0479                 qCCritical(KSTARS) << "FILE FORMAT ERROR: Could not read StarData structure for star #" << j << " under trixel #"
0480                                    << trixel;
0481                 continue;
0482             }
0483 
0484             /* Swap Bytes when required */
0485             if (swapBytes)
0486                 byteSwap(&stardata);
0487 
0488             named  = false;
0489             gnamed = false;
0490 
0491             /* Named Star - Read the nameFile */
0492             if (stardata.flags & 0x01)
0493             {
0494                 visibleName = "";
0495                 if (1 != fread(&starname, sizeof(starName), 1, nameFile))
0496                     qCCritical(KSTARS) << "ERROR: fread() call on nameFile failed in trixel " << trixel << " star " << j;
0497 
0498                 name  = QByteArray(starname.longName, 32);
0499                 named = !name.isEmpty();
0500 
0501                 gname  = QByteArray(starname.bayerName, 8);
0502                 gnamed = !gname.isEmpty();
0503 
0504                 if (gnamed && starname.bayerName[0] != '.')
0505                     visibleName = gname;
0506 
0507                 if (named)
0508                 {
0509                     // HEV: look up star name in internationalization filesource
0510                     name = i18nc("star name", name.toLocal8Bit().data());
0511                 }
0512                 else
0513                 {
0514                     name = i18n("star");
0515                 }
0516             }
0517             else
0518                 qCCritical(KSTARS) << "ERROR: Named star file contains unnamed stars! Expect trouble.";
0519 
0520             /* Create the new StarObject */
0521             star = new StarObject;
0522             star->init(&stardata);
0523             //if( star->getHDIndex() != 0 && name == i18n("star"))
0524             if (stardata.HD)
0525             {
0526                 m_HDHash.insert(stardata.HD, star);
0527                 if (named == false)
0528                 {
0529                     name  = QString("HD %1").arg(stardata.HD);
0530                     named = true;
0531                 }
0532             }
0533 
0534             star->setNames(name, visibleName);
0535             //star->EquatorialToHorizontal( data->lst(), data->geo()->lat() );
0536             ++nstars;
0537 
0538             if (gnamed)
0539                 m_genName.insert(gname, star);
0540 
0541             //if ( ! name.isEmpty() && name != i18n("star"))
0542             if (named)
0543             {
0544                 objectNames(SkyObject::STAR).append(name);
0545                 objectLists(SkyObject::STAR).append(QPair<QString, const SkyObject *>(name, star));
0546             }
0547 
0548             if (!visibleName.isEmpty() && gname != name)
0549             {
0550                 QString gName = star->gname(false);
0551                 objectNames(SkyObject::STAR).append(gName);
0552                 objectLists(SkyObject::STAR).append(QPair<QString, const SkyObject *>(gName, star));
0553             }
0554 
0555             appendListObject(star);
0556 
0557             m_starIndex->at(trixel)->append(star);
0558             double pm = star->pmMagnitude();
0559 
0560             for (auto &list : m_highPMStars)
0561             {
0562                 if (list->append(trixel, star, pm))
0563                     break;
0564             }
0565         }
0566     }
0567 
0568     dataReader.closeFile();
0569     nameReader.closeFile();
0570 
0571     starsLoaded = true;
0572     return true;
0573 }
0574 
0575 void StarComponent::appendListObject(SkyObject *object)
0576 {
0577     m_ObjectList.append(object);
0578     m_ObjectHash.insert(object->name().toLower(), object);
0579     m_ObjectHash.insert(object->longname().toLower(), object);
0580     m_ObjectHash.insert(object->name2().toLower(), object);
0581     m_ObjectHash.insert(object->name2().toLower(), object);
0582     m_ObjectHash.insert((dynamic_cast<StarObject *>(object))->gname(false).toLower(), object);
0583 }
0584 
0585 SkyObject *StarComponent::findStarByGenetiveName(const QString name)
0586 {
0587     if (name.startsWith(QLatin1String("HD")))
0588     {
0589         QStringList fields = name.split(' ', Qt::SkipEmptyParts);
0590         bool Ok            = false;
0591         unsigned int HDNum = fields[1].toInt(&Ok);
0592         if (Ok)
0593             return findByHDIndex(HDNum);
0594     }
0595     return m_genName.value(name);
0596 }
0597 
0598 void StarComponent::objectsInArea(QList<SkyObject *> &list, const SkyRegion &region)
0599 {
0600     for (SkyRegion::const_iterator it = region.constBegin(); it != region.constEnd(); ++it)
0601     {
0602         Trixel trixel      = it.key();
0603         StarList *starlist = m_starIndex->at(trixel);
0604         for (int i = 0; starlist && i < starlist->size(); i++)
0605             if (starlist->at(i) && starlist->at(i)->name() != QString("star"))
0606                 list.push_back(starlist->at(i));
0607     }
0608 }
0609 
0610 StarObject *StarComponent::findByHDIndex(int HDnum)
0611 {
0612     KStarsData *data = KStarsData::Instance();
0613     StarObject *o = nullptr;
0614     BinFileHelper hdidxReader;
0615 
0616     // First check the hash to see if we have a corresponding StarObject already
0617     if ((o = m_HDHash.value(HDnum, nullptr)))
0618         return o;
0619     // If we don't have the StarObject here, try it in the DeepStarComponents' hashes
0620     if (m_DeepStarComponents.size() >= 1)
0621         if ((o = m_DeepStarComponents.at(0)->findByHDIndex(HDnum)))
0622             return o;
0623     if (m_DeepStarComponents.size() >= 2)
0624     {
0625         qint32 offset = 0;
0626         int ret = 0;
0627         FILE *hdidxFile = hdidxReader.openFile("Henry-Draper.idx");
0628         FILE *dataFile = nullptr;
0629 
0630         if (!hdidxFile)
0631             return nullptr;
0632         //KDE_fseek( hdidxFile, (HDnum - 1) * 4, SEEK_SET );
0633         QT_FSEEK(hdidxFile, (HDnum - 1) * 4, SEEK_SET);
0634         // TODO: Offsets need to be byteswapped if this is a big endian machine.
0635         // This means that the Henry Draper Index needs a endianness indicator.
0636         ret = fread(&offset, 4, 1, hdidxFile);
0637         if (offset <= 0)
0638             return nullptr;
0639         dataFile = m_DeepStarComponents.at(1)->getStarReader()->getFileHandle();
0640         //KDE_fseek( dataFile, offset, SEEK_SET );
0641         QT_FSEEK(dataFile, offset, SEEK_SET);
0642         {
0643             int rc = fread(&stardata, sizeof(StarData), 1, dataFile);
0644             Q_UNUSED(rc)
0645         }
0646         if (m_DeepStarComponents.at(1)->getStarReader()->getByteSwap())
0647         {
0648             byteSwap(&stardata);
0649         }
0650         m_starObject.init(&stardata);
0651         m_starObject.EquatorialToHorizontal(data->lst(), data->geo()->lat());
0652         m_starObject.JITupdate();
0653         focusStar = m_starObject.clone();
0654         m_HDHash.insert(HDnum, focusStar);
0655         hdidxReader.closeFile();
0656         return focusStar;
0657     }
0658 
0659     return nullptr;
0660 }
0661 
0662 // This uses the main star index for looking up nearby stars but then
0663 // filters out objects with the generic name "star".  We could easily
0664 // build an index for just the named stars which would make this go
0665 // much faster still.  -jbb
0666 //
0667 SkyObject *StarComponent::objectNearest(SkyPoint *p, double &maxrad)
0668 {
0669     m_zoomMagLimit = zoomMagnitudeLimit();
0670 
0671     SkyObject *oBest = nullptr;
0672 
0673     MeshIterator region(m_skyMesh, OBJ_NEAREST_BUF);
0674 
0675     while (region.hasNext())
0676     {
0677         Trixel currentRegion = region.next();
0678         StarList *starList   = m_starIndex->at(currentRegion);
0679 
0680         for (auto &star : *starList)
0681         {
0682             if (!star)
0683                 continue;
0684             if (star->mag() > m_zoomMagLimit)
0685                 continue;
0686 
0687             double r = star->angularDistanceTo(p).Degrees();
0688 
0689             if (r < maxrad)
0690             {
0691                 oBest  = star;
0692                 maxrad = r;
0693             }
0694         }
0695     }
0696 
0697     // Check up with our Deep Star Components too!
0698     double rTry, rBest;
0699     SkyObject *oTry;
0700     // JM 2016-03-30: Multiply rBest by a factor of 0.5 so that named stars are preferred to unnamed stars searched below
0701     rBest = maxrad * 0.5;
0702     rTry  = maxrad;
0703     for (auto &component : m_DeepStarComponents)
0704     {
0705         oTry = component->objectNearest(p, rTry);
0706         if (rTry < rBest)
0707         {
0708             rBest = rTry;
0709             oBest = oTry;
0710         }
0711     }
0712     maxrad = rBest;
0713 
0714     return oBest;
0715 }
0716 
0717 void StarComponent::starsInAperture(QList<StarObject *> &list, const SkyPoint &center, float radius, float maglim)
0718 {
0719     // Ensure that we have deprecessed the (RA, Dec) to (RA0, Dec0)
0720     Q_ASSERT(center.ra0().Degrees() >= 0.0);
0721     Q_ASSERT(center.dec0().Degrees() <= 90.0);
0722 
0723     m_skyMesh->intersect(center.ra0().Degrees(), center.dec0().Degrees(), radius, static_cast<BufNum>(OBJ_NEAREST_BUF));
0724 
0725     MeshIterator region(m_skyMesh, OBJ_NEAREST_BUF);
0726 
0727     if (maglim < -28)
0728         maglim = m_FaintMagnitude;
0729 
0730     while (region.hasNext())
0731     {
0732         Trixel currentRegion = region.next();
0733         StarList *starList   = m_starIndex->at(currentRegion);
0734 
0735         for (auto &star : *starList)
0736         {
0737             if (!star)
0738                 continue;
0739             if (star->mag() > m_FaintMagnitude)
0740                 continue;
0741             if (star->angularDistanceTo(&center).Degrees() <= radius)
0742                 list.append(star);
0743         }
0744     }
0745 
0746     // Add stars from the DeepStarComponents as well
0747     for (auto &component : m_DeepStarComponents)
0748     {
0749         component->starsInAperture(list, center, radius, maglim);
0750     }
0751 }
0752 
0753 void StarComponent::byteSwap(StarData *stardata)
0754 {
0755     stardata->RA       = bswap_32(stardata->RA);
0756     stardata->Dec      = bswap_32(stardata->Dec);
0757     stardata->dRA      = bswap_32(stardata->dRA);
0758     stardata->dDec     = bswap_32(stardata->dDec);
0759     stardata->parallax = bswap_32(stardata->parallax);
0760     stardata->HD       = bswap_32(stardata->HD);
0761     stardata->mag      = bswap_16(stardata->mag);
0762     stardata->bv_index = bswap_16(stardata->bv_index);
0763 }
0764 /*
0765 void StarComponent::printDebugInfo() {
0766 
0767     int nTrixels = 0;
0768     int nBlocks = 0;
0769     long int nStars = 0;
0770     float faintMag = -5.0;
0771 
0772     MeshIterator trixels( m_skyMesh, DRAW_BUF );
0773     Trixel trixel;
0774 
0775     while( trixels.hasNext() ) {
0776         trixel = trixels.next();
0777         nTrixels++;
0778         for(int i = 0; i < m_starBlockList[ trixel ]->getBlockCount(); ++i) {
0779             nBlocks++;
0780             StarBlock *block = m_starBlockList[ trixel ]->block( i );
0781             for(int j = 0; j < block->getStarCount(); ++j) {
0782                 nStars++;
0783             }
0784             if( block->getFaintMag() > faintMag ) {
0785                 faintMag = block->getFaintMag();
0786             }
0787         }
0788     }
0789 
0790     printf( "========== UNNAMED STAR MEMORY ALLOCATION INFORMATION ==========\n" );
0791     printf( "Number of visible trixels                    = %8d\n", nTrixels );
0792     printf( "Number of visible StarBlocks                 = %8d\n", nBlocks );
0793     printf( "Number of StarBlocks allocated via SBF       = %8d\n", m_StarBlockFactory.getBlockCount() );
0794     printf( "Number of unnamed stars in memory            = %8ld\n", nStars );
0795     printf( "Magnitude of the faintest star in memory     = %8.2f\n", faintMag );
0796     printf( "Target magnitude limit                       = %8.2f\n", magLim );
0797     printf( "Size of each StarBlock                       = %8d bytes\n", sizeof( StarBlock ) );
0798     printf( "Size of each StarObject                      = %8d bytes\n", sizeof( StarObject ) );
0799     printf( "Memory use due to visible unnamed stars      = %8.2f MB\n", ( sizeof( StarObject ) * nStars / 1048576.0 ) );
0800     printf( "Memory use due to visible StarBlocks         = %8d bytes\n", sizeof( StarBlock ) * nBlocks );
0801     printf( "Memory use due to StarBlocks in SBF          = %8d bytes\n", sizeof( StarBlock ) * m_StarBlockFactory.getBlockCount() );
0802     printf( "=============== STAR DRAW LOOP TIMING INFORMATION ==============\n" );
0803     printf( "Time taken for drawing named stars           = %8ld ms\n", t_drawNamed );
0804     printf( "Time taken for dynamic load of data          = %8ld ms\n", t_dynamicLoad );
0805     printf( "Time taken for updating LRU cache            = %8ld ms\n", t_updateCache );
0806     printf( "Time taken for drawing unnamed stars         = %8ld ms\n", t_drawUnnamed );
0807     printf( "================================================================\n" );
0808 }
0809 
0810 bool StarComponent::verifySBLIntegrity() {
0811 
0812     float faintMag = -5.0;
0813     bool integrity = true;
0814     for(Trixel trixel = 0; trixel < m_skyMesh->size(); ++trixel) {
0815         for(int i = 0; i < m_starBlockList[ trixel ]->getBlockCount(); ++i) {
0816             StarBlock *block = m_starBlockList[ trixel ]->block( i );
0817             if( i == 0 )
0818                 faintMag = block->getBrightMag();
0819             // NOTE: Assumes 2 decimal places in magnitude field. TODO: Change if it ever does change
0820             if( block->getBrightMag() != faintMag && ( block->getBrightMag() - faintMag ) > 0.016) {
0821                 qDebug() << Q_FUNC_INFO << "Trixel " << trixel << ": ERROR: faintMag of prev block = " << faintMag
0822                          << ", brightMag of block #" << i << " = " << block->getBrightMag();
0823                 integrity = false;
0824             }
0825             if( i > 1 && ( !block->prev ) )
0826                 qDebug() << Q_FUNC_INFO << "Trixel " << trixel << ": ERROR: Block" << i << "is unlinked in LRU Cache";
0827             if( block->prev && block->prev->parent == m_starBlockList[ trixel ]
0828                 && block->prev != m_starBlockList[ trixel ]->block( i - 1 ) ) {
0829                 qDebug() << Q_FUNC_INFO << "Trixel " << trixel << ": ERROR: SBF LRU Cache linked list seems to be broken at before block " << i;
0830                 integrity = false;
0831             }
0832             faintMag = block->getFaintMag();
0833         }
0834     }
0835     return integrity;
0836 }
0837 */