You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kev/Drawer/Module/GeoSigmaDraw/MeshGenerationBorder.h

260 lines
6.7 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#pragma once
#include "StdAfx.h"
#include <vector>
#include <cmath>
#include <algorithm>
#include <list>
// 简单的点结构
struct Pnt {
double x, y;
};
namespace NoGeosUtils
{
double Dist(const Pnt& a, const Pnt& b) {
return std::sqrt(std::pow(a.x - b.x, 2) + std::pow(a.y - b.y, 2));
}
double PerpendicularDistance(const Pnt& pt, const Pnt& lineStart, const Pnt& lineEnd) {
double dx = lineEnd.x - lineStart.x;
double dy = lineEnd.y - lineStart.y;
double mag = std::sqrt(dx * dx + dy * dy);
if (mag < 1e-10) return Dist(pt, lineStart);
double u = ((pt.x - lineStart.x) * dx + (pt.y - lineStart.y) * dy) / (mag * mag);
Pnt intersection;
if (u < 0.0f) intersection = lineStart;
else if (u > 1.0f) intersection = lineEnd;
else {
intersection.x = lineStart.x + u * dx;
intersection.y = lineStart.y + u * dy;
}
return Dist(pt, intersection);
}
// 道格拉斯-普克 (Douglas-Peucker) 抽稀算法
void DouglasPeucker(const std::vector<Pnt>& pointList, double epsilon, std::vector<Pnt>& out) {
if (pointList.size() < 2) return;
double dmax = 0.0;
size_t index = 0;
size_t end = pointList.size() - 1;
for (size_t i = 1; i < end; i++) {
double d = PerpendicularDistance(pointList[i], pointList[0], pointList[end]);
if (d > dmax) {
index = i;
dmax = d;
}
}
if (dmax > epsilon) {
std::vector<Pnt> recResults1;
std::vector<Pnt> recResults2;
std::vector<Pnt> firstLine(pointList.begin(), pointList.begin() + index + 1);
std::vector<Pnt> lastLine(pointList.begin() + index, pointList.end());
DouglasPeucker(firstLine, epsilon, recResults1);
DouglasPeucker(lastLine, epsilon, recResults2);
out.assign(recResults1.begin(), recResults1.end() - 1);
out.insert(out.end(), recResults2.begin(), recResults2.end());
}
else {
out.clear();
out.push_back(pointList[0]);
out.push_back(pointList[end]);
}
}
// Chaikin 平滑算法 (保留直角)
// 迭代一次会把一个角切成两个,但会智能跳过 90 度左右的角
std::vector<Pnt> ChaikinSmooth(const std::vector<Pnt>& points, int iterations) {
if (points.size() < 3) return points;
std::vector<Pnt> current = points;
const double RIGHT_ANGLE_TOLERANCE = 0.15;
for (int iter = 0; iter < iterations; iter++) {
size_t n = current.size();
if (n < 3) break;
std::vector<Pnt> next;
// 标记哪些点需要保留 (是直角)
std::vector<bool> keepCorner(n, false);
for (size_t i = 0; i < n - 1; i++) {
// 获取前后点 (处理闭合回路)
Pnt prev = (i == 0) ? current[n - 2] : current[i - 1];
Pnt curr = current[i];
Pnt succ = current[i + 1];
// 计算向量
double x1 = prev.x - curr.x;
double y1 = prev.y - curr.y;
double x2 = succ.x - curr.x;
double y2 = succ.y - curr.y;
double len1 = std::sqrt(x1 * x1 + y1 * y1);
double len2 = std::sqrt(x2 * x2 + y2 * y2);
if (len1 > 1e-9 && len2 > 1e-9) {
// 计算余弦值: (v1 . v2) / |v1||v2|
double dot = x1 * x2 + y1 * y2;
double cosTheta = dot / (len1 * len2);
// 判断是否接近 90 度 (点积接近 0)
if (std::abs(cosTheta) < RIGHT_ANGLE_TOLERANCE) {
keepCorner[i] = true;
}
}
}
// 对于闭合多边形,首尾是同一个点,状态必须同步
if (n > 0) keepCorner[n - 1] = keepCorner[0];
// 生成新点
for (size_t i = 0; i < n - 1; i++)
{
Pnt p0 = current[i];
Pnt p1 = current[i + 1];
Pnt q, r;
// --- 确定 Q 点 (靠近 p0) ---
if (keepCorner[i])
{
q = p0; // 如果 p0 是直角Q 点就是 p0 本身 (不切割)
}
else
{
q.x = 0.75 * p0.x + 0.25 * p1.x;
q.y = 0.75 * p0.y + 0.25 * p1.y;
}
// --- 确定 R 点 (靠近 p1) ---
// 注意p1 的保留状态由 keepCorner[i+1] 决定
if (keepCorner[i + 1])
{
r = p1; // 如果 p1 是直角R 点就是 p1 本身
}
else
{
r.x = 0.25 * p0.x + 0.75 * p1.x;
r.y = 0.25 * p0.y + 0.75 * p1.y;
}
// 添加 Q (避免重复)
if (next.empty() || std::abs(next.back().x - q.x) > 1e-9 || std::abs(next.back().y - q.y) > 1e-9) {
next.push_back(q);
}
// 添加 R (避免重复)
if (std::abs(next.back().x - r.x) > 1e-9 || std::abs(next.back().y - r.y) > 1e-9) {
next.push_back(r);
}
}
// 确保闭合 (如果生成结果首尾不接,强制闭合)
if (!next.empty()) {
Pnt first = next.front();
Pnt last = next.back();
if (std::abs(first.x - last.x) > 1e-9 || std::abs(first.y - last.y) > 1e-9) {
next.push_back(first);
}
}
current = next;
}
return current;
}
// 简单的 Moore-Neighbor 边界追踪 (获取网格边界)
// gridFunc: 返回 true 表示该位置是有效网格
std::vector<Pnt> TraceBoundary(int nx, int ny, double xmin, double ymin, double dx, double dy,
std::function<bool(int, int)> gridFunc)
{
std::vector<Pnt> boundary;
// --- 寻找起始点 ---
int sx = -1, sy = -1;
for (int j = 0; j < ny; ++j)
{
for (int i = 0; i < nx; ++i)
{
if (gridFunc(i, j))
{
sx = i; sy = j;
goto FoundStart; // 找到左下角第一个点
}
}
}
return boundary; // 空
FoundStart:
int cx = sx; // 当前像素坐标
int cy = sy;
int dir = 0; // 0:上, 1:右, 2:下, 3:左 (相对于像素的移动方向)
struct Edge
{
double x1, y1, x2, y2;
bool operator==(const Edge& other) const
{
// 无向边比较
return (std::abs(x1 - other.x1) < 1e-9 && std::abs(y1 - other.y1) < 1e-9 && std::abs(x2 - other.x2) < 1e-9 && std::abs(y2 - other.y2) < 1e-9) ||
(std::abs(x1 - other.x2) < 1e-9 && std::abs(y1 - other.y2) < 1e-9 && std::abs(x2 - other.x1) < 1e-9 && std::abs(y2 - other.y1) < 1e-9);
}
};
std::vector<Pnt> rawPoints;
int vx = sx, vy = sy;
int facing = 1; // 0:上(y+), 1:右(x+), 2:下(y-), 3:左(x-)
// 起始状态记录
int start_vx = vx, start_vy = vy, start_facing = facing;
bool first_step = true;
auto is_valid = [&](int c, int r) {
if (c < 0 || c >= nx || r < 0 || r >= ny) return false;
return gridFunc(c, r);
};
// 这里的逻辑是追踪格子的**间隙**
// 左手定则:始终保持有效格子在左侧
int loop_safety = 0;
int max_loop = nx * ny * 4;
do {
// 记录当前点
Pnt p;
p.x = xmin + vx * dx - dx * 0.5; // 注意GridGenerationBoundary里xmin是中心还是边缘
p.y = ymin + vy * dy - dy * 0.5; // 根据Python代码 cx = xmin + c * dx通常xmin是第一个网格中心
boundary.push_back(p);
int left_x = vx, left_y = vy; // 需根据 facing 调整
int right_x = vx, right_y = vy;
// 当前边左边的格子
int c1_x, c1_y; // 左侧格子
if (facing == 0) { c1_x = vx - 1; c1_y = vy; }
else if (facing == 1) { c1_x = vx; c1_y = vy; }
else if (facing == 2) { c1_x = vx; c1_y = vy - 1; }
else { c1_x = vx - 1; c1_y = vy - 1; }
bool q1 = is_valid(vx, vy);
bool q2 = is_valid(vx - 1, vy);
bool q3 = is_valid(vx - 1, vy - 1);
bool q4 = is_valid(vx, vy - 1);
int state = (q2 ? 8 : 0) | (q1 ? 4 : 0) | (q4 ? 2 : 0) | (q3 ? 1 : 0);
break;
} while (vx != start_vx || vy != start_vy || first_step);
return boundary;
}
};