|
|
#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;
|
|
|
}
|
|
|
}; |