#pragma once #include "StdAfx.h" #include #include #include #include // 简单的点结构 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& pointList, double epsilon, std::vector& 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 recResults1; std::vector recResults2; std::vector firstLine(pointList.begin(), pointList.begin() + index + 1); std::vector 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 ChaikinSmooth(const std::vector& points, int iterations) { if (points.size() < 3) return points; std::vector 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 next; // 标记哪些点需要保留 (是直角) std::vector 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 TraceBoundary(int nx, int ny, double xmin, double ymin, double dx, double dy, std::function gridFunc) { std::vector 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 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; } };