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.

311 lines
12 KiB
C++

#include "StdAfx.h"
#include "GenerateSEC.h"
#include <cmath>
#include <algorithm>
#include <limits>
// --- GEOS 上下文管理 ---
void geos_message_handler(const char* fmt, ...) { /* 忽略通知 */ }
class GEOSContext {
public:
GEOSContext() { handle = initGEOS_r(geos_message_handler, geos_message_handler); }
~GEOSContext() { finishGEOS_r(handle); }
GEOSContextHandle_t handle;
};
static GEOSContext g_geos;
// --- 辅助工具 ---
inline double Round6(double v) { return std::floor(v * 1e6 + 0.5) / 1e6; }
/**
* 核心逻辑:深度复刻 Python export_to_csv_single_line
* 1. 动态更新 Shell (包含已连接孔洞的点)
* 2. 6位精度搜索最近点
* 3. 严格的跳转重复点序列
*/
static std::vector<CPoint2D> GEOSPolygonToSingleLine(const GEOSGeometry* poly) {
GEOSContextHandle_t h = g_geos.handle;
if (!poly || GEOSGeomTypeId_r(h, poly) != GEOS_POLYGON) return {};
auto extract_ring = [&](const GEOSGeometry* ring) {
const GEOSCoordSequence* seq = GEOSGeom_getCoordSeq_r(h, ring);
uint32_t size;
GEOSCoordSeq_getSize_r(h, seq, &size);
std::vector<CPoint2D> pts;
for (uint32_t i = 0; i < size - 1; ++i) { // [:-1] 移除闭合点
double x, y;
GEOSCoordSeq_getXY_r(h, seq, i, &x, &y);
pts.push_back({ x, y });
}
return pts;
};
// 1. 提取初始外环
std::vector<CPoint2D> shell = extract_ring(GEOSGetExteriorRing_r(h, poly));
// 2. 提取所有内孔
int num_holes = GEOSGetNumInteriorRings_r(h, poly);
std::vector<std::vector<CPoint2D>> holes;
for (int i = 0; i < num_holes; ++i) {
holes.push_back(extract_ring(GEOSGetInteriorRingN_r(h, poly, i)));
}
// 3. 贪婪连接逻辑
while (!holes.empty()) {
double min_d2 = (std::numeric_limits<double>::max)();
int best_hole_idx = -1;
size_t best_s = 0, best_h = 0;
for (int i = 0; i < (int)holes.size(); ++i) {
const auto& hole = holes[i];
for (size_t s_idx = 0; s_idx < shell.size(); ++s_idx) {
double sx = Round6(shell[s_idx].x0);
double sy = Round6(shell[s_idx].y0);
for (size_t h_idx = 0; h_idx < hole.size(); ++h_idx) {
double hx = Round6(hole[h_idx].x0);
double hy = Round6(hole[h_idx].y0);
double d2 = (sx - hx) * (sx - hx) + (sy - hy) * (sy - hy);
// 严格小于,模拟 np.argmin 的第一个索引优先原则
if (d2 < min_d2 - 1e-12) {
min_d2 = d2;
best_hole_idx = i;
best_s = s_idx;
best_h = h_idx;
}
}
}
}
// 4. 执行拼接
std::vector<CPoint2D> target_hole = holes[best_hole_idx];
holes.erase(holes.begin() + best_hole_idx);
CPoint2D conn_shell = shell[best_s];
CPoint2D conn_hole = target_hole[best_h];
std::vector<CPoint2D> next_shell;
next_shell.reserve(shell.size() + target_hole.size() + 2);
for (size_t i = 0; i <= best_s; ++i) next_shell.push_back(shell[i]);
for (size_t i = 0; i < target_hole.size(); ++i) {
next_shell.push_back(target_hole[(best_h + i) % target_hole.size()]);
}
next_shell.push_back(conn_hole);
next_shell.push_back(conn_shell);
for (size_t i = best_s + 1; i < shell.size(); ++i) next_shell.push_back(shell[i]);
shell = next_shell; // 更新 shell
}
if (!shell.empty()) shell.push_back(shell[0]); // 最后补回闭合点
return shell;
}
/**
* 基础生成逻辑:对齐 Python _generate_base_shapes
*/
static GEOSGeometry* InternalGenerateBase(const std::vector<CPoint2D>& wells, double w, double h, double ang, double gap) {
GEOSContextHandle_t handle = g_geos.handle;
std::vector<GEOSGeometry*> polys;
double rad = ang * 3.14159265358979 / 180.0;
for (const auto& well : wells) {
GEOSCoordSequence* seq = GEOSCoordSeq_create_r(handle, 5, 2);
double dx[] = { -w / 2, w / 2, w / 2, -w / 2, -w / 2 };
double dy[] = { -h / 2, -h / 2, h / 2, h / 2, -h / 2 };
for (int i = 0; i < 5; ++i) {
double rx = dx[i] * cos(rad) - dy[i] * sin(rad);
double ry = dx[i] * sin(rad) + dy[i] * cos(rad);
GEOSCoordSeq_setXY_r(handle, seq, i, well.x0 + rx, well.y0 + ry);
}
GEOSGeometry* ring = GEOSGeom_createLinearRing_r(handle, seq);
polys.push_back(GEOSGeom_createPolygon_r(handle, ring, nullptr, 0));
}
// 1. Unary Union (对应 Python unary_union)
GEOSGeometry* coll = GEOSGeom_createCollection_r(handle, GEOS_MULTIPOLYGON, polys.data(), (uint32_t)polys.size());
GEOSGeometry* merged = GEOSUnaryUnion_r(handle, coll);
// 2. Simplify (对应 Python simplify(0.01))
GEOSGeometry* simplified = GEOSSimplify_r(handle, merged, 0.01);
GEOSGeom_destroy_r(handle, merged);
// 3. Gap Buffer (斜接策略)
if (gap > 0.01) {
GEOSBufferParams* params = GEOSBufferParams_create_r(handle);
GEOSBufferParams_setJoinStyle_r(handle, params, GEOSBUF_JOIN_MITRE);
GEOSBufferParams_setMitreLimit_r(handle, params, 5.0);
GEOSGeometry* temp = GEOSBufferWithParams_r(handle, simplified, params, gap / 2.0);
GEOSGeom_destroy_r(handle, simplified);
simplified = GEOSBufferWithParams_r(handle, temp, params, -gap / 2.0);
GEOSGeom_destroy_r(handle, temp);
GEOSBufferParams_destroy_r(handle, params);
}
return simplified;
}
NItem::GenerateSECReserve::GenerateSECReserve()
{
}
NItem::GenerateSECReserve::~GenerateSECReserve()
{
}
std::vector<std::vector<CPoint2D>> NItem::GenerateSECReserve::CalculatePDP(
const std::vector<CPoint2D>& wells, double width, double height, double angle_deg, double gap,
const std::vector<CPoint2D>& boundaryPoints, bool useClip)
{
GEOSContextHandle_t h = g_geos.handle;
GEOSGeometry* pdp = InternalGenerateBase(wells, width, height, angle_deg, gap);
if (useClip && boundaryPoints.size() >= 3) {
GEOSCoordSequence* seq = GEOSCoordSeq_create_r(h, (uint32_t)boundaryPoints.size() + 1, 2);
for (uint32_t i = 0; i < boundaryPoints.size(); ++i) {
GEOSCoordSeq_setXY_r(h, seq, i, boundaryPoints[i].x0, boundaryPoints[i].y0);
}
GEOSCoordSeq_setXY_r(h, seq, (uint32_t)boundaryPoints.size(), boundaryPoints[0].x0, boundaryPoints[0].y0);
GEOSGeometry* b_ring = GEOSGeom_createLinearRing_r(h, seq);
GEOSGeometry* b_poly = GEOSGeom_createPolygon_r(h, b_ring, nullptr, 0);
GEOSGeometry* clipped = GEOSIntersection_r(h, pdp, b_poly);
GEOSGeom_destroy_r(h, pdp);
GEOSGeom_destroy_r(h, b_poly);
pdp = clipped;
}
std::vector<std::vector<CPoint2D>> results;
int n = GEOSGetNumGeometries_r(h, pdp);
for (int i = 0; i < n; ++i) {
const GEOSGeometry* g = GEOSGetGeometryN_r(h, pdp, i);
GEOSGeometry* g_simple = GEOSSimplify_r(h, g, 0.01); // 导出前 simplify
results.push_back(GEOSPolygonToSingleLine(g_simple));
GEOSGeom_destroy_r(h, g_simple);
}
GEOSGeom_destroy_r(h, pdp);
return results;
}
void NItem::GenerateSECReserve::CalculatePDNP(const std::vector<CPoint2D>& wells, double width, double height,
double angle_deg, double gap, bool useClip, const std::vector<CPoint2D>& boundaryPoints, std::vector<std::vector<CPoint2D>>& pdnpLines)
{
GEOSGeometry* pdnp = InternalGenerateBase(wells, width, height, angle_deg, gap);
InternalClipAndExport(pdnp, boundaryPoints, useClip, pdnpLines);
}
void NItem::GenerateSECReserve::CalculatePUD(const std::vector<CPoint2D>& wells, double width, double height,
double angle_deg, double gap, double pud_mult, bool useClip, const std::vector<CPoint2D>& boundaryPoints, std::vector<std::vector<CPoint2D>>& pudLines)
{
GEOSContextHandle_t h = g_geos.handle;
// 生成作为基准的 PDNP 形状
GEOSGeometry* base_pdnp = InternalGenerateBase(wells, width, height, angle_deg, gap);
// 执行扩边运算
GEOSBufferParams* params = GEOSBufferParams_create_r(h);
GEOSBufferParams_setJoinStyle_r(h, params, GEOSBUF_JOIN_MITRE);
GEOSBufferParams_setMitreLimit_r(h, params, 5.0);
GEOSGeometry* expanded = GEOSBufferWithParams_r(h, base_pdnp, params, width * pud_mult);
// PUD = 扩边区域 - 原始区域
GEOSGeometry* pud = GEOSDifference_r(h, expanded, base_pdnp);
// 清理中间变量
GEOSGeom_destroy_r(h, base_pdnp);
GEOSGeom_destroy_r(h, expanded);
GEOSBufferParams_destroy_r(h, params);
// 裁剪并导出
InternalClipAndExport(pud, boundaryPoints, useClip, pudLines);
}
/**
* 内部辅助函数:处理裁剪、简化并导出单线点集
* 为了避免内存泄漏,此函数会消耗(destroy)传入的 geom
*/
void NItem::GenerateSECReserve::InternalClipAndExport(GEOSGeometry* geom, const std::vector<CPoint2D>& boundaryPoints, bool useClip,
std::vector<std::vector<CPoint2D>>& outLines)
{
GEOSContextHandle_t h = g_geos.handle;
if (!geom) return;
// 1. 裁剪逻辑
GEOSGeometry* finalGeom = geom;
if (useClip && boundaryPoints.size() >= 3) {
GEOSCoordSequence* seq = GEOSCoordSeq_create_r(h, (uint32_t)boundaryPoints.size() + 1, 2);
for (uint32_t i = 0; i < boundaryPoints.size(); ++i) {
GEOSCoordSeq_setXY_r(h, seq, i, boundaryPoints[i].x0, boundaryPoints[i].y0);
}
GEOSCoordSeq_setXY_r(h, seq, (uint32_t)boundaryPoints.size(), boundaryPoints[0].x0, boundaryPoints[0].y0);
GEOSGeometry* b_poly = GEOSGeom_createPolygon_r(h, GEOSGeom_createLinearRing_r(h, seq), nullptr, 0);
finalGeom = GEOSIntersection_r(h, geom, b_poly);
GEOSGeom_destroy_r(h, geom);
GEOSGeom_destroy_r(h, b_poly);
}
// 2. 导出逻辑
if (finalGeom) {
int n = GEOSGetNumGeometries_r(h, finalGeom);
for (int i = 0; i < n; ++i) {
const GEOSGeometry* g = GEOSGetGeometryN_r(h, finalGeom, i);
GEOSGeometry* g_simple = GEOSSimplify_r(h, g, 0.01);
outLines.push_back(GEOSPolygonToSingleLine(g_simple));
GEOSGeom_destroy_r(h, g_simple);
}
GEOSGeom_destroy_r(h, finalGeom);
}
}
void NItem::GenerateSECReserve::CalculateExpansion(
const std::vector<CPoint2D>& wells, double width, double height, double angle_deg, double gap, double pud_mult,
const std::vector<CPoint2D>& boundaryPoints, bool useClip,
std::vector<std::vector<CPoint2D>>& pdnpLines, std::vector<std::vector<CPoint2D>>& pudLines)
{
GEOSContextHandle_t h = g_geos.handle;
GEOSGeometry* pdnp = InternalGenerateBase(wells, width, height, angle_deg, gap);
// PUD 派生
GEOSBufferParams* params = GEOSBufferParams_create_r(h);
GEOSBufferParams_setJoinStyle_r(h, params, GEOSBUF_JOIN_MITRE);
GEOSBufferParams_setMitreLimit_r(h, params, 5.0);
GEOSGeometry* expanded = GEOSBufferWithParams_r(h, pdnp, params, width * pud_mult);
GEOSGeometry* pud = GEOSDifference_r(h, expanded, pdnp);
GEOSGeom_destroy_r(h, expanded);
GEOSBufferParams_destroy_r(h, params);
// 裁剪 (逻辑同 PDP)...
auto clip_func = [&](GEOSGeometry* geom) {
if (!useClip || boundaryPoints.size() < 3) return geom;
GEOSCoordSequence* seq = GEOSCoordSeq_create_r(h, (uint32_t)boundaryPoints.size() + 1, 2);
for (uint32_t i = 0; i < boundaryPoints.size(); ++i) {
GEOSCoordSeq_setXY_r(h, seq, i, boundaryPoints[i].x0, boundaryPoints[i].y0);
}
GEOSCoordSeq_setXY_r(h, seq, (uint32_t)boundaryPoints.size(), boundaryPoints[0].x0, boundaryPoints[0].y0);
GEOSGeometry* b_poly = GEOSGeom_createPolygon_r(h, GEOSGeom_createLinearRing_r(h, seq), nullptr, 0);
GEOSGeometry* res = GEOSIntersection_r(h, geom, b_poly);
GEOSGeom_destroy_r(h, geom); GEOSGeom_destroy_r(h, b_poly);
return res;
};
pdnp = clip_func(pdnp);
pud = clip_func(pud);
auto to_lines = [&](GEOSGeometry* geom, std::vector<std::vector<CPoint2D>>& out) {
int n = GEOSGetNumGeometries_r(h, geom);
for (int i = 0; i < n; ++i) {
GEOSGeometry* g_simple = GEOSSimplify_r(h, GEOSGetGeometryN_r(h, geom, i), 0.01);
out.push_back(GEOSPolygonToSingleLine(g_simple));
GEOSGeom_destroy_r(h, g_simple);
}
};
to_lines(pdnp, pdnpLines);
to_lines(pud, pudLines);
GEOSGeom_destroy_r(h, pdnp); GEOSGeom_destroy_r(h, pud);
}