Navigator.cpp   StelNavigator.cpp 
skipping to change at line 21 skipping to change at line 21
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, U SA. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, U SA.
*/ */
#include "StelApp.hpp" #include "StelApp.hpp"
#include "Navigator.hpp" #include "StelNavigator.hpp"
#include "StelUtils.hpp" #include "StelUtils.hpp"
#include "SolarSystem.hpp" #include "SolarSystem.hpp"
#include "Observer.hpp" #include "StelObserver.hpp"
#include "Planet.hpp" #include "Planet.hpp"
#include "StelObjectMgr.hpp" #include "StelObjectMgr.hpp"
#include "StelCore.hpp" #include "StelCore.hpp"
#include "LocationMgr.hpp" #include "StelLocationMgr.hpp"
#include "StelModuleMgr.hpp"
#include "StelMovementMgr.hpp"
#include <QSettings> #include <QSettings>
#include <QStringList> #include <QStringList>
#include <QDateTime> #include <QDateTime>
#include <QDebug> #include <QDebug>
// Init statics transfo matrices
// See vsop87.doc:
const Mat4d StelNavigator::matJ2000ToVsop87(Mat4d::xrotation(-23.4392803055
555555556*(M_PI/180)) * Mat4d::zrotation(0.0000275*(M_PI/180)));
const Mat4d StelNavigator::matVsop87ToJ2000(matJ2000ToVsop87.transpose());
/////////////////////////////////////////////////////////////////////////// ///// /////////////////////////////////////////////////////////////////////////// /////
Navigator::Navigator() : timeSpeed(JD_SECOND), JDay(0.), position(NULL) StelNavigator::StelNavigator() : timeSpeed(JD_SECOND), JDay(0.), position(N ULL)
{ {
localVision=Vec3d(1.,0.,0.); altAzVisionDirection=Vec3d(1.,0.,0.);
equVision=Vec3d(1.,0.,0.); earthEquVisionDirection=Vec3d(1.,0.,0.);
J2000EquVision=Vec3d(1.,0.,0.); // not correct yet... J2000EquVisionDirection=Vec3d(1.,0.,0.); // not correct yet...
viewingMode = ViewHorizon; // default viewingMode = ViewHorizon; // default
} }
Navigator::~Navigator() StelNavigator::~StelNavigator()
{ {
delete position; delete position;
position=NULL; position=NULL;
} }
const Planet *Navigator::getHomePlanet(void) const const Planet *StelNavigator::getHomePlanet(void) const
{ {
return position->getHomePlanet(); return position->getHomePlanet();
} }
void Navigator::init() void StelNavigator::init()
{ {
QSettings* conf = StelApp::getInstance().getSettings(); QSettings* conf = StelApp::getInstance().getSettings();
Q_ASSERT(conf); Q_ASSERT(conf);
defaultLocationID = conf->value("init_location/location","Paris, Par is, France").toString(); defaultLocationID = conf->value("init_location/location","Paris, Par is, France").toString();
position = new Observer(StelApp::getInstance().getLocationMgr().loca tionForSmallString(defaultLocationID)); position = new StelObserver(StelApp::getInstance().getLocationMgr(). locationForSmallString(defaultLocationID));
setTimeNow(); setTimeNow();
setLocalVision(Vec3f(1,1e-05,0.2)); setAltAzVisionDirection(Vec3f(1,1e-05,0.2));
// Compute transform matrices between coordinates systems // Compute transform matrices between coordinates systems
updateTransformMatrices(); updateTransformMatrices();
updateModelViewMat(); updateModelViewMat();
QString tmpstr = conf->value("navigation/viewing_mode", "horizon").t oString(); QString tmpstr = conf->value("navigation/viewing_mode", "horizon").t oString();
if (tmpstr=="equator") if (tmpstr=="equator")
setViewingMode(Navigator::ViewEquator); setViewingMode(StelNavigator::ViewEquator);
else else
{ {
if (tmpstr=="horizon") if (tmpstr=="horizon")
setViewingMode(Navigator::ViewHorizon); setViewingMode(StelNavigator::ViewHorizon);
else else
{ {
qDebug() << "ERROR : Unknown viewing mode type : " < < tmpstr; qDebug() << "ERROR : Unknown viewing mode type : " < < tmpstr;
assert(0); Q_ASSERT(0);
} }
} }
initViewPos = StelUtils::strToVec3f(conf->value("navigation/init_vie w_pos").toString()); initViewPos = StelUtils::strToVec3f(conf->value("navigation/init_vie w_pos").toString());
setLocalVision(initViewPos); setAltAzVisionDirection(initViewPos);
// we want to be able to handle the old style preset time, recorded as a double // we want to be able to handle the old style preset time, recorded as a double
// jday, or as a more human readable string... // jday, or as a more human readable string...
bool ok; bool ok;
QString presetTimeStr = conf->value("navigation/preset_sky_time",245 1545.).toString(); QString presetTimeStr = conf->value("navigation/preset_sky_time",245 1545.).toString();
presetSkyTime = presetTimeStr.toDouble(&ok); presetSkyTime = presetTimeStr.toDouble(&ok);
if (ok) if (ok)
qDebug() << "navigation/preset_sky_time is a double - treati ng as jday:" << presetSkyTime; qDebug() << "navigation/preset_sky_time is a double - treati ng as jday:" << presetSkyTime;
else else
{ {
skipping to change at line 112 skipping to change at line 119
if (startupTimeMode=="preset") if (startupTimeMode=="preset")
setJDay(presetSkyTime - StelUtils::getGMTShiftFromQT(presetS kyTime) * JD_HOUR); setJDay(presetSkyTime - StelUtils::getGMTShiftFromQT(presetS kyTime) * JD_HOUR);
else if (startupTimeMode=="today") else if (startupTimeMode=="today")
setTodayTime(getInitTodayTime()); setTodayTime(getInitTodayTime());
// we previously set the time to "now" already, so we don't need to // we previously set the time to "now" already, so we don't need to
// explicitly do it if the startupTimeMode=="now". // explicitly do it if the startupTimeMode=="now".
} }
// Set the location to use by default at startup // Set the location to use by default at startup
void Navigator::setDefaultLocationID(const QString& id) void StelNavigator::setDefaultLocationID(const QString& id)
{ {
defaultLocationID = id; defaultLocationID = id;
StelApp::getInstance().getLocationMgr().locationForSmallString(id); StelApp::getInstance().getLocationMgr().locationForSmallString(id);
QSettings* conf = StelApp::getInstance().getSettings(); QSettings* conf = StelApp::getInstance().getSettings();
Q_ASSERT(conf); Q_ASSERT(conf);
conf->setValue("init_location/location", id); conf->setValue("init_location/location", id);
} }
//! Set stellarium time to current real world time //! Set stellarium time to current real world time
void Navigator::setTimeNow() void StelNavigator::setTimeNow()
{ {
setJDay(StelUtils::getJDFromSystem()); setJDay(StelUtils::getJDFromSystem());
} }
void Navigator::setTodayTime(const QTime& target) void StelNavigator::setTodayTime(const QTime& target)
{ {
QDateTime dt = QDateTime::currentDateTime(); QDateTime dt = QDateTime::currentDateTime();
if (target.isValid()) if (target.isValid())
{ {
dt.setTime(target); dt.setTime(target);
// don't forget to adjust for timezone / daylight savings. // don't forget to adjust for timezone / daylight savings.
setJDay(StelUtils::qDateTimeToJd(dt)-(StelUtils::getGMTShift FromQT(StelUtils::getJDFromSystem()) * JD_HOUR)); setJDay(StelUtils::qDateTimeToJd(dt)-(StelUtils::getGMTShift FromQT(StelUtils::getJDFromSystem()) * JD_HOUR));
} }
else else
{ {
qWarning() << "WARNING - time passed to Navigator::setTodayT ime is not valid. The system time will be used." << target; qWarning() << "WARNING - time passed to StelNavigator::setTo dayTime is not valid. The system time will be used." << target;
setTimeNow(); setTimeNow();
} }
} }
//! Get whether the current stellarium time is the real world time //! Get whether the current stellarium time is the real world time
bool Navigator::getIsTimeNow(void) const bool StelNavigator::getIsTimeNow(void) const
{ {
// cache last time to prevent to much slow system call // cache last time to prevent to much slow system call
static double lastJD = getJDay(); static double lastJD = getJDay();
static bool previousResult = (fabs(getJDay()-StelUtils::getJDFromSys tem())<JD_SECOND); static bool previousResult = (fabs(getJDay()-StelUtils::getJDFromSys tem())<JD_SECOND);
if (fabs(lastJD-getJDay())>JD_SECOND/4) if (fabs(lastJD-getJDay())>JD_SECOND/4)
{ {
lastJD = getJDay(); lastJD = getJDay();
previousResult = (fabs(getJDay()-StelUtils::getJDFromSystem( ))<JD_SECOND); previousResult = (fabs(getJDay()-StelUtils::getJDFromSystem( ))<JD_SECOND);
} }
return previousResult; return previousResult;
} }
void Navigator::addSolarDays(double d) void StelNavigator::addSolarDays(double d)
{ {
setJDay(getJDay() + d); setJDay(getJDay() + d);
} }
void Navigator::addSiderealDays(double d) void StelNavigator::addSiderealDays(double d)
{ {
const Planet* home = position->getHomePlanet(); const Planet* home = position->getHomePlanet();
if (home->getEnglishName() != "Solar System Observer") if (home->getEnglishName() != "Solar System StelObserver")
d *= home->getSiderealDay(); d *= home->getSiderealDay();
setJDay(getJDay() + d); setJDay(getJDay() + d);
} }
void Navigator::moveObserverToSelected(void) void StelNavigator::moveObserverToSelected(void)
{ {
if (StelApp::getInstance().getStelObjectMgr().getWasSelected()) if (StelApp::getInstance().getStelObjectMgr().getWasSelected())
{ {
Planet* pl = dynamic_cast<Planet*>(StelApp::getInstance().ge tStelObjectMgr().getSelectedObject()[0].get()); Planet* pl = dynamic_cast<Planet*>(StelApp::getInstance().ge tStelObjectMgr().getSelectedObject()[0].get());
if (pl) if (pl)
{ {
// We need to move to the selected planet. Try to ge nerate a location from the current one // We need to move to the selected planet. Try to ge nerate a location from the current one
Location loc = getCurrentLocation(); StelLocation loc = getCurrentLocation();
loc.planetName = pl->getEnglishName(); loc.planetName = pl->getEnglishName();
loc.name = "-"; loc.name = "-";
loc.state = ""; loc.state = "";
moveObserverTo(loc); moveObserverTo(loc);
} }
} }
StelMovementMgr* mmgr = (StelMovementMgr*)GETSTELMODULE("StelMovemen
tMgr");
Q_ASSERT(mmgr);
mmgr->setFlagTracking(false);
} }
// Get the informations on the current location // Get the informations on the current location
const Location& Navigator::getCurrentLocation() const const StelLocation& StelNavigator::getCurrentLocation() const
{ {
return position->getCurrentLocation(); return position->getCurrentLocation();
} }
// Smoothly move the observer to the given location // Smoothly move the observer to the given location
void Navigator::moveObserverTo(const Location& target, double duration, dou ble durationIfPlanetChange) void StelNavigator::moveObserverTo(const StelLocation& target, double durat ion, double durationIfPlanetChange)
{ {
double d = (getCurrentLocation().planetName==target.planetName) ? du ration : durationIfPlanetChange; double d = (getCurrentLocation().planetName==target.planetName) ? du ration : durationIfPlanetChange;
if (d>0.) if (d>0.)
{ {
SpaceShipObserver* newObs = new SpaceShipObserver(getCurrent Location(), target, d); SpaceShipObserver* newObs = new SpaceShipObserver(getCurrent Location(), target, d);
delete position; delete position;
position = newObs; position = newObs;
} }
else else
{ {
delete position; delete position;
position = new Observer(target); position = new StelObserver(target);
} }
} }
// Get the sideral time shifted by the observer longitude // Get the sideral time shifted by the observer longitude
double Navigator::getLocalSideralTime() const double StelNavigator::getLocalSideralTime() const
{ {
return (position->getHomePlanet()->getSiderealTime(JDay)+position->g etCurrentLocation().longitude)*M_PI/180.; return (position->getHomePlanet()->getSiderealTime(JDay)+position->g etCurrentLocation().longitude)*M_PI/180.;
} }
void Navigator::setInitViewDirectionToCurrent(void) void StelNavigator::setInitViewDirectionToCurrent(void)
{ {
initViewPos = localVision; initViewPos = altAzVisionDirection;
QString dirStr = QString("%1,%2,%3").arg(localVision[0]).arg(localVi QString dirStr = QString("%1,%2,%3").arg(altAzVisionDirection[0]).ar
sion[1]).arg(localVision[2]); g(altAzVisionDirection[1]).arg(altAzVisionDirection[2]);
StelApp::getInstance().getSettings()->setValue("navigation/init_view _pos", dirStr); StelApp::getInstance().getSettings()->setValue("navigation/init_view _pos", dirStr);
} }
//! Increase the time speed //! Increase the time speed
void Navigator::increaseTimeSpeed() void StelNavigator::increaseTimeSpeed()
{ {
double s = getTimeSpeed(); double s = getTimeRate();
if (s>=JD_SECOND) s*=10.; if (s>=JD_SECOND) s*=10.;
else if (s<-JD_SECOND) s/=10.; else if (s<-JD_SECOND) s/=10.;
else if (s>=0. && s<JD_SECOND) s=JD_SECOND; else if (s>=0. && s<JD_SECOND) s=JD_SECOND;
else if (s>=-JD_SECOND && s<0.) s=0.; else if (s>=-JD_SECOND && s<0.) s=0.;
setTimeSpeed(s); setTimeRate(s);
} }
//! Decrease the time speed //! Decrease the time speed
void Navigator::decreaseTimeSpeed() void StelNavigator::decreaseTimeSpeed()
{ {
double s = getTimeSpeed(); double s = getTimeRate();
if (s>JD_SECOND) s/=10.; if (s>JD_SECOND) s/=10.;
else if (s<=-JD_SECOND) s*=10.; else if (s<=-JD_SECOND) s*=10.;
else if (s>-JD_SECOND && s<=0.) s=-JD_SECOND; else if (s>-JD_SECOND && s<=0.) s=-JD_SECOND;
else if (s>0. && s<=JD_SECOND) s=0.; else if (s>0. && s<=JD_SECOND) s=0.;
setTimeSpeed(s); setTimeRate(s);
} }
/////////////////////////////////////////////////////////////////////////// ///// /////////////////////////////////////////////////////////////////////////// /////
void Navigator::setLocalVision(const Vec3d& _pos) void StelNavigator::setAltAzVisionDirection(const Vec3d& _pos)
{ {
localVision = _pos; altAzVisionDirection = _pos;
equVision=localToEarthEqu(localVision); earthEquVisionDirection=altAzToEquinoxEqu(altAzVisionDirection);
J2000EquVision = matEarthEquToJ2000*equVision; J2000EquVisionDirection = matEquinoxEquToJ2000*earthEquVisionDirecti
on;
updateModelViewMat();
} }
/////////////////////////////////////////////////////////////////////////// ///// /////////////////////////////////////////////////////////////////////////// /////
void Navigator::setEquVision(const Vec3d& _pos) void StelNavigator::setEquinoxEquVisionDirection(const Vec3d& _pos)
{ {
equVision = _pos; earthEquVisionDirection = _pos;
J2000EquVision = matEarthEquToJ2000*equVision; J2000EquVisionDirection = matEquinoxEquToJ2000*earthEquVisionDirecti
localVision = earthEquToLocal(equVision); on;
altAzVisionDirection = equinoxEquToAltAz(earthEquVisionDirection);
updateModelViewMat();
} }
/////////////////////////////////////////////////////////////////////////// ///// /////////////////////////////////////////////////////////////////////////// /////
void Navigator::setJ2000EquVision(const Vec3d& _pos) void StelNavigator::setJ2000EquVisionDirection(const Vec3d& _pos)
{ {
J2000EquVision = _pos; J2000EquVisionDirection = _pos;
equVision = matJ2000ToEarthEqu*J2000EquVision; earthEquVisionDirection = matJ2000ToEquinoxEqu*J2000EquVisionDirecti
localVision = earthEquToLocal(equVision); on;
altAzVisionDirection = equinoxEquToAltAz(earthEquVisionDirection);
updateModelViewMat();
} }
/////////////////////////////////////////////////////////////////////////// ///// /////////////////////////////////////////////////////////////////////////// /////
// Increment time // Increment time
void Navigator::updateTime(double deltaTime) void StelNavigator::updateTime(double deltaTime)
{ {
JDay+=timeSpeed*deltaTime; JDay+=timeSpeed*deltaTime;
// Fix time limits to -100000 to +100000 to prevent bugs // Fix time limits to -100000 to +100000 to prevent bugs
if (JDay>38245309.499988) JDay = 38245309.499988; if (JDay>38245309.499988) JDay = 38245309.499988;
if (JDay<-34803211.500012) JDay = -34803211.500012; if (JDay<-34803211.500012) JDay = -34803211.500012;
if (position->isObserverLifeOver()) if (position->isObserverLifeOver())
{ {
// Unselect if the new home planet is the previously selecte d object // Unselect if the new home planet is the previously selecte d object
StelObjectMgr &objmgr(StelApp::getInstance().getStelObjectMg r()); StelObjectMgr &objmgr(StelApp::getInstance().getStelObjectMg r());
if (objmgr.getWasSelected() && objmgr.getSelectedObject()[0] .get()==position->getHomePlanet()) if (objmgr.getWasSelected() && objmgr.getSelectedObject()[0] .get()==position->getHomePlanet())
{ {
objmgr.unSelect(); objmgr.unSelect();
} }
Observer* newObs = position->getNextObserver(); StelObserver* newObs = position->getNextObserver();
delete position; delete position;
position = newObs; position = newObs;
} }
position->update(deltaTime); position->update(deltaTime);
} }
/////////////////////////////////////////////////////////////////////////// ///// /////////////////////////////////////////////////////////////////////////// /////
// The non optimized (more clear version is available on the CVS : before d ate 25/07/2003) // The non optimized (more clear version is available on the CVS : before d ate 25/07/2003)
// see vsop87.doc: void StelNavigator::updateTransformMatrices(void)
const Mat4d matJ2000ToVsop87(Mat4d::xrotation(-23.4392803055555555556*(M_PI
/180)) * Mat4d::zrotation(0.0000275*(M_PI/180)));
const Mat4d matVsop87ToJ2000(matJ2000ToVsop87.transpose());
void Navigator::updateTransformMatrices(void)
{ {
matLocalToEarthEqu = position->getRotLocalToEquatorial(JDay); matAltAzToEquinoxEqu = position->getRotAltAzToEquatorial(JDay);
matEarthEquToLocal = matLocalToEarthEqu.transpose(); matEquinoxEquToAltAz = matAltAzToEquinoxEqu.transpose();
matEarthEquToJ2000 = matVsop87ToJ2000 * position->getRotEquatorialTo matEquinoxEquToJ2000 = matVsop87ToJ2000 * position->getRotEquatorial
Vsop87(); ToVsop87();
matJ2000ToEarthEqu = matEarthEquToJ2000.transpose(); matJ2000ToEquinoxEqu = matEquinoxEquToJ2000.transpose();
matJ2000ToLocal = matEarthEquToLocal*matJ2000ToEarthEqu; matJ2000ToAltAz = matEquinoxEquToAltAz*matJ2000ToEquinoxEqu;
matHelioToEarthEqu = matJ2000ToEarthEqu * matVsop87ToJ2000 * Mat4d:: translation(-position->getCenterVsop87Pos()); matHeliocentricEclipticToEquinoxEqu = matJ2000ToEquinoxEqu * matVsop 87ToJ2000 * Mat4d::translation(-position->getCenterVsop87Pos());
// These two next have to take into account the position of the obse rver on the earth // These two next have to take into account the position of the obse rver on the earth
Mat4d tmp = matJ2000ToVsop87 * matEarthEquToJ2000 * matLocalToEarthE qu; Mat4d tmp = matJ2000ToVsop87 * matEquinoxEquToJ2000 * matAltAzToEqui noxEqu;
matLocalToHelio = Mat4d::translation(position->getCenterVsop87Pos() ) * tmp * matAltAzToHeliocentricEcliptic = Mat4d::translation(position->getCe nterVsop87Pos()) * tmp *
Mat4d::translation(Vec3d(0.,0., position->getD istanceFromCenter())); Mat4d::translation(Vec3d(0.,0., position->getD istanceFromCenter()));
matHelioToLocal = Mat4d::translation(Vec3d(0.,0.,-position->getDist anceFromCenter())) * tmp.transpose() * matHeliocentricEclipticToAltAz = Mat4d::translation(Vec3d(0.,0.,-po sition->getDistanceFromCenter())) * tmp.transpose() *
Mat4d::translation(-position->getCenterVsop87P os()); Mat4d::translation(-position->getCenterVsop87P os());
} }
void Navigator::setStartupTimeMode(const QString& s) void StelNavigator::setStartupTimeMode(const QString& s)
{ {
startupTimeMode = s; startupTimeMode = s;
} }
/////////////////////////////////////////////////////////////////////////// ///// /////////////////////////////////////////////////////////////////////////// /////
// Update the modelview matrices // Update the modelview matrices
void Navigator::updateModelViewMat(void) void StelNavigator::updateModelViewMat(void)
{ {
Vec3d f; Vec3d f;
if( viewingMode == ViewEquator) if( viewingMode == ViewEquator)
{ {
// view will use equatorial coordinates, so that north is al ways up // view will use equatorial coordinates, so that north is al ways up
f = equVision; f = earthEquVisionDirection;
} }
else else
{ {
// view will correct for horizon (always down) // view will correct for horizon (always down)
f = localVision; f = altAzVisionDirection;
} }
f.normalize(); f.normalize();
Vec3d s(f[1],-f[0],0.); Vec3d s(f[1],-f[0],0.);
if( viewingMode == ViewEquator) if( viewingMode == ViewEquator)
{ {
// convert everything back to local coord // convert everything back to local coord
f = localVision; f = altAzVisionDirection;
f.normalize(); f.normalize();
s = earthEquToLocal( s ); s = equinoxEquToAltAz( s );
} }
Vec3d u(s^f); Vec3d u(s^f);
s.normalize(); s.normalize();
u.normalize(); u.normalize();
matLocalToEye.set(s[0],u[0],-f[0],0., matAltAzModelView.set(s[0],u[0],-f[0],0.,
s[1],u[1],-f[1],0., s[1],u[1],-f[1],0.,
s[2],u[2],-f[2],0., s[2],u[2],-f[2],0.,
0.,0.,0.,1.); 0.,0.,0.,1.);
matEarthEquToEye = matLocalToEye*matEarthEquToLocal;
matHelioToEye = matLocalToEye*matHelioToLocal;
matJ2000ToEye = matEarthEquToEye*matJ2000ToEarthEqu;
} }
/////////////////////////////////////////////////////////////////////////// ///// /////////////////////////////////////////////////////////////////////////// /////
// Return the observer heliocentric position // Return the observer heliocentric position
Vec3d Navigator::getObserverHelioPos(void) const Vec3d StelNavigator::getObserverHeliocentricEclipticPos(void) const
{ {
static const Vec3d v(0.,0.,0.); static const Vec3d v(0);
return matLocalToHelio*v; return matAltAzToHeliocentricEcliptic*v;
} }
void Navigator::setPresetSkyTime(QDateTime dt) void StelNavigator::setPresetSkyTime(QDateTime dt)
{ {
setPresetSkyTime(StelUtils::qDateTimeToJd(dt)); setPresetSkyTime(StelUtils::qDateTimeToJd(dt));
} }
/////////////////////////////////////////////////////////////////////////// ///// /////////////////////////////////////////////////////////////////////////// /////
// Set type of viewing mode (align with horizon or equatorial coordinates) // Set type of viewing mode (align with horizon or equatorial coordinates)
void Navigator::setViewingMode(ViewingModeType viewMode) void StelNavigator::setViewingMode(ViewingModeType viewMode)
{ {
viewingMode = viewMode; viewingMode = viewMode;
// TODO: include some nice smoothing function trigger here to rotate between // TODO: include some nice smoothing function trigger here to rotate between
// the two modes // the two modes
} }
 End of changes. 65 change blocks. 
87 lines changed or deleted 95 lines changed or added

This html diff was produced by rfcdiff 1.41. The latest version is available from http://tools.ietf.org/tools/rfcdiff/