Files
2026-08-07 17:51:03 +08:00

439 lines
9.0 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "dl_math.h"
namespace dl::Math
{
std::vector<dl::PointU> GetCircle(const Size& size, const PointU& xy, unsigned rad)
{
if (rad == 0)
return { xy };
std::vector<PointU> ret;
PointU r = { rad, rad };
PointU lt = Math::SubU(xy, r);
PointU rb = Each::Limit(xy + r, {}, size);
for (unsigned j = lt[1]; j <= rb[1]; ++j)
{
for (unsigned i = lt[0]; i <= rb[0]; ++i)
{
if (Math::DistancePow2U({i, j}, xy) <= rad * rad)
ret.push_back(PointU{ i, j });
}
}
return ret;
}
std::vector<dl::PointU> RingX::GetCircle(const Size& size, const PointU& xy, unsigned rad)
{
if (rad == 0)
return { xy };
std::vector<PointU> ret;
ret.reserve(GetCircleNumMax(rad));
int r = (int)rad;
Point xy_i = toPoint(xy);
for (int y = -r; y <= r; ++y)
{
// 计算该行最大x偏移
int maxX = (int)std::sqrt(r * r - y * y);
for (int x = -maxX; x <= maxX; ++x)
{
PointU p;
if (Clamp(size, xy_i + Point{ x, y }, p))
ret.push_back(p);
}
}
assert(ret.size() <= GetCircleNumMax(rad));
return ret;
}
std::vector<dl::PointU> RingX::Diffusion(const Size& size, const PointU& xy, unsigned count, FnDiffusionCheck func_valid, FnDiffusionCb func_cb)
{
MapLimit map;
map.SetSize(size);
// 返回值
std::vector<PointU> ret;
// 是否已经遍历过
std::unordered_map<unsigned, bool> map_index;
// 当前需要扩散的格子
std::list<PointU> list_open;
// 加入首节点
unsigned index = xy.x + xy.y * size.w;
map_index[index] = true;
list_open.push_back(xy);
for (unsigned i = 0; i < count; ++i)
{
std::list<PointU> list_temp; // 此轮遍历到的格子
for (auto& iter : list_open)
{
if (func_valid(iter))
{
// 添加到返回列表
ret.push_back(iter);
PointU xy_near;
xy_near = iter;
if (map.Left(xy_near))
{
unsigned index = xy_near.x + xy_near.y * size.w;
if (!map_index.contains(index))
{
map_index[index] = true;
list_temp.push_back(xy_near);
}
}
xy_near = iter;
if (map.Right(xy_near))
{
unsigned index = xy_near.x + xy_near.y * size.w;
if (!map_index.contains(index))
{
map_index[index] = true;
list_temp.push_back(xy_near);
}
}
xy_near = iter;
if (map.Bottom(xy_near))
{
unsigned index = xy_near.x + xy_near.y * size.w;
if (!map_index.contains(index))
{
map_index[index] = true;
list_temp.push_back(xy_near);
}
}
xy_near = iter;
if (map.Top(xy_near))
{
unsigned index = xy_near.x + xy_near.y * size.w;
if (!map_index.contains(index))
{
map_index[index] = true;
list_temp.push_back(xy_near);
}
}
}
}
func_cb(i, list_temp);
list_open = list_temp;
}
return ret;
}
std::vector<dl::Point> GetCircle(const Point& xy, unsigned rad)
{
if (rad == 0)
return { xy };
std::vector<Point> ret;
int r = rad;
for (int j = -r; j <= r; ++j)
{
for (int i = -r; i <= r; ++i)
{
if (i * i + j * j <= r * r)
ret.push_back(Point{ i, j } + xy);
}
}
return ret;
}
dl::Position4 Slerp(const Position4& q0, const Position4& q1, const Float& t)
{
Float magnitude = sqrt(DistancePow2(q0) * DistancePow2(q1));
assert(magnitude > 0);
Float product = DotProduct(q0, q1) / magnitude;
Float product_abs = std::abs(product);
if (product_abs < 1_f - std::numeric_limits<Float>::epsilon())
{
Float theta = acos(product_abs);
Float d = sin(theta);
assert(d > 0);
Float sign = product < 0 ? -1_f : 1_f;
Float s0 = sin((1_f - t) * theta) / d;
Float s1 = sin(sign * t * theta) / d;
return q0 * s0 + q1 * s1;
}
else
return q0;
}
std::vector<dl::Point> DrawLineLerp(const Point& p0, const Point& p1)
{
std::vector<Point> ret;
int N = DistanceDiagonal(p0, p1);
for (int step = 0; step <= N; ++step)
{
double t = N ? ((double)step / N) : 0.0;
ret.push_back(Each::Lerp(p0, p1, t, std::round));
}
return ret;
}
std::vector<dl::Point> DrawLineBresenham(const Point& p0, const Point& p1)
{
std::vector<Point> ret;
const int& x0 = p0[0];
const int& y0 = p0[1];
const int& x1 = p1[0];
const int& y1 = p1[1];
int dx = x1 - x0;
int dy = y1 - y0;
int ux = dx > 0 ? 1 : -1;
int uy = dy > 0 ? 1 : -1;
dx = abs(dx);
dy = abs(dy);
int dx_2 = dx * 2;
int dy_2 = dy * 2;
if (dx > dy)
{
int e = -dx;
int x = x0;
int y = y0;
for (x = x0; x != x1; x += ux)
{
ret.push_back({ x, y });
e = e + dy_2;
if (e > 0)
{
y += uy;
e = e - dx_2;
}
}
ret.push_back({ x, y });
}
else
{
int e = -dy;
int x = x0;
int y = y0;
for (y = y0; y != y1; y += uy)
{
ret.push_back({ x, y });
e = e + dx_2;
if (e > 0)
{
x += ux;
e = e - dy_2;
}
}
ret.push_back({ x, y });
}
return ret;
}
std::vector<dl::Point> DrawLineOrth(const Point& p0, const Point& p1)
{
std::vector<Point> ret;
int dx = p1[0] - p0[0];
int dy = p1[1] - p0[1];
int nx = abs(dx);
int ny = abs(dy);
int sign_x = dx > 0 ? 1 : -1;
int sign_y = dy > 0 ? 1 : -1;
Point p = p0;
ret.push_back(p);
int ix = 0;
int iy = 0;
for (; ix < nx || iy < ny;)
{
int decision = (1 + 2 * ix) * ny - (1 + 2 * iy) * nx;
if (decision < 0)
{//水平
p[0] += sign_x;
ix++;
}
else
{//垂直
p[1] += sign_y;
iy++;
}
ret.push_back(p);
}
return ret;
}
std::vector<dl::Point> DrawLineCover(const Point& p0, const Point& p1)
{
std::vector<Point> ret;
int dx = p1[0] - p0[0];
int dy = p1[1] - p0[1];
int nx = abs(dx);
int ny = abs(dy);
int sign_x = dx > 0 ? 1 : -1;
int sign_y = dy > 0 ? 1 : -1;
Point p = p0;
ret.push_back(p);
int ix = 0;
int iy = 0;
for (; ix < nx || iy < ny;)
{
int decision = (1 + 2 * ix) * ny - (1 + 2 * iy) * nx;
if (decision == 0)
{//对角线
p[0] += sign_x;
p[1] += sign_y;
ix++;
iy++;
}
else if (decision < 0)
{//水平
p[0] += sign_x;
ix++;
}
else
{//垂直
p[1] += sign_y;
iy++;
}
ret.push_back(p);
}
return ret;
}
void MergePath(std::list<Point>& list_pos)
{
size_t size = list_pos.size();
if (list_pos.size() < 3)
return;//少于3个节点不用处理
//初始向量
auto iter_0 = list_pos.begin();//前一个节点
auto iter_1 = iter_0;//后一个节点
++iter_1;
Point v = *iter_1 - *iter_0;//向量指向前一个节点
++iter_0;
++iter_1;
//直到后一个到end
for (; iter_1 != list_pos.end();)
{
Point v1 = *iter_1 - *iter_0;
if (v1 == v)
{//方向相同,删除前一个节点
list_pos.erase(iter_0);
iter_0 = iter_1++;//iter_0失效、指向iter_1,iter_1指向下一位置
}
else
{
v = v1;
++iter_0;
++iter_1;
}
}
}
double GetSolarDeclination(int day)
{
// 近似公式:赤纬 = 23.44° × sin(360/365 × (284 + N))
// 这是地理学中常用的经验公式
double angle = (360.0 / 365.0) * (284 + day);
double declination = 23.44 * std::sin(DegreesToRadians(angle));
return declination;
}
double GetSolarElevation(double latitude, double declination)
{
// H = 90° - |纬度 - 赤纬|
double diff = fabs(latitude - declination);
double elevation = 90.0 - diff;
return elevation;
}
int GetSeason(int day, double latitude)
{
bool is_northern = (latitude >= 0);
// 定义节气的大致日期(北半球)
// 春分:3月20日(第79天)
// 夏至:6月21日(第172天)
// 秋分:9月23日(第266天)
// 冬至:12月22日(第356天)
int spring_equinox = 79;
int summer_solstice = 172;
int autumn_equinox = 266;
int winter_solstice = 356;
if (!is_northern)
{
// 南半球:季节相反,交换春分/秋分,夏至/冬至
std::swap(spring_equinox, autumn_equinox);
std::swap(summer_solstice, winter_solstice);
}
// 判断季节(使用环形日期)
// 为了处理跨年,将日期映射到0-364的环上
int day_ring = day % 365;
// 简化判断:将四个节点之间的区间分配给四个季节
// 这里用区间中点划分更准确
if (day_ring >= spring_equinox && day_ring < summer_solstice)
{
return 0; // 春季
}
else if (day_ring >= summer_solstice && day_ring < autumn_equinox)
{
return 1; // 夏季
}
else if (day_ring >= autumn_equinox && day_ring < winter_solstice)
{
return 2; // 秋季
}
else
{
return 3; // 冬季(包含跨年部分)
}
}
double GetBaseTemperature(double latitude, int day)
{
// 基础纬度降温效应(年均温基准)
double base_temp = 26.0 - (std::fabs(latitude) * 0.6);
// 太阳高度角
double solar_elevation = GetSolarElevation(latitude, GetSolarDeclination(day));
// 太阳高度角修正系数
// 高度角为90°时热力系数为1.0,为0°时系数为0.0
double heat_factor = std::sin(DegreesToRadians(solar_elevation));
// 确保热力系数在合理范围
heat_factor = (std::max)(0.1, (std::min)(1.0, heat_factor));
// 季节修正(太阳高度角已经反映了季节变化,但这里做额外微调)
double seasonal_offset = 0.0;
if (GetSeason(day, latitude) == 1)
{
seasonal_offset = 2.0; // 夏季额外增温
}
else if (GetSeason(day, latitude) == 3)
{
seasonal_offset = -2.0; // 冬季额外降温
}
// 4. 组合计算:基准温度 × 热力系数 + 季节偏移
double estimated_temp = base_temp * heat_factor + seasonal_offset;
return estimated_temp;
}
}