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

universe.cpp

Go to the documentation of this file.
00001 // universe.cpp
00002 // 
00003 // Copyright (C) 2001, Chris Laurel <claurel@shatters.net>
00004 //
00005 // A container for catalogs of galaxies, stars, and planets.
00006 //
00007 // This program is free software; you can redistribute it and/or
00008 // modify it under the terms of the GNU General Public License
00009 // as published by the Free Software Foundation; either version 2
00010 // of the License, or (at your option) any later version.
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     /*boundaries(NULL),*/
00033     markers(NULL)
00034 {
00035     markers = new MarkerList();
00036 }
00037 
00038 Universe::~Universe()
00039 {
00040     delete markers;
00041     // TODO: Clean up!
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 // Return the planetary system of a star, or NULL if it has no planets.
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 // A more general version of the method above--return the solar system
00114 // that contains an object, or NULL if there is no solar sytstem.
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 // Create a new solar system for a star and return a pointer to it; if it
00146 // already has a solar system, just return a pointer to the existing one.
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             // Handle the case when the object is already marked.  If the
00180             // priority is higher than the existing marker, replace it.
00181             // Otherwise, do nothing.
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     // Reject invisible bodies and bodies that don't exist at the current time
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     // Check the apparent radius of the orbit against our tolerance factor.
00316     // This check exists to make sure than when picking a distant, we select
00317     // the planet rather than one of its satellites.
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 // Perform an intersection test between the pick ray and a body
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     // Test for intersection with the bounding sphere
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             // There's no mesh, so the object is an ellipsoid.  If it's
00359             // oblate, do a ray intersection test to see if the object was
00360             // picked.  Otherwise, the object is spherical and we've already
00361             // done all the work we need to.
00362             if (body->getOblateness() != 0.0f)
00363             {
00364                 // Oblate sphere; use ray ellipsoid intersection calculation
00365                 Vec3d ellipsoidAxes(radius,
00366                                     radius * (1.0 - body->getOblateness()),
00367                                     radius);
00368                 // Transform rotate the pick ray into object coordinates
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             // Transform rotate the pick ray into object coordinates
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             // The mesh vertices are normalized, then multiplied by a scale
00388             // factor.  Thus, the ray needs to be multiplied by the inverse of
00389             // the mesh scale factor.
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         // Make also sure that the pickRay does not intersect the body in the
00404         // opposite hemisphere! Hence, need again the "bodyMiss" angle
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     // Transform the pick ray origin into astrocentric coordinates
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     // First see if there's a planet|moon that the pick ray intersects.
00456     // Select the closest planet|moon intersected.
00457 
00458     solarSystem.getPlanets()->traverse(ExactPlanetPickTraversal,
00459                                        (void*) &pickInfo);
00460 
00461     if (pickInfo.closestBody != NULL)
00462     {
00463         // Retain that body
00464         Body* closestBody = pickInfo.closestBody;
00465 
00466         // Check if there is a satellite in front of the primary body that is
00467         // sufficiently close to the pickRay 
00468 
00469         solarSystem.getPlanets()->traverse(ApproxPlanetPickTraversal,
00470                                            (void*) &pickInfo);
00471         if (pickInfo.closestBody == closestBody)
00472           return  Selection(closestBody); 
00473         // Nothing else around, select the body and return
00474 
00475         // Are we close enough to the satellite and is it in front of the body?
00476         if ((pickInfo.sinAngle2Closest <= sinTol2) && 
00477             (pickInfo.closestDistance > pickInfo.closestApproxDistance)) 
00478         return Selection(pickInfo.closestBody);
00479             // Yes, select the satellite
00480         else
00481             return  Selection(closestBody); 
00482            //  No, select the primary body 
00483     }
00484 
00485     // If no planet was intersected by the pick ray, choose the planet|moon
00486     // with the smallest angular separation from the pick ray.  Very distant
00487     // planets are likley to fail the intersection test even if the user
00488     // clicks on a pixel where the planet's disc has been rendered--in order
00489     // to make distant planets visible on the screen at all, their apparent
00490     // size has to be greater than their actual disc size.
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 // StarPicker is a callback class for StarDatabase::findVisibleStars
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     // Stars with orbits need special handling
00540     float orbitalRadius = star.getOrbitalRadius();
00541     if (orbitalRadius != 0.0f)
00542     {
00543         float distance;
00544 
00545         // Check for an intersection with orbital bounding sphere; if there's
00546         // no intersection, then just use normal calculation.  We actually test
00547         // intersection with a larger sphere to make sure we don't miss a star
00548         // right on the edge of the sphere.
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 = &star;
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 = &star;
00633                 closestDistance = starDir.length();
00634                 sinAngle2Closest = ANGULAR_RES; 
00635                 // An exact hit--set the angle to "zero"
00636             }
00637         }
00638     }
00639     else
00640     {
00641         // We don't have an exact hit; check to see if we're close enough
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 = &star;
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     // Use a high precision pick test for any stars that are close to the
00672     // observer.  If this test fails, use a low precision pick test for stars
00673     // which are further away.  All this work is necessary because the low
00674     // precision pick test isn't reliable close to a star and the high
00675     // precision test isn't nearly fast enough to use on our database of
00676     // over 100k stars.
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     // Find visible stars expects an orientation, but we just have a direction
00683     // vector.  Convert the direction vector into an orientation by computing
00684     // the rotation required to map (0, 0, -1) to the direction.
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         // Don't select the object the observer is currently in:
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 // Search by name for an immediate child of the specified object.
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             // First, search for a satellite
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             // If a satellite wasn't found, check this object's locations
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         // Locations have no children
00974         break;
00975 
00976     case Selection::Type_DeepSky:
00977         // Deep sky objects have no children
00978         break;
00979 
00980     default:
00981         break;
00982     }
00983 
00984     return Selection();
00985 }
00986 
00987 
00988 // Search for a name within an object's context.  For stars, planets (bodies),
00989 // and locations, the context includes all bodies in the associated solar
00990 // system.  For locations and planets, the context additionally includes
00991 // sibling or child locations, respectively.
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     // First, search for bodies . . .
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     // . . . and then locations.
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 // Select an object by name, with the following priority:
01038 //   1. Try to look up the name in the star catalog
01039 //   2. Search the deep sky catalog for a matching name.
01040 //   3. Check the solar systems for planet names; we don't make any decisions
01041 //      about which solar systems are relevant, and let the caller pass them
01042 //      to us to search.
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 // Find an object from a path, for example Sol/Earth/Moon or Upsilon And/b
01074 // Currently, 'absolute' paths starting with a / are not supported nor are
01075 // paths that contain galaxies.  The caller may pass in a list of solar systems
01076 // to search for objects--this is roughly analgous to the PATH environment
01077 // variable in Unix and Windows.  Typically, the solar system will be one
01078 // in which the user is currently located.
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     // No delimiter found--just do a normal find.
01087     if (pos == string::npos)
01088         return find(s, contexts, nContexts, i18n);
01089 
01090     // Find the base object
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     // Solar bodies first:
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     // Deep sky objects:
01152     if (dsoCatalog != NULL)
01153         {
01154         vector<string> dsos  = dsoCatalog->getCompletion(s);
01155         completion.insert(completion.end(), dsos.begin(), dsos.end());
01156     }
01157 
01158     // and finally stars;
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 // Return the closest solar system to position, or NULL if there are no planets
01232 // with in one light year.
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 }

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