00001
00002
00003
00004
00005
00006
00007
00008
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;
00042
00043 const char* StarDatabase::FILE_HEADER = "CELSTARS";
00044 const char* StarDatabase::CROSSINDEX_FILE_HEADER = "CELINDEX";
00045
00046
00047
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
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
00097
00098
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
00270
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
00307 if (!name.empty() && namesDB != NULL)
00308 return namesDB->getCompletion(name);
00309 else
00310 return completion;
00311 }
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326
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
00343
00344
00345
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
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
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
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 *) ¶llax, 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 *) ¶llaxError, sizeof parallaxError);
00615 if ( in.bad() )
00616 break;
00617
00618 Star* star = &stars[nStars];
00619
00620
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
00632
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
00652
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
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
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
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
00780
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
00796
00797
00798
00799
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
00836
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
00885
00886
00887
00888
00889
00890
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
00920 string barycenterName;
00921 if (starData->getString("OrbitBarycenter", barycenterName))
00922 {
00923 uint32 barycenterCatNo = namesDB->findCatalogNumberByName(barycenterName);
00924 if (barycenterCatNo != Star::InvalidCatalogNumber)
00925 {
00926
00927
00928
00929
00930 BarycenterUsage bc;
00931 bc.catNo = catalogNumber;
00932 bc.barycenterCatNo = barycenterCatNo;
00933 barycenters.push_back(bc);
00934
00935
00936
00937
00938
00939
00940
00941
00942
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
00977
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
01009
01010
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
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
01114 if (nStars == capacity)
01115 {
01116
01117
01118
01119
01120 capacity = (int) (capacity * 1.05);
01121
01122
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
01145
01146 namesDB->erase(catalogNumber);
01147
01148
01149
01150
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
01179
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
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
01201 delete[] stars;
01202 delete root;
01203
01204 stars = sortedStars;
01205 }
01206
01207
01208 void StarDatabase::buildIndexes()
01209 {
01210
01211
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 }