Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members

stardb.cpp

Go to the documentation of this file.
00001 // stardb.cpp
00002 //
00003 // Copyright (C) 2001, Chris Laurel <claurel@shatters.net>
00004 //
00005 // This program is free software; you can redistribute it and/or
00006 // modify it under the terms of the GNU General Public License
00007 // as published by the Free Software Foundation; either version 2
00008 // of the License, or (at your option) any later version.
00009 
00010 #include <cmath>
00011 #include <cstdlib>
00012 #include <cstdio>
00013 #include <cassert>
00014 #include <algorithm>
00015 #include <celmath/mathlib.h>
00016 #include <celmath/plane.h>
00017 #include <celutil/util.h>
00018 #include <celutil/bytes.h>
00019 #include <celengine/stardb.h>
00020 #include "celestia.h"
00021 #include "astro.h"
00022 #include "parser.h"
00023 #include "parseobject.h"
00024 #include "multitexture.h"
00025 #include "meshmanager.h"
00026 #include <celutil/debug.h>
00027 
00028 using namespace std;
00029 
00030 
00031 static string HDCatalogPrefix("HD ");
00032 static string HIPPARCOSCatalogPrefix("HIP ");
00033 static string GlieseCatalogPrefix("Gliese ");
00034 static string RossCatalogPrefix("Ross ");
00035 static string LacailleCatalogPrefix("Lacaille ");
00036 static string TychoCatalogPrefix("TYC ");
00037 static string SAOCatalogPrefix("SAO ");
00038 
00039 static const float STAR_OCTREE_ROOT_SIZE  = 15000.0f;
00040 static const float STAR_OCTREE_MAGNITUDE  = 6.0f;
00041 static const float STAR_EXTRA_ROOM        = 0.01f; // Reserve 1% capacity for extra stars
00042 
00043 const char* StarDatabase::FILE_HEADER            = "CELSTARS";
00044 const char* StarDatabase::CROSSINDEX_FILE_HEADER = "CELINDEX";
00045 
00046 
00047 // Used to sort stars by catalog number
00048 struct CatalogNumberOrderingPredicate
00049 {
00050     int unused;
00051 
00052     CatalogNumberOrderingPredicate() {};
00053 
00054     bool operator()(const Star& star0, const Star& star1) const
00055     {
00056         return (star0.getCatalogNumber() < star1.getCatalogNumber());
00057     }
00058 };
00059 
00060 
00061 struct CatalogNumberEquivalencePredicate
00062 {
00063     int unused;
00064 
00065     CatalogNumberEquivalencePredicate() {};
00066 
00067     bool operator()(const Star& star0, const Star& star1) const
00068     {
00069         return (star0.getCatalogNumber() == star1.getCatalogNumber());
00070     }
00071 };
00072 
00073 
00074 // Used to sort star pointers by catalog number
00075 struct PtrCatalogNumberOrderingPredicate
00076 {
00077     int unused;
00078 
00079     PtrCatalogNumberOrderingPredicate() {};
00080 
00081     bool operator()(const Star* const & star0, const Star* const & star1) const
00082     {
00083         return (star0->getCatalogNumber() < star1->getCatalogNumber());
00084     }
00085 };
00086 
00087 
00088 static bool parseSimpleCatalogNumber(const string& name,
00089                                      const string& prefix,
00090                                      uint32* catalogNumber)
00091 {
00092     char extra[4];
00093     if (compareIgnoringCase(name, prefix, prefix.length()) == 0)
00094     {
00095         unsigned int num;
00096         // Use scanf to see if we have a valid catalog number; it must be
00097         // of the form: <prefix> <non-negative integer>  No additional
00098         // characters other than whitespace are allowed after the number.
00099         if (sscanf(name.c_str() + prefix.length(), " %u %c", &num, extra) == 1)
00100         {
00101             *catalogNumber = (uint32) num;
00102             return true;
00103         }
00104     }
00105 
00106     return false;
00107 }
00108 
00109 
00110 static bool parseHIPPARCOSCatalogNumber(const string& name, 
00111                                         uint32* catalogNumber)
00112 {
00113     return parseSimpleCatalogNumber(name,
00114                                     HIPPARCOSCatalogPrefix,
00115                                     catalogNumber);
00116 }
00117 
00118 
00119 static bool parseHDCatalogNumber(const string& name, 
00120                                  uint32* catalogNumber)
00121 {
00122     return parseSimpleCatalogNumber(name,
00123                                     HDCatalogPrefix,
00124                                     catalogNumber);
00125 }
00126 
00127 
00128 static bool parseTychoCatalogNumber(const string& name,
00129                                     uint32* catalogNumber)
00130 {
00131     if (compareIgnoringCase(name, TychoCatalogPrefix, TychoCatalogPrefix.length()) == 0)
00132     {
00133         unsigned int tyc1 = 0, tyc2 = 0, tyc3 = 0;
00134         if (sscanf(string(name, TychoCatalogPrefix.length(),
00135                    string::npos).c_str(),
00136                    " %u-%u-%u", &tyc1, &tyc2, &tyc3) == 3)
00137         {
00138             *catalogNumber = (uint32) (tyc3 * 1000000000 + tyc2 * 10000 + tyc1);
00139             return true;
00140         }
00141     }
00142 
00143     return false;
00144 }
00145 
00146 
00147 static bool parseCelestiaCatalogNumber(const string& name,
00148                                        uint32* catalogNumber)
00149 {
00150     char extra[4];
00151 
00152     if (name[0] == '#')
00153     {
00154         unsigned int num;
00155         if (sscanf(name.c_str(), "#%u %c", &num, &extra) == 1)
00156         {
00157             *catalogNumber = (uint32) num;
00158             return true;
00159         }
00160     }
00161 
00162     return false;
00163 }
00164 
00165 
00166 bool operator< (const StarDatabase::CrossIndexEntry& a,
00167                 const StarDatabase::CrossIndexEntry& b)
00168 {
00169     return a.catalogNumber < b.catalogNumber;
00170 }
00171 
00172 
00173 StarDatabase::StarDatabase():
00174     nStars               (0),
00175     capacity             (0),
00176     stars                (NULL),
00177     namesDB              (NULL),
00178     octreeRoot           (NULL),
00179     nextAutoCatalogNumber(0xfffffffe)
00180 {
00181     crossIndexes.resize(MaxCatalog);
00182 }
00183 
00184 
00185 StarDatabase::~StarDatabase()
00186 {
00187     if (stars != NULL)
00188         delete [] stars;
00189 
00190     if (catalogNumberIndex != NULL)
00191         delete [] catalogNumberIndex;
00192 
00193     for (vector<CrossIndex*>::iterator iter = crossIndexes.begin(); iter != crossIndexes.end(); ++iter)
00194     {
00195         if (*iter != NULL)
00196             delete *iter;
00197     }
00198 }
00199 
00200 
00201 Star* StarDatabase::find(const uint32 catalogNumber) const
00202 {
00203     Star refStar;
00204     refStar.setCatalogNumber(catalogNumber);
00205 
00206     Star** star   = lower_bound(catalogNumberIndex,
00207                                 catalogNumberIndex + nStars,
00208                                 &refStar,
00209                                 PtrCatalogNumberOrderingPredicate());
00210 
00211     if (star != catalogNumberIndex + nStars && (*star)->getCatalogNumber() == catalogNumber)
00212         return *star;
00213     else
00214         return NULL;
00215 }
00216 
00217 
00218 Star* StarDatabase::find(const string& name) const
00219 {
00220     if (name.empty())
00221         return NULL;
00222 
00223     uint32 catalogNumber = 0;
00224 
00225     if (parseCelestiaCatalogNumber(name, &catalogNumber))
00226     {
00227         return find(catalogNumber);
00228     }
00229     else if (parseHIPPARCOSCatalogNumber(name, &catalogNumber))
00230     {
00231         return find(catalogNumber);
00232     }
00233     else if (parseTychoCatalogNumber(name, &catalogNumber))
00234     {
00235         return find(catalogNumber);
00236     }
00237     else if (parseHDCatalogNumber(name, &catalogNumber))
00238     {
00239         return searchCrossIndex(HenryDraper, catalogNumber);
00240     }
00241     else if (parseSimpleCatalogNumber(name, SAOCatalogPrefix,
00242                                       &catalogNumber))
00243     {
00244         return searchCrossIndex(SAO, catalogNumber);
00245     }
00246     else
00247     {
00248         if (namesDB != NULL)
00249         {
00250             uint32 catalogNumber   = namesDB->findCatalogNumberByName(name);
00251             if (catalogNumber != Star::InvalidCatalogNumber)
00252                 return find(catalogNumber);
00253         }
00254 
00255         return NULL;
00256     }
00257 }
00258 
00259 
00260 uint32 StarDatabase::crossIndex(const Catalog catalog, const uint32 celCatalogNumber) const
00261 {
00262     if (static_cast<uint32>(catalog) >= crossIndexes.size())
00263         return Star::InvalidCatalogNumber;
00264 
00265     CrossIndex* xindex = crossIndexes[catalog];
00266     if (xindex == NULL)
00267         return Star::InvalidCatalogNumber;
00268 
00269     // A simple linear search.  We could store cross indices sorted by
00270     // both catalog numbers and trade memory for speed
00271     for (CrossIndex::const_iterator iter = xindex->begin(); iter != xindex->end(); iter++)
00272     {
00273         if (celCatalogNumber == iter->celCatalogNumber)
00274             return iter->catalogNumber;
00275     }
00276 
00277     return Star::InvalidCatalogNumber;
00278 }
00279 
00280 
00281 Star* StarDatabase::searchCrossIndex(const Catalog catalog, const uint32 number) const
00282 {
00283     if (static_cast<unsigned int>(catalog) >= crossIndexes.size())
00284         return NULL;
00285 
00286     CrossIndex* xindex = crossIndexes[catalog];
00287     if (xindex == NULL)
00288         return NULL;
00289 
00290     CrossIndexEntry xindexEnt;
00291     xindexEnt.catalogNumber = number;
00292 
00293     CrossIndex::iterator iter = lower_bound(xindex->begin(), xindex->end(),
00294                                             xindexEnt);
00295     if (iter == xindex->end() || iter->catalogNumber != number)
00296         return NULL;
00297     else
00298         return find(iter->celCatalogNumber);
00299 }
00300 
00301 
00302 vector<string> StarDatabase::getCompletion(const string& name) const
00303 {
00304     vector<string> completion;
00305 
00306     // only named stars are supported by completion.
00307     if (!name.empty() && namesDB != NULL)
00308         return namesDB->getCompletion(name);
00309     else
00310         return completion;
00311 }
00312 
00313 
00314 // Return the name for the star with specified catalog number.  The returned
00315 // string will be:
00316 //      the common name if it exists, otherwise
00317 //      the Bayer or Flamsteed designation if it exists, otherwise
00318 //      the HD catalog number if it exists, otherwise
00319 //      the HIPPARCOS catalog number.
00320 //
00321 // CAREFUL:
00322 // If the star name is not present in the names database, a new
00323 // string is constructed to contain the catalog number--keep in
00324 // mind that calling this method could possibly incur the overhead
00325 // of a memory allocation (though no explcit deallocation is
00326 // required as it's all wrapped in the string class.)
00327 string StarDatabase::getStarName(const Star& star) const
00328 {
00329     uint32 catalogNumber = star.getCatalogNumber();
00330 
00331     if (namesDB != NULL)
00332     {
00333         StarNameDatabase::NumberIndex::const_iterator iter = namesDB->getFirstNameIter(catalogNumber);
00334         if (iter != namesDB->getFinalNameIter() && iter->first == catalogNumber)
00335         {
00336             return iter->second;
00337         }
00338     }
00339 
00340     char buf[20];
00341     /*
00342       // Get the HD catalog name
00343       if (star.getCatalogNumber() != Star::InvalidCatalogNumber)
00344       sprintf(buf, "HD %d", star.getCatalogNumber(Star::HDCatalog));
00345       else
00346     */
00347     {
00348         if (catalogNumber < 1000000)
00349         {
00350             sprintf(buf, "HIP %d", catalogNumber);
00351         }
00352         else
00353         {
00354             uint32 tyc3 = catalogNumber / 1000000000;
00355             catalogNumber -= tyc3 * 1000000000;
00356             uint32 tyc2 = catalogNumber / 10000;
00357             catalogNumber -= tyc2 * 10000;
00358             uint32 tyc1 = catalogNumber;
00359             sprintf(buf, "TYC %d-%d-%d", tyc1, tyc2, tyc3);
00360         }
00361     }
00362     return string(buf);
00363 }
00364 
00365 
00366 string StarDatabase::getStarNameList(const Star& star, const unsigned int maxNames) const
00367 {
00368     string starNames;
00369     char numString[32];
00370 
00371     unsigned int catalogNumber    = star.getCatalogNumber();
00372 
00373     StarNameDatabase::NumberIndex::const_iterator iter  = namesDB->getFirstNameIter(catalogNumber);
00374 
00375     unsigned int count = 0;
00376     while (iter != namesDB->getFinalNameIter() && iter->first == catalogNumber && count < maxNames)
00377     {
00378         if (count != 0)
00379             starNames   += " / ";
00380 
00381         starNames   += ReplaceGreekLetterAbbr(_(iter->second.c_str()));
00382         ++iter;
00383         ++count;
00384     }
00385 
00386     uint32 hip  = catalogNumber;
00387     if (hip != Star::InvalidCatalogNumber && hip != 0 && count < maxNames)
00388     {
00389         if (hip <= Star::MaxTychoCatalogNumber)
00390         {
00391             if (count != 0)
00392                 starNames   += " / ";
00393             if (hip >= 1000000)
00394             {
00395                 uint32 h      = hip;
00396                 uint32 tyc3   = h / 1000000000;
00397                        h     -= tyc3 * 1000000000;
00398                 uint32 tyc2   = h / 10000;
00399                        h     -= tyc2 * 10000;
00400                 uint32 tyc1   = h;
00401 
00402                 sprintf(numString, "TYC %u-%u-%u", tyc1, tyc2, tyc3);
00403                 starNames    += numString;
00404             }
00405             else
00406             {
00407                 sprintf(numString, "HIP %u", hip);
00408                 starNames    += numString;
00409             }
00410 
00411             ++count;
00412         }
00413     }
00414 
00415     uint32 hd   = crossIndex(StarDatabase::HenryDraper, hip);
00416     if (count < maxNames && hd != Star::InvalidCatalogNumber)
00417     {
00418         if (count != 0)
00419             starNames   += " / ";
00420         sprintf(numString, "HD %u", hd);
00421         starNames   += numString;
00422     }
00423 
00424     uint32 sao   = crossIndex(StarDatabase::SAO, hip);
00425     if (count < maxNames && sao != Star::InvalidCatalogNumber)
00426     {
00427         if (count != 0)
00428             starNames   += " / ";
00429         sprintf(numString, "SAO %u", sao);
00430         starNames   += numString;
00431     }
00432 
00433     return starNames;
00434 }
00435 
00436 
00437 void StarDatabase::findVisibleStars(StarHandler& starHandler,
00438                                     const Point3f& position,
00439                                     const Quatf& orientation,
00440                                     float fovY,
00441                                     float aspectRatio,
00442                                     float limitingMag) const
00443 {
00444     // Compute the bounding planes of an infinite view frustum
00445     Planef frustumPlanes[5];
00446     Vec3f planeNormals[5];
00447     Mat3f rot = orientation.toMatrix3();
00448     float h = (float) tan(fovY / 2);
00449     float w = h * aspectRatio;
00450     planeNormals[0] = Vec3f(0, 1, -h);
00451     planeNormals[1] = Vec3f(0, -1, -h);
00452     planeNormals[2] = Vec3f(1, 0, -w);
00453     planeNormals[3] = Vec3f(-1, 0, -w);
00454     planeNormals[4] = Vec3f(0, 0, -1);
00455     for (int i = 0; i < 5; i++)
00456     {
00457         planeNormals[i].normalize();
00458         planeNormals[i] = planeNormals[i] * rot;
00459         frustumPlanes[i] = Planef(planeNormals[i], position);
00460     }
00461     
00462     octreeRoot->processVisibleObjects(starHandler,
00463                                       position,
00464                                       frustumPlanes,
00465                                       limitingMag,
00466                                       STAR_OCTREE_ROOT_SIZE);
00467 }
00468 
00469 
00470 void StarDatabase::findCloseStars(StarHandler& starHandler,
00471                                   const Point3f& position,
00472                                   float radius) const
00473 {
00474     octreeRoot->processCloseObjects(starHandler,
00475                                     position,
00476                                     radius,
00477                                     STAR_OCTREE_ROOT_SIZE);
00478 }
00479 
00480 
00481 StarNameDatabase* StarDatabase::getNameDatabase() const
00482 {
00483     return namesDB;
00484 }
00485 
00486 
00487 void StarDatabase::setNameDatabase(StarNameDatabase* _namesDB)
00488 {
00489     namesDB    = _namesDB;
00490 }
00491 
00492 
00493 bool StarDatabase::loadCrossIndex(const Catalog catalog, istream& in)
00494 {
00495     if (static_cast<unsigned int>(catalog) >= crossIndexes.size())
00496         return false;
00497 
00498     if (crossIndexes[catalog] != NULL)
00499         delete crossIndexes[catalog];
00500 
00501     // Verify that the star database file has a correct header
00502     {
00503         int headerLength = strlen(CROSSINDEX_FILE_HEADER);
00504         char* header = new char[headerLength];
00505         in.read(header, headerLength);
00506         if (strncmp(header, CROSSINDEX_FILE_HEADER, headerLength))
00507         {
00508             cerr << _("Bad header for cross index\n");
00509             return false;
00510         }
00511         delete[] header;
00512     }
00513 
00514     // Verify the version
00515     {
00516         uint16 version;
00517         in.read((char*) &version, sizeof version);
00518         LE_TO_CPU_INT16(version, version);
00519         if (version != 0x0100)
00520         {
00521             cerr << _("Bad version for cross index\n");
00522             return false;
00523         }
00524     }
00525 
00526     CrossIndex* xindex = new CrossIndex();
00527     if (xindex == NULL)
00528         return false;
00529 
00530     unsigned int record = 0;
00531     for (;;)
00532     {
00533         CrossIndexEntry ent;
00534         in.read((char *) &ent.catalogNumber, sizeof ent.catalogNumber);
00535         LE_TO_CPU_INT32(ent.catalogNumber, ent.catalogNumber);
00536         if (in.eof())
00537             break;
00538 
00539         in.read((char *) &ent.celCatalogNumber, sizeof ent.celCatalogNumber);
00540         LE_TO_CPU_INT32(ent.celCatalogNumber, ent.celCatalogNumber);
00541         if (in.fail())
00542         {
00543             cerr << _("Loading cross index failed at record ") << record << '\n';
00544             delete xindex;
00545             return false;
00546         }
00547 
00548         xindex->insert(xindex->end(), ent);
00549 
00550         record++;
00551     }
00552 
00553     sort(xindex->begin(), xindex->end());
00554 
00555     crossIndexes[catalog] = xindex;
00556 
00557     return true;
00558 }
00559 
00560 
00561 bool StarDatabase::loadOldFormatBinary(istream& in)
00562 {
00563     uint32 nStarsInFile = 0;
00564     in.read((char *) &nStarsInFile, sizeof nStarsInFile);
00565     LE_TO_CPU_INT32(nStarsInFile, nStarsInFile);
00566     if (!in.good())
00567         return false;
00568 
00569     int requiredCapacity = (int) ((nStars + nStarsInFile) * (1.0 + STAR_EXTRA_ROOM));
00570     if (capacity < requiredCapacity)
00571     {
00572         Star* newStars = new Star[requiredCapacity];
00573         if (newStars == NULL)
00574             return false;
00575         
00576         if (stars != NULL)
00577         {
00578             copy(stars, stars + nStars, newStars);
00579             delete[] stars;
00580         }
00581 
00582         stars = newStars;
00583 
00584         capacity = requiredCapacity;
00585     }
00586     
00587     uint32 throwOut = 0;
00588     uint32 fixUp = 0;
00589     unsigned int totalStars = nStars + nStarsInFile;
00590 
00591     while (((unsigned int) nStars) < totalStars)
00592     {
00593         uint32 catNo = 0;
00594         uint32 hdCatNo = 0;
00595         float RA = 0, dec = 0, parallax = 0;
00596         int16 appMag;
00597         uint16 spectralType;
00598         uint8 parallaxError;
00599 
00600         in.read((char *) &catNo, sizeof catNo);
00601         LE_TO_CPU_INT32(catNo, catNo);
00602         in.read((char *) &hdCatNo, sizeof hdCatNo);
00603         LE_TO_CPU_INT32(hdCatNo, hdCatNo);
00604         in.read((char *) &RA, sizeof RA);
00605         LE_TO_CPU_FLOAT(RA, RA);
00606         in.read((char *) &dec, sizeof dec);
00607         LE_TO_CPU_FLOAT(dec, dec);
00608         in.read((char *) &parallax, sizeof parallax);
00609         LE_TO_CPU_FLOAT(parallax, parallax);
00610         in.read((char *) &appMag, sizeof appMag);
00611         LE_TO_CPU_INT16(appMag, appMag);
00612         in.read((char *) &spectralType, sizeof spectralType);
00613         LE_TO_CPU_INT16(spectralType, spectralType);
00614         in.read((char *) &parallaxError, sizeof parallaxError);
00615         if ( in.bad() )
00616             break;
00617 
00618         Star* star = &stars[nStars];
00619 
00620         // Compute distance based on parallax
00621         double distance = LY_PER_PARSEC / (parallax > 0.0 ? parallax / 1000.0 : 1e-6);
00622 #ifdef DEBUG
00623         if (distance > 50000.0)
00624         {
00625             DPRINTF(0, "Warning, distance of star # %ld of %12.2f ly seems excessive (parallax: %2.5f)!\n", catNo, distance, parallax);
00626         }
00627 #endif // DEBUG
00628         Point3d pos = astro::equatorialToCelestialCart((double) RA, (double) dec, distance);
00629         star->setPosition(Point3f((float) pos.x, (float) pos.y, (float) pos.z));
00630 
00631         // Use apparent magnitude and distance to determine the absolute
00632         // magnitude of the star.
00633         star->setAbsoluteMagnitude((float) (appMag / 256.0 + 5 -
00634                                             5 * log10(distance / 3.26)));
00635 
00636         
00637         StarDetails* details = NULL;
00638         StellarClass sc;
00639         if (sc.unpack(spectralType))
00640             details = StarDetails::GetStarDetails(sc);
00641 
00642         if (details == NULL)
00643         {
00644             cerr << _("Bad spectral type in star database, star #\n");
00645             return false;
00646         }
00647         
00648         star->setDetails(details);
00649         star->setCatalogNumber(catNo);
00650 
00651         // TODO: Use a photometric estimate of distance if parallaxError is
00652         // greater than 25%.
00653         if (parallaxError > 50)
00654         {
00655             if (appMag / 256.0 > 6)
00656                 throwOut++;
00657             else
00658                 fixUp++;
00659         }
00660 
00661         nStars++;
00662     }
00663 
00664     if (in.bad())
00665         return false;
00666 
00667     DPRINTF(0, "StarDatabase::read: nStars = %d\n", nStarsInFile);
00668     cout << "nStars: " << nStars << '\n';
00669 
00670     return true;
00671 }
00672 
00673 
00674 bool StarDatabase::loadBinary(istream& in)
00675 {
00676     uint32 nStarsInFile = 0;
00677 
00678     // Verify that the star database file has a correct header
00679     {
00680         int headerLength = strlen(FILE_HEADER);
00681         char* header = new char[headerLength];
00682         in.read(header, headerLength);
00683         if (strncmp(header, FILE_HEADER, headerLength))
00684             return false;
00685         delete[] header;
00686     }
00687 
00688     // Verify the version
00689     {
00690         uint16 version;
00691         in.read((char*) &version, sizeof version);
00692         LE_TO_CPU_INT16(version, version);
00693         if (version != 0x0100)
00694             return false;
00695     }
00696 
00697     // Read the star count
00698     in.read((char *) &nStarsInFile, sizeof nStarsInFile);
00699     LE_TO_CPU_INT32(nStarsInFile, nStarsInFile);
00700     if (!in.good())
00701         return false;
00702 
00703     int requiredCapacity = (int) ((nStars + nStarsInFile) * (1.0 + STAR_EXTRA_ROOM));
00704     if (capacity < requiredCapacity)
00705     {
00706         Star* newStars = new Star[requiredCapacity];
00707         if (newStars == NULL)
00708             return false;
00709         
00710         if (stars != NULL)
00711         {
00712             copy(stars, stars + nStars, newStars);
00713             delete[] stars;
00714         }
00715 
00716         stars = newStars;
00717 
00718         capacity = requiredCapacity;
00719     }
00720     
00721     unsigned int totalStars = nStars + nStarsInFile;
00722 
00723     while (((unsigned int) nStars) < totalStars)
00724     {
00725         uint32 catNo = 0;
00726         float x = 0.0f, y = 0.0f, z = 0.0f;
00727         int16 absMag;
00728         uint16 spectralType;
00729 
00730         in.read((char *) &catNo, sizeof catNo);
00731         LE_TO_CPU_INT32(catNo, catNo);
00732         in.read((char *) &x, sizeof x);
00733         LE_TO_CPU_FLOAT(x, x);
00734         in.read((char *) &y, sizeof y);
00735         LE_TO_CPU_FLOAT(y, y);
00736         in.read((char *) &z, sizeof z);
00737         LE_TO_CPU_FLOAT(z, z);
00738         in.read((char *) &absMag, sizeof absMag);
00739         LE_TO_CPU_INT16(absMag, absMag);
00740         in.read((char *) &spectralType, sizeof spectralType);
00741         LE_TO_CPU_INT16(spectralType, spectralType);
00742         if (in.bad())
00743             break;
00744 
00745         Star* star = &stars[nStars];
00746 
00747         star->setPosition(x, y, z);
00748         star->setAbsoluteMagnitude((float) absMag / 256.0f);
00749 
00750         StarDetails* details = NULL;
00751         StellarClass sc;
00752         if (sc.unpack(spectralType))
00753             details = StarDetails::GetStarDetails(sc);
00754 
00755         if (details == NULL)
00756         {
00757             cerr << _("Bad spectral type in star database, star #") << nStars << "\n";
00758             return false;
00759         }
00760 
00761         star->setDetails(details);
00762         star->setCatalogNumber(catNo);
00763 
00764         nStars++;
00765     }
00766 
00767     if (in.bad())
00768         return false;
00769 
00770     DPRINTF(0, "StarDatabase::read: nStars = %d\n", nStarsInFile);
00771     clog << nStars << _(" stars in binary database\n");
00772 
00773     return true;
00774 }
00775 
00776 
00777 void StarDatabase::finish()
00778 {
00779     // Eliminate duplicate stars; reverse the list so that for stars with
00780     // identical catalog numbers, the most recently added one is kept.
00781     reverse(stars, stars + nStars);
00782     stable_sort(stars, stars + nStars, CatalogNumberOrderingPredicate());
00783     Star* lastStar = unique(stars, stars + nStars,
00784                             CatalogNumberEquivalencePredicate());
00785 
00786     int nUniqueStars = lastStar - stars;
00787     clog << _("Total star count: ") << nUniqueStars <<
00788         " (" << (nStars - nUniqueStars) <<
00789         _(" star(s) with duplicate catalog numbers deleted.)\n");
00790     nStars = nUniqueStars;
00791 
00792     buildOctree();
00793     buildIndexes();
00794 
00795     // Resolve all barycenters; this can't be done before star sorting. There's
00796     // still a bug here: final orbital radii aren't available until after
00797     // the barycenters have been resolved, and these are required when building
00798     // the octree.  This will only rarely cause a problem, but it still needs
00799     // to be addressed.
00800     for (vector<BarycenterUsage>::const_iterator iter = barycenters.begin();
00801          iter != barycenters.end(); iter++)
00802     {
00803         Star* star = find(iter->catNo);
00804         Star* barycenter = find(iter->barycenterCatNo);
00805         assert(star != NULL);
00806         assert(barycenter != NULL);
00807         if (star != NULL && barycenter != NULL)
00808             star->setOrbitBarycenter(barycenter);
00809     }
00810     barycenters.clear();
00811 }
00812 
00813 
00814 static void errorMessagePrelude(const Tokenizer& tok)
00815 {
00816     cerr << _("Error in .stc file (line ") << tok.getLineNumber() << "): ";
00817 }
00818 
00819 static void stcError(const Tokenizer& tok,
00820                      const string& msg)
00821 {
00822     errorMessagePrelude(tok);
00823     cerr << msg << '\n';
00824 }
00825 
00826 
00827 Star* StarDatabase::createStar(const uint32 catalogNumber,
00828                          Hash* starData,
00829                          const string& path,
00830                          bool isBarycenter)
00831 {
00832     StarDetails* details = NULL;
00833     string spectralType;
00834 
00835     // Get the magnitude and spectral type; if the star is actually
00836     // a barycenter placeholder, these fields are ignored.
00837     double magnitude = 0.0;
00838     if (isBarycenter)
00839     {
00840         details = StarDetails::GetBarycenterDetails();
00841     }
00842     else
00843     {
00844         if (!starData->getString("SpectralType", spectralType))
00845         {
00846             cerr << _("Invalid star: missing spectral type.\n");
00847             return NULL;
00848         }
00849         StellarClass sc = StellarClass::parse(spectralType);
00850         details = StarDetails::GetStarDetails(sc);
00851         if (details == NULL)
00852         {
00853             cerr << _("Invalid star: bad spectral type.\n");
00854             return NULL;
00855         }
00856     }
00857 
00858     string modelName;
00859     string textureName;
00860     bool hasTexture = starData->getString("Texture", textureName);
00861     bool hasModel = starData->getString("Mesh", modelName);
00862 
00863     RotationElements re = details->getRotationElements();
00864     FillinRotationElements(starData, re);
00865     bool hasRotationElements = !(re == details->getRotationElements());
00866 
00867     Vec3d semiAxes;
00868     bool hasSemiAxes = starData->getVector("SemiAxes", semiAxes);
00869     bool hasBarycenter = false;
00870     Point3f barycenterPosition;
00871 
00872     double radius;
00873     bool hasRadius = starData->getNumber("Radius", radius);
00874 
00875     Orbit* orbit = CreateOrbit(NULL, starData, path, true);
00876 
00877     if (hasTexture      ||
00878         hasModel        ||
00879         orbit != NULL   ||
00880         hasSemiAxes     ||
00881         hasRadius       ||
00882         hasRotationElements)
00883     {
00884         // If the star definition has extended information, clone the
00885         // star details so we can customize it without affecting other
00886         // stars of the same spectral type.
00887         // TODO: Need better management of star details objects.  The
00888         // standard ones should be persistent, but the custom ones should
00889         // probably be destroyed in the star destructor.  Reference counting
00890         // is probably the best strategy.
00891         details = new StarDetails(*details);
00892 
00893         if (hasTexture)
00894             details->setTexture(MultiResTexture(textureName, path));
00895 
00896         if (hasModel)
00897         {
00898             ResourceHandle modelHandle = GetModelManager()->getHandle(ModelInfo(modelName, path, Vec3f(0.0f, 0.0f, 0.0f)));
00899             details->setModel(modelHandle);
00900         }
00901 
00902         if (hasSemiAxes)
00903         {
00904             details->setEllipsoidSemiAxes(Vec3f((float) semiAxes.x,
00905                                                 (float) semiAxes.y,
00906                                                 (float) semiAxes.z));
00907         }
00908 
00909         if (hasRadius)
00910         {
00911             details->setRadius((float) radius);
00912             details->addKnowledge(StarDetails::KnowRadius);
00913         }
00914 
00915         if (orbit != NULL)
00916         {
00917             details->setOrbit(orbit);
00918 
00919             // See if a barycenter was specified as well
00920             string barycenterName;
00921             if (starData->getString("OrbitBarycenter", barycenterName))
00922             {
00923                 uint32 barycenterCatNo   = namesDB->findCatalogNumberByName(barycenterName);
00924                 if (barycenterCatNo != Star::InvalidCatalogNumber)
00925                 {
00926                     // We can't actually resolve the barycenter catalog number
00927                     // to a Star pointer until after all stars have been loaded
00928                     // and spatially sorted.  Just store it in a list to be
00929                     // resolved after sorting.
00930                     BarycenterUsage bc;
00931                     bc.catNo = catalogNumber;
00932                     bc.barycenterCatNo = barycenterCatNo;
00933                     barycenters.push_back(bc);
00934 
00935                     // Even though we can't actually get the Star pointer for
00936                     // the barycenter, we can get the star information.  We
00937                     // need this in order to get the star's position.  Since
00938                     // the stars aren't sorted, we're stuck doing a linear
00939                     // search of the currently loaded stars. But, since
00940                     // barycenters are typically defined immediately before
00941                     // stars that use them, a reverse linear search will have
00942                     // very good performance most of the time.
00943                     if (nStars > 0)
00944                     {
00945                         uint32 starIndex = nStars;
00946                         do 
00947                         {
00948                             starIndex--;
00949                             if (stars[starIndex].getCatalogNumber() == 
00950                                 barycenterCatNo)
00951                             {
00952                                 hasBarycenter = true;
00953                                 barycenterPosition = stars[starIndex].getPosition();
00954                                 break;
00955                             }
00956                         } while (starIndex != 0);
00957                     }
00958                 }
00959 
00960                 if (!hasBarycenter)
00961                 {
00962                     cerr << _("Barycenter ") << barycenterName << _(" does not exist.\n");
00963                     return NULL;
00964                 }
00965             }
00966         }
00967 
00968         if (hasRotationElements)
00969             details->setRotationElements(re);
00970     }
00971 
00972     Star* star = new Star();
00973     star->setDetails(details);
00974     star->setCatalogNumber(catalogNumber);
00975 
00976     // Compute the position in rectangular coordinates.  If a star has an
00977     // orbit and barycenter, it's position is the position of the barycenter.
00978     if (hasBarycenter)
00979     {
00980         star->setPosition(barycenterPosition);
00981     }
00982     else
00983     {
00984         double ra = 0.0;
00985         double dec = 0.0;
00986         double distance = 0.0;
00987         if (!starData->getNumber("RA", ra))
00988         {
00989             cerr << _("Invalid star: missing right ascension\n");
00990             delete star;
00991             return NULL;
00992         }
00993 
00994         if (!starData->getNumber("Dec", dec))
00995         {
00996             cerr << _("Invalid star: missing declination.\n");
00997             delete star;
00998             return NULL;
00999         }
01000 
01001         if (!starData->getNumber("Distance", distance))
01002         {
01003             cerr << _("Invalid star: missing distance.\n");
01004             delete star;
01005             return NULL;
01006         }
01007 
01008         // Truncate to floats to match behavior of reading from binary file.
01009         // The conversion to rectangular coordinates is still performed at
01010         // double precision, however.
01011         float raf = ((float) (ra * 24.0 / 360.0));
01012         float decf = ((float) dec);
01013         float distancef = ((float) distance);
01014         Point3d pos = astro::equatorialToCelestialCart((double) raf, (double) decf, (double) distancef);
01015         star->setPosition(Point3f((float) pos.x, (float) pos.y, (float) pos.z));
01016     }
01017 
01018     if (isBarycenter)
01019     {
01020         star->setAbsoluteMagnitude(30.0f);
01021     }
01022     else
01023     {
01024         double magnitude;
01025         if (!starData->getNumber("AbsMag", magnitude))
01026         {
01027             if (!starData->getNumber("AppMag", magnitude))
01028             {
01029                 cerr << _("Invalid star: missing magnitude.\n");
01030                 magnitude = 30.0;
01031             }
01032             else
01033             {
01034                 float distance = star->getPosition().distanceFromOrigin();
01035                 magnitude = astro::appToAbsMag((float) magnitude, distance);
01036             }
01037         }
01038 
01039         star->setAbsoluteMagnitude((float) magnitude);
01040     }
01041 
01042     return star;
01043 }
01044 
01045 
01046 bool StarDatabase::load(istream& in, const string& resourcePath)
01047 {
01048     Tokenizer tokenizer(&in);
01049     Parser parser(&tokenizer);
01050 
01051     while (tokenizer.nextToken() != Tokenizer::TokenEnd)
01052     {
01053         bool isStar = true;
01054         if (tokenizer.getTokenType() == Tokenizer::TokenName)
01055         {
01056             if (tokenizer.getNameValue() == "Star")
01057             {
01058                 isStar = true;
01059             }
01060             else if (tokenizer.getNameValue() == "Barycenter")
01061             {
01062                 isStar = false;
01063             }
01064             else
01065             {
01066                 stcError(tokenizer, "unrecognized object type");
01067                 return false;
01068             }
01069             tokenizer.nextToken();
01070         }
01071 
01072         bool autoGenCatalogNumber = true;
01073         uint32 catalogNumber = Star::InvalidCatalogNumber;
01074         if (tokenizer.getTokenType() == Tokenizer::TokenNumber)
01075         {
01076             autoGenCatalogNumber = false;
01077             catalogNumber = (uint32) tokenizer.getNumberValue();
01078             tokenizer.nextToken();
01079         }
01080 
01081         if (autoGenCatalogNumber)
01082         {
01083             catalogNumber = nextAutoCatalogNumber--;
01084         }
01085 
01086         string objName;
01087         if (tokenizer.getTokenType() == Tokenizer::TokenString)
01088         {
01089             // A star name (or names) is present
01090             objName    = tokenizer.getStringValue();
01091             tokenizer.nextToken();
01092         }
01093         
01094         tokenizer.pushBack();
01095 
01096         Value* starDataValue = parser.readValue();
01097         if (starDataValue == NULL)
01098         {
01099             DPRINTF(0, "Error reading star.\n");
01100             return false;
01101         }
01102 
01103         if (starDataValue->getType() != Value::HashType)
01104         {
01105             DPRINTF(0, "Bad star definition.\n");
01106             return false;
01107         }
01108         Hash* starData = starDataValue->getHash();
01109 
01110         Star* star   = createStar(catalogNumber, starData, resourcePath, !isStar);
01111         if (star != NULL)
01112         {
01113             // Ensure that the star array is large enough
01114             if (nStars == capacity)
01115             {
01116                 // Grow the array by 5%--this may be too little, but the
01117                 // assumption here is that there will be small numbers of
01118                 // stars in text files added to a big collection loaded from
01119                 // a binary file.
01120                 capacity = (int) (capacity * 1.05);
01121 
01122                 // 100 stars seems like a reasonable minimum
01123                 if (capacity < 100)
01124                     capacity = 100;
01125 
01126                 Star* newStars = new Star[capacity];
01127                 if (newStars == NULL)
01128                 {
01129                     DPRINTF(0, "Out of memory!");
01130                     return false;
01131                 }
01132 
01133                 if (stars != NULL)
01134                 {
01135                     copy(stars, stars + nStars, newStars);
01136                     delete[] stars;
01137                 }
01138                 stars = newStars;
01139             }
01140             stars[nStars++] = *star;
01141 
01142             if (namesDB != NULL && !objName.empty())
01143             {
01144                 // List of namesDB will replace any that already exist for
01145                 // this star.
01146                 namesDB->erase(catalogNumber);
01147 
01148                 // Iterate through the string for names delimited
01149                 // by ':', and insert them into the star database.
01150                 // Note that db->add() will skip empty namesDB.
01151                 string::size_type startPos = 0; 
01152                 while (startPos != string::npos)
01153                 {
01154                     string::size_type next    = objName.find(':', startPos);
01155                     string::size_type length = string::npos;
01156                     if (next != string::npos)
01157                     {
01158                         length = next - startPos;
01159                         ++next;
01160                     }
01161                     namesDB->add(catalogNumber, objName.substr(startPos, length));
01162                     startPos = next;
01163                 }
01164             }
01165         }
01166         else
01167         {
01168             DPRINTF(1, "Bad star definition--will continue parsing file.\n");
01169         }
01170     }
01171 
01172     return true;
01173 }
01174 
01175 
01176 void StarDatabase::buildOctree()
01177 {
01178     // This should only be called once for the database
01179     // ASSERT(octreeRoot == NULL);
01180 
01181     DPRINTF(1, "Sorting stars into octree . . .\n");
01182     float absMag = astro::appToAbsMag(STAR_OCTREE_MAGNITUDE,
01183                                       STAR_OCTREE_ROOT_SIZE * (float) sqrt(3.0));
01184     DynamicStarOctree* root = new DynamicStarOctree(Point3f(1000, 1000, 1000),
01185                                                     absMag);
01186     for (int i=0; i<nStars; ++i)
01187     {
01188         root->insertObject(stars[i], STAR_OCTREE_ROOT_SIZE);
01189     }
01190     DPRINTF(1, "Spatially sorting stars for improved locality of reference . . .\n");
01191     Star* sortedStars    = new Star[nStars];
01192     Star* firstStar      = sortedStars;
01193     root->rebuildAndSort(octreeRoot, firstStar);
01194 
01195     // ASSERT((int) (firstStar - sortedStars) == nStars);
01196     DPRINTF(1, "%d stars total\n", (int) (firstStar - sortedStars));
01197     DPRINTF(1, "Octree has %d nodes and %d stars.\n",
01198             1 + octreeRoot->countChildren(), octreeRoot->countObjects());
01199 
01200     // Clean up . . .
01201     delete[] stars;
01202     delete root;
01203 
01204     stars = sortedStars;
01205 }
01206 
01207 
01208 void StarDatabase::buildIndexes()
01209 {
01210     // This should only be called once for the database
01211     // assert(catalogNumberIndexes[0] == NULL);
01212 
01213     DPRINTF(1, "Building catalog number indexes . . .\n");
01214 
01215     catalogNumberIndex = new Star*[nStars];
01216     for (int i = 0; i < nStars; ++i)
01217         catalogNumberIndex[i] = &stars[i];
01218 
01219     sort(catalogNumberIndex, catalogNumberIndex + nStars, PtrCatalogNumberOrderingPredicate());
01220 }

Generated on Sat Jan 14 22:30:29 2006 for Celestia by  doxygen 1.4.1