1 /*
2     SPDX-FileCopyrightText: 2005 Thomas Kabelmann <thomas.kabelmann@gmx.de>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "starcomponent.h"
8 
9 #include "binfilehelper.h"
10 #include "deepstarcomponent.h"
11 #include "highpmstarlist.h"
12 #ifndef KSTARS_LITE
13 #include "kstars.h"
14 #endif
15 #include "kstarsdata.h"
16 #include "kstarssplash.h"
17 #include "Options.h"
18 #include "skylabeler.h"
19 #include "skymap.h"
20 #include "skymesh.h"
21 #ifndef KSTARS_LITE
22 #include "skyqpainter.h"
23 #endif
24 #include "htmesh/MeshIterator.h"
25 #include "projections/projector.h"
26 
27 #include "kstars_debug.h"
28 
29 #include <qplatformdefs.h>
30 
31 #ifdef _WIN32
32 #include <windows.h>
33 #endif
34 
35 #if defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
36 #include <sys/endian.h>
37 #define bswap_16(x) bswap16(x)
38 #define bswap_32(x) bswap32(x)
39 #else
40 #include "byteorder.h"
41 #endif
42 
43 StarComponent *StarComponent::pinstance = nullptr;
44 
StarComponent(SkyComposite * parent)45 StarComponent::StarComponent(SkyComposite *parent)
46     : ListComponent(parent), m_reindexNum(J2000)
47 {
48     m_skyMesh          = SkyMesh::Instance();
49     m_StarBlockFactory = StarBlockFactory::Instance();
50 
51     m_starIndex.reset(new StarIndex());
52     for (int i = 0; i < m_skyMesh->size(); i++)
53         m_starIndex->append(new StarList());
54     m_highPMStars.append(new HighPMStarList(840.0));
55     m_highPMStars.append(new HighPMStarList(304.0));
56     m_reindexInterval = StarObject::reindexInterval(304.0);
57 
58     for (int i = 0; i <= MAX_LINENUMBER_MAG; i++)
59         m_labelList[i] = new LabelList;
60 
61     // Actually load data
62     emitProgressText(i18n("Loading stars"));
63 
64     loadStaticData();
65     // Load any deep star catalogs that are available
66     loadDeepStarCatalogs();
67 
68     // The following works but can cause crashes sometimes
69     //QtConcurrent::run(this, &StarComponent::loadDeepStarCatalogs);
70 
71     //In KStars Lite star images are initialized in SkyMapLite
72 #ifndef KSTARS_LITE
73     SkyQPainter::initStarImages();
74 #endif
75 }
76 
~StarComponent()77 StarComponent::~StarComponent()
78 {
79     qDeleteAll(*m_starIndex);
80     m_starIndex->clear();
81     qDeleteAll(m_DeepStarComponents);
82     m_DeepStarComponents.clear();
83     qDeleteAll(m_highPMStars);
84     m_highPMStars.clear();
85     for (int i = 0; i <= MAX_LINENUMBER_MAG; i++)
86         delete m_labelList[i];
87 }
88 
Create(SkyComposite * parent)89 StarComponent *StarComponent::Create(SkyComposite *parent)
90 {
91     delete pinstance;
92     pinstance = new StarComponent(parent);
93     return pinstance;
94 }
95 
selected()96 bool StarComponent::selected()
97 {
98     return Options::showStars();
99 }
100 
addDeepStarCatalogIfExists(const QString & fileName,float trigMag,bool staticstars)101 bool StarComponent::addDeepStarCatalogIfExists(const QString &fileName, float trigMag, bool staticstars)
102 {
103     if (BinFileHelper::testFileExists(fileName))
104     {
105         m_DeepStarComponents.append(new DeepStarComponent(parent(), fileName, trigMag, staticstars));
106         return true;
107     }
108     return false;
109 }
110 
loadDeepStarCatalogs()111 int StarComponent::loadDeepStarCatalogs()
112 {
113     // Look for the basic unnamed star catalog to mag 8.0
114     if (!addDeepStarCatalogIfExists("unnamedstars.dat", -5.0, true))
115         return 0;
116 
117     // Look for the Tycho-2 add-on with 2.5 million stars to mag 12.5
118     if (!addDeepStarCatalogIfExists("tycho2.dat", 8.0) && !addDeepStarCatalogIfExists("deepstars.dat", 8.0))
119         return 1;
120 
121     // Look for the USNO NOMAD 1e8 star catalog add-on with stars to mag 16
122     if (!addDeepStarCatalogIfExists("USNO-NOMAD-1e8.dat", 11.0))
123         return 2;
124 
125     return 3;
126 }
127 
128 //This function is empty for a reason; we override the normal
129 //update function in favor of JiT updates for stars.
update(KSNumbers *)130 void StarComponent::update(KSNumbers *)
131 {
132 }
133 
134 // We use the update hook to re-index all the stars when the date has changed by
135 // more than 150 years.
136 
reindex(KSNumbers * num)137 bool StarComponent::reindex(KSNumbers *num)
138 {
139     if (!num)
140         return false;
141 
142     // for large time steps we re-index all points
143     if (fabs(num->julianCenturies() - m_reindexNum.julianCenturies()) > m_reindexInterval)
144     {
145         reindexAll(num);
146         return true;
147     }
148 
149     bool highPM = true;
150 
151     // otherwise we just re-index fast movers as needed
152     for (auto &star : m_highPMStars)
153         highPM &= !(star->reindex(num, m_starIndex.get()));
154 
155     return !(highPM);
156 }
157 
reindexAll(KSNumbers * num)158 void StarComponent::reindexAll(KSNumbers *num)
159 {
160 #if 0
161     if (0 && !m_reindexSplash)
162     {
163         m_reindexSplash = new KStarsSplash(i18n("Please wait while re-indexing stars..."));
164         QObject::connect(KStarsData::Instance(), SIGNAL(progressText(QString)), m_reindexSplash,
165                          SLOT(setMessage(QString)));
166 
167         m_reindexSplash->show();
168         m_reindexSplash->raise();
169         return;
170     }
171 #endif
172 
173     qCInfo(KSTARS) << "Re-indexing Stars to year" << 2000.0 + num->julianCenturies() * 100.0;
174 
175     m_reindexNum = KSNumbers(*num);
176     m_skyMesh->setKSNumbers(num);
177 
178     // clear out the old index
179     for (auto &item : *m_starIndex)
180     {
181         item->clear();
182     }
183 
184     // re-populate it from the objectList
185     for (auto &object : m_ObjectList)
186     {
187         StarObject *star = dynamic_cast<StarObject *>(object);
188         Trixel trixel    = m_skyMesh->indexStar(star);
189 
190         m_starIndex->at(trixel)->append(star);
191     }
192 
193     // Let everyone else know we have re-indexed to num
194     for (auto &star : m_highPMStars)
195     {
196         star->setIndexTime(num);
197     }
198 
199     //delete m_reindexSplash;
200     //m_reindexSplash = 0;
201 }
202 
faintMagnitude() const203 float StarComponent::faintMagnitude() const
204 {
205     float faintmag = m_FaintMagnitude;
206 
207     for (auto &component : m_DeepStarComponents)
208     {
209         if (faintmag < component->faintMagnitude())
210             faintmag = component->faintMagnitude();
211     }
212     return faintmag;
213 }
214 
zoomMagnitudeLimit()215 float StarComponent::zoomMagnitudeLimit()
216 {
217     //adjust maglimit for ZoomLevel
218     double lgmin = log10(MINZOOM);
219     double lgz   = log10(Options::zoomFactor());
220 
221     // Old formula:
222     //    float maglim = ( 2.000 + 2.444 * Options::memUsage() / 10.0 ) * ( lgz - lgmin ) + Options::magLimitDrawStarZoomOut();
223 
224     /*
225      Explanation for the following formula:
226      --------------------------------------
227      Estimates from a sample of 125000 stars shows that, magnitude
228      limit vs. number of stars follows the formula:
229        nStars = 10^(.45 * maglim + .95)
230      (A better formula is available here: https://www.aa.quae.nl/en/antwoorden/magnituden.html
231       which we do not implement for simplicity)
232      We want to keep the star density on screen a constant. This is directly proportional to the number of stars
233      and directly proportional to the area on screen. The area is in turn inversely proportional to the square
234      of the zoom factor ( zoomFactor / MINZOOM ). This means that (taking logarithms):
235        0.45 * maglim + 0.95 - 2 * log( ZoomFactor ) - log( Star Density ) - log( Some proportionality constant )
236      hence the formula. We've gathered together all the constants and set it to 3.5, so as to set the minimum
237      possible value of maglim to 3.5
238     */
239 
240     //    float maglim = 4.444 * ( lgz - lgmin ) + 2.222 * log10( Options::starDensity() ) + 3.5;
241 
242     // Reducing the slope w.r.t zoom factor to avoid the extremely fast increase in star density with zoom
243     // that 4.444 gives us (although that is what the derivation gives us)
244 
245     return 3.5 + 3.7 * (lgz - lgmin) + 2.222 * log10(static_cast<float>(Options::starDensity()));
246 }
247 
draw(SkyPainter * skyp)248 void StarComponent::draw(SkyPainter *skyp)
249 {
250 #ifndef KSTARS_LITE
251     if (!selected())
252         return;
253 
254     SkyMap *map           = SkyMap::Instance();
255     const Projector *proj = map->projector();
256     KStarsData *data      = KStarsData::Instance();
257     UpdateID updateID     = data->updateID();
258 
259     bool checkSlewing = (map->isSlewing() && Options::hideOnSlew());
260     m_hideLabels      = checkSlewing || !(Options::showStarMagnitudes() || Options::showStarNames());
261 
262     //shortcuts to inform whether to draw different objects
263     bool hideFaintStars = checkSlewing && Options::hideStars();
264     double hideStarsMag = Options::magLimitHideStar();
265     reindex(data->updateNum());
266 
267     double lgmin = log10(MINZOOM);
268     double lgmax = log10(MAXZOOM);
269     double lgz   = log10(Options::zoomFactor());
270 
271     double maglim;
272     m_zoomMagLimit = maglim = zoomMagnitudeLimit();
273 
274     double labelMagLim = Options::starLabelDensity() / 5.0;
275     labelMagLim += (12.0 - labelMagLim) * (lgz - lgmin) / (lgmax - lgmin);
276     if (labelMagLim > 8.0)
277         labelMagLim = 8.0;
278 
279     //Calculate sizeMagLim
280     // Old formula:
281     //    float sizeMagLim = ( 2.000 + 2.444 * Options::memUsage() / 10.0 ) * ( lgz - lgmin ) + 5.8;
282 
283     // Using the maglim to compute the sizes of stars reduces
284     // discernability between brighter and fainter stars at high zoom
285     // levels. To fix that, we use an "arbitrary" constant in place of
286     // the variable star density.
287     // Not using this formula now.
288     //    float sizeMagLim = 4.444 * ( lgz - lgmin ) + 5.0;
289 
290     float sizeMagLim = zoomMagnitudeLimit();
291     if (sizeMagLim > faintMagnitude() * (1 - 1.5 / 16))
292         sizeMagLim = faintMagnitude() * (1 - 1.5 / 16);
293     skyp->setSizeMagLimit(sizeMagLim);
294 
295     //Loop for drawing star images
296 
297     MeshIterator region(m_skyMesh, DRAW_BUF);
298     magLim = maglim;
299 
300     // If we are hiding faint stars, then maglim is really the brighter of hideStarsMag and maglim
301     if (hideFaintStars && maglim > hideStarsMag)
302         maglim = hideStarsMag;
303 
304     m_StarBlockFactory->drawID = m_skyMesh->drawID();
305 
306     int nTrixels = 0;
307 
308     while (region.hasNext())
309     {
310         ++nTrixels;
311         Trixel currentRegion = region.next();
312         StarList *starList   = m_starIndex->at(currentRegion);
313 
314         for (auto &star : *starList)
315         {
316             if (!star)
317                 continue;
318 
319             float mag = star->mag();
320 
321             // break loop if maglim is reached
322             if (mag > maglim)
323                 break;
324 
325             if (star->updateID != updateID)
326                 star->JITupdate();
327 
328             bool drawn = skyp->drawPointSource(star, mag, star->spchar());
329 
330             //FIXME_SKYPAINTER: find a better way to do this.
331             if (drawn && !(m_hideLabels || mag > labelMagLim))
332                 addLabel(proj->toScreen(star), star);
333         }
334     }
335 
336     // Draw focusStar if not null
337     if (focusStar)
338     {
339         if (focusStar->updateID != updateID)
340             focusStar->JITupdate();
341         float mag = focusStar->mag();
342         skyp->drawPointSource(focusStar, mag, focusStar->spchar());
343     }
344 
345     // Now draw each of our DeepStarComponents
346     for (auto &component : m_DeepStarComponents)
347     {
348         component->draw(skyp);
349     }
350 #else
351     Q_UNUSED(skyp)
352 #endif
353 }
354 
addLabel(const QPointF & p,StarObject * star)355 void StarComponent::addLabel(const QPointF &p, StarObject *star)
356 {
357     int idx = int(star->mag() * 10.0);
358     if (idx < 0)
359         idx = 0;
360     if (idx > MAX_LINENUMBER_MAG)
361         idx = MAX_LINENUMBER_MAG;
362     m_labelList[idx]->append(SkyLabel(p, star));
363 }
364 
drawLabels()365 void StarComponent::drawLabels()
366 {
367     if (m_hideLabels)
368         return;
369 
370     SkyLabeler *labeler = SkyLabeler::Instance();
371     labeler->setPen(QColor(KStarsData::Instance()->colorScheme()->colorNamed("SNameColor")));
372 
373     int max = int(m_zoomMagLimit * 10.0);
374     if (max < 0)
375         max = 0;
376     if (max > MAX_LINENUMBER_MAG)
377         max = MAX_LINENUMBER_MAG;
378 
379     for (int i = 0; i <= max; i++)
380     {
381         LabelList *list = m_labelList[i];
382 
383         for (const auto &item : *list)
384         {
385             labeler->drawNameLabel(item.obj, item.o);
386         }
387         list->clear();
388     }
389 }
390 
loadStaticData()391 bool StarComponent::loadStaticData()
392 {
393     // We break from Qt / KDE API and use traditional file handling here, to obtain speed.
394     // We also avoid C++ constructors for the same reason.
395     FILE *dataFile, *nameFile;
396     bool swapBytes = false, named = false, gnamed = false;
397     BinFileHelper dataReader, nameReader;
398     QString name, gname, visibleName;
399     StarObject *star;
400 
401     if (starsLoaded)
402         return true;
403 
404     // prepare to index stars to this date
405     m_skyMesh->setKSNumbers(&m_reindexNum);
406 
407     /* Open the data files */
408     // TODO: Maybe we don't want to hardcode the filename?
409     if ((dataFile = dataReader.openFile("namedstars.dat")) == nullptr)
410     {
411         qCWarning(KSTARS) << "Could not open data file namedstars.dat";
412         return false;
413     }
414 
415     if (!(nameFile = nameReader.openFile("starnames.dat")))
416     {
417         qCWarning(KSTARS) << "Could not open data file starnames.dat";
418         return false;
419     }
420 
421     if (!dataReader.readHeader())
422     {
423         qCWarning(KSTARS) << "Error reading namedstars.dat header : " << dataReader.getErrorNumber() << " : "
424                           << dataReader.getError();
425         return false;
426     }
427 
428     if (!nameReader.readHeader())
429     {
430         qCWarning(KSTARS) << "Error reading starnames.dat header : " << nameReader.getErrorNumber() << " : "
431                           << nameReader.getError();
432         return false;
433     }
434     //KDE_fseek(nameFile, nameReader.getDataOffset(), SEEK_SET);
435     QT_FSEEK(nameFile, nameReader.getDataOffset(), SEEK_SET);
436     swapBytes = dataReader.getByteSwap();
437 
438     long int nstars = 0;
439 
440     //KDE_fseek(dataFile, dataReader.getDataOffset(), SEEK_SET);
441     QT_FSEEK(dataFile, dataReader.getDataOffset(), SEEK_SET);
442 
443     qint16 faintmag;
444     quint8 htm_level;
445     quint16 t_MSpT;
446     int ret = 0;
447 
448     // Faint Magnitude
449     ret = fread(&faintmag, 2, 1, dataFile);
450     if (swapBytes)
451         faintmag = bswap_16(faintmag);
452 
453     // HTM Level
454     ret = fread(&htm_level, 1, 1, dataFile);
455 
456     // Unused
457     {
458         int rc = fread(&t_MSpT, 2, 1, dataFile);
459         Q_UNUSED(rc)
460     }
461 
462     if (faintmag / 100.0 > m_FaintMagnitude)
463         m_FaintMagnitude = faintmag / 100.0;
464 
465     if (htm_level != m_skyMesh->level())
466         qCWarning(KSTARS)
467                 << "HTM Level in shallow star data file and HTM Level in m_skyMesh do not match. EXPECT TROUBLE"
468                 ;
469     for (int i = 0; i < m_skyMesh->size(); ++i)
470     {
471         Trixel trixel = i; // = ( ( i >= 256 ) ? ( i - 256 ) : ( i + 256 ) );
472         for (unsigned long j = 0; j < static_cast<unsigned long>(dataReader.getRecordCount(i)); ++j)
473         {
474             if (1 != fread(&stardata, sizeof(StarData), 1, dataFile))
475             {
476                 qCCritical(KSTARS) << "FILE FORMAT ERROR: Could not read StarData structure for star #" << j << " under trixel #"
477                                    << trixel;
478                 continue;
479             }
480 
481             /* Swap Bytes when required */
482             if (swapBytes)
483                 byteSwap(&stardata);
484 
485             named  = false;
486             gnamed = false;
487 
488             /* Named Star - Read the nameFile */
489             if (stardata.flags & 0x01)
490             {
491                 visibleName = "";
492                 if (1 != fread(&starname, sizeof(starName), 1, nameFile))
493                     qCCritical(KSTARS) << "ERROR: fread() call on nameFile failed in trixel " << trixel << " star " << j;
494 
495                 name  = QByteArray(starname.longName, 32);
496                 named = !name.isEmpty();
497 
498                 gname  = QByteArray(starname.bayerName, 8);
499                 gnamed = !gname.isEmpty();
500 
501                 if (gnamed && starname.bayerName[0] != '.')
502                     visibleName = gname;
503 
504                 if (named)
505                 {
506                     // HEV: look up star name in internationalization filesource
507                     name = i18nc("star name", name.toLocal8Bit().data());
508                 }
509                 else
510                 {
511                     name = i18n("star");
512                 }
513             }
514             else
515                 qCCritical(KSTARS) << "ERROR: Named star file contains unnamed stars! Expect trouble.";
516 
517             /* Create the new StarObject */
518             star = new StarObject;
519             star->init(&stardata);
520             //if( star->getHDIndex() != 0 && name == i18n("star"))
521             if (stardata.HD)
522             {
523                 m_HDHash.insert(stardata.HD, star);
524                 if (named == false)
525                 {
526                     name  = QString("HD %1").arg(stardata.HD);
527                     named = true;
528                 }
529             }
530 
531             star->setNames(name, visibleName);
532             //star->EquatorialToHorizontal( data->lst(), data->geo()->lat() );
533             ++nstars;
534 
535             if (gnamed)
536                 m_genName.insert(gname, star);
537 
538             //if ( ! name.isEmpty() && name != i18n("star"))
539             if (named)
540             {
541                 objectNames(SkyObject::STAR).append(name);
542                 objectLists(SkyObject::STAR).append(QPair<QString, const SkyObject *>(name, star));
543             }
544 
545             if (!visibleName.isEmpty() && gname != name)
546             {
547                 QString gName = star->gname(false);
548                 objectNames(SkyObject::STAR).append(gName);
549                 objectLists(SkyObject::STAR).append(QPair<QString, const SkyObject *>(gName, star));
550             }
551 
552             appendListObject(star);
553 
554             m_starIndex->at(trixel)->append(star);
555             double pm = star->pmMagnitude();
556 
557             for (auto &list : m_highPMStars)
558             {
559                 if (list->append(trixel, star, pm))
560                     break;
561             }
562         }
563     }
564 
565     dataReader.closeFile();
566     nameReader.closeFile();
567 
568     starsLoaded = true;
569     return true;
570 }
571 
appendListObject(SkyObject * object)572 void StarComponent::appendListObject(SkyObject *object)
573 {
574     m_ObjectList.append(object);
575     m_ObjectHash.insert(object->name().toLower(), object);
576     m_ObjectHash.insert(object->longname().toLower(), object);
577     m_ObjectHash.insert(object->name2().toLower(), object);
578     m_ObjectHash.insert(object->name2().toLower(), object);
579     m_ObjectHash.insert((dynamic_cast<StarObject *>(object))->gname(false).toLower(), object);
580 }
581 
findStarByGenetiveName(const QString name)582 SkyObject *StarComponent::findStarByGenetiveName(const QString name)
583 {
584     if (name.startsWith(QLatin1String("HD")))
585     {
586         QStringList fields = name.split(' ', QString::SkipEmptyParts);
587         bool Ok            = false;
588         unsigned int HDNum = fields[1].toInt(&Ok);
589         if (Ok)
590             return findByHDIndex(HDNum);
591     }
592     return m_genName.value(name);
593 }
594 
objectsInArea(QList<SkyObject * > & list,const SkyRegion & region)595 void StarComponent::objectsInArea(QList<SkyObject *> &list, const SkyRegion &region)
596 {
597     for (SkyRegion::const_iterator it = region.constBegin(); it != region.constEnd(); ++it)
598     {
599         Trixel trixel      = it.key();
600         StarList *starlist = m_starIndex->at(trixel);
601         for (int i = 0; starlist && i < starlist->size(); i++)
602             if (starlist->at(i) && starlist->at(i)->name() != QString("star"))
603                 list.push_back(starlist->at(i));
604     }
605 }
606 
findByHDIndex(int HDnum)607 StarObject *StarComponent::findByHDIndex(int HDnum)
608 {
609     KStarsData *data = KStarsData::Instance();
610     StarObject *o = nullptr;
611     BinFileHelper hdidxReader;
612 
613     // First check the hash to see if we have a corresponding StarObject already
614     if ((o = m_HDHash.value(HDnum, nullptr)))
615         return o;
616     // If we don't have the StarObject here, try it in the DeepStarComponents' hashes
617     if (m_DeepStarComponents.size() >= 1)
618         if ((o = m_DeepStarComponents.at(0)->findByHDIndex(HDnum)))
619             return o;
620     if (m_DeepStarComponents.size() >= 2)
621     {
622         qint32 offset = 0;
623         int ret = 0;
624         FILE *hdidxFile = hdidxReader.openFile("Henry-Draper.idx");
625         FILE *dataFile = nullptr;
626 
627         if (!hdidxFile)
628             return nullptr;
629         //KDE_fseek( hdidxFile, (HDnum - 1) * 4, SEEK_SET );
630         QT_FSEEK(hdidxFile, (HDnum - 1) * 4, SEEK_SET);
631         // TODO: Offsets need to be byteswapped if this is a big endian machine.
632         // This means that the Henry Draper Index needs a endianness indicator.
633         ret = fread(&offset, 4, 1, hdidxFile);
634         if (offset <= 0)
635             return nullptr;
636         dataFile = m_DeepStarComponents.at(1)->getStarReader()->getFileHandle();
637         //KDE_fseek( dataFile, offset, SEEK_SET );
638         QT_FSEEK(dataFile, offset, SEEK_SET);
639         {
640             int rc = fread(&stardata, sizeof(StarData), 1, dataFile);
641             Q_UNUSED(rc)
642         }
643         if (m_DeepStarComponents.at(1)->getStarReader()->getByteSwap())
644         {
645             byteSwap(&stardata);
646         }
647         m_starObject.init(&stardata);
648         m_starObject.EquatorialToHorizontal(data->lst(), data->geo()->lat());
649         m_starObject.JITupdate();
650         focusStar = m_starObject.clone();
651         m_HDHash.insert(HDnum, focusStar);
652         hdidxReader.closeFile();
653         return focusStar;
654     }
655 
656     return nullptr;
657 }
658 
659 // This uses the main star index for looking up nearby stars but then
660 // filters out objects with the generic name "star".  We could easily
661 // build an index for just the named stars which would make this go
662 // much faster still.  -jbb
663 //
objectNearest(SkyPoint * p,double & maxrad)664 SkyObject *StarComponent::objectNearest(SkyPoint *p, double &maxrad)
665 {
666     m_zoomMagLimit = zoomMagnitudeLimit();
667 
668     SkyObject *oBest = nullptr;
669 
670     MeshIterator region(m_skyMesh, OBJ_NEAREST_BUF);
671 
672     while (region.hasNext())
673     {
674         Trixel currentRegion = region.next();
675         StarList *starList   = m_starIndex->at(currentRegion);
676 
677         for (auto &star : *starList)
678         {
679             if (!star)
680                 continue;
681             if (star->mag() > m_zoomMagLimit)
682                 continue;
683 
684             double r = star->angularDistanceTo(p).Degrees();
685 
686             if (r < maxrad)
687             {
688                 oBest  = star;
689                 maxrad = r;
690             }
691         }
692     }
693 
694     // Check up with our Deep Star Components too!
695     double rTry, rBest;
696     SkyObject *oTry;
697     // JM 2016-03-30: Multiply rBest by a factor of 0.5 so that named stars are preferred to unnamed stars searched below
698     rBest = maxrad * 0.5;
699     rTry  = maxrad;
700     for (auto &component : m_DeepStarComponents)
701     {
702         oTry = component->objectNearest(p, rTry);
703         if (rTry < rBest)
704         {
705             rBest = rTry;
706             oBest = oTry;
707         }
708     }
709     maxrad = rBest;
710 
711     return oBest;
712 }
713 
starsInAperture(QList<StarObject * > & list,const SkyPoint & center,float radius,float maglim)714 void StarComponent::starsInAperture(QList<StarObject *> &list, const SkyPoint &center, float radius, float maglim)
715 {
716     // Ensure that we have deprecessed the (RA, Dec) to (RA0, Dec0)
717     Q_ASSERT(center.ra0().Degrees() >= 0.0);
718     Q_ASSERT(center.dec0().Degrees() <= 90.0);
719 
720     m_skyMesh->intersect(center.ra0().Degrees(), center.dec0().Degrees(), radius, static_cast<BufNum>(OBJ_NEAREST_BUF));
721 
722     MeshIterator region(m_skyMesh, OBJ_NEAREST_BUF);
723 
724     if (maglim < -28)
725         maglim = m_FaintMagnitude;
726 
727     while (region.hasNext())
728     {
729         Trixel currentRegion = region.next();
730         StarList *starList   = m_starIndex->at(currentRegion);
731 
732         for (auto &star : *starList)
733         {
734             if (!star)
735                 continue;
736             if (star->mag() > m_FaintMagnitude)
737                 continue;
738             if (star->angularDistanceTo(&center).Degrees() <= radius)
739                 list.append(star);
740         }
741     }
742 
743     // Add stars from the DeepStarComponents as well
744     for (auto &component : m_DeepStarComponents)
745     {
746         component->starsInAperture(list, center, radius, maglim);
747     }
748 }
749 
byteSwap(StarData * stardata)750 void StarComponent::byteSwap(StarData *stardata)
751 {
752     stardata->RA       = bswap_32(stardata->RA);
753     stardata->Dec      = bswap_32(stardata->Dec);
754     stardata->dRA      = bswap_32(stardata->dRA);
755     stardata->dDec     = bswap_32(stardata->dDec);
756     stardata->parallax = bswap_32(stardata->parallax);
757     stardata->HD       = bswap_32(stardata->HD);
758     stardata->mag      = bswap_16(stardata->mag);
759     stardata->bv_index = bswap_16(stardata->bv_index);
760 }
761 /*
762 void StarComponent::printDebugInfo() {
763 
764     int nTrixels = 0;
765     int nBlocks = 0;
766     long int nStars = 0;
767     float faintMag = -5.0;
768 
769     MeshIterator trixels( m_skyMesh, DRAW_BUF );
770     Trixel trixel;
771 
772     while( trixels.hasNext() ) {
773         trixel = trixels.next();
774         nTrixels++;
775         for(int i = 0; i < m_starBlockList[ trixel ]->getBlockCount(); ++i) {
776             nBlocks++;
777             StarBlock *block = m_starBlockList[ trixel ]->block( i );
778             for(int j = 0; j < block->getStarCount(); ++j) {
779                 nStars++;
780             }
781             if( block->getFaintMag() > faintMag ) {
782                 faintMag = block->getFaintMag();
783             }
784         }
785     }
786 
787     printf( "========== UNNAMED STAR MEMORY ALLOCATION INFORMATION ==========\n" );
788     printf( "Number of visible trixels                    = %8d\n", nTrixels );
789     printf( "Number of visible StarBlocks                 = %8d\n", nBlocks );
790     printf( "Number of StarBlocks allocated via SBF       = %8d\n", m_StarBlockFactory.getBlockCount() );
791     printf( "Number of unnamed stars in memory            = %8ld\n", nStars );
792     printf( "Magnitude of the faintest star in memory     = %8.2f\n", faintMag );
793     printf( "Target magnitude limit                       = %8.2f\n", magLim );
794     printf( "Size of each StarBlock                       = %8d bytes\n", sizeof( StarBlock ) );
795     printf( "Size of each StarObject                      = %8d bytes\n", sizeof( StarObject ) );
796     printf( "Memory use due to visible unnamed stars      = %8.2f MB\n", ( sizeof( StarObject ) * nStars / 1048576.0 ) );
797     printf( "Memory use due to visible StarBlocks         = %8d bytes\n", sizeof( StarBlock ) * nBlocks );
798     printf( "Memory use due to StarBlocks in SBF          = %8d bytes\n", sizeof( StarBlock ) * m_StarBlockFactory.getBlockCount() );
799     printf( "=============== STAR DRAW LOOP TIMING INFORMATION ==============\n" );
800     printf( "Time taken for drawing named stars           = %8ld ms\n", t_drawNamed );
801     printf( "Time taken for dynamic load of data          = %8ld ms\n", t_dynamicLoad );
802     printf( "Time taken for updating LRU cache            = %8ld ms\n", t_updateCache );
803     printf( "Time taken for drawing unnamed stars         = %8ld ms\n", t_drawUnnamed );
804     printf( "================================================================\n" );
805 }
806 
807 bool StarComponent::verifySBLIntegrity() {
808 
809     float faintMag = -5.0;
810     bool integrity = true;
811     for(Trixel trixel = 0; trixel < m_skyMesh->size(); ++trixel) {
812         for(int i = 0; i < m_starBlockList[ trixel ]->getBlockCount(); ++i) {
813             StarBlock *block = m_starBlockList[ trixel ]->block( i );
814             if( i == 0 )
815                 faintMag = block->getBrightMag();
816             // NOTE: Assumes 2 decimal places in magnitude field. TODO: Change if it ever does change
817             if( block->getBrightMag() != faintMag && ( block->getBrightMag() - faintMag ) > 0.016) {
818                 qDebug() << "Trixel " << trixel << ": ERROR: faintMag of prev block = " << faintMag
819                          << ", brightMag of block #" << i << " = " << block->getBrightMag();
820                 integrity = false;
821             }
822             if( i > 1 && ( !block->prev ) )
823                 qDebug() << "Trixel " << trixel << ": ERROR: Block" << i << "is unlinked in LRU Cache";
824             if( block->prev && block->prev->parent == m_starBlockList[ trixel ]
825                 && block->prev != m_starBlockList[ trixel ]->block( i - 1 ) ) {
826                 qDebug() << "Trixel " << trixel << ": ERROR: SBF LRU Cache linked list seems to be broken at before block " << i;
827                 integrity = false;
828             }
829             faintMag = block->getFaintMag();
830         }
831     }
832     return integrity;
833 }
834 */
835