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