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

samporbit.cpp

Go to the documentation of this file.
00001 // samporbit.cpp
00002 //
00003 // Copyright (C) 2002, Chris Laurel <claurel@shatters.net>
00004 //
00005 // Implementation of the VSOP87 theory for the the orbits of the
00006 // major planets.  The data is a truncated version of the complete
00007 // data set available here:
00008 // ftp://ftp.bdl.fr/pub/ephem/planets/vsop87/
00009 //
00010 // This program is free software; you can redistribute it and/or
00011 // modify it under the terms of the GNU General Public License
00012 // as published by the Free Software Foundation; either version 2
00013 // of the License, or (at your option) any later version.
00014 
00015 #include <cmath>
00016 #include <string>
00017 #include <algorithm>
00018 #include <vector>
00019 #include <iostream>
00020 #include <fstream>
00021 #include <celmath/mathlib.h>
00022 #include <celengine/astro.h>
00023 #include <celengine/orbit.h>
00024 
00025 using namespace std;
00026 
00027 // Trajectories are sampled adaptively for rendering.  MaxSampleInterval
00028 // is the maximum time (in days) between samples.  The threshold angle
00029 // is the maximum angle allowed between path segments.
00030 static const double MinSampleInterval = 1.0 / 1440.0; // one minute
00031 static const double MaxSampleInterval = 50.0;
00032 static const double SampleThresholdAngle = 2.0;
00033 
00034 struct Sample
00035 {
00036     double t;
00037     float x, y, z;
00038 };
00039 
00040 bool operator<(const Sample& a, const Sample& b)
00041 {
00042     return a.t < b.t;
00043 }
00044 
00045 class SampledOrbit : public CachingOrbit
00046 {
00047 public:
00048     SampledOrbit();
00049     virtual ~SampledOrbit();
00050 
00051     void addSample(double t, double x, double y, double z);
00052     void setPeriod();
00053 
00054     double getPeriod() const;
00055     double getBoundingRadius() const;
00056     Point3d computePosition(double jd) const;
00057 
00058     bool isPeriodic() const;
00059     void getValidRange(double& begin, double& end) const;
00060 
00061     virtual void sample(double, double, int, OrbitSampleProc& proc) const;
00062 
00063 private:
00064     vector<Sample> samples;
00065     double boundingRadius;
00066     double period;
00067     mutable int lastSample;
00068 
00069     enum InterpolationType
00070     {
00071         Linear = 0,
00072         Cubic = 1,
00073     };
00074 
00075     InterpolationType interpolation;
00076 };
00077 
00078 
00079 SampledOrbit::SampledOrbit() :
00080     boundingRadius(0.0),
00081     period(1.0),
00082     lastSample(0),
00083     interpolation(Cubic)
00084 {
00085 }
00086 
00087 
00088 SampledOrbit::~SampledOrbit()
00089 {
00090 }
00091 
00092 
00093 void SampledOrbit::addSample(double t, double x, double y, double z)
00094 {
00095     double r = sqrt(x * x + y * y + z * z);
00096     if (r > boundingRadius)
00097         boundingRadius = r;
00098 
00099     Sample samp;
00100     samp.x = (float) x;
00101     samp.y = (float) y;
00102     samp.z = (float) z;
00103     samp.t = t;
00104     samples.insert(samples.end(), samp);
00105 }
00106 
00107 double SampledOrbit::getPeriod() const
00108 {
00109     return samples[samples.size() - 1].t - samples[0].t;
00110 }
00111 
00112 
00113 bool SampledOrbit::isPeriodic() const
00114 {
00115     return false;
00116 }
00117 
00118 
00119 void SampledOrbit::getValidRange(double& begin, double& end) const
00120 {
00121     begin = samples[0].t;
00122     end = samples[samples.size() - 1].t;
00123 }
00124 
00125 
00126 double SampledOrbit::getBoundingRadius() const
00127 {
00128     return boundingRadius;
00129 }
00130 
00131 
00132 static Point3d cubicInterpolate(Point3d& p0, Vec3d& v0,
00133                                 Point3d& p1, Vec3d& v1,
00134                                 double t)
00135 {
00136     return p0 + (((2.0 * (p0 - p1) + v1 + v0) * (t * t * t)) +
00137                  ((3.0 * (p1 - p0) - 2.0 * v0 - v1) * (t * t)) +
00138                  (v0 * t));
00139 }
00140 
00141 
00142 Point3d SampledOrbit::computePosition(double jd) const
00143 {
00144     Point3d pos;
00145     if (samples.size() == 0)
00146     {
00147         pos = Point3d(0.0, 0.0, 0.0);
00148     }
00149     else if (samples.size() == 1)
00150     {
00151         pos = Point3d(samples[0].x, samples[1].y, samples[2].z);
00152     }
00153     else
00154     {
00155         Sample samp;
00156         samp.t = jd;
00157         int n = lastSample;
00158 
00159         if (n < 1 || jd < samples[n - 1].t || jd > samples[n].t)
00160         {
00161             vector<Sample>::const_iterator iter = lower_bound(samples.begin(),
00162                                                               samples.end(),
00163                                                               samp);
00164             n = iter - samples.begin();
00165             lastSample = n;
00166         }
00167 
00168         if (n == 0)
00169         {
00170             pos = Point3d(samples[n].x, samples[n].y, samples[n].z);
00171         }
00172         else if (n < (int) samples.size())
00173         {
00174             if (interpolation == Linear)
00175             {
00176                 Sample s0 = samples[n - 1];
00177                 Sample s1 = samples[n];
00178 
00179                 double t = (jd - s0.t) / (s1.t - s0.t);
00180                 pos = Point3d(Mathd::lerp(t, (double) s0.x, (double) s1.x),
00181                               Mathd::lerp(t, (double) s0.y, (double) s1.y),
00182                               Mathd::lerp(t, (double) s0.z, (double) s1.z));
00183             }
00184             else if (interpolation == Cubic)
00185             {
00186                 Sample s0, s1, s2, s3;
00187                 if (n > 1)
00188                     s0 = samples[n - 2];
00189                 else
00190                     s0 = samples[n - 1];
00191                 s1 = samples[n - 1];
00192                 s2 = samples[n];
00193                 if (n < (int) samples.size() - 1)
00194                     s3 = samples[n + 1];
00195                 else
00196                     s3 = samples[n];
00197 
00198                 double t = (jd - s1.t) / (s2.t - s1.t);
00199                 Point3d p0(s1.x, s1.y, s1.z);
00200                 Point3d p1(s2.x, s2.y, s2.z);
00201 
00202                 Vec3d v0((double) s2.x - (double) s0.x,
00203                          (double) s2.y - (double) s0.y,
00204                          (double) s2.z - (double) s0.z);
00205                 Vec3d v1((double) s3.x - (double) s1.x,
00206                          (double) s3.y - (double) s1.y,
00207                          (double) s3.z - (double) s1.z);
00208                 v0 *= (s2.t - s1.t) / (s2.t - s0.t);
00209                 v1 *= (s2.t - s1.t) / (s3.t - s1.t);
00210 
00211                 pos = cubicInterpolate(p0, v0, p1, v1, t);
00212             }
00213             else
00214             {
00215                 // Unknown interpolation type
00216                 pos = Point3d(0.0, 0.0, 0.0);
00217             }
00218         }
00219         else
00220         {
00221             pos = Point3d(samples[n - 1].x, samples[n - 1].y, samples[n - 1].z);
00222         }
00223     }
00224 
00225     return Point3d(pos.x, pos.z, -pos.y);
00226 }
00227 
00228 
00229 void SampledOrbit::sample(double start, double t, int nSamples,
00230                           OrbitSampleProc& proc) const
00231 {
00232     double cosThresholdAngle = cos(degToRad(SampleThresholdAngle));
00233     double dt = MinSampleInterval;
00234     double end = start + t;
00235     double current = start;
00236 
00237     proc.sample(current, positionAtTime(current));
00238 
00239     while (current < end)
00240     {
00241         double dt2 = dt;
00242 
00243         Point3d goodpt;
00244         double gooddt = dt;
00245         Point3d pos0 = positionAtTime(current);
00246         goodpt = positionAtTime(current + dt2);
00247         while (1)
00248         {
00249             Point3d pos1 = positionAtTime(current + dt2);
00250             Point3d pos2 = positionAtTime(current + dt2 * 2.0);
00251             Vec3d vec1 = pos1 - pos0;
00252             Vec3d vec2 = pos2 - pos0;
00253 
00254             vec1.normalize();
00255             vec2.normalize();
00256             double dot = vec1 * vec2;
00257 
00258             if (dot > 1.0)
00259                 dot = 1.0;
00260             else if (dot < -1.0)
00261                 dot = -1.0;
00262 
00263             if (dot > cosThresholdAngle && dt2 < MaxSampleInterval)
00264             {
00265                 gooddt = dt2;
00266                 goodpt = pos1;
00267                 dt2 *= 2.0;
00268             }
00269             else
00270             {
00271                 proc.sample(current + gooddt, goodpt);
00272                 break;
00273             }
00274         }
00275         current += gooddt;
00276     }
00277 }
00278 
00279 
00280 Orbit* LoadSampledOrbit(const string& filename)
00281 {
00282     ifstream in(filename.c_str());
00283     if (!in.good())
00284         return NULL;
00285 
00286     SampledOrbit* orbit = new SampledOrbit();
00287     int nSamples = 0;
00288     while (in.good())
00289     {
00290         double t, x, y, z;
00291         in >> t;
00292         in >> x;
00293         in >> y;
00294         in >> z;
00295 
00296         double jd = t;
00297 
00298         // A bit of a hack . . .  assume that a time >= 1000000 is a Julian
00299         // Day, and anything else is a year + fraction.  This is just here
00300         // for backward compatibility.
00301         if (jd < 1000000.0)
00302         {
00303             int year = (int) t;
00304             double frac = t - year;
00305             // Not quite correct--doesn't account for leap years
00306             jd = (double) astro::Date(year, 1, 1) + 365.0 * frac;
00307         }
00308 
00309         orbit->addSample(jd, x, y, z);
00310         nSamples++;
00311     }
00312 
00313     return orbit;
00314 }

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