celestia/src/celengine/univcoord.h

181 lines
5.9 KiB
C++

// univcoord.h
//
// Copyright (C) 2001-2009, the Celestia Development Team
// Original version by Chris Laurel <claurel@gmail.com>
//
// Universal coordinate is a high-precision fixed point coordinate for
// locating objects in 3D space on scales ranging from millimeters to
// thousands of light years.
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
#pragma once
#include <Eigen/Core>
#include <celutil/bigfix.h>
#include "astro.h"
class UniversalCoord
{
public:
UniversalCoord()
{
}
UniversalCoord(BigFix _x, BigFix _y, BigFix _z) :
x(_x), y(_y), z(_z)
{
}
UniversalCoord(double _x, double _y, double _z) :
x(_x), y(_y), z(_z)
{
}
explicit UniversalCoord(const Eigen::Vector3d& v) :
x(v.x()), y(v.y()), z(v.z())
{
}
friend UniversalCoord operator+(const UniversalCoord&, const UniversalCoord&);
friend UniversalCoord operator-(const UniversalCoord&, const UniversalCoord&);
/** Compute a universal coordinate that is the sum of this coordinate and
* an offset in kilometers.
*/
UniversalCoord offsetKm(const Eigen::Vector3d& v)
{
Eigen::Vector3d vUly = v * astro::kilometersToMicroLightYears(1.0);
return *this + UniversalCoord(vUly);
}
/** Compute a universal coordinate that is the sum of this coordinate and
* an offset in micro-light years.
*
* This method is only here to help in porting older code; it shouldn't be
* necessary to use it in new code, where the use of the rather the rather
* obscure unit micro-light year isn't necessary.
*/
UniversalCoord offsetUly(const Eigen::Vector3d& vUly)
{
return *this + UniversalCoord(vUly);
}
/** Get the offset in kilometers of this coordinate from another coordinate.
* The result is double precision, calculated as (this - uc) * scale, where
* scale is a factor that converts from Celestia's internal units to kilometers.
*/
Eigen::Vector3d offsetFromKm(const UniversalCoord& uc) const
{
return Eigen::Vector3d(static_cast<double>(x - uc.x),
static_cast<double>(y - uc.y),
static_cast<double>(z - uc.z)) * astro::microLightYearsToKilometers(1.0);
}
/** Get the offset in light years of this coordinate from a point (also with
* units of light years.) The difference is calculated at high precision and
* the reduced to single precision.
*/
Eigen::Vector3f offsetFromLy(const Eigen::Vector3f& v) const
{
Eigen::Vector3f vUly = v * 1.0e6f;
Eigen::Vector3f offsetUly(static_cast<float>(x - BigFix(vUly.x())),
static_cast<float>(y - BigFix(vUly.y())),
static_cast<float>(z - BigFix(vUly.z())));
return offsetUly * 1.0e-6f;
}
/** Get the offset in light years of this coordinate from a point (also with
* units of light years.) The difference is calculated at high precision and
* the reduced to single precision.
*
* This method is only here to help in porting older code; it shouldn't be
* necessary to use it in new code, where the use of the rather the rather
* obscure unit micro-light year isn't necessary.
*/
Eigen::Vector3d offsetFromUly(const UniversalCoord& uc) const
{
return Eigen::Vector3d(static_cast<double>(x - uc.x),
static_cast<double>(y - uc.y),
static_cast<double>(z - uc.z));
}
/** Get the value of the coordinate in light years. The result is truncated to
* double precision.
*/
Eigen::Vector3d toLy() const
{
return Eigen::Vector3d(static_cast<double>(x),
static_cast<double>(y),
static_cast<double>(z)) * 1.0e-6;
}
double distanceFromKm(const UniversalCoord& uc)
{
return offsetFromKm(uc).norm();
}
double distanceFromLy(const UniversalCoord& uc)
{
return astro::kilometersToLightYears(offsetFromKm(uc).norm());
}
static UniversalCoord Zero()
{
// Default constructor returns zero, but this static method is clearer
return UniversalCoord();
}
/** Convert double precision coordinates in kilometers to high precision
* universal coordinates.
*/
static UniversalCoord CreateKm(const Eigen::Vector3d& v)
{
Eigen::Vector3d vUly = v * astro::microLightYearsToKilometers(1.0);
return UniversalCoord(vUly.x(), vUly.y(), vUly.z());
}
/** Convert double precision coordinates in light years to high precision
* universal coordinates.
*/
static UniversalCoord CreateLy(const Eigen::Vector3d& v)
{
Eigen::Vector3d vUly = v * 1.0e6;
return UniversalCoord(vUly.x(), vUly.y(), vUly.z());
}
/** Convert double precision coordinates in micro-light years to high precision
* universal coordinates. This method is intended only for porting older code;
* it should not be used by new code.
*/
static UniversalCoord CreateUly(const Eigen::Vector3d& v)
{
Eigen::Vector3d vUly = v;
return UniversalCoord(vUly.x(), vUly.y(), vUly.z());
}
bool isOutOfBounds() const
{
return x.isOutOfBounds() || y.isOutOfBounds() || z.isOutOfBounds();
}
public:
BigFix x, y, z;
};
inline UniversalCoord operator+(const UniversalCoord& uc0, const UniversalCoord& uc1)
{
return UniversalCoord(uc0.x + uc1.x, uc0.y + uc1.y, uc0.z + uc1.z);
}
inline UniversalCoord operator-(const UniversalCoord& uc0, const UniversalCoord& uc1)
{
return UniversalCoord(uc0.x - uc1.x, uc0.y - uc1.y, uc0.z - uc1.z);
}