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