stardb.cpp
上传用户:center1979
上传日期:2022-07-26
资源大小:50633k
文件大小:44k
源码类别:

OpenGL

开发平台:

Visual C++

  1. // stardb.cpp
  2. //
  3. // Copyright (C) 2001-2008, Chris Laurel <claurel@shatters.net>
  4. //
  5. // This program is free software; you can redistribute it and/or
  6. // modify it under the terms of the GNU General Public License
  7. // as published by the Free Software Foundation; either version 2
  8. // of the License, or (at your option) any later version.
  9. #include <cstring>
  10. #include <cmath>
  11. #include <cstdlib>
  12. #include <cstdio>
  13. #include <cassert>
  14. #include <algorithm>
  15. #include <celmath/mathlib.h>
  16. #include <celmath/plane.h>
  17. #include <celutil/util.h>
  18. #include <celutil/bytes.h>
  19. #include <celengine/stardb.h>
  20. #include "celestia.h"
  21. #include "astro.h"
  22. #include "parser.h"
  23. #include "parseobject.h"
  24. #include "multitexture.h"
  25. #include "meshmanager.h"
  26. #include <celutil/debug.h>
  27. using namespace std;
  28. static string HDCatalogPrefix("HD ");
  29. static string HIPPARCOSCatalogPrefix("HIP ");
  30. static string GlieseCatalogPrefix("Gliese ");
  31. static string RossCatalogPrefix("Ross ");
  32. static string LacailleCatalogPrefix("Lacaille ");
  33. static string TychoCatalogPrefix("TYC ");
  34. static string SAOCatalogPrefix("SAO ");
  35. // The size of the root star octree node is also the maximum distance
  36. // distance from the Sun at which any star may be located. The current
  37. // setting of 1.0e7 light years is large enough to contain the entire
  38. // local group of galaxies. A larger value should be OK, but the
  39. // performance implications for octree traversal still need to be
  40. // investigated.
  41. static const float STAR_OCTREE_ROOT_SIZE  = 10000000.0f;
  42. static const float STAR_OCTREE_MAGNITUDE  = 6.0f;
  43. static const float STAR_EXTRA_ROOM        = 0.01f; // Reserve 1% capacity for extra stars
  44. const char* StarDatabase::FILE_HEADER            = "CELSTARS";
  45. const char* StarDatabase::CROSSINDEX_FILE_HEADER = "CELINDEX";
  46. // Used to sort stars by catalog number
  47. struct CatalogNumberOrderingPredicate
  48. {
  49.     int unused;
  50.     CatalogNumberOrderingPredicate() {};
  51.     bool operator()(const Star& star0, const Star& star1) const
  52.     {
  53.         return (star0.getCatalogNumber() < star1.getCatalogNumber());
  54.     }
  55. };
  56. struct CatalogNumberEquivalencePredicate
  57. {
  58.     int unused;
  59.     CatalogNumberEquivalencePredicate() {};
  60.     bool operator()(const Star& star0, const Star& star1) const
  61.     {
  62.         return (star0.getCatalogNumber() == star1.getCatalogNumber());
  63.     }
  64. };
  65. // Used to sort star pointers by catalog number
  66. struct PtrCatalogNumberOrderingPredicate
  67. {
  68.     int unused;
  69.     PtrCatalogNumberOrderingPredicate() {};
  70.     bool operator()(const Star* const & star0, const Star* const & star1) const
  71.     {
  72.         return (star0->getCatalogNumber() < star1->getCatalogNumber());
  73.     }
  74. };
  75. static bool parseSimpleCatalogNumber(const string& name,
  76.                                      const string& prefix,
  77.                                      uint32* catalogNumber)
  78. {
  79.     char extra[4];
  80.     if (compareIgnoringCase(name, prefix, prefix.length()) == 0)
  81.     {
  82.         unsigned int num;
  83.         // Use scanf to see if we have a valid catalog number; it must be
  84.         // of the form: <prefix> <non-negative integer>  No additional
  85.         // characters other than whitespace are allowed after the number.
  86.         if (sscanf(name.c_str() + prefix.length(), " %u %c", &num, extra) == 1)
  87.         {
  88.             *catalogNumber = (uint32) num;
  89.             return true;
  90.         }
  91.     }
  92.     return false;
  93. }
  94. static bool parseHIPPARCOSCatalogNumber(const string& name,
  95.                                         uint32* catalogNumber)
  96. {
  97.     return parseSimpleCatalogNumber(name,
  98.                                     HIPPARCOSCatalogPrefix,
  99.                                     catalogNumber);
  100. }
  101. static bool parseHDCatalogNumber(const string& name,
  102.                                  uint32* catalogNumber)
  103. {
  104.     return parseSimpleCatalogNumber(name,
  105.                                     HDCatalogPrefix,
  106.                                     catalogNumber);
  107. }
  108. static bool parseTychoCatalogNumber(const string& name,
  109.                                     uint32* catalogNumber)
  110. {
  111.     if (compareIgnoringCase(name, TychoCatalogPrefix, TychoCatalogPrefix.length()) == 0)
  112.     {
  113.         unsigned int tyc1 = 0, tyc2 = 0, tyc3 = 0;
  114.         if (sscanf(string(name, TychoCatalogPrefix.length(),
  115.                    string::npos).c_str(),
  116.                    " %u-%u-%u", &tyc1, &tyc2, &tyc3) == 3)
  117.         {
  118.             *catalogNumber = (uint32) (tyc3 * 1000000000 + tyc2 * 10000 + tyc1);
  119.             return true;
  120.         }
  121.     }
  122.     return false;
  123. }
  124. static bool parseCelestiaCatalogNumber(const string& name,
  125.                                        uint32* catalogNumber)
  126. {
  127.     char extra[4];
  128.     if (name[0] == '#')
  129.     {
  130.         unsigned int num;
  131.         if (sscanf(name.c_str(), "#%u %c", &num, extra) == 1)
  132.         {
  133.             *catalogNumber = (uint32) num;
  134.             return true;
  135.         }
  136.     }
  137.     return false;
  138. }
  139. bool StarDatabase::CrossIndexEntry::operator<(const StarDatabase::CrossIndexEntry& e) const
  140. {
  141.     return catalogNumber < e.catalogNumber;
  142. }
  143. StarDatabase::StarDatabase():
  144.     nStars               (0),
  145.     stars                (NULL),
  146.     namesDB              (NULL),
  147.     octreeRoot           (NULL),
  148.     nextAutoCatalogNumber(0xfffffffe),
  149.     binFileCatalogNumberIndex(NULL),
  150.     binFileStarCount(0)
  151. {
  152.     crossIndexes.resize(MaxCatalog);
  153. }
  154. StarDatabase::~StarDatabase()
  155. {
  156.     if (stars != NULL)
  157.         delete [] stars;
  158.     if (catalogNumberIndex != NULL)
  159.         delete [] catalogNumberIndex;
  160.     for (vector<CrossIndex*>::iterator iter = crossIndexes.begin(); iter != crossIndexes.end(); ++iter)
  161.     {
  162.         if (*iter != NULL)
  163.             delete *iter;
  164.     }
  165. }
  166. Star* StarDatabase::find(uint32 catalogNumber) const
  167. {
  168.     Star refStar;
  169.     refStar.setCatalogNumber(catalogNumber);
  170.     Star** star   = lower_bound(catalogNumberIndex,
  171.                                 catalogNumberIndex + nStars,
  172.                                 &refStar,
  173.                                 PtrCatalogNumberOrderingPredicate());
  174.     if (star != catalogNumberIndex + nStars && (*star)->getCatalogNumber() == catalogNumber)
  175.         return *star;
  176.     else
  177.         return NULL;
  178. }
  179. uint32 StarDatabase::findCatalogNumberByName(const string& name) const
  180. {
  181.     if (name.empty())
  182.         return Star::InvalidCatalogNumber;
  183.     uint32 catalogNumber = Star::InvalidCatalogNumber;
  184.     if (namesDB != NULL)
  185.     {
  186.         catalogNumber = namesDB->findCatalogNumberByName(name);
  187.         if (catalogNumber != Star::InvalidCatalogNumber)
  188.             return catalogNumber;
  189.     }
  190.     
  191.     if (parseCelestiaCatalogNumber(name, &catalogNumber))
  192.     {
  193.         return catalogNumber;
  194.     }
  195.     else if (parseHIPPARCOSCatalogNumber(name, &catalogNumber))
  196.     {
  197.         return catalogNumber;
  198.     }
  199.     else if (parseTychoCatalogNumber(name, &catalogNumber))
  200.     {
  201.         return catalogNumber;
  202.     }
  203.     else if (parseHDCatalogNumber(name, &catalogNumber))
  204.     {
  205.         return searchCrossIndexForCatalogNumber(HenryDraper, catalogNumber);
  206.     }
  207.     else if (parseSimpleCatalogNumber(name, SAOCatalogPrefix,
  208.                                       &catalogNumber))
  209.     {
  210.         return searchCrossIndexForCatalogNumber(SAO, catalogNumber);
  211.     }
  212.     else
  213.     {
  214.         return Star::InvalidCatalogNumber;
  215.     }
  216. }
  217. Star* StarDatabase::find(const string& name) const
  218. {
  219.     uint32 catalogNumber = findCatalogNumberByName(name);
  220.     if (catalogNumber != Star::InvalidCatalogNumber)
  221.         return find(catalogNumber);
  222.     else
  223.         return NULL;
  224. }
  225. uint32 StarDatabase::crossIndex(const Catalog catalog, const uint32 celCatalogNumber) const
  226. {
  227.     if (static_cast<uint32>(catalog) >= crossIndexes.size())
  228.         return Star::InvalidCatalogNumber;
  229.     CrossIndex* xindex = crossIndexes[catalog];
  230.     if (xindex == NULL)
  231.         return Star::InvalidCatalogNumber;
  232.     // A simple linear search.  We could store cross indices sorted by
  233.     // both catalog numbers and trade memory for speed
  234.     for (CrossIndex::const_iterator iter = xindex->begin(); iter != xindex->end(); iter++)
  235.     {
  236.         if (celCatalogNumber == iter->celCatalogNumber)
  237.             return iter->catalogNumber;
  238.     }
  239.     return Star::InvalidCatalogNumber;
  240. }
  241. // Return the Celestia catalog number for the star with a specified number
  242. // in a cross index.
  243. uint32 StarDatabase::searchCrossIndexForCatalogNumber(const Catalog catalog, const uint32 number) const
  244. {
  245.     if (static_cast<unsigned int>(catalog) >= crossIndexes.size())
  246.         return Star::InvalidCatalogNumber;
  247.     CrossIndex* xindex = crossIndexes[catalog];
  248.     if (xindex == NULL)
  249.         return Star::InvalidCatalogNumber;
  250.     CrossIndexEntry xindexEnt;
  251.     xindexEnt.catalogNumber = number;
  252.     CrossIndex::iterator iter = lower_bound(xindex->begin(), xindex->end(),
  253.                                             xindexEnt);
  254.     if (iter == xindex->end() || iter->catalogNumber != number)
  255.         return Star::InvalidCatalogNumber;
  256.     else
  257.         return iter->celCatalogNumber;
  258. }
  259. Star* StarDatabase::searchCrossIndex(const Catalog catalog, const uint32 number) const
  260. {
  261.     uint32 celCatalogNumber = searchCrossIndexForCatalogNumber(catalog, number);
  262.     if (celCatalogNumber != Star::InvalidCatalogNumber)
  263.         return find(celCatalogNumber);
  264.     else
  265.         return NULL;
  266. }
  267. vector<string> StarDatabase::getCompletion(const string& name) const
  268. {
  269.     vector<string> completion;
  270.     // only named stars are supported by completion.
  271.     if (!name.empty() && namesDB != NULL)
  272.         return namesDB->getCompletion(name);
  273.     else
  274.         return completion;
  275. }
  276. static void catalogNumberToString(uint32 catalogNumber, char* buf, unsigned int bufSize)
  277. {
  278.     // Just return an empty string if there's any chance that the buffer is too small
  279.     if (bufSize < 20 && bufSize > 0)
  280.     {
  281.         buf[0] = '';
  282.     }
  283. if (catalogNumber <= StarDatabase::MAX_HIPPARCOS_NUMBER)
  284.     {
  285.         sprintf(buf, "HIP %d", catalogNumber);
  286.     }
  287.     else
  288.     {
  289.         uint32 tyc3 = catalogNumber / 1000000000;
  290.         catalogNumber -= tyc3 * 1000000000;
  291.         uint32 tyc2 = catalogNumber / 10000;
  292.         catalogNumber -= tyc2 * 10000;
  293.         uint32 tyc1 = catalogNumber;
  294.         sprintf(buf, "TYC %d-%d-%d", tyc1, tyc2, tyc3);
  295.     }
  296. }
  297. // Return the name for the star with specified catalog number.  The returned
  298. // string will be:
  299. //      the common name if it exists, otherwise
  300. //      the Bayer or Flamsteed designation if it exists, otherwise
  301. //      the HD catalog number if it exists, otherwise
  302. //      the HIPPARCOS catalog number.
  303. //
  304. // CAREFUL:
  305. // If the star name is not present in the names database, a new
  306. // string is constructed to contain the catalog number--keep in
  307. // mind that calling this method could possibly incur the overhead
  308. // of a memory allocation (though no explcit deallocation is
  309. // required as it's all wrapped in the string class.)
  310. string StarDatabase::getStarName(const Star& star, bool i18n) const
  311. {
  312.     uint32 catalogNumber = star.getCatalogNumber();
  313.     if (namesDB != NULL)
  314.     {
  315.         StarNameDatabase::NumberIndex::const_iterator iter = namesDB->getFirstNameIter(catalogNumber);
  316.         if (iter != namesDB->getFinalNameIter() && iter->first == catalogNumber)
  317.         {
  318.             if (i18n && iter->second != _(iter->second.c_str()))
  319.                 return _(iter->second.c_str());
  320.             else
  321.                 return iter->second;
  322.         }
  323.     }
  324.     char buf[20];
  325.     /*
  326.       // Get the HD catalog name
  327.       if (star.getCatalogNumber() != Star::InvalidCatalogNumber)
  328.       sprintf(buf, "HD %d", star.getCatalogNumber(Star::HDCatalog));
  329.       else
  330.     */
  331.     catalogNumberToString(catalogNumber, buf, sizeof(buf));
  332.     return string(buf);
  333. }
  334. // A less convenient version of getStarName that writes to a char
  335. // array instead of a string. The advantage is that no memory allocation
  336. // will every occur.
  337. void StarDatabase::getStarName(const Star& star, char* nameBuffer, unsigned int bufferSize, bool i18n) const
  338. {
  339.     assert(bufferSize != 0);
  340.     uint32 catalogNumber = star.getCatalogNumber();
  341.     if (namesDB != NULL)
  342.     {
  343.         StarNameDatabase::NumberIndex::const_iterator iter = namesDB->getFirstNameIter(catalogNumber);
  344.         if (iter != namesDB->getFinalNameIter() && iter->first == catalogNumber)
  345.         {
  346.             if (i18n && iter->second != _(iter->second.c_str()))
  347.                 strncpy(nameBuffer, _(iter->second.c_str()), bufferSize);
  348.             else
  349.                 strncpy(nameBuffer, iter->second.c_str(), bufferSize);
  350.             nameBuffer[bufferSize - 1] = '';
  351.             return;
  352.         }
  353.     }
  354.     catalogNumberToString(catalogNumber, nameBuffer, bufferSize);
  355. }
  356. string StarDatabase::getStarNameList(const Star& star, const unsigned int maxNames) const
  357. {
  358.     string starNames;
  359.     char numString[32];
  360.     unsigned int catalogNumber    = star.getCatalogNumber();
  361.     StarNameDatabase::NumberIndex::const_iterator iter  = namesDB->getFirstNameIter(catalogNumber);
  362.     unsigned int count = 0;
  363.     while (iter != namesDB->getFinalNameIter() && iter->first == catalogNumber && count < maxNames)
  364.     {
  365.         if (count != 0)
  366.             starNames   += " / ";
  367.         starNames   += ReplaceGreekLetterAbbr(iter->second.c_str());
  368.         ++iter;
  369.         ++count;
  370.     }
  371.     uint32 hip  = catalogNumber;
  372.     if (hip != Star::InvalidCatalogNumber && hip != 0 && count < maxNames)
  373.     {
  374.         if (hip <= Star::MaxTychoCatalogNumber)
  375.         {
  376.             if (count != 0)
  377.                 starNames   += " / ";
  378.             if (hip >= 1000000)
  379.             {
  380.                 uint32 h      = hip;
  381.                 uint32 tyc3   = h / 1000000000;
  382.                        h     -= tyc3 * 1000000000;
  383.                 uint32 tyc2   = h / 10000;
  384.                        h     -= tyc2 * 10000;
  385.                 uint32 tyc1   = h;
  386.                 sprintf(numString, "TYC %u-%u-%u", tyc1, tyc2, tyc3);
  387.                 starNames    += numString;
  388.             }
  389.             else
  390.             {
  391.                 sprintf(numString, "HIP %u", hip);
  392.                 starNames    += numString;
  393.             }
  394.             ++count;
  395.         }
  396.     }
  397.     uint32 hd   = crossIndex(StarDatabase::HenryDraper, hip);
  398.     if (count < maxNames && hd != Star::InvalidCatalogNumber)
  399.     {
  400.         if (count != 0)
  401.             starNames   += " / ";
  402.         sprintf(numString, "HD %u", hd);
  403.         starNames   += numString;
  404.     }
  405.     uint32 sao   = crossIndex(StarDatabase::SAO, hip);
  406.     if (count < maxNames && sao != Star::InvalidCatalogNumber)
  407.     {
  408.         if (count != 0)
  409.             starNames   += " / ";
  410.         sprintf(numString, "SAO %u", sao);
  411.         starNames   += numString;
  412.     }
  413.     return starNames;
  414. }
  415. void StarDatabase::findVisibleStars(StarHandler& starHandler,
  416.                                     const Point3f& position,
  417.                                     const Quatf& orientation,
  418.                                     float fovY,
  419.                                     float aspectRatio,
  420.                                     float limitingMag) const
  421. {
  422.     // Compute the bounding planes of an infinite view frustum
  423.     Planef frustumPlanes[5];
  424.     Vec3f planeNormals[5];
  425.     Mat3f rot = orientation.toMatrix3();
  426.     float h = (float) tan(fovY / 2);
  427.     float w = h * aspectRatio;
  428.     planeNormals[0] = Vec3f(0, 1, -h);
  429.     planeNormals[1] = Vec3f(0, -1, -h);
  430.     planeNormals[2] = Vec3f(1, 0, -w);
  431.     planeNormals[3] = Vec3f(-1, 0, -w);
  432.     planeNormals[4] = Vec3f(0, 0, -1);
  433.     for (int i = 0; i < 5; i++)
  434.     {
  435.         planeNormals[i].normalize();
  436.         planeNormals[i] = planeNormals[i] * rot;
  437.         frustumPlanes[i] = Planef(planeNormals[i], position);
  438.     }
  439.     octreeRoot->processVisibleObjects(starHandler,
  440.                                       position,
  441.                                       frustumPlanes,
  442.                                       limitingMag,
  443.                                       STAR_OCTREE_ROOT_SIZE);
  444. }
  445. void StarDatabase::findCloseStars(StarHandler& starHandler,
  446.                                   const Point3f& position,
  447.                                   float radius) const
  448. {
  449.     octreeRoot->processCloseObjects(starHandler,
  450.                                     position,
  451.                                     radius,
  452.                                     STAR_OCTREE_ROOT_SIZE);
  453. }
  454. StarNameDatabase* StarDatabase::getNameDatabase() const
  455. {
  456.     return namesDB;
  457. }
  458. void StarDatabase::setNameDatabase(StarNameDatabase* _namesDB)
  459. {
  460.     namesDB    = _namesDB;
  461. }
  462. bool StarDatabase::loadCrossIndex(const Catalog catalog, istream& in)
  463. {
  464.     if (static_cast<unsigned int>(catalog) >= crossIndexes.size())
  465.         return false;
  466.     if (crossIndexes[catalog] != NULL)
  467.         delete crossIndexes[catalog];
  468.     // Verify that the star database file has a correct header
  469.     {
  470.         int headerLength = strlen(CROSSINDEX_FILE_HEADER);
  471.         char* header = new char[headerLength];
  472.         in.read(header, headerLength);
  473.         if (strncmp(header, CROSSINDEX_FILE_HEADER, headerLength))
  474.         {
  475.             cerr << _("Bad header for cross indexn");
  476.             return false;
  477.         }
  478.         delete[] header;
  479.     }
  480.     // Verify the version
  481.     {
  482.         uint16 version;
  483.         in.read((char*) &version, sizeof version);
  484.         LE_TO_CPU_INT16(version, version);
  485.         if (version != 0x0100)
  486.         {
  487.             cerr << _("Bad version for cross indexn");
  488.             return false;
  489.         }
  490.     }
  491.     CrossIndex* xindex = new CrossIndex();
  492.     if (xindex == NULL)
  493.         return false;
  494.     unsigned int record = 0;
  495.     for (;;)
  496.     {
  497.         CrossIndexEntry ent;
  498.         in.read((char *) &ent.catalogNumber, sizeof ent.catalogNumber);
  499.         LE_TO_CPU_INT32(ent.catalogNumber, ent.catalogNumber);
  500.         if (in.eof())
  501.             break;
  502.         in.read((char *) &ent.celCatalogNumber, sizeof ent.celCatalogNumber);
  503.         LE_TO_CPU_INT32(ent.celCatalogNumber, ent.celCatalogNumber);
  504.         if (in.fail())
  505.         {
  506.             cerr << _("Loading cross index failed at record ") << record << 'n';
  507.             delete xindex;
  508.             return false;
  509.         }
  510.         xindex->insert(xindex->end(), ent);
  511.         record++;
  512.     }
  513.     sort(xindex->begin(), xindex->end());
  514.     crossIndexes[catalog] = xindex;
  515.     return true;
  516. }
  517. bool StarDatabase::loadBinary(istream& in)
  518. {
  519.     uint32 nStarsInFile = 0;
  520.     // Verify that the star database file has a correct header
  521.     {
  522.         int headerLength = strlen(FILE_HEADER);
  523.         char* header = new char[headerLength];
  524.         in.read(header, headerLength);
  525.         if (strncmp(header, FILE_HEADER, headerLength))
  526.             return false;
  527.         delete[] header;
  528.     }
  529.     // Verify the version
  530.     {
  531.         uint16 version;
  532.         in.read((char*) &version, sizeof version);
  533.         LE_TO_CPU_INT16(version, version);
  534.         if (version != 0x0100)
  535.             return false;
  536.     }
  537.     // Read the star count
  538.     in.read((char *) &nStarsInFile, sizeof nStarsInFile);
  539.     LE_TO_CPU_INT32(nStarsInFile, nStarsInFile);
  540.     if (!in.good())
  541.         return false;
  542.     unsigned int totalStars = nStars + nStarsInFile;
  543.     while (((unsigned int) nStars) < totalStars)
  544.     {
  545.         uint32 catNo = 0;
  546.         float x = 0.0f, y = 0.0f, z = 0.0f;
  547.         int16 absMag;
  548.         uint16 spectralType;
  549.         in.read((char *) &catNo, sizeof catNo);
  550.         LE_TO_CPU_INT32(catNo, catNo);
  551.         in.read((char *) &x, sizeof x);
  552.         LE_TO_CPU_FLOAT(x, x);
  553.         in.read((char *) &y, sizeof y);
  554.         LE_TO_CPU_FLOAT(y, y);
  555.         in.read((char *) &z, sizeof z);
  556.         LE_TO_CPU_FLOAT(z, z);
  557.         in.read((char *) &absMag, sizeof absMag);
  558.         LE_TO_CPU_INT16(absMag, absMag);
  559.         in.read((char *) &spectralType, sizeof spectralType);
  560.         LE_TO_CPU_INT16(spectralType, spectralType);
  561.         if (in.bad())
  562.         break;
  563.         Star star;
  564.         star.setPosition(x, y, z);
  565.         star.setAbsoluteMagnitude((float) absMag / 256.0f);
  566.         StarDetails* details = NULL;
  567.         StellarClass sc;
  568.         if (sc.unpack(spectralType))
  569.             details = StarDetails::GetStarDetails(sc);
  570.         if (details == NULL)
  571.         {
  572.             cerr << _("Bad spectral type in star database, star #") << nStars << "n";
  573.             return false;
  574.         }
  575.         star.setDetails(details);
  576.         star.setCatalogNumber(catNo);
  577.         unsortedStars.add(star);
  578.         
  579.         nStars++;
  580.     }
  581.     if (in.bad())
  582.         return false;
  583.     DPRINTF(0, "StarDatabase::read: nStars = %dn", nStarsInFile);
  584.     clog << nStars << _(" stars in binary databasen");
  585.     
  586.     // Create the temporary list of stars sorted by catalog number; this
  587.     // will be used to lookup stars during file loading. After loading is
  588.     // complete, the stars are sorted into an octree and this list gets
  589.     // replaced.
  590.     if (unsortedStars.size() > 0)
  591.     {
  592.         binFileStarCount = unsortedStars.size();
  593.         binFileCatalogNumberIndex = new Star*[binFileStarCount];
  594.         for (unsigned int i = 0; i < binFileStarCount; i++)
  595.         {
  596.             binFileCatalogNumberIndex[i] = &unsortedStars[i];    
  597.         }
  598.         sort(binFileCatalogNumberIndex, binFileCatalogNumberIndex + binFileStarCount,
  599.              PtrCatalogNumberOrderingPredicate());
  600.     }
  601.         
  602.     return true;
  603. }
  604. void StarDatabase::finish()
  605. {
  606.     clog << _("Total star count: ") << nStars << endl;
  607.     
  608.     buildOctree();
  609.     buildIndexes();
  610.     // Delete the temporary indices used only during loading
  611.     delete binFileCatalogNumberIndex;
  612.     stcFileCatalogNumberIndex.clear();
  613.     
  614.     // Resolve all barycenters; this can't be done before star sorting. There's
  615.     // still a bug here: final orbital radii aren't available until after
  616.     // the barycenters have been resolved, and these are required when building
  617.     // the octree.  This will only rarely cause a problem, but it still needs
  618.     // to be addressed.
  619.     for (vector<BarycenterUsage>::const_iterator iter = barycenters.begin();
  620.          iter != barycenters.end(); iter++)
  621.     {
  622.         Star* star = find(iter->catNo);
  623.         Star* barycenter = find(iter->barycenterCatNo);
  624.         assert(star != NULL);
  625.         assert(barycenter != NULL);
  626.         if (star != NULL && barycenter != NULL)
  627.         {
  628.             star->setOrbitBarycenter(barycenter);
  629.             barycenter->addOrbitingStar(star);
  630.         }
  631.     }
  632.     barycenters.clear();
  633. }
  634. static void errorMessagePrelude(const Tokenizer& tok)
  635. {
  636.     cerr << _("Error in .stc file (line ") << tok.getLineNumber() << "): ";
  637. }
  638. static void stcError(const Tokenizer& tok,
  639.                      const string& msg)
  640. {
  641.     errorMessagePrelude(tok);
  642.     cerr << msg << 'n';
  643. }
  644. /*! Load star data from a property list into a star instance.
  645.  */
  646. bool StarDatabase::createStar(Star* star,
  647.                               StcDisposition disposition,
  648.                               uint32 catalogNumber,
  649.                               Hash* starData,
  650.                               const string& path,
  651.                               bool isBarycenter)
  652. {
  653.     StarDetails* details = NULL;
  654.     string spectralType;
  655.     // Get the magnitude and spectral type; if the star is actually
  656.     // a barycenter placeholder, these fields are ignored.
  657.     if (isBarycenter)
  658.     {
  659.         details = StarDetails::GetBarycenterDetails();
  660.     }
  661.     else
  662.     {
  663.         if (starData->getString("SpectralType", spectralType))
  664.         {
  665.             StellarClass sc = StellarClass::parse(spectralType);
  666.             details = StarDetails::GetStarDetails(sc);
  667.             if (details == NULL)
  668.             {
  669.                 cerr << _("Invalid star: bad spectral type.n");
  670.                 return false;
  671.             }
  672.         }
  673.         else
  674.         {
  675.             // Spectral type is required for new stars
  676.             if (disposition != ModifyStar)
  677.             {
  678.                 cerr << _("Invalid star: missing spectral type.n");
  679.                 return false;
  680.             }
  681.         }
  682.     }
  683.     
  684.     bool modifyExistingDetails = false;
  685.     if (disposition == ModifyStar)
  686.     {
  687.         StarDetails* existingDetails = star->getDetails();
  688.         
  689.         // If we're modifying an existing star and it already has a
  690.         // customized details record, we'll just modify that.
  691.         if (!existingDetails->shared())
  692.         {
  693.             modifyExistingDetails = true;
  694.             if (details != NULL)
  695.             {
  696.                 // If the spectral type was modified, copy the new data
  697.                 // to the custom details record.
  698.                 existingDetails->setSpectralType(details->getSpectralType());
  699.                 existingDetails->setTemperature(details->getTemperature());
  700.                 existingDetails->setBolometricCorrection(details->getBolometricCorrection());
  701.                 if ((existingDetails->getKnowledge() & StarDetails::KnowTexture) == 0)
  702.                     existingDetails->setTexture(details->getTexture());
  703.                 if ((existingDetails->getKnowledge() & StarDetails::KnowRotation) == 0)
  704.                     existingDetails->setRotationModel(details->getRotationModel());
  705.                 existingDetails->setVisibility(details->getVisibility());
  706.             }
  707.             
  708.             details = existingDetails;
  709.         }
  710.         else if (details == NULL)
  711.         {
  712.             details = existingDetails;
  713.         }
  714.     }
  715.     
  716.     string modelName;
  717.     string textureName;
  718.     bool hasTexture = starData->getString("Texture", textureName);
  719.     bool hasModel = starData->getString("Mesh", modelName);
  720.     RotationModel* rm = CreateRotationModel(starData, path, 1.0);
  721.     bool hasRotationModel = (rm != NULL);
  722.     Vec3d semiAxes;
  723.     bool hasSemiAxes = starData->getVector("SemiAxes", semiAxes);
  724.     bool hasBarycenter = false;
  725.     Point3f barycenterPosition;
  726.     double radius;
  727.     bool hasRadius = starData->getNumber("Radius", radius);
  728.     
  729.     string infoURL;
  730.     bool hasInfoURL = starData->getString("InfoURL", infoURL);
  731.     Orbit* orbit = CreateOrbit(Selection(), starData, path, true);
  732.     if (hasTexture      ||
  733.         hasModel        ||
  734.         orbit != NULL   ||
  735.         hasSemiAxes     ||
  736.         hasRadius       ||
  737.         hasRotationModel ||
  738.         hasInfoURL)
  739.     {
  740.         // If the star definition has extended information, clone the
  741.         // star details so we can customize it without affecting other
  742.         // stars of the same spectral type.
  743.         if (!modifyExistingDetails)
  744.             details = new StarDetails(*details);
  745.         if (hasTexture)
  746.         {
  747.             details->setTexture(MultiResTexture(textureName, path));
  748.             details->addKnowledge(StarDetails::KnowTexture);
  749.         }
  750.         if (hasModel)
  751.         {
  752.             ResourceHandle geometryHandle = GetGeometryManager()->getHandle(GeometryInfo(modelName, path, Vec3f(0.0f, 0.0f, 0.0f), 1.0f, true));
  753.             details->setGeometry(geometryHandle);
  754.         }
  755.         if (hasSemiAxes)
  756.         {
  757.             details->setEllipsoidSemiAxes(Vec3f((float) semiAxes.x,
  758.                                                 (float) semiAxes.y,
  759.                                                 (float) semiAxes.z));
  760.         }
  761.         if (hasRadius)
  762.         {
  763.             details->setRadius((float) radius);
  764.             details->addKnowledge(StarDetails::KnowRadius);
  765.         }
  766.         
  767.         if (hasInfoURL)
  768.         {
  769.             details->setInfoURL(infoURL);
  770.         }
  771.         if (orbit != NULL)
  772.         {
  773.             details->setOrbit(orbit);
  774.             // See if a barycenter was specified as well
  775.             uint32 barycenterCatNo = Star::InvalidCatalogNumber;
  776.             bool barycenterDefined = false;
  777.             string barycenterName;
  778.             if (starData->getString("OrbitBarycenter", barycenterName))
  779.             {
  780.                 barycenterCatNo   = findCatalogNumberByName(barycenterName);
  781.                 barycenterDefined = true;
  782.             }
  783.             else if (starData->getNumber("OrbitBarycenter", barycenterCatNo))
  784.             {
  785.                 barycenterDefined = true;
  786.             }
  787.             if (barycenterDefined)
  788.             {
  789.                 if (barycenterCatNo != Star::InvalidCatalogNumber)
  790.                 {
  791.                     // We can't actually resolve the barycenter catalog number
  792.                     // to a Star pointer until after all stars have been loaded
  793.                     // and spatially sorted.  Just store it in a list to be
  794.                     // resolved after sorting.
  795.                     BarycenterUsage bc;
  796.                     bc.catNo = catalogNumber;
  797.                     bc.barycenterCatNo = barycenterCatNo;
  798.                     barycenters.push_back(bc);
  799.                     // Even though we can't actually get the Star pointer for
  800.                     // the barycenter, we can get the star information.
  801.                     Star* barycenter = findWhileLoading(barycenterCatNo);
  802.                     if (barycenter != NULL)
  803.                     {
  804.                         hasBarycenter = true;
  805.                         barycenterPosition = barycenter->getPosition();
  806.                     }                    
  807.                 }
  808.                 if (!hasBarycenter)
  809.                 {
  810.                     cerr << _("Barycenter ") << barycenterName << _(" does not exist.n");
  811.                     return false;
  812.                 }
  813.             }
  814.         }
  815.         if (hasRotationModel)
  816.             details->setRotationModel(rm);
  817.     }
  818.     if (!modifyExistingDetails)
  819.         star->setDetails(details);
  820.     if (disposition != ModifyStar)
  821.         star->setCatalogNumber(catalogNumber);
  822.     // Compute the position in rectangular coordinates.  If a star has an
  823.     // orbit and barycenter, it's position is the position of the barycenter.
  824.     if (hasBarycenter)
  825.     {
  826.         star->setPosition(barycenterPosition);
  827.     }
  828.     else
  829.     {
  830.         double ra = 0.0;
  831.         double dec = 0.0;
  832.         double distance = 0.0;
  833.         if (disposition == ModifyStar)
  834.         {
  835.             Point3f pos = star->getPosition();
  836.             // Convert from Celestia's coordinate system
  837.             Vec3f v = Vec3f(pos.x, -pos.z, pos.y);
  838.             v = v * Mat3f::xrotation((float) -astro::J2000Obliquity);
  839.             distance = v.length();
  840.             if (distance > 0.0)
  841.             {
  842.                 v.normalize();
  843.                 ra = radToDeg(std::atan2(v.y, v.x));
  844.                 dec = radToDeg(std::asin(v.z));
  845.             }
  846.         }
  847.         
  848.         bool modifyPosition = false;
  849.         if (!starData->getNumber("RA", ra))
  850.         {
  851.             if (disposition != ModifyStar)
  852.             {
  853.                 cerr << _("Invalid star: missing right ascensionn");
  854.                 return false;
  855.             }
  856.         }
  857.         else
  858.         {
  859.             modifyPosition = true;
  860.         }
  861.         if (!starData->getNumber("Dec", dec))
  862.         {
  863.             if (disposition != ModifyStar)
  864.             {
  865.                 cerr << _("Invalid star: missing declination.n");
  866.                 return false;
  867.             }
  868.         }
  869.         else
  870.         {
  871.             modifyPosition = true;
  872.         }
  873.         if (!starData->getNumber("Distance", distance))
  874.         {
  875.             if (disposition != ModifyStar)
  876.             {
  877.                 cerr << _("Invalid star: missing distance.n");
  878.                 return false;
  879.             }
  880.         }
  881.         else
  882.         {
  883.             modifyPosition = true;
  884.         }
  885.         // Truncate to floats to match behavior of reading from binary file.
  886.         // The conversion to rectangular coordinates is still performed at
  887.         // double precision, however.
  888.         if (modifyPosition)
  889.         {
  890.             float raf = ((float) (ra * 24.0 / 360.0));
  891.             float decf = ((float) dec);
  892.             float distancef = ((float) distance);
  893.             Point3d pos = astro::equatorialToCelestialCart((double) raf, (double) decf, (double) distancef);
  894.             star->setPosition(Point3f((float) pos.x, (float) pos.y, (float) pos.z));
  895.         }
  896.     }
  897.     if (isBarycenter)
  898.     {
  899.         star->setAbsoluteMagnitude(30.0f);
  900.     }
  901.     else
  902.     {
  903.         double magnitude = 0.0;
  904.         bool magnitudeModified = true;
  905.         if (!starData->getNumber("AbsMag", magnitude))
  906.         {
  907.             if (!starData->getNumber("AppMag", magnitude))
  908.             {
  909.                 if (disposition != ModifyStar)
  910.                 {
  911.                     clog << _("Invalid star: missing magnitude.n");
  912.                     return false;
  913.                 }
  914.                 else
  915.                 {
  916.                     magnitudeModified = false;
  917.                 }
  918.             }
  919.             else
  920.             {
  921.                 float distance = star->getPosition().distanceFromOrigin();
  922.                 // We can't compute the intrinsic brightness of the star from
  923.                 // the apparent magnitude if the star is within a few AU of the
  924.                 // origin.
  925.                 if (distance < 1e-5f)
  926.                 {
  927.                     clog << _("Invalid star: absolute (not apparent) magnitude must be specified for star near originn");
  928.                     return false;
  929.                 }
  930.                 magnitude = astro::appToAbsMag((float) magnitude, distance);
  931.             }
  932.         }
  933.         if (magnitudeModified)
  934.             star->setAbsoluteMagnitude((float) magnitude);
  935.     }
  936.     return true;
  937. }
  938. /*! Load an STC file with star definitions. Each definition has the form:
  939.  *
  940.  *  [disposition] [object type] [catalog number] [name]
  941.  *  {
  942.  *      [properties]
  943.  *  }
  944.  *
  945.  *  Disposition is either Add, Replace, or Modify; Add is the default.
  946.  *  Object type is either Star or Barycenter, with Star the default
  947.  *  It is an error to omit both the catalog number and the name.
  948.  *
  949.  *  The dispositions are slightly more complicated than suggested by
  950.  *  their names. Every star must have an unique catalog number. But
  951.  *  instead of generating an error, Adding a star with a catalog
  952.  *  number that already exists will actually replace that star. Here
  953.  *  are how all of the possibilities are handled:
  954.  *
  955.  *  <name> or <number> already exists:
  956.  *  Add <name>        : new star
  957.  *  Add <number>      : replace star
  958.  *  Replace <name>    : replace star
  959.  *  Replace <number>  : replace star
  960.  *  Modify <name>     : modify star
  961.  *  Modify <number>   : modify star
  962.  *
  963.  *  <name> or <number> doesn't exist:
  964.  *  Add <name>        : new star
  965.  *  Add <number>      : new star
  966.  *  Replace <name>    : new star
  967.  *  Replace <number>  : new star
  968.  *  Modify <name>     : error
  969.  *  Modify <number>   : error
  970.  */
  971. bool StarDatabase::load(istream& in, const string& resourcePath)
  972. {
  973.     Tokenizer tokenizer(&in);
  974.     Parser parser(&tokenizer);
  975.     while (tokenizer.nextToken() != Tokenizer::TokenEnd)
  976.     {
  977.         bool isStar = true;
  978.         
  979.         // Parse the disposition--either Add, Replace, or Modify. The disposition
  980.         // may be omitted. The default value is Add.
  981.         StcDisposition disposition = AddStar;
  982.         if (tokenizer.getTokenType() == Tokenizer::TokenName)
  983.         {
  984.             if (tokenizer.getNameValue() == "Modify")
  985.             {
  986.                 disposition = ModifyStar;
  987.                 tokenizer.nextToken();
  988.             }
  989.             else if (tokenizer.getNameValue() == "Replace")
  990.             {
  991.                 disposition = ReplaceStar;
  992.                 tokenizer.nextToken();
  993.             }
  994.             else if (tokenizer.getNameValue() == "Add")
  995.             {
  996.                 disposition = AddStar;
  997.                 tokenizer.nextToken();
  998.             }
  999.         }
  1000.         
  1001.         // Parse the object type--either Star or Barycenter. The object type
  1002.         // may be omitted. The default is Star.
  1003.         if (tokenizer.getTokenType() == Tokenizer::TokenName)
  1004.         {
  1005.             if (tokenizer.getNameValue() == "Star")
  1006.             {
  1007.                 isStar = true;
  1008.             }
  1009.             else if (tokenizer.getNameValue() == "Barycenter")
  1010.             {
  1011.                 isStar = false;
  1012.             }
  1013.             else
  1014.             {
  1015.                 stcError(tokenizer, "unrecognized object type");
  1016.                 return false;
  1017.             }
  1018.             tokenizer.nextToken();
  1019.         }
  1020.         // Parse the catalog number; it may be omitted if a name is supplied.
  1021.         uint32 catalogNumber = Star::InvalidCatalogNumber;
  1022.         if (tokenizer.getTokenType() == Tokenizer::TokenNumber)
  1023.         {
  1024.             catalogNumber = (uint32) tokenizer.getNumberValue();
  1025.             tokenizer.nextToken();
  1026.         }
  1027.         string objName;
  1028.         string firstName;
  1029.         if (tokenizer.getTokenType() == Tokenizer::TokenString)
  1030.         {
  1031.             // A star name (or names) is present
  1032.             objName    = tokenizer.getStringValue();
  1033.             tokenizer.nextToken();
  1034.             if (!objName.empty())
  1035.             {
  1036.                 string::size_type next = objName.find(':', 0);
  1037.                 firstName = objName.substr(0, next);
  1038.             }
  1039.         }
  1040.         
  1041.         Star* star = NULL;
  1042.         switch (disposition)
  1043.         {
  1044.         case AddStar:
  1045.             // Automatically generate a catalog number for the star if one isn't
  1046.             // supplied.
  1047.             if (catalogNumber == Star::InvalidCatalogNumber)
  1048.             {
  1049.                 catalogNumber = nextAutoCatalogNumber--;
  1050.             }
  1051.             else
  1052.             {
  1053.                 star = findWhileLoading(catalogNumber);
  1054.             }
  1055.             break;
  1056.         case ReplaceStar:
  1057.             if (catalogNumber == Star::InvalidCatalogNumber)
  1058.             {
  1059.                 if (!firstName.empty())
  1060.                 {
  1061.                     catalogNumber = findCatalogNumberByName(firstName);
  1062.                 }
  1063.             }
  1064.                 
  1065.             if (catalogNumber == Star::InvalidCatalogNumber)
  1066.             {
  1067.                 catalogNumber = nextAutoCatalogNumber--;
  1068.             }
  1069.             else
  1070.             {
  1071.                 star = findWhileLoading(catalogNumber);
  1072.             }
  1073.             break;
  1074.                 
  1075.         case ModifyStar:
  1076.             // If no catalog number was specified, try looking up the star by name
  1077.             if (catalogNumber == Star::InvalidCatalogNumber && !firstName.empty())
  1078.             {
  1079.                 catalogNumber = findCatalogNumberByName(firstName);
  1080.             }
  1081.                 
  1082.             if (catalogNumber != Star::InvalidCatalogNumber)
  1083.             {
  1084.                 star = findWhileLoading(catalogNumber);
  1085.             }
  1086.                 
  1087.             break;
  1088.         }
  1089.         
  1090.         bool isNewStar = star == NULL;
  1091.         tokenizer.pushBack();
  1092.         Value* starDataValue = parser.readValue();
  1093.         if (starDataValue == NULL)
  1094.         {
  1095.             clog << "Error reading star." << endl;
  1096.             return false;
  1097.         }
  1098.         
  1099.         if (starDataValue->getType() != Value::HashType)
  1100.         {
  1101.             DPRINTF(0, "Bad star definition.n");
  1102.             delete starDataValue;
  1103.             return false;
  1104.         }
  1105.         Hash* starData = starDataValue->getHash();
  1106.         if (isNewStar)
  1107.             star = new Star();
  1108.         
  1109.         bool ok = false;
  1110.         if (isNewStar && disposition == ModifyStar)
  1111.         {
  1112.             clog << "Modify requested for nonexistent star." << endl;
  1113.         }
  1114.         else
  1115.         {
  1116.             ok = createStar(star, disposition, catalogNumber, starData, resourcePath, !isStar);
  1117.         }
  1118.         delete starDataValue;
  1119.         if (ok)
  1120.         {
  1121.             if (isNewStar)
  1122.             {
  1123.                 unsortedStars.add(*star);
  1124.                 nStars++;
  1125.                 delete star;
  1126.                 
  1127.                 // Add the new star to the temporary (load time) index.
  1128.                 stcFileCatalogNumberIndex[catalogNumber] = &unsortedStars[unsortedStars.size() - 1];
  1129.             }
  1130.             if (namesDB != NULL && !objName.empty())
  1131.             {
  1132.                 // List of namesDB will replace any that already exist for
  1133.                 // this star.
  1134.                 namesDB->erase(catalogNumber);
  1135.                 // Iterate through the string for names delimited
  1136.                 // by ':', and insert them into the star database.
  1137.                 // Note that db->add() will skip empty namesDB.
  1138.                 string::size_type startPos = 0;
  1139.                 while (startPos != string::npos)
  1140.                 {
  1141.                     string::size_type next    = objName.find(':', startPos);
  1142.                     string::size_type length = string::npos;
  1143.                     if (next != string::npos)
  1144.                     {
  1145.                         length = next - startPos;
  1146.                         ++next;
  1147.                     }
  1148.                     string starName = objName.substr(startPos, length);
  1149.                     namesDB->add(catalogNumber, starName);
  1150.                     if (starName != _(starName.c_str()))
  1151.                         namesDB->add(catalogNumber, _(starName.c_str()));
  1152.                     startPos = next;
  1153.                 }
  1154.             }
  1155.         }
  1156.         else
  1157.         {
  1158.             if (isNewStar)
  1159.                 delete star;
  1160.             DPRINTF(1, "Bad star definition--will continue parsing file.n");
  1161.         }
  1162.     }
  1163.     return true;
  1164. }
  1165. void StarDatabase::buildOctree()
  1166. {
  1167.     // This should only be called once for the database
  1168.     // ASSERT(octreeRoot == NULL);
  1169.     DPRINTF(1, "Sorting stars into octree . . .n");
  1170.     float absMag = astro::appToAbsMag(STAR_OCTREE_MAGNITUDE,
  1171.                                       STAR_OCTREE_ROOT_SIZE * (float) sqrt(3.0));
  1172.     DynamicStarOctree* root = new DynamicStarOctree(Point3f(1000, 1000, 1000),
  1173.                                                     absMag);
  1174.     for (unsigned int i = 0; i < unsortedStars.size(); ++i)
  1175.     {
  1176.         root->insertObject(unsortedStars[i], STAR_OCTREE_ROOT_SIZE);
  1177.     }
  1178.     
  1179.     DPRINTF(1, "Spatially sorting stars for improved locality of reference . . .n");
  1180.     Star* sortedStars    = new Star[nStars];
  1181.     Star* firstStar      = sortedStars;
  1182.     root->rebuildAndSort(octreeRoot, firstStar);
  1183.     // ASSERT((int) (firstStar - sortedStars) == nStars);
  1184.     DPRINTF(1, "%d stars totaln", (int) (firstStar - sortedStars));
  1185.     DPRINTF(1, "Octree has %d nodes and %d stars.n",
  1186.             1 + octreeRoot->countChildren(), octreeRoot->countObjects());
  1187. #if PROFILE_OCTREE
  1188.     vector<OctreeLevelStatistics> stats;
  1189.     octreeRoot->computeStatistics(stats);
  1190.     for (vector<OctreeLevelStatistics>::const_iterator iter = stats.begin(); iter != stats.end(); ++iter)
  1191.     {
  1192.         int level = iter - stats.begin();
  1193.         clog << "Level " << level << ", "
  1194.              << STAR_OCTREE_ROOT_SIZE / pow(2.0, (double) level) << "ly, "
  1195.              << iter->nodeCount << " nodes, "
  1196.              << iter->objectCount << " starsn";
  1197.     }
  1198. #endif
  1199.     // Clean up . . .
  1200.     //delete[] stars;
  1201.     unsortedStars.clear();
  1202.     delete root;
  1203.     stars = sortedStars;
  1204. }
  1205. void StarDatabase::buildIndexes()
  1206. {
  1207.     // This should only be called once for the database
  1208.     // assert(catalogNumberIndexes[0] == NULL);
  1209.     DPRINTF(1, "Building catalog number indexes . . .n");
  1210.     catalogNumberIndex = new Star*[nStars];
  1211.     for (int i = 0; i < nStars; ++i)
  1212.         catalogNumberIndex[i] = &stars[i];
  1213.     sort(catalogNumberIndex, catalogNumberIndex + nStars, PtrCatalogNumberOrderingPredicate());
  1214. }
  1215. /*! While loading the star catalogs, this function must be called instead of
  1216.  *  find(). The final catalog number index for stars cannot be built until
  1217.  *  after all stars have been loaded. During catalog loading, there are two
  1218.  *  separate indexes: one for the binary catalog and another index for stars
  1219.  *  loaded from stc files. They binary catalog index is a sorted array, while
  1220.  *  the stc catalog index is an STL map. Since the binary file can be quite
  1221.  *  large, we want to avoid creating a map with as many nodes as there are
  1222.  *  stars. Stc files should collectively contain many fewer stars, and stars
  1223.  *  in an stc file may reference each other (barycenters). Thus, a dynamic
  1224.  *  structure like a map is both practical and essential.
  1225.  */
  1226. Star* StarDatabase::findWhileLoading(uint32 catalogNumber) const
  1227. {
  1228.     // First check for stars loaded from the binary database
  1229.     if (binFileCatalogNumberIndex != NULL)
  1230.     {
  1231.         Star refStar;
  1232.         refStar.setCatalogNumber(catalogNumber);
  1233.         
  1234.         Star** star   = lower_bound(binFileCatalogNumberIndex,
  1235.                                     binFileCatalogNumberIndex + binFileStarCount,
  1236.                                     &refStar,
  1237.                                     PtrCatalogNumberOrderingPredicate());
  1238.         
  1239.         if (star != binFileCatalogNumberIndex + binFileStarCount && (*star)->getCatalogNumber() == catalogNumber)
  1240.             return *star;
  1241.     }
  1242.     
  1243.     // Next check for stars loaded from an stc file
  1244.     map<uint32, Star*>::const_iterator iter = stcFileCatalogNumberIndex.find(catalogNumber);
  1245.     if (iter != stcFileCatalogNumberIndex.end())
  1246.     {
  1247.         return iter->second;
  1248.     }
  1249.     
  1250.     // Star not found
  1251.     return NULL;
  1252. }