00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
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
00028
00029
00030 static const double MinSampleInterval = 1.0 / 1440.0;
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
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
00299
00300
00301 if (jd < 1000000.0)
00302 {
00303 int year = (int) t;
00304 double frac = t - year;
00305
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 }