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.

312 lines
6.5 KiB
C++

#pragma once
#include <cassert>
#include "DrawOperator/Mesh.h"
#include "DrawModel/Dimension2D.h"
#include "DrawOperator/unordered_dense.h"
#include "Util.h"
#include "clipper2/clipper.h"
/**
* 游离区计算类
*/
class UndecidedZone
{
public:
UndecidedZone(CMesh* pMesh)
: m_pMesh(pMesh)
{
m_pMesh->GetNumber(m_numx, m_numy);
m_pMesh->GetDelt(m_dx, m_dy);
m_pDfg = m_pMesh->GetDfg();
}
/**
* 计算在这个范围内的连通区域
*
* \param zMin z 值最小值
* \param zMax z 值最大值
* \return
*/
std::vector<std::unique_ptr<CCurveEx>> ComputeRegions(double zMin, double zMax) const
{
std::vector<std::unique_ptr<CCurveEx>> result;
Regions regions = FindAllRegions(zMin, zMax);
for (const Region& region : regions)
{
auto regionResult = ComputeRegion(region);
result.insert(result.end(), std::make_move_iterator(regionResult.begin()), std::make_move_iterator(regionResult.end()));
}
return result;
}
/**
* 获取指定范围的网格下标,连通的会放在一起
*
* \param zMin
* \param zMax
* \return
*/
std::vector<std::vector<std::pair<int, int>>> FindAllRegions(double zMin, double zMax) const
{
if (zMin > zMax)
{
return {};
}
if (IsZOutofMesh(zMin, zMax))
{
return {};
}
auto pred = [this, zMin, zMax](int x, int y)
{
double z = m_pDfg->Value(x, y);
return InRange(z, zMin, zMax);
};
return FindAllRegions(pred);
}
private:
using Point = std::pair<int, int>;
using Region = std::vector<Point>;
using Regions = std::vector<Region>;
using Pred = std::function<bool(int x, int y)>;
/**
* 根据连通的节点计算包围的区域
*
* \param region
* \return
*/
std::vector<std::unique_ptr<CCurveEx>> ComputeRegion(const Region& region) const
{
struct PointHash
{
size_t operator()(const std::pair<int, int>& p) const
{
return std::hash<int>()(p.first) ^ (std::hash<int>()(p.second) << 1);
}
};
struct Edge
{
Point p1, p2;
void normalize()
{
if (p1 > p2) std::swap(p1, p2);
}
bool operator==(const Edge& other) const
{
return p1 == other.p1 && p2 == other.p2;
}
};
struct EdgeHash
{
size_t operator()(const Edge& e) const
{
return std::hash<int>()(e.p1.first) ^ (std::hash<int>()(e.p1.second) << 1) ^
(std::hash<int>()(e.p2.first) << 2) ^ (std::hash<int>()(e.p2.second) << 3);
}
};
std::unordered_map<Edge, int, EdgeHash> edgeCount;
// 1. 统计所有边出现次数(四条边构成每个格子)
for (const Point& pt : region)
{
int x = pt.first;
int y = pt.second;
std::array<Edge, 4> edges = {
Edge{{x, y}, {x + 1, y}}, // bottom
Edge{{x + 1, y}, {x + 1, y + 1}}, // right
Edge{{x + 1, y + 1}, {x, y + 1}}, // top
Edge{{x, y + 1}, {x, y}} // left
};
for (auto& edge : edges)
{
edge.normalize();
edgeCount[edge]++;
}
}
// 2. 只保留出现一次的边(即外边)
std::unordered_map<Point, std::vector<Edge>, PointHash> pointToEdges;
std::unordered_set<Edge, EdgeHash> boundaryEdges;
for (const auto& [edge, count] : edgeCount)
{
if (count == 1)
{
pointToEdges[edge.p1].push_back(edge);
pointToEdges[edge.p2].push_back(edge);
boundaryEdges.insert(edge);
}
}
// 3. 从边界边追踪构造轮廓(可能多个多边形)
std::vector<std::unique_ptr<CCurveEx>> result;
std::unordered_set<Edge, EdgeHash> visited;
while (!boundaryEdges.empty())
{
// 任选一条未使用边开始追踪
Edge startEdge = *boundaryEdges.begin();
Point current = startEdge.p1;
std::vector<Point> contour;
contour.push_back(current);
visited.insert(startEdge);
boundaryEdges.erase(startEdge);
Point next = startEdge.p2;
contour.push_back(next);
while (next != current)
{
const auto& edges = pointToEdges[next];
bool found = false;
for (const Edge& e : edges)
{
if (visited.count(e) == 0)
{
Point other = (e.p1 == next) ? e.p2 : e.p1;
contour.push_back(other);
visited.insert(e);
boundaryEdges.erase(e);
next = other;
found = true;
break;
}
}
if (!found) break; // 不闭合就退出
}
// 4. 转换 contour 到实际坐标系并生成 CCurveEx
if (contour.size() >= 3)
{
auto curve = std::make_unique<CCurveEx>();
curve->Create(static_cast<int>(contour.size()));
for (size_t i = 0; i < contour.size(); ++i)
{
curve->x[i] = m_pMesh->left + contour[i].first * m_dx;
curve->y[i] = m_pMesh->bottom + contour[i].second * m_dy;
}
result.push_back(std::move(curve));
}
}
return result;
}
Clipper2Lib::PathD CreatePathD(const Point& point) const
{
double left = m_pMesh->left + point.first * m_dx;
double bottom = m_pMesh->bottom + point.second * m_dy;
double right = left + m_dx;
double top = bottom + m_dy;
return Clipper2Lib::PathD{ {left, top}, { right, top}, {right, bottom}, {left, bottom} };
}
std::unique_ptr<CCurveEx> CreateCurveEx(const Clipper2Lib::PathD& path) const
{
assert(!path.empty());
auto pCurve = std::make_unique<CCurveEx>();
pCurve->Create(static_cast<int>(path.size()) + 1);
size_t i = 0;
for (; i < path.size(); i++)
{
pCurve->x[i] = path[i].x;
pCurve->y[i] = path[i].y;
}
pCurve->x[i] = path[0].x;
pCurve->y[i] = path[0].y;
return pCurve;
}
Regions FindAllRegions(Pred pred) const
{
Regions regions;
std::vector<std::vector<bool>> visited(m_numx, std::vector<bool>(m_numy, false));
for (int i = 0; i < m_numx; i++)
{
for (int j = 0; j < m_numy; j++)
{
if (!visited[i][j] && pred(i, j))
{
regions.push_back(Bfs(i, j, visited, pred));
}
}
}
return regions;
}
std::vector<Point> Bfs(int x, int y, std::vector<std::vector<bool>>& visited, Pred pred) const
{
std::queue<Point> q;
std::vector<Point> region;
// 上下左右
std::vector<int> dx = { -1, 1, 0, 0 };
std::vector<int> dy = { 0, 0, -1, 1 };
q.push({ x, y });
visited[x][y] = true;
while (!q.empty())
{
Point p = q.front();
q.pop();
region.push_back(p);
for (int d = 0; d < 4; d++)
{
int ni = p.first + dx[d];
int nj = p.second + dy[d];
if ((ni >= 0 && ni < m_numx) && (nj >= 0 && nj < m_numy))
{
if (pred(ni, nj) && !visited[ni][nj])
{
visited[ni][nj] = true;
q.push({ ni, nj });
}
}
}
}
return region;
}
bool IsZOutofMesh(double zMin, double zMax) const
{
double mZmin = 0.0;
double mZmax = 0.0;
m_pMesh->GetM(mZmin, mZmax);
return (zMin >= mZmax || zMax <= mZmin);
}
CMesh* m_pMesh = nullptr;
CDimension2D* m_pDfg = nullptr;
double m_dx = 0.0;
double m_dy = 0.0;
long m_numx = 0;
long m_numy = 0;
};