|
|
|
|
|
#pragma once
|
|
|
|
|
|
#include "StdAfx.h"
|
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
#include <cmath>
|
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
|
#include <list>
|
|
|
|
|
|
|
|
|
|
|
|
// <20>ĵ<F2B5A5B5><C4B5>ṹ
|
|
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>˹-<2D>տ<EFBFBD> (Douglas-Peucker) <20><>ϡ<EFBFBD>㷨
|
|
|
|
|
|
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 ƽ<><C6BD><EFBFBD>㷨 (<28><><EFBFBD><EFBFBD>ֱ<EFBFBD><D6B1>)
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD>һ<EFBFBD>λ<EFBFBD><CEBB><EFBFBD>һ<EFBFBD><D2BB><EFBFBD><EFBFBD><EFBFBD>г<EFBFBD><D0B3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> 90 <20><><EFBFBD><EFBFBD><EFBFBD>ҵĽ<D2B5>
|
|
|
|
|
|
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;
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Щ<EFBFBD><D0A9><EFBFBD><EFBFBD>Ҫ<EFBFBD><D2AA><EFBFBD><EFBFBD> (<28><>ֱ<EFBFBD><D6B1>)
|
|
|
|
|
|
std::vector<bool> keepCorner(n, false);
|
|
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < n - 1; i++) {
|
|
|
|
|
|
// <20><>ȡǰ<C8A1><C7B0><EFBFBD><EFBFBD> (<28><><EFBFBD><EFBFBD><EFBFBD>պϻ<D5BA>·)
|
|
|
|
|
|
Pnt prev = (i == 0) ? current[n - 2] : current[i - 1];
|
|
|
|
|
|
Pnt curr = current[i];
|
|
|
|
|
|
Pnt succ = current[i + 1];
|
|
|
|
|
|
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
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) {
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֵ: (v1 . v2) / |v1||v2|
|
|
|
|
|
|
double dot = x1 * x2 + y1 * y2;
|
|
|
|
|
|
double cosTheta = dot / (len1 * len2);
|
|
|
|
|
|
|
|
|
|
|
|
// <20>ж<EFBFBD><D0B6>Ƿ<EFBFBD><C7B7>ӽ<EFBFBD> 90 <20><> (<28><><EFBFBD><EFBFBD><EFBFBD>ӽ<EFBFBD> 0)
|
|
|
|
|
|
if (std::abs(cosTheta) < RIGHT_ANGLE_TOLERANCE) {
|
|
|
|
|
|
keepCorner[i] = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// <20><><EFBFBD>ڱպ϶<D5BA><CFB6><EFBFBD><EFBFBD>Σ<EFBFBD><CEA3><EFBFBD>β<EFBFBD><CEB2>ͬһ<CDAC><D2BB><EFBFBD>㣬״̬<D7B4><CCAC><EFBFBD><EFBFBD>ͬ<EFBFBD><CDAC>
|
|
|
|
|
|
if (n > 0) keepCorner[n - 1] = keepCorner[0];
|
|
|
|
|
|
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD>µ<EFBFBD>
|
|
|
|
|
|
for (size_t i = 0; i < n - 1; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
Pnt p0 = current[i];
|
|
|
|
|
|
Pnt p1 = current[i + 1];
|
|
|
|
|
|
|
|
|
|
|
|
Pnt q, r;
|
|
|
|
|
|
|
|
|
|
|
|
// --- ȷ<><C8B7> Q <20><> (<28><><EFBFBD><EFBFBD> p0) ---
|
|
|
|
|
|
if (keepCorner[i])
|
|
|
|
|
|
{
|
|
|
|
|
|
q = p0; // <20><><EFBFBD><EFBFBD> p0 <20><>ֱ<EFBFBD>ǣ<EFBFBD>Q <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> p0 <20><><EFBFBD><EFBFBD> (<28><><EFBFBD>и<EFBFBD>)
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
q.x = 0.75 * p0.x + 0.25 * p1.x;
|
|
|
|
|
|
q.y = 0.75 * p0.y + 0.25 * p1.y;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// --- ȷ<><C8B7> R <20><> (<28><><EFBFBD><EFBFBD> p1) ---
|
|
|
|
|
|
// ע<>⣺p1 <20>ı<EFBFBD><C4B1><EFBFBD>״̬<D7B4><CCAC> keepCorner[i+1] <20><><EFBFBD><EFBFBD>
|
|
|
|
|
|
if (keepCorner[i + 1])
|
|
|
|
|
|
{
|
|
|
|
|
|
r = p1; // <20><><EFBFBD><EFBFBD> p1 <20><>ֱ<EFBFBD>ǣ<EFBFBD>R <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> p1 <20><><EFBFBD><EFBFBD>
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
r.x = 0.25 * p0.x + 0.75 * p1.x;
|
|
|
|
|
|
r.y = 0.25 * p0.y + 0.75 * p1.y;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD> Q (<28><><EFBFBD><EFBFBD><EFBFBD>ظ<EFBFBD>)
|
|
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD> R (<28><><EFBFBD><EFBFBD><EFBFBD>ظ<EFBFBD>)
|
|
|
|
|
|
if (std::abs(next.back().x - r.x) > 1e-9 || std::abs(next.back().y - r.y) > 1e-9) {
|
|
|
|
|
|
next.push_back(r);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ȷ<><C8B7><EFBFBD>պ<EFBFBD> (<28><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ɽ<EFBFBD><C9BD><EFBFBD><EFBFBD><EFBFBD>β<EFBFBD><CEB2><EFBFBD>ӣ<EFBFBD>ǿ<EFBFBD>Ʊպ<C6B1>)
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// <20><EFBFBD> Moore-Neighbor <20>߽<EFBFBD><EFBFBD><D7B7> (<28><>ȡ<EFBFBD><C8A1><EFBFBD><EFBFBD><EFBFBD>߽<EFBFBD>)
|
|
|
|
|
|
// gridFunc: <20><><EFBFBD><EFBFBD> true <20><>ʾ<EFBFBD><CABE>λ<EFBFBD><CEBB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ч<EFBFBD><D0A7><EFBFBD><EFBFBD>
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
|
|
// --- Ѱ<><D1B0><EFBFBD><EFBFBD>ʼ<EFBFBD><CABC> ---
|
|
|
|
|
|
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; // <20>ҵ<EFBFBD><D2B5><EFBFBD><EFBFBD>½ǵ<C2BD>һ<EFBFBD><D2BB><EFBFBD><EFBFBD>
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return boundary; // <20><>
|
|
|
|
|
|
|
|
|
|
|
|
FoundStart:
|
|
|
|
|
|
|
|
|
|
|
|
int cx = sx; // <20><>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
int cy = sy;
|
|
|
|
|
|
int dir = 0; // 0:<3A><>, 1:<3A><>, 2:<3A><>, 3:<3A><> (<28><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ص<EFBFBD><D8B5>ƶ<EFBFBD><C6B6><EFBFBD><EFBFBD><EFBFBD>)
|
|
|
|
|
|
struct Edge
|
|
|
|
|
|
{
|
|
|
|
|
|
double x1, y1, x2, y2;
|
|
|
|
|
|
bool operator==(const Edge& other) const
|
|
|
|
|
|
{
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD>߱Ƚ<DFB1>
|
|
|
|
|
|
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:<3A><>(y+), 1:<3A><>(x+), 2:<3A><>(y-), 3:<3A><>(x-)
|
|
|
|
|
|
|
|
|
|
|
|
// <20><>ʼ״̬<D7B4><CCAC>¼
|
|
|
|
|
|
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);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><DFBC><EFBFBD><EFBFBD>ٸ<EFBFBD><D9B8>ӵ<EFBFBD>**<2A><>϶**
|
|
|
|
|
|
// <20><><EFBFBD>ֶ<EFBFBD><D6B6><EFBFBD><EFBFBD><EFBFBD>ʼ<EFBFBD>ձ<EFBFBD><D5B1><EFBFBD><EFBFBD><EFBFBD>Ч<EFBFBD><D0A7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
int loop_safety = 0;
|
|
|
|
|
|
int max_loop = nx * ny * 4;
|
|
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
|
// <20><>¼<EFBFBD><C2BC>ǰ<EFBFBD><C7B0>
|
|
|
|
|
|
Pnt p;
|
|
|
|
|
|
p.x = xmin + vx * dx - dx * 0.5; // ע<>⣺GridGenerationBoundary<72><79>xmin<69><6E><EFBFBD><EFBFBD><EFBFBD>Ļ<EFBFBD><C4BB>DZ<EFBFBD>Ե<EFBFBD><D4B5>
|
|
|
|
|
|
p.y = ymin + vy * dy - dy * 0.5; // <20><><EFBFBD><EFBFBD>Python<6F><6E><EFBFBD><EFBFBD> cx = xmin + c * dx<64><78>ͨ<EFBFBD><CDA8>xmin<69>ǵ<EFBFBD>һ<EFBFBD><D2BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
boundary.push_back(p);
|
|
|
|
|
|
|
|
|
|
|
|
int left_x = vx, left_y = vy; // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> facing <20><><EFBFBD><EFBFBD>
|
|
|
|
|
|
int right_x = vx, right_y = vy;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// <20><>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD><EFBFBD>ߵĸ<DFB5><C4B8><EFBFBD>
|
|
|
|
|
|
int c1_x, c1_y; // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|