00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012 #include <celmath/mathlib.h>
00013 #include <celmath/vecmath.h>
00014 #include <celmath/intersect.h>
00015 #include <celutil/utf8.h>
00016 #include <cassert>
00017 #include "astro.h"
00018 #include "3dsmesh.h"
00019 #include "meshmanager.h"
00020 #include "universe.h"
00021
00022 #define ANGULAR_RES 3.5e-6
00023
00024 using namespace std;
00025
00026
00027 Universe::Universe() :
00028 starCatalog(NULL),
00029 solarSystemCatalog(NULL),
00030 dsoCatalog(NULL),
00031 asterisms(NULL),
00032
00033 markers(NULL)
00034 {
00035 markers = new MarkerList();
00036 }
00037
00038 Universe::~Universe()
00039 {
00040 delete markers;
00041
00042 }
00043
00044
00045 StarDatabase* Universe::getStarCatalog() const
00046 {
00047 return starCatalog;
00048 }
00049
00050 void Universe::setStarCatalog(StarDatabase* catalog)
00051 {
00052 starCatalog = catalog;
00053 }
00054
00055
00056 SolarSystemCatalog* Universe::getSolarSystemCatalog() const
00057 {
00058 return solarSystemCatalog;
00059 }
00060
00061 void Universe::setSolarSystemCatalog(SolarSystemCatalog* catalog)
00062 {
00063 solarSystemCatalog = catalog;
00064 }
00065
00066
00067 DSODatabase* Universe::getDSOCatalog() const
00068 {
00069 return dsoCatalog;
00070 }
00071
00072 void Universe::setDSOCatalog(DSODatabase* catalog)
00073 {
00074 dsoCatalog = catalog;
00075 }
00076
00077
00078 AsterismList* Universe::getAsterisms() const
00079 {
00080 return asterisms;
00081 }
00082
00083 void Universe::setAsterisms(AsterismList* _asterisms)
00084 {
00085 asterisms = _asterisms;
00086 }
00087
00088 ConstellationBoundaries* Universe::getBoundaries() const
00089 {
00090 return boundaries;
00091 }
00092
00093 void Universe::setBoundaries(ConstellationBoundaries* _boundaries)
00094 {
00095 boundaries = _boundaries;
00096 }
00097
00098
00099 SolarSystem* Universe::getSolarSystem(const Star* star) const
00100 {
00101 if (star == NULL)
00102 return NULL;
00103
00104 uint32 starNum = star->getCatalogNumber();
00105 SolarSystemCatalog::iterator iter = solarSystemCatalog->find(starNum);
00106 if (iter == solarSystemCatalog->end())
00107 return NULL;
00108 else
00109 return iter->second;
00110 }
00111
00112
00113
00114
00115 SolarSystem* Universe::getSolarSystem(const Selection& sel) const
00116 {
00117 switch (sel.getType())
00118 {
00119 case Selection::Type_Star:
00120 return getSolarSystem(sel.star());
00121
00122 case Selection::Type_Body:
00123 {
00124 PlanetarySystem* system = sel.body()->getSystem();
00125 while (system != NULL)
00126 {
00127 Body* parent = system->getPrimaryBody();
00128 if (parent != NULL)
00129 system = parent->getSystem();
00130 else
00131 return getSolarSystem(Selection(system->getStar()));
00132 }
00133 return NULL;
00134 }
00135
00136 case Selection::Type_Location:
00137 return getSolarSystem(Selection(sel.location()->getParentBody()));
00138
00139 default:
00140 return NULL;
00141 }
00142 }
00143
00144
00145
00146
00147 SolarSystem* Universe::createSolarSystem(Star* star) const
00148 {
00149 SolarSystem* solarSystem = getSolarSystem(star);
00150 if (solarSystem != NULL)
00151 return solarSystem;
00152
00153 solarSystem = new SolarSystem(star);
00154 solarSystemCatalog->insert(SolarSystemCatalog::
00155 value_type(star->getCatalogNumber(),
00156 solarSystem));
00157
00158 return solarSystem;
00159 }
00160
00161
00162 MarkerList* Universe::getMarkers() const
00163 {
00164 return markers;
00165 }
00166
00167
00168 void Universe::markObject(const Selection& sel,
00169 float size,
00170 Color color,
00171 Marker::Symbol symbol,
00172 int priority)
00173 {
00174 for (MarkerList::iterator iter = markers->begin();
00175 iter != markers->end(); iter++)
00176 {
00177 if (iter->getObject() == sel)
00178 {
00179
00180
00181
00182 if (priority > iter->getPriority())
00183 {
00184 markers->erase(iter);
00185 break;
00186 }
00187 else
00188 {
00189 return;
00190 }
00191 }
00192 }
00193
00194 Marker marker(sel);
00195 marker.setColor(color);
00196 marker.setSymbol(symbol);
00197 marker.setSize(size);
00198 marker.setPriority(priority);
00199 markers->insert(markers->end(), marker);
00200 }
00201
00202
00203 void Universe::unmarkObject(const Selection& sel, int priority)
00204 {
00205 for (MarkerList::iterator iter = markers->begin();
00206 iter != markers->end(); iter++)
00207 {
00208 if (iter->getObject() == sel)
00209 {
00210 if (priority >= iter->getPriority())
00211 markers->erase(iter);
00212 break;
00213 }
00214 }
00215 }
00216
00217
00218 void Universe::unmarkAll()
00219 {
00220 markers->erase(markers->begin(), markers->end());
00221 }
00222
00223
00224 bool Universe::isMarked(const Selection& sel, int priority) const
00225 {
00226 for (MarkerList::iterator iter = markers->begin();
00227 iter != markers->end(); iter++)
00228 {
00229 if (iter->getObject() == sel)
00230 return iter->getPriority() >= priority;
00231 }
00232
00233 return false;
00234 }
00235
00236
00237 class ClosestStarFinder : public StarHandler
00238 {
00239 public:
00240 ClosestStarFinder(float _maxDistance);
00241 ~ClosestStarFinder() {};
00242 void process(const Star& star, float distance, float appMag);
00243
00244 public:
00245 float maxDistance;
00246 float closestDistance;
00247 Star* closestStar;
00248 };
00249
00250 ClosestStarFinder::ClosestStarFinder(float _maxDistance) :
00251 maxDistance(_maxDistance), closestDistance(_maxDistance), closestStar(NULL)
00252 {
00253 }
00254
00255 void ClosestStarFinder::process(const Star& star, float distance, float appMag)
00256 {
00257 if (distance < closestDistance)
00258 {
00259 closestStar = const_cast<Star*>(&star);
00260 closestDistance = distance;
00261 }
00262 }
00263
00264
00265 class NearStarFinder : public StarHandler
00266 {
00267 public:
00268 NearStarFinder(float _maxDistance, vector<const Star*>& nearStars);
00269 ~NearStarFinder() {};
00270 void process(const Star& star, float distance, float appMag);
00271
00272 private:
00273 float maxDistance;
00274 vector<const Star*>& nearStars;
00275 };
00276
00277 NearStarFinder::NearStarFinder(float _maxDistance,
00278 vector<const Star*>& _nearStars) :
00279 maxDistance(_maxDistance),
00280 nearStars(_nearStars)
00281 {
00282 }
00283
00284 void NearStarFinder::process(const Star& star, float distance, float appMag)
00285 {
00286 if (distance < maxDistance)
00287 nearStars.push_back(&star);
00288 }
00289
00290
00291
00292 struct PlanetPickInfo
00293 {
00294 double sinAngle2Closest;
00295 double closestDistance;
00296 double closestApproxDistance;
00297 Body* closestBody;
00298 Ray3d pickRay;
00299 double jd;
00300 float atanTolerance;
00301 };
00302
00303 static bool ApproxPlanetPickTraversal(Body* body, void* info)
00304 {
00305 PlanetPickInfo* pickInfo = (PlanetPickInfo*) info;
00306
00307
00308 if (body->getClassification() == Body::Invisible || !body->extant(pickInfo->jd))
00309 return true;
00310
00311 Point3d bpos = body->getHeliocentricPosition(pickInfo->jd);
00312 Vec3d bodyDir = bpos - pickInfo->pickRay.origin;
00313 double distance = bodyDir.length();
00314
00315
00316
00317
00318 float appOrbitRadius = (float) (body->getOrbit()->getBoundingRadius() /
00319 distance);
00320
00321 if ((pickInfo->atanTolerance > ANGULAR_RES ? pickInfo->atanTolerance:
00322 ANGULAR_RES) > appOrbitRadius)
00323 {
00324 return true;
00325 }
00326
00327 bodyDir.normalize();
00328 Vec3d bodyMiss = bodyDir - pickInfo->pickRay.direction;
00329 double sinAngle2 = sqrt(bodyMiss * bodyMiss)/2.0;
00330
00331 if (sinAngle2 <= pickInfo->sinAngle2Closest)
00332 {
00333 pickInfo->sinAngle2Closest = sinAngle2 > ANGULAR_RES ? sinAngle2 :
00334 ANGULAR_RES;
00335 pickInfo->closestBody = body;
00336 pickInfo->closestApproxDistance = distance;
00337 }
00338
00339 return true;
00340 }
00341
00342
00343
00344 static bool ExactPlanetPickTraversal(Body* body, void* info)
00345 {
00346 PlanetPickInfo* pickInfo = reinterpret_cast<PlanetPickInfo*>(info);
00347 Point3d bpos = body->getHeliocentricPosition(pickInfo->jd);
00348 float radius = body->getRadius();
00349 double distance = -1.0;
00350
00351
00352 if (body->getClassification() != Body::Invisible &&
00353 body->extant(pickInfo->jd) &&
00354 testIntersection(pickInfo->pickRay, Sphered(bpos, radius), distance))
00355 {
00356 if (body->getModel() == InvalidResource)
00357 {
00358
00359
00360
00361
00362 if (body->getOblateness() != 0.0f)
00363 {
00364
00365 Vec3d ellipsoidAxes(radius,
00366 radius * (1.0 - body->getOblateness()),
00367 radius);
00368
00369 Mat3d m = conjugate(body->getEclipticalToEquatorial(pickInfo->jd)).toMatrix3();
00370 Ray3d r(Point3d(0, 0, 0) + (pickInfo->pickRay.origin - bpos),
00371 pickInfo->pickRay.direction);
00372 r = r * m;
00373 if (!testIntersection(r, Ellipsoidd(ellipsoidAxes), distance))
00374 distance = -1.0;
00375 }
00376 }
00377 else
00378 {
00379
00380 Quatf qf = body->getOrientation();
00381 Quatd qd(qf.w, qf.x, qf.y, qf.z);
00382 Mat3d m = conjugate(qd * body->getEclipticalToGeographic(pickInfo->jd)).toMatrix3();
00383 Ray3d r(Point3d(0, 0, 0) + (pickInfo->pickRay.origin - bpos),
00384 pickInfo->pickRay.direction);
00385 r = r * m;
00386
00387
00388
00389
00390 double s = 1.0 / radius;
00391 r.origin.x *= s;
00392 r.origin.y *= s;
00393 r.origin.z *= s;
00394 r.direction *= s;
00395
00396 Model* model = GetModelManager()->find(body->getModel());
00397 if (model != NULL)
00398 {
00399 if (!model->pick(r, distance))
00400 distance = -1.0;
00401 }
00402 }
00403
00404
00405
00406 Vec3d bodyDir = bpos - pickInfo->pickRay.origin;
00407 bodyDir.normalize();
00408 Vec3d bodyMiss = bodyDir - pickInfo->pickRay.direction;
00409 double sinAngle2 = sqrt(bodyMiss * bodyMiss)/2.0;
00410
00411
00412 if (sinAngle2 < sin(PI/4.0) && distance > 0.0 &&
00413 distance <= pickInfo->closestDistance)
00414 {
00415 pickInfo->closestDistance = distance;
00416 pickInfo->closestBody = body;
00417 }
00418 }
00419
00420 return true;
00421 }
00422
00423
00424 Selection Universe::pickPlanet(SolarSystem& solarSystem,
00425 const UniversalCoord& origin,
00426 const Vec3f& direction,
00427 double when,
00428 float faintestMag,
00429 float tolerance)
00430 {
00431 double sinTol2 = (sin(tolerance/2.0) > ANGULAR_RES ?
00432 sin(tolerance/2.0) : ANGULAR_RES);
00433 PlanetPickInfo pickInfo;
00434
00435 Star* star = solarSystem.getStar();
00436 assert(star != NULL);
00437
00438
00439 UniversalCoord starPos = star->getPosition(when);
00440 Vec3d v = origin - starPos;
00441 Point3d astrocentricOrigin(astro::microLightYearsToKilometers(v.x),
00442 astro::microLightYearsToKilometers(v.y),
00443 astro::microLightYearsToKilometers(v.z));
00444
00445 pickInfo.pickRay = Ray3d(astrocentricOrigin,
00446 Vec3d(direction.x, direction.y, direction.z));
00447
00448 pickInfo.sinAngle2Closest = 1.0;
00449 pickInfo.closestDistance = 1.0e50;
00450 pickInfo.closestApproxDistance = 1.0e50;
00451 pickInfo.closestBody = NULL;
00452 pickInfo.jd = when;
00453 pickInfo.atanTolerance = (float) atan(tolerance);
00454
00455
00456
00457
00458 solarSystem.getPlanets()->traverse(ExactPlanetPickTraversal,
00459 (void*) &pickInfo);
00460
00461 if (pickInfo.closestBody != NULL)
00462 {
00463
00464 Body* closestBody = pickInfo.closestBody;
00465
00466
00467
00468
00469 solarSystem.getPlanets()->traverse(ApproxPlanetPickTraversal,
00470 (void*) &pickInfo);
00471 if (pickInfo.closestBody == closestBody)
00472 return Selection(closestBody);
00473
00474
00475
00476 if ((pickInfo.sinAngle2Closest <= sinTol2) &&
00477 (pickInfo.closestDistance > pickInfo.closestApproxDistance))
00478 return Selection(pickInfo.closestBody);
00479
00480 else
00481 return Selection(closestBody);
00482
00483 }
00484
00485
00486
00487
00488
00489
00490
00491
00492 solarSystem.getPlanets()->traverse(ApproxPlanetPickTraversal,
00493 (void*) &pickInfo);
00494 if (pickInfo.sinAngle2Closest <= sinTol2)
00495 return Selection(pickInfo.closestBody);
00496 else
00497 return Selection();
00498 }
00499
00500
00501
00502 class StarPicker : public StarHandler
00503 {
00504 public:
00505 StarPicker(const Point3f&, const Vec3f&, double, float);
00506 ~StarPicker() {};
00507
00508 void process(const Star&, float, float);
00509
00510 public:
00511 const Star* pickedStar;
00512 Point3f pickOrigin;
00513 Vec3f pickRay;
00514 double sinAngle2Closest;
00515 double when;
00516 };
00517
00518 StarPicker::StarPicker(const Point3f& _pickOrigin,
00519 const Vec3f& _pickRay,
00520 double _when,
00521 float angle) :
00522 pickedStar(NULL),
00523 pickOrigin(_pickOrigin),
00524 pickRay(_pickRay),
00525 sinAngle2Closest(sin(angle/2.0) > ANGULAR_RES ? sin(angle/2.0) :
00526 ANGULAR_RES ),
00527 when(_when)
00528 {
00529 }
00530
00531 void StarPicker::process(const Star& star, float distance, float appMag)
00532 {
00533 Vec3f relativeStarPos = star.getPosition() - pickOrigin;
00534 Vec3f starDir = relativeStarPos;
00535 starDir.normalize();
00536
00537 double sinAngle2 = 0.0;
00538
00539
00540 float orbitalRadius = star.getOrbitalRadius();
00541 if (orbitalRadius != 0.0f)
00542 {
00543 float distance;
00544
00545
00546
00547
00548
00549 if (testIntersection(Ray3f(Point3f(0.0f, 0.0f, 0.0f), pickRay),
00550 Spheref(Point3f(0.0f, 0.0f, 0.0f) + relativeStarPos,
00551 orbitalRadius * 2.0f),
00552 distance))
00553 {
00554 Point3d starPos = star.getPosition(when);
00555 starDir = Vec3f((float) (starPos.x * 1.0e-6 - pickOrigin.x),
00556 (float) (starPos.y * 1.0e-6 - pickOrigin.y),
00557 (float) (starPos.z * 1.0e-6 - pickOrigin.z));
00558 starDir.normalize();
00559 }
00560 }
00561
00562 Vec3f starMiss = starDir - pickRay;
00563 Vec3d sMd = Vec3d(starMiss.x, starMiss.y, starMiss.z);
00564 sinAngle2 = sqrt(sMd * sMd)/2.0;
00565
00566 if (sinAngle2 <= sinAngle2Closest)
00567 {
00568 sinAngle2Closest = sinAngle2 > ANGULAR_RES ? sinAngle2 : ANGULAR_RES;
00569 pickedStar = ☆
00570 }
00571 }
00572
00573
00574 class CloseStarPicker : public StarHandler
00575 {
00576 public:
00577 CloseStarPicker(const UniversalCoord& pos,
00578 const Vec3f& dir,
00579 double t,
00580 float _maxDistance,
00581 float angle);
00582 ~CloseStarPicker() {};
00583 void process(const Star& star, float distance, float appMag);
00584
00585 public:
00586 UniversalCoord pickOrigin;
00587 Vec3f pickDir;
00588 double now;
00589 float maxDistance;
00590 const Star* closestStar;
00591 float closestDistance;
00592 double sinAngle2Closest;
00593 };
00594
00595
00596 CloseStarPicker::CloseStarPicker(const UniversalCoord& pos,
00597 const Vec3f& dir,
00598 double t,
00599 float _maxDistance,
00600 float angle) :
00601 pickOrigin(pos),
00602 pickDir(dir),
00603 now(t),
00604 maxDistance(_maxDistance),
00605 closestStar(NULL),
00606 closestDistance(0.0f),
00607 sinAngle2Closest(sin(angle/2.0) > ANGULAR_RES ? sin(angle/2.0) :
00608 ANGULAR_RES )
00609 {
00610 }
00611
00612 void CloseStarPicker::process(const Star& star,
00613 float lowPrecDistance,
00614 float appMag)
00615 {
00616 if (lowPrecDistance > maxDistance)
00617 return;
00618
00619 Vec3d hPos = (star.getPosition(now) - pickOrigin) *
00620 astro::microLightYearsToKilometers(1.0);
00621 Vec3f starDir((float) hPos.x, (float) hPos.y, (float) hPos.z);
00622 float distance = 0.0f;
00623
00624 if (testIntersection(Ray3f(Point3f(0, 0, 0), pickDir),
00625 Spheref(Point3f(starDir.x, starDir.y, starDir.z),
00626 star.getRadius()), distance))
00627 {
00628 if (distance > 0.0f)
00629 {
00630 if (closestStar == NULL || distance < closestDistance)
00631 {
00632 closestStar = ☆
00633 closestDistance = starDir.length();
00634 sinAngle2Closest = ANGULAR_RES;
00635
00636 }
00637 }
00638 }
00639 else
00640 {
00641
00642 float distance = starDir.length();
00643 starDir.normalize();
00644 Vec3f starMiss = starDir - pickDir;
00645 Vec3d sMd = Vec3d(starMiss.x, starMiss.y, starMiss.z );
00646
00647 double sinAngle2 = sqrt(sMd * sMd)/2.0;
00648
00649 if (sinAngle2 <= sinAngle2Closest &&
00650 (closestStar == NULL || distance < closestDistance))
00651 {
00652 closestStar = ☆
00653 closestDistance = distance;
00654 sinAngle2Closest = sinAngle2 > ANGULAR_RES ? sinAngle2 : ANGULAR_RES;
00655 }
00656 }
00657 }
00658
00659
00660 Selection Universe::pickStar(const UniversalCoord& origin,
00661 const Vec3f& direction,
00662 double when,
00663 float faintestMag,
00664 float tolerance)
00665 {
00666 Point3f o = (Point3f) origin;
00667 o.x *= 1e-6f;
00668 o.y *= 1e-6f;
00669 o.z *= 1e-6f;
00670
00671
00672
00673
00674
00675
00676
00677 CloseStarPicker closePicker(origin, direction, when, 1.0f, tolerance);
00678 starCatalog->findCloseStars(closePicker, o, 1.0f);
00679 if (closePicker.closestStar != NULL)
00680 return Selection(const_cast<Star*>(closePicker.closestStar));
00681
00682
00683
00684
00685 Quatf rotation;
00686 Vec3f n(0, 0, -1);
00687 Vec3f Missf = n - direction;
00688 Vec3d Miss = Vec3d(Missf.x, Missf.y, Missf.z);
00689 double sinAngle2 = sqrt(Miss * Miss)/2.0;
00690
00691 if (sinAngle2 <= ANGULAR_RES)
00692 {
00693 rotation.setAxisAngle(Vec3f(1, 0, 0), 0);
00694 }
00695 else if (sinAngle2 >= 1.0 - 0.5 * ANGULAR_RES * ANGULAR_RES)
00696 {
00697 rotation.setAxisAngle(Vec3f(1, 0, 0), (float) PI);
00698 }
00699 else
00700 {
00701 Vec3f axis = direction ^ n;
00702 axis.normalize();
00703 rotation.setAxisAngle(axis, (float) (2.0 * asin(sinAngle2)));
00704 }
00705 StarPicker picker(o, direction, when, tolerance);
00706 starCatalog->findVisibleStars(picker,
00707 o,
00708 rotation,
00709 tolerance, 1.0f,
00710 faintestMag);
00711 if (picker.pickedStar != NULL)
00712 return Selection(const_cast<Star*>(picker.pickedStar));
00713 else
00714 return Selection();
00715 }
00716
00717
00718 class DSOPicker : public DSOHandler
00719 {
00720 public:
00721 DSOPicker(const Point3d&, const Vec3d&, float);
00722 ~DSOPicker() {};
00723
00724 void process(DeepSkyObject* const &, double, float);
00725
00726 public:
00727 const DeepSkyObject* pickedDSO;
00728 Point3d pickOrigin;
00729 Vec3d pickDir;
00730 double sinAngle2Closest;
00731 };
00732
00733
00734 DSOPicker::DSOPicker(const Point3d& pickOrigin,
00735 const Vec3d& pickDir,
00736 float angle) :
00737 pickedDSO (NULL),
00738 pickOrigin (pickOrigin),
00739 pickDir (pickDir),
00740 sinAngle2Closest(sin(angle/2.0) > ANGULAR_RES ? sin(angle/2.0) :
00741 ANGULAR_RES )
00742 {
00743 }
00744
00745
00746 void DSOPicker::process(DeepSkyObject* const & dso, double distance, float appMag)
00747 {
00748 Vec3d relativeDSOPos = dso->getPosition() - pickOrigin;
00749 Vec3d dsoDir = relativeDSOPos;
00750 dsoDir.normalize();
00751
00752 double sinAngle2 = 0.0;
00753
00754 double distance2;
00755 if (testIntersection(Ray3d(Point3d(0.0, 0.0, 0.0), pickDir),
00756 Sphered(Point3d(0.0, 0.0, 0.0) + relativeDSOPos, (double) dso->getRadius()), distance2))
00757 {
00758 Point3d dsoPos = dso->getPosition();
00759 dsoDir = Vec3d(dsoPos.x * 1.0e-6 - pickOrigin.x,
00760 dsoPos.y * 1.0e-6 - pickOrigin.y,
00761 dsoPos.z * 1.0e-6 - pickOrigin.z);
00762 dsoDir.normalize();
00763 }
00764
00765 Vec3d dsoMissd = dsoDir - Vec3d(pickDir.x, pickDir.y, pickDir.z);
00766 sinAngle2 = sqrt(dsoMissd * dsoMissd)/2.0;
00767
00768 if (sinAngle2 <= sinAngle2Closest)
00769 {
00770 sinAngle2Closest = sinAngle2 > ANGULAR_RES ? sinAngle2 : ANGULAR_RES;
00771 pickedDSO = dso;
00772 }
00773 }
00774
00775
00776 class CloseDSOPicker : public DSOHandler
00777 {
00778 public:
00779 CloseDSOPicker(const Point3d& pos,
00780 const Vec3d& dir,
00781 double maxDistance,
00782 float angle);
00783 ~CloseDSOPicker() {};
00784
00785 void process(DeepSkyObject* const & dso, double distance, float appMag);
00786
00787 public:
00788 Point3d pickOrigin;
00789 Vec3d pickDir;
00790 double maxDistance;
00791 const DeepSkyObject* closestDSO;
00792 double closestDistance;
00793 };
00794
00795
00796 CloseDSOPicker::CloseDSOPicker(const Point3d& pos,
00797 const Vec3d& dir,
00798 double maxDistance,
00799 float angle) :
00800 pickOrigin (pos),
00801 pickDir (dir),
00802 maxDistance (maxDistance),
00803 closestDSO (NULL),
00804 closestDistance(1.0e32)
00805 {
00806 }
00807
00808
00809 void CloseDSOPicker::process(DeepSkyObject* const & dso,
00810 double distance,
00811 float appMag)
00812 {
00813
00814 if (distance > maxDistance)
00815 return;
00816
00817 double distance2 = 0.0;
00818 if (testIntersection(Ray3d(pickOrigin, pickDir),
00819 Sphered(dso->getPosition(), (double) dso->getRadius()),
00820 distance2))
00821 {
00822
00823 if (pickOrigin.distanceTo(dso->getPosition()) > dso->getRadius() &&
00824 distance2 < closestDistance)
00825 {
00826 closestDSO = dso;
00827 closestDistance = distance2;
00828 }
00829 }
00830 }
00831
00832
00833 Selection Universe::pickDeepSkyObject(const UniversalCoord& origin,
00834 const Vec3f& direction,
00835 float faintestMag,
00836 float tolerance)
00837 {
00838 Point3d orig = (Point3d) origin;
00839 orig.x *= 1e-6;
00840 orig.y *= 1e-6;
00841 orig.z *= 1e-6;
00842
00843 Vec3d dir = Vec3d(direction.x, direction.y, direction.z);
00844
00845 CloseDSOPicker closePicker(orig, dir, 1e9, tolerance);
00846
00847 dsoCatalog->findCloseDSOs(closePicker, orig, 1e9);
00848 if (closePicker.closestDSO != NULL)
00849 {
00850 return Selection(const_cast<DeepSkyObject*>(closePicker.closestDSO));
00851 }
00852
00853 Quatf rotation;
00854 Vec3d n(0, 0, -1);
00855 Vec3d Miss = n - dir;
00856 double sinAngle2 = sqrt(Miss * Miss)/2.0;
00857
00858 if (sinAngle2 <= ANGULAR_RES)
00859 {
00860 rotation.setAxisAngle(Vec3f(1, 0, 0), 0);
00861 }
00862 else if (sinAngle2 >= 1.0 - 0.5 * ANGULAR_RES * ANGULAR_RES)
00863 {
00864 rotation.setAxisAngle(Vec3f(1, 0, 0), (float) PI);
00865 }
00866 else
00867 {
00868 Vec3f axis = direction ^ Vec3f((float)n.x, (float)n.y, (float)n.z);
00869 axis.normalize();
00870 rotation.setAxisAngle(axis, (float) (2.0 * asin(sinAngle2)));
00871 }
00872
00873 DSOPicker picker(orig, dir, tolerance);
00874 dsoCatalog->findVisibleDSOs(picker,
00875 orig,
00876 rotation,
00877 tolerance,
00878 1.0f,
00879 faintestMag);
00880 if (picker.pickedDSO != NULL)
00881 return Selection(const_cast<DeepSkyObject*>(picker.pickedDSO));
00882 else
00883 return Selection();
00884 }
00885
00886
00887 Selection Universe::pick(const UniversalCoord& origin,
00888 const Vec3f& direction,
00889 double when,
00890 float faintestMag,
00891 float tolerance)
00892 {
00893 Selection sel;
00894
00895 closeStars.clear();
00896 getNearStars(origin, 1.0f, closeStars);
00897 for (vector<const Star*>::const_iterator iter = closeStars.begin();
00898 iter != closeStars.end(); iter++)
00899 {
00900 SolarSystem* solarSystem = getSolarSystem(*iter);
00901 if (solarSystem != NULL)
00902 {
00903 sel = pickPlanet(*solarSystem,
00904 origin, direction,
00905 when,
00906 faintestMag,
00907 tolerance);
00908 if (!sel.empty())
00909 break;
00910 }
00911 }
00912
00913 #if 0
00914 SolarSystem* closestSolarSystem = getNearestSolarSystem(origin);
00915 if (closestSolarSystem != NULL)
00916 {
00917 sel = pickPlanet(*closestSolarSystem,
00918 origin, direction,
00919 when,
00920 faintestMag,
00921 tolerance);
00922 }
00923 #endif
00924
00925 if (sel.empty())
00926 sel = pickStar(origin, direction, when, faintestMag, tolerance);
00927
00928 if (sel.empty())
00929 sel = pickDeepSkyObject(origin, direction, faintestMag, tolerance);
00930
00931 return sel;
00932 }
00933
00934
00935
00936 Selection Universe::findChildObject(const Selection& sel,
00937 const string& name,
00938 bool i18n) const
00939 {
00940 switch (sel.getType())
00941 {
00942 case Selection::Type_Star:
00943 {
00944 SolarSystem* sys = getSolarSystem(sel.star());
00945 if (sys != NULL)
00946 {
00947 PlanetarySystem* planets = sys->getPlanets();
00948 if (planets != NULL)
00949 return Selection(planets->find(name, false, i18n));
00950 }
00951 }
00952 break;
00953
00954 case Selection::Type_Body:
00955 {
00956
00957 PlanetarySystem* sats = sel.body()->getSatellites();
00958 if (sats != NULL)
00959 {
00960 Body* body = sats->find(name, false, i18n);
00961 if (body != NULL)
00962 return Selection(body);
00963 }
00964
00965
00966 Location* loc = sel.body()->findLocation(name, i18n);
00967 if (loc != NULL)
00968 return Selection(loc);
00969 }
00970 break;
00971
00972 case Selection::Type_Location:
00973
00974 break;
00975
00976 case Selection::Type_DeepSky:
00977
00978 break;
00979
00980 default:
00981 break;
00982 }
00983
00984 return Selection();
00985 }
00986
00987
00988
00989
00990
00991
00992 Selection Universe::findObjectInContext(const Selection& sel,
00993 const string& name,
00994 bool i18n) const
00995 {
00996 Body* contextBody = NULL;
00997
00998 switch (sel.getType())
00999 {
01000 case Selection::Type_Body:
01001 contextBody = sel.body();
01002 break;
01003
01004 case Selection::Type_Location:
01005 contextBody = sel.location()->getParentBody();
01006 break;
01007
01008 default:
01009 break;
01010 }
01011
01012
01013 SolarSystem* sys = getSolarSystem(sel);
01014 if (sys != NULL)
01015 {
01016 PlanetarySystem* planets = sys->getPlanets();
01017 if (planets != NULL)
01018 {
01019 Body* body = planets->find(name, true, i18n);
01020 if (body != NULL)
01021 return Selection(body);
01022 }
01023 }
01024
01025
01026 if (contextBody != NULL)
01027 {
01028 Location* loc = contextBody->findLocation(name, i18n);
01029 if (loc != NULL)
01030 return Selection(loc);
01031 }
01032
01033 return Selection();
01034 }
01035
01036
01037
01038
01039
01040
01041
01042
01043 Selection Universe::find(const string& s,
01044 Selection* contexts,
01045 int nContexts,
01046 bool i18n)
01047 {
01048 if (starCatalog != NULL)
01049 {
01050 Star* star = starCatalog->find(s);
01051 if (star != NULL)
01052 return Selection(star);
01053 }
01054
01055 if (dsoCatalog != NULL)
01056 {
01057 DeepSkyObject* dso = dsoCatalog->find(s);
01058 if (dso != NULL)
01059 return Selection(dso);
01060 }
01061
01062 for (int i=0; i<nContexts; ++i)
01063 {
01064 Selection sel = findObjectInContext(contexts[i], s, i18n);
01065 if (!sel.empty())
01066 return sel;
01067 }
01068
01069 return Selection();
01070 }
01071
01072
01073
01074
01075
01076
01077
01078
01079 Selection Universe::findPath(const string& s,
01080 Selection contexts[],
01081 int nContexts,
01082 bool i18n)
01083 {
01084 string::size_type pos = s.find('/', 0);
01085
01086
01087 if (pos == string::npos)
01088 return find(s, contexts, nContexts, i18n);
01089
01090
01091 string base(s, 0, pos);
01092
01093 Selection sel = find(base, contexts, nContexts, i18n);
01094
01095 while (!sel.empty() && pos != string::npos)
01096 {
01097 string::size_type nextPos = s.find('/', pos + 1);
01098 string::size_type len;
01099 if (nextPos == string::npos)
01100 len = s.size() - pos - 1;
01101 else
01102 len = nextPos - pos - 1;
01103 string name = string(s, pos + 1, len);
01104
01105 sel = findChildObject(sel, name, i18n);
01106
01107 pos = nextPos;
01108 }
01109
01110 return sel;
01111 }
01112
01113
01114 vector<string> Universe::getCompletion(const string& s,
01115 Selection* contexts,
01116 int nContexts,
01117 bool withLocations)
01118 {
01119 vector<string> completion;
01120
01121
01122 for (int i = 0; i < nContexts; i++)
01123 {
01124 if (withLocations && contexts[i].getType() == Selection::Type_Body)
01125 {
01126 vector<Location*>* locations = contexts[i].body()->getLocations();
01127 if (locations != NULL)
01128 {
01129 for (vector<Location*>::const_iterator iter = locations->begin();
01130 iter != locations->end(); iter++)
01131 {
01132 if (!UTF8StringCompare(s, (*iter)->getName(true), s.length()))
01133 completion.push_back((*iter)->getName(true));
01134 }
01135 }
01136 }
01137
01138 SolarSystem* sys = getSolarSystem(contexts[i]);
01139 if (sys != NULL)
01140 {
01141 PlanetarySystem* planets = sys->getPlanets();
01142 if (planets != NULL)
01143 {
01144 vector<string> bodies = planets->getCompletion(s);
01145 completion.insert(completion.end(),
01146 bodies.begin(), bodies.end());
01147 }
01148 }
01149 }
01150
01151
01152 if (dsoCatalog != NULL)
01153 {
01154 vector<string> dsos = dsoCatalog->getCompletion(s);
01155 completion.insert(completion.end(), dsos.begin(), dsos.end());
01156 }
01157
01158
01159 if (starCatalog != NULL)
01160 {
01161 vector<string> stars = starCatalog->getCompletion(s);
01162 completion.insert(completion.end(), stars.begin(), stars.end());
01163 }
01164
01165 return completion;
01166 }
01167
01168
01169 vector<string> Universe::getCompletionPath(const string& s,
01170 Selection* contexts,
01171 int nContexts,
01172 bool withLocations)
01173 {
01174 vector<string> completion;
01175 vector<string> locationCompletion;
01176 string::size_type pos = s.rfind('/', s.length());
01177
01178 if (pos == string::npos)
01179 return getCompletion(s, contexts, nContexts, withLocations);
01180
01181 string base(s, 0, pos);
01182 Selection sel = findPath(base, contexts, nContexts, true);
01183
01184 if (sel.empty())
01185 {
01186 cerr << "nothing found" << endl;
01187 return completion;
01188 }
01189
01190 if (sel.getType() == Selection::Type_DeepSky)
01191 {
01192 completion.push_back(dsoCatalog->getDSOName(sel.deepsky()));
01193 return completion;
01194 }
01195
01196 PlanetarySystem* worlds = NULL;
01197 if (sel.getType() == Selection::Type_Body)
01198 {cerr << "body found" << endl;
01199 worlds = sel.body()->getSatellites();
01200 vector<Location*>* locations = sel.body()->getLocations();
01201 if (locations != NULL && withLocations)
01202 {
01203 string search = s.substr(pos + 1);
01204 for (vector<Location*>::const_iterator iter = locations->begin();
01205 iter != locations->end(); iter++)
01206 {
01207 if (!UTF8StringCompare(search, (*iter)->getName(true),
01208 search.length()))
01209 {
01210 locationCompletion.push_back((*iter)->getName(true));
01211 }
01212 }
01213 }
01214 }
01215 else if (sel.getType() == Selection::Type_Star)
01216 {
01217 SolarSystem* ssys = getSolarSystem(sel.star());
01218 if (ssys != NULL)
01219 worlds = ssys->getPlanets();
01220 }
01221
01222 if (worlds != NULL)
01223 completion = worlds->getCompletion(s.substr(pos + 1), false);
01224
01225 completion.insert(completion.end(), locationCompletion.begin(), locationCompletion.end());
01226
01227 return completion;
01228 }
01229
01230
01231
01232
01233 SolarSystem* Universe::getNearestSolarSystem(const UniversalCoord& position) const
01234 {
01235 Point3f pos = (Point3f) position;
01236 Point3f lyPos(pos.x * 1.0e-6f, pos.y * 1.0e-6f, pos.z * 1.0e-6f);
01237 ClosestStarFinder closestFinder(1.0f);
01238 starCatalog->findCloseStars(closestFinder, lyPos, 1.0f);
01239 return getSolarSystem(closestFinder.closestStar);
01240 }
01241
01242
01243 void
01244 Universe::getNearStars(const UniversalCoord& position,
01245 float maxDistance,
01246 vector<const Star*>& nearStars) const
01247 {
01248 Point3f pos = (Point3f) position;
01249 Point3f lyPos(pos.x * 1.0e-6f, pos.y * 1.0e-6f, pos.z * 1.0e-6f);
01250 NearStarFinder finder(1.0f, nearStars);
01251 starCatalog->findCloseStars(finder, lyPos, maxDistance);
01252 }