This commit is contained in:
zd383321154 2025-05-26 23:27:31 +08:00
parent 6ead17de2f
commit c8b1414fca
7 changed files with 228 additions and 52 deletions

View File

@ -1,10 +1,13 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
// SPDX-FileCopyrightText: 2006 Mathias Froehlich <mathias.froehlich@web.de>
// SGGeoc 头文件
// 定义了地心坐标类SGGeoc用于表示地球上的位置 lon为经度, lat为纬度, radius为距离地球中心的距离
#ifndef SGGeoc_H
#define SGGeoc_H
#include <simgear/constants.h>
#include <simgear/constants.h> // 包含常量定义的头文件
// #define SG_GEOC_NATIVE_DEGREE
@ -12,65 +15,91 @@
class SGGeoc {
public:
/// Default constructor, initializes the instance to lat = lon = lat = 0
// 默认构造函数,将实例初始化为 lat = lon = lat = 0
SGGeoc(void);
/// Factory from angular values in radians and radius in ft
// 工厂方法:从弧度值和半径(单位:英尺)创建实例
static SGGeoc fromRadFt(double lon, double lat, double radius);
/// Factory from angular values in degrees and radius in ft
// 工厂方法:从角度值和半径(单位:英尺)创建实例
static SGGeoc fromDegFt(double lon, double lat, double radius);
/// Factory from angular values in radians and radius in m
// 工厂方法:从弧度值和半径(单位:米)创建实例
static SGGeoc fromRadM(double lon, double lat, double radius);
/// Factory from angular values in degrees and radius in m
// 工厂方法:从角度值和半径(单位:米)创建实例
static SGGeoc fromDegM(double lon, double lat, double radius);
/// Factory to convert position from a cartesian position assumed to be
/// in wgs84 measured in meters
/// Note that this conversion is relatively expensive to compute
// 工厂方法从ECEF坐标单位创建实例, 使用WGS84坐标系, 注意这个转换耗时较长
static SGGeoc fromCart(const SGVec3<double>& cart);
/// Factory to convert position from a geodetic position
/// Note that this conversion is relatively expensive to compute
// 工厂方法:从地理坐标创建实例, 注意这个转换耗时较长
static SGGeoc fromGeod(const SGGeod& geod);
/// Return the geocentric longitude in radians
// 返回经度(单位:弧度)
double getLongitudeRad(void) const;
/// Set the geocentric longitude from the argument given in radians
// 设置经度(单位:弧度)
void setLongitudeRad(double lon);
/// Return the geocentric longitude in degrees
// 返回经度(单位:度)
double getLongitudeDeg(void) const;
/// Set the geocentric longitude from the argument given in degrees
// 设置经度(单位:度)
void setLongitudeDeg(double lon);
/// Return the geocentric latitude in radians
// 返回纬度(单位:弧度)
double getLatitudeRad(void) const;
/// Set the geocentric latitude from the argument given in radians
// 设置纬度(单位:弧度)
void setLatitudeRad(double lat);
/// Return the geocentric latitude in degrees
// 返回纬度(单位:度)
double getLatitudeDeg(void) const;
/// Set the geocentric latitude from the argument given in degrees
// 设置纬度(单位:度)
void setLatitudeDeg(double lat);
/// Return the geocentric radius in meters
// 返回距地心距离(单位:米)
double getRadiusM(void) const;
/// Set the geocentric radius from the argument given in meters
// 设置距地心距离(单位:米)
void setRadiusM(double radius);
/// Return the geocentric radius in feet
// 返回距地心距离(单位:英尺)
double getRadiusFt(void) const;
/// Set the geocentric radius from the argument given in feet
// 设置距地心距离(单位:英尺)
void setRadiusFt(double radius);
/// 通过航向和距离计算一个新的地心坐标(单位:弧度),
/// 航向是相对于当前坐标的航向,距离是相对于当前坐标的距离
SGGeoc advanceRadM(double course, double distance) const;
/// 返回两个地心坐标之间的航向(单位:弧度)
static double courseRad(const SGGeoc& from, const SGGeoc& to);
/// 返回两个地心坐标之间的航向(单位:度)
static double courseDeg(const SGGeoc& from, const SGGeoc& to);
/// 返回两个地心坐标之间的距离(单位:米)
static double distanceM(const SGGeoc& from, const SGGeoc& to);
// Compare two geocentric positions for equality
// 比较两个地心坐标是否相等
bool operator == ( const SGGeoc & other ) const;
private:
/// This one is private since construction is not unique if you do
/// not know the units of the arguments, use the factory methods for
/// that purpose
// 私有构造函数,因为构造函数不是唯一的,如果不知道参数的单位,请使用工厂方法
SGGeoc(double lon, double lat, double radius);
/// The actual data, angles in degree, radius in meters
@ -80,9 +109,15 @@ private:
/// to other representations or compute rotation matrices. But both tasks
/// are computionally intensive anyway and that additional 'toRadian'
/// conversion does not hurt too much
double _lon;
double _lat;
double _radius;
/// 实际数据:角度以度为单位,半径以米为单位
/// 选择以度为单位存储值的原因是flightgear/terragear中的大多数代码
/// 都使用度作为原生的输入和输出值。
/// 使用弧度更有意义的情况是在我们进行坐标转换
/// 或计算旋转矩阵时。但这两项任务本身计算量就很大,
/// 所以额外的"转换为弧度"操作并不会造成太大影响
double _lon; // 经度
double _lat; // 纬度
double _radius; // 距地心距离
};
inline
@ -321,6 +356,7 @@ SGGeoc::operator == ( const SGGeoc & other ) const
}
/// Output to an ostream
// 自定义的输出流操作符用于将SGGeoc对象输出到流中
template<typename char_type, typename traits_type>
inline
std::basic_ostream<char_type, traits_type>&

View File

@ -1,6 +1,9 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
// SPDX-FileCopyrightText: 2006 Mathias Froehlich <mathias.froehlich@web.de>
// SGGeod 头文件
// 定义了地理坐标类SGGeod用于表示地球上的位置 lon为经度, lat为纬度, elevation为海拔高度
#ifndef SGGeod_H
#define SGGeod_H
@ -20,72 +23,98 @@ public:
This is necessary because for historical reasons, ther default constructor above initialises to zero,zero,zero
which *is*
*/
// 返回一个无效的SGGeod对象用于表示无效的地理坐标
static SGGeod invalid();
/// Factory from angular values in radians and elevation is 0
// 工厂方法:从弧度值创建实例, 海拔高度为0
static SGGeod fromRad(double lon, double lat);
/// Factory from angular values in degrees and elevation is 0
// 工厂方法:从角度值创建实例, 海拔高度为0
static SGGeod fromDeg(double lon, double lat);
/// Factory from angular values in radians and elevation in ft
// 工厂方法:从弧度值和海拔高度(单位:英尺)创建实例
static SGGeod fromRadFt(double lon, double lat, double elevation);
/// Factory from angular values in degrees and elevation in ft
// 工厂方法:从角度值和海拔高度(单位:英尺)创建实例
static SGGeod fromDegFt(double lon, double lat, double elevation);
/// Factory from angular values in radians and elevation in m
// 工厂方法:从弧度值和海拔高度(单位:米)创建实例
static SGGeod fromRadM(double lon, double lat, double elevation);
/// Factory from angular values in degrees and elevation in m
// 工厂方法:从角度值和海拔高度(单位:米)创建实例
static SGGeod fromDegM(double lon, double lat, double elevation);
/// Factory from an other SGGeod and a different elevation in m
// 工厂方法:从另一个地理坐标和不同的海拔高度(单位:米)创建实例
static SGGeod fromGeodM(const SGGeod& geod, double elevation);
/// Factory from an other SGGeod and a different elevation in ft
// 工厂方法:从另一个地理坐标和不同的海拔高度(单位:英尺)创建实例
static SGGeod fromGeodFt(const SGGeod& geod, double elevation);
/// Factory to convert position from a cartesian position assumed to be
/// in wgs84 measured in meters
/// Note that this conversion is relatively expensive to compute
// 工厂方法从ECEF坐标单位创建实例, 使用WGS84坐标系, 注意这个转换耗时较长
static SGGeod fromCart(const SGVec3<double>& cart);
/// Factory to convert position from a geocentric position
/// Note that this conversion is relatively expensive to compute
// 工厂方法:从地心坐标创建实例, 注意这个转换耗时较长
static SGGeod fromGeoc(const SGGeoc& geoc);
/// Return the geodetic longitude in radians
// 返回经度(单位:弧度)
double getLongitudeRad(void) const;
/// Set the geodetic longitude from the argument given in radians
// 设置经度(单位:弧度)
void setLongitudeRad(double lon);
/// Return the geodetic longitude in degrees
// 返回经度(单位:度)
double getLongitudeDeg(void) const;
/// Set the geodetic longitude from the argument given in degrees
// 设置经度(单位:度)
void setLongitudeDeg(double lon);
/// Return the geodetic latitude in radians
// 返回纬度(单位:弧度)
double getLatitudeRad(void) const;
/// Set the geodetic latitude from the argument given in radians
// 设置纬度(单位:弧度)
void setLatitudeRad(double lat);
/// Return the geodetic latitude in degrees
// 返回纬度(单位:度)
double getLatitudeDeg(void) const;
/// Set the geodetic latitude from the argument given in degrees
// 设置纬度(单位:度)
void setLatitudeDeg(double lat);
/// Return the geodetic elevation in meters
// 返回海拔高度(单位:米)
double getElevationM(void) const;
/// Set the geodetic elevation from the argument given in meters
// 设置海拔高度(单位:米)
void setElevationM(double elevation);
/// Return the geodetic elevation in feet
// 返回海拔高度(单位:英尺)
double getElevationFt(void) const;
/// Set the geodetic elevation from the argument given in feet
// 设置海拔高度(单位:英尺)
void setElevationFt(double elevation);
/// Compare two geodetic positions for equality
// 比较两个地理坐标是否相等
bool operator == ( const SGGeod & other ) const;
/// check the Geod contains sane values (finite, inside appropriate
/// ranges for lat/lon)
// 检查地理坐标是否包含合理的值(有限、在适当的范围内)
bool isValid() const;
private:
/// This one is private since construction is not unique if you do
/// not know the units of the arguments. Use the factory methods for
/// that purpose
// 私有构造函数,因为构造函数不是唯一的,如果不知道参数的单位,请使用工厂方法
SGGeod(double lon, double lat, double elevation);
//// FIXME: wrong comment!
@ -96,9 +125,15 @@ private:
/// to other representations or compute rotation matrices. But both tasks
/// are computationally intensive anyway and that additional 'toRadian'
/// conversion does not hurt too much
// 实际数据:角度以度为单位,海拔高度以米为单位
// 选择以度为单位存储值的原因是flightgear/terragear中的大多数代码
// 都使用度作为原生的输入和输出值。
// 使用弧度更有意义的情况是在我们进行坐标转换
/// 或计算旋转矩阵时。但这两项任务本身计算量就很大,
/// 所以额外的"转换为弧度"操作并不会造成太大影响
double _lon;
double _lat;
double _elevation;
double _elevation; // 海拔高度
};
inline

View File

@ -5,17 +5,17 @@
#include <cmath>
#include <simgear/debug/logstream.hxx>
#include <simgear/misc/strutils.hxx>
#include <simgear/sg_inlines.h>
#include <simgear/structure/exception.hxx>
#include <simgear/debug/logstream.hxx> // 包含日志记录相关的头文件
#include <simgear/misc/strutils.hxx> // 包含字符串处理相关的头文件
#include <simgear/sg_inlines.h> // 包含SG相关的内联函数
#include <simgear/structure/exception.hxx> // 包含异常处理相关的头文件
#include "SGMath.hxx"
// These are hard numbers from the WGS84 standard. DON'T MODIFY
// unless you want to change the datum.
#define _EQURAD 6378137.0
#define _FLATTENING 298.257223563
#define _EQURAD 6378137.0 // 地球椭球体长半轴
#define _FLATTENING 298.257223563 // 地球椭球体扁率
// These are derived quantities more useful to the code:
#if 0
@ -27,9 +27,12 @@
// precision calculator (the compiler might lose a few bits in the FPU
// operations). These are specified to 81 bits of mantissa, which is
// higher than any FPU known to me:
#define _SQUASH 0.9966471893352525192801545
#define _STRETCH 1.0033640898209764189003079
#define _POLRAD 6356752.3142451794975639668
// 上述值的高精度版本是使用任意精度计算器生成的
// (编译器在浮点运算中可能会丢失几位精度)。
// 这些值指定了81位的尾数精度这比我所知道的任何浮点运算单元(FPU)的精度都要高:
#define _SQUASH 0.9966471893352525192801545 // 地球椭球体压缩率
#define _STRETCH 1.0033640898209764189003079 // 地球椭球体拉伸率
#define _POLRAD 6356752.3142451794975639668 // 地球椭球体极半径
#endif
// The constants from the WGS84 standard
@ -42,12 +45,12 @@ const double SGGeodesy::POLRAD = _POLRAD;
// additional derived and precomputable ones
// for the geodetic conversion algorithm
#define E2 fabs(1 - _SQUASH*_SQUASH)
static double a = _EQURAD;
static double ra2 = 1/(_EQURAD*_EQURAD);
#define E2 fabs(1 - _SQUASH*_SQUASH) // 地球椭球体偏心率平方
static double a = _EQURAD; // 地球椭球体长半轴
static double ra2 = 1/(_EQURAD*_EQURAD); // 地球椭球体长半轴平方的倒数
//static double e = sqrt(E2);
static double e2 = E2;
static double e4 = E2*E2;
static double e2 = E2; // 地球椭球体偏心率平方
static double e4 = E2*E2; // 地球椭球体偏心率平方的平方
#undef _EQURAD
#undef _FLATTENING
@ -56,7 +59,7 @@ static double e4 = E2*E2;
#undef _POLRAD
#undef E2
using namespace std::string_literals;
using namespace std::string_literals; // 使用字符串字面量
void
SGGeodesy::SGCartToGeod(const SGVec3<double>& cart, SGGeod& geod)
@ -74,6 +77,10 @@ SGGeodesy::SGCartToGeod(const SGVec3<double>& cart, SGGeod& geod)
// Define the innermost sphere of small radius as earth center and return the
// coordinates 0/0/-EQURAD. It may be any other place on geoide's surface,
// the Northpole, Hawaii or Wentorf. This one was easy to code ;-)
// 这个函数在地球中心区域失败,所以在这里捕获这个特殊情况。
// 定义最内层的球体为地球中心并返回坐标0/0/-EQURAD。
// 它可以是地球表面上的任何其他地方,
// 北极、夏威夷或文托夫。这个很简单。
geod.setLongitudeRad( 0.0 );
geod.setLatitudeRad( 0.0 );
geod.setElevationM( -EQURAD );
@ -90,6 +97,11 @@ SGGeodesy::SGCartToGeod(const SGVec3<double>& cart, SGGeod& geod)
slightly negative values for s due to floating point rounding errors
cause nan for sqrt(s*(2+s))
We can probably clamp the resulting parable to positive numbers
*/
/*
s=[-2..0]s*(2+s)
s的稍微负值会导致sqrt(s*(2+s))nan
线
*/
if( s >= -2.0 && s <= 0.0 )
s = 0.0;
@ -130,6 +142,8 @@ SGGeodesy::SGGeodToSeaLevelRadius(const SGGeod& geod)
{
// this is just a simplified version of the SGGeodToCart function above,
// substitute h = 0, take the 2-norm of the cartesian vector and simplify
// 这是一个简化的SGGeodToCart函数的版本
// 将h=0代入取笛卡尔向量的2-范数并简化
double phi = geod.getLatitudeRad();
double sphi = sin(phi);
double sphi2 = sphi*sphi;
@ -171,19 +185,25 @@ SGGeodesy::SGGeocToCart(const SGGeoc& geoc, SGVec3<double>& cart)
// The XYZ/cartesian coordinate system in use puts the X axis through
// zero lat/lon (off west Africa), the Z axis through the north pole,
// and the Y axis through 90 degrees longitude (in the Indian Ocean).
// 使用XYZ/笛卡尔坐标系X轴通过0/0位于西非Z轴通过北极Y轴通过90度经度在印度洋
//
// All latitude and longitude values are in radians. Altitude is in
// meters, with zero on the WGS84 ellipsoid.
// 所有纬度和经度值都是以弧度为单位。
// 高度是以米为单位零点在WGS84椭球体上。
//
// The code below makes use of the notion of "squashed" space. This
// is a 2D cylindrical coordinate system where the radius from the Z
// axis is multiplied by SQUASH; the earth in this space is a perfect
// circle with a radius of POLRAD.
// 下面的代码使用“压缩”空间的观念。
// 这是一个2D圆柱坐标系其中Z轴的半径乘以SQUASH
// 在这个空间中地球是一个完美的圆半径为POLRAD。
////////////////////////////////////////////////////////////////////////
//
// Direct and inverse distance functions
//
// 直接和反向距离函数
// Proceedings of the 7th International Symposium on Geodetic
// Computations, 1985
//
@ -204,6 +224,7 @@ static inline double M0( double e2 ) {
// given, lat1, lon1, az1 and distance (s), calculate lat2, lon2
// and az2. Lat, lon, and azimuth are in degrees. distance in meters
// 给定起始点经纬度和方位角以及距离,计算目标点经纬度和方位角
static int _geo_direct_wgs_84 ( double lat1, double lon1, double az1,
double s, double *lat2, double *lon2,
double *az2 )

View File

@ -1,70 +1,95 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
// SPDX-FileCopyrightText: 2006 Mathias Froehlich <mathias.froehlich@web.de>
// SGGeodesy 头文件
// 包含了一系列地理计算相关的函数,包括地球参数、坐标转换、距离计算等
#ifndef SGGeodesy_H
#define SGGeodesy_H
class SGGeodesy {
public:
// Hard numbers from the WGS84 standard.
static const double EQURAD;
static const double iFLATTENING;
static const double SQUASH;
static const double STRETCH;
static const double POLRAD;
static const double EQURAD; // 地球赤道半径
static const double iFLATTENING; // 地球扁率
static const double SQUASH; // 地球压缩率
static const double STRETCH; // 地球拉伸率
static const double POLRAD; // 地球极半径
/// Takes a cartesian coordinate data and returns the geodetic
/// coordinates.
// 将ECEF坐标转换为地理坐标
static void SGCartToGeod(const SGVec3<double>& cart, SGGeod& geod);
/// Takes a geodetic coordinate data and returns the cartesian
/// coordinates.
// 将地理坐标转换为ECEF坐标
static void SGGeodToCart(const SGGeod& geod, SGVec3<double>& cart);
/// Takes a geodetic coordinate data and returns the sea level radius.
// 根据地理坐标计算此处海平面的地球半径
static double SGGeodToSeaLevelRadius(const SGGeod& geod);
/// Takes a cartesian coordinate data and returns the geocentric
/// coordinates.
// 将ECEF坐标转换为地心坐标
static void SGCartToGeoc(const SGVec3<double>& cart, SGGeoc& geoc);
/// Takes a geocentric coordinate data and returns the cartesian
/// coordinates.
// 将地心坐标转换为ECEF坐标
static void SGGeocToCart(const SGGeoc& geoc, SGVec3<double>& cart);
// Geodetic course/distance computation
// 给定起始点地理坐标和方位角以及距离,计算目标点经纬度和目标点看起始点的方位角
static bool direct(const SGGeod& p1, double course1,
double distance, SGGeod& p2, double& course2);
/// overloaded version of above, returns new value directly, throws
/// an sg_exception on failure.
// 给定起始点地理坐标和方位角以及距离,计算目标点地理坐标
static SGGeod direct(const SGGeod& p1, double course1,
double distance);
// 给定起始点和目标点地理坐标,计算起始点看目标点的方位角和目标点看起始点的方位角以及距离
static bool inverse(const SGGeod& p1, const SGGeod& p2, double& course1,
double& course2, double& distance);
// 给定起始点和目标点地理坐标,计算起始点看目标点的方位角
static double courseDeg(const SGGeod& from, const SGGeod& to);
// 给定起始点和目标点地理坐标,计算起始点和目标点之间的距离(单位:米)
static double distanceM(const SGGeod& from, const SGGeod& to);
// 给定起始点和目标点地理坐标,计算起始点和目标点之间的距离(单位:海里)
static double distanceNm(const SGGeod& from, const SGGeod& to);
// Geocentric course/distance computation
// 给定起始点地心坐标和方位角(单位:弧度)以及距离(单位:米),计算目标点地心坐标
static void advanceRadM(const SGGeoc& geoc, double course, double distance,
SGGeoc& result);
// 给定起始点地心坐标和方位角(单位:度)以及距离(单位:米),计算目标点地心坐标
static SGGeoc advanceDegM(const SGGeoc &geoc, double course, double distance);
// 给定起始点和目标点地心坐标,计算起始点和目标点之间的方位角(单位:弧度)
static double courseRad(const SGGeoc& from, const SGGeoc& to);
// 给定起始点和目标点地心坐标,计算起始点和目标点之间的距离(单位:弧度)
static double distanceRad(const SGGeoc& from, const SGGeoc& to);
// 给定起始点和目标点地心坐标,计算起始点和目标点之间的距离(单位:米)
static double distanceM(const SGGeoc& from, const SGGeoc& to);
/**
* compute the intersection of two (true) radials (in degrees), or return false
* if no intersection culd be computed.
*/
// 给定两个点地心坐标和方位角,计算两个方向向量之间的交点
static bool radialIntersection(const SGGeoc& a, double aRadial,
const SGGeoc& b, double bRadial, SGGeoc& result);
// 给定两个点地理坐标和方位角,计算两个方向向量之间的交点
static bool radialIntersection(const SGGeod& a, double aRadial,
const SGGeod& b, double bRadial, SGGeod& result);
};

View File

@ -1,6 +1,9 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
// SPDX-FileCopyrightText: 2006 Mathias Froehlich <mathias.froehlich@web.de>
// SGMath 头文件
// 包含了一系列数学相关的头文件
#ifndef SGMath_H
#define SGMath_H
@ -10,13 +13,13 @@
#include "SGMathFwd.hxx"
#include "SGCMath.hxx"
#include "SGLimits.hxx"
#include "SGMisc.hxx"
#include "SGGeodesy.hxx"
#include "SGVec2.hxx"
#include "SGVec3.hxx"
#include "SGVec4.hxx"
#include "SGCMath.hxx" // 引入了std标准数学库
#include "SGLimits.hxx" // 引入了std标准库的limits头文件定义了用于SG的limits类
#include "SGMisc.hxx" // 提供了一系列数学计算函数,包括最大值、最小值、无溢出加法、渐进、幂运算、归一化、四舍五入等
#include "SGGeodesy.hxx" // 包含了一系列地理计算相关的函数,包括地球参数、坐标转换、距离计算等
#include "SGVec2.hxx" // 定义了2D向量类SGVec2提供了向量运算、点积、叉积等操作
#include "SGVec3.hxx" // 定义了3D向量类SGVec3提供了向量运算、点积、叉积等操作
#include "SGVec4.hxx" // 定义了4D向量类SGVec4提供了向量运算、点积、叉积等操作
#include "SGGeoc.hxx"
#include "SGGeod.hxx"
#include "SGQuat.hxx"

View File

@ -1,6 +1,9 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
// SPDX-FileCopyrightText: 2006 Mathias Froehlich <mathias.froehlich@web.de>
// 数学工具类
// 提供了一系列数学计算函数,包括最大值、最小值、无溢出加法、渐进、幂运算、归一化、四舍五入等
#ifndef SGMisc_H
#define SGMisc_H
@ -26,11 +29,15 @@ public:
{ return max(max(max(a, b), c), d); }
// clip the value of a to be in the range between and including _min and _max
// 如果a小于_min则返回_min如果a大于_max则返回_max否则返回a
static T clip(const T& a, const T& _min, const T& _max)
{ return max(_min, min(_max, a)); }
/// Add two (integer) values taking care of overflows.
// 无溢出加法
// 如果b大于0则检查a+b是否溢出如果溢出则返回SGLimits<T>::max()否则返回a+b
// 如果b小于0则检查a+b是否溢出如果溢出则返回SGLimits<T>::min()否则返回a+b
static T addClipOverflow(T a, T b)
{
if( b > 0 )
@ -48,6 +55,7 @@ public:
}
/// Add two (integer) values in place, taking care of overflows.
// 无溢出原位置加法
static T& addClipOverflowInplace(T& a, T b)
{
return a = addClipOverflow(a, b);
@ -61,6 +69,7 @@ public:
* @param rate Max. change rate/sec
* @param dt Time step (sec)
*/
// 渐进: var 将以指定的速率 rate 按时间 dt 的递增向 target 渐进。
template<class Var>
static T seek(Var& var, T target, T rate, T dt)
{
@ -76,6 +85,7 @@ public:
* @tparam N Exponent
* @param base Base
*/
// 幂运算
template<int N>
static T pow(T base)
{
@ -101,7 +111,8 @@ public:
static T deg2rad(const T& val)
{ return val*pi()/180; }
// normalize the value to be in a range between [min, max[
// normalize the value to be in a range between [min, max]
// 归一化周期值:将 value 归一化到 [min, max] 周期范围内
static T
normalizePeriodic(const T& min, const T& max, const T& value)
{
@ -117,22 +128,28 @@ public:
return normalized;
}
// normalize the angle to be in a range between [-pi, pi[
// normalize the angle to be in a range between [-pi, pi]
// 归一化角度:将 angle 归一化到 [-pi, pi] 范围内
static T
normalizeAngle(const T& angle)
{ return normalizePeriodic(-pi(), pi(), angle); }
// normalize the angle to be in a range between [0, 2pi[
// normalize the angle to be in a range between [0, 2pi]
// 归一化角度:将 angle 归一化到 [0, 2pi] 范围内
static T
normalizeAngle2(const T& angle)
{ return normalizePeriodic(0, twopi(), angle); }
// 四舍五入:将 v 四舍五入到最接近的整数,返回结果为 T 类型
static T round(const T& v)
{ return floor(v + T(0.5)); }
// 将 v 四舍五入到最接近的整数
static int roundToInt(const T& v)
{ return int(round(v)); }
// Linear interpolation between two arbitrary typed values
// 线性插值:在 val0 和 val1 之间进行线性插值t 是插值因子,取值范围为 [0, 1]
template<typename S>
static S lerp(const S& val0, const S& val1, const T& t)
{ return val0*(T(1) - t) + val1*t; }
@ -144,9 +161,11 @@ public:
return std::isnan(v);
}
// 判断两个值是否相等
static bool eq(const T& a, const T& b, const T& epsilon = SGLimits<T>::epsilon())
{ return std::abs(a - b) < epsilon; }
// 判断两个值是否不相等
static bool neq(const T& a, const T& b, const T& epsilon = SGLimits<T>::epsilon())
{ return !eq(a, b, epsilon); }
};

View File

@ -1,6 +1,7 @@
// SPDX-License-Identifier: LGPL-2.1-or-later
// SPDX-FileCopyrightText: 2016-2017 Erik Hofman <erik@ehofman.com>
// sim4d一个向量类支持4个或更少元素的向量主要是实现向量的运算优化
#pragma once
#include <cstdint>
@ -17,6 +18,7 @@ template<typename T, int N> class simd4_t;
namespace simd4
{
// min函数返回两个向量中各分量的最小值
template<typename T, int N>
inline simd4_t<T,N> min(simd4_t<T,N> v1, const simd4_t<T,N>& v2) {
for (int i=0; i<N; ++i) {
@ -25,6 +27,7 @@ inline simd4_t<T,N> min(simd4_t<T,N> v1, const simd4_t<T,N>& v2) {
return v1;
}
// max函数返回两个向量中各分量的最大值
template<typename T, int N>
inline simd4_t<T,N> max(simd4_t<T,N> v1, const simd4_t<T,N>& v2) {
for (int i=0; i<N; ++i) {
@ -33,6 +36,7 @@ inline simd4_t<T,N> max(simd4_t<T,N> v1, const simd4_t<T,N>& v2) {
return v1;
}
// abs函数返回向量中各分量的绝对值
template<typename T, int N>
inline simd4_t<T,N> abs(simd4_t<T,N> v) {
for (int i=0; i<N; ++i) {
@ -41,6 +45,7 @@ inline simd4_t<T,N> abs(simd4_t<T,N> v) {
return v;
}
// magnitude2函数返回向量中各分量的平方和即二范数的平方
template<typename T, int N>
inline T magnitude2(const simd4_t<T,N>& vi) {
simd4_t<T,N> v(vi);
@ -52,16 +57,19 @@ inline T magnitude2(const simd4_t<T,N>& vi) {
return mag2;
}
// interpolate函数返回两个向量之间的线性插值
template<typename T, int N>
inline simd4_t<T,N> interpolate(T tau, const simd4_t<T,N>& v1, const simd4_t<T,N>& v2) {
return v1 + tau*(v2-v1);
}
// magnitude函数返回向量的二范数
template<typename T, int N>
inline T magnitude(const simd4_t<T,N>& v) {
return std::sqrt(magnitude2(v));
}
// normalize函数归一化向量
template<typename T, int N>
inline T normalize(simd4_t<T,N>& v) {
T mag = simd4::magnitude(v);
@ -73,6 +81,7 @@ inline T normalize(simd4_t<T,N>& v) {
return mag;
}
// dot函数返回两个向量的点积
template<typename T, int N>
inline T dot(const simd4_t<T,N>& v1, const simd4_t<T,N>& v2) {
simd4_t<T,N> v(v1*v2);
@ -83,6 +92,7 @@ inline T dot(const simd4_t<T,N>& v1, const simd4_t<T,N>& v2) {
return dp;
}
// cross函数返回两个向量的叉积仅三维向量有效
template<typename T>
inline simd4_t<T,3> cross(const simd4_t<T,3>& v1, const simd4_t<T,3>& v2)
{
@ -95,45 +105,55 @@ inline simd4_t<T,3> cross(const simd4_t<T,3>& v1, const simd4_t<T,3>& v2)
} /* namespace simd4 */
// simd4_t类支持4个或更少元素的向量主要是实现向量的运算优化
template<typename T, int N>
class simd4_t final
{
private:
// 联合体用于存储4个元素的向量或N个元素的向量N<=4
union {
T _v4[4];
T vec[N];
};
public:
// 构造函数初始化为全0
simd4_t(void) {
for (int i=0; i<4; ++i) _v4[i] = 0;
}
// 构造函数,初始化为一个标量
simd4_t(T s) {
for (int i=0; i<N; ++i) vec[i] = s;
for (int i=N; i<4; ++i) _v4[i] = 0;
}
// 构造函数初始化为二维向量其余维度为0
simd4_t(T x, T y) : simd4_t(x,y,0,0) {}
// 构造函数初始化为三维向量其余维度为0
simd4_t(T x, T y, T z) : simd4_t(x,y,z,0) {}
// 构造函数,初始化为四维向量
simd4_t(T x, T y, T z, T w) {
_v4[0] = x; _v4[1] = y; _v4[2] = z; _v4[3] = w;
for (int i=N; i<4; ++i) _v4[i] = 0;
}
// 构造函数由一个数组初始化为N维向量
explicit simd4_t(const T v[N]) {
std::memcpy(vec, v, sizeof(T[N]));
for (int i=N; i<4; ++i) _v4[i] = 0;
}
// 构造函数由一个M维向量初始化为N维向量M与N均不大于4
template<int M>
simd4_t(const simd4_t<T,M>& v) {
std::memcpy(_v4, v.ptr(), sizeof(T[M]));
for (int i=(M<N)?M:N; i<4; ++i) _v4[i] = 0;
}
~simd4_t(void) {} // non-virtual intentional
// 析构函数
~simd4_t(void) {} // non-virtual intentional // 非虚拟析构函数,故意的
// 返回一个N维数组的引用
inline T (&v4(void))[N] {
return vec;
}
// 返回一个N维数组的常量引用
inline const T (&v4(void) const)[N] {
return vec;
}
@ -141,95 +161,104 @@ public:
inline const T (&ptr(void) const)[N] {
return vec;
}
// 返回一个N维数组的引用
inline T (&ptr(void))[N] {
return vec;
}
// []运算符重载返回第n个元素的常量引用
inline const T& operator[](unsigned n) const {
assert(n<N);
return vec[n];
}
// []运算符重载返回第n个元素的引用
inline T& operator[](unsigned n) {
assert(n<N);
return vec[n];
}
// 赋值运算符重载将一个M维向量赋值给一个N维向量
template<int M>
inline simd4_t<T,N>& operator=(const simd4_t<T,M>& v) {
*this = simd4_t<T,N>(v);
return *this;
}
// +=运算符重载将一个标量加到N维向量的每个元素上
inline simd4_t<T,N>& operator+=(T s) {
for (int i=0; i<N; ++i) {
vec[i] += s;
}
return *this;
}
// +=运算符重载将一个N维数组加到该N维向量上
inline simd4_t<T,N>& operator+=(const T v[N]) {
for (int i=0; i<N; ++i) {
vec[i] += v[i];
}
return *this;
}
// +=运算符重载将一个N维向量加到该N维向量上
inline simd4_t<T,N>& operator+=(const simd4_t<T,N>& v) {
for (int i=0; i<N; ++i) {
vec[i] += v[i];
}
return *this;
}
// -=运算符重载在该N维向量的每个元素上减去一个标量
inline simd4_t<T,N>& operator-=(T s) {
for (int i=0; i<N; ++i) {
vec[i] -= s;
}
return *this;
}
// -=运算符重载在该N维向量上减去一个N维数组
inline simd4_t<T,N>& operator-=(const T v[N]) {
for (int i=0; i<N; ++i) {
vec[i] -= v[i];
}
return *this;
}
// -=运算符重载在该N维向量上减去一个N维向量
inline simd4_t<T,N>& operator-=(const simd4_t<T,N>& v) {
for (int i=0; i<N; ++i) {
vec[i] -= v[i];
}
return *this;
}
// *=运算符重载将该N维向量的每个元素乘以一个标量
inline simd4_t<T,N>& operator*=(T s) {
for (int i=0; i<N; ++i) {
vec[i] *= s;
}
return *this;
}
// *=运算符重载将该N维向量的每个元素乘以一个N维数组每个元素
inline simd4_t<T,N>& operator*=(const T v[N]) {
for (int i=0; i<N; ++i) {
vec[i] *= v[i];
}
return *this;
}
// *=运算符重载将该N维向量的每个元素乘以一个N维向量的每个元素
inline simd4_t<T,N>& operator*=(const simd4_t<T,N>& v) {
for (int i=0; i<N; ++i) {
vec[i] *= v[i];
}
return *this;
}
// /=运算符重载将该N维向量的每个元素除以一个标量
inline simd4_t<T,N>& operator/=(T s) {
for (int i=0; i<N; ++i) {
vec[i] /= s;
}
return *this;
}
// /=运算符重载将该N维向量的每个元素除以一个N维数组每个元素
inline simd4_t<T,N>& operator/=(const T v[N]) {
for (int i=0; i<N; ++i) {
vec[i] /= v[i];
}
return *this;
}
// /=运算符重载将该N维向量的每个元素除以一个N维向量的每个元素
inline simd4_t<T,N>& operator/=(const simd4_t<T,N>& v) {
for (int i=0; i<N; ++i) {
vec[i] /= v[i];
@ -238,6 +267,8 @@ public:
}
};
// 以下均为类外重载
// -运算符重载返回一个N维向量的负值
template<typename T, int N>
inline simd4_t<T,N> operator-(const simd4_t<T,N>& v) {
simd4_t<T,N> r(T(0));
@ -245,36 +276,42 @@ inline simd4_t<T,N> operator-(const simd4_t<T,N>& v) {
return r;
}
// +运算符重载返回两个N维向量的和
template<typename T, int N>
inline simd4_t<T,N> operator+(simd4_t<T,N> v1, const simd4_t<T,N>& v2) {
v1 += v2;
return v1;
}
// -运算符重载返回两个N维向量的差
template<typename T, int N>
inline simd4_t<T,N> operator-(simd4_t<T,N> v1, const simd4_t<T,N>& v2) {
v1 -= v2;
return v1;
}
// *运算符重载将一个N维向量乘以一个N维向量对应元素相乘
template<typename T, int N>
inline simd4_t<T,N> operator*(simd4_t<T,N> v1, const simd4_t<T,N>& v2) {
v1 *= v2;
return v1;
}
// /运算符重载将一个N维向量除以一个N维向量对应元素相除
template<typename T, int N>
inline simd4_t<T,N> operator/(simd4_t<T,N> v1, const simd4_t<T,N>& v2) {
v1 /= v2;
return v1;
}
// *运算符重载将一个标量乘以一个N维向量
template<typename T, int N>
inline simd4_t<T,N> operator*(T f, simd4_t<T,N> v) {
v *= f;
return v;
}
// *运算符重载将一个N维向量乘以一个标量
template<typename T, int N>
inline simd4_t<T,N> operator*(simd4_t<T,N> v, T f) {
v *= f;