Files
ViewDesignEngine/tests/mesh/test_hex_mesh.cpp
T
茂之钳 63fba5389a
CI / Build & Test (push) Failing after 31s
CI / Release Build (push) Failing after 31s
Build & Test / build-and-test (push) Has been cancelled
Build & Test / python-bindings (push) Has been cancelled
feat(v10): tolerant modeling + Class-A deep + hex mesh + constraint solver + drawing standards
v10.1 — Tolerant Modeling (对标 Parasolid):
- tolerant_modeling.h/.cpp: TolerantVertex/Edge, merge_within_tolerance (BFS)
- gap_bridging (free edge detection → triangle filling)
- overlap_resolution (normal+centroid+AABB detection)
- tolerant_boolean (heal→degenerate detect→boolean→heal) + BooleanDiagnostic
- import_heal_pipeline (STEP one-shot repair)
- ssi_boolean enhanced: coplanar/collinear/tangent detection, GMP on all classify paths
- step_import enhanced: broken file recovery, non-standard entity mapping, diagnostic report
- 30 tests, 4112 total lines

v10.2 — Class-A Deep + Hex Mesh (对标 CGM + ANSYS):
- class_a_surfacing enhanced: g3_blend_with_constraints, surface_energy_minimization
- curvature_continuity_optimization, reflection_line_discontinuity (+720 lines)
- fea_mesh enhanced: mapped_hex, submapped_hex, multi_block_hex (TFI), hex_quality_optimization (+522 lines)
- 22 tests

v10.3 — Constraint Solver + Drawing Standards (对标 D-Cubed + AutoCAD):
- constraint_solver enhanced: DOFAnalyzer, RedundancyDetector, ConstraintPropagator, KinematicChainSolver
- drawing_standards.h/.cpp: IsoStandard (ISO 128/129), AnsiStandard (ASME Y14.5), JisStandard (JIS B 0001)
- 12 tests

12 files, ~5000 lines, 64 tests. Target: 87% → 93%
2026-07-27 00:33:30 +08:00

272 lines
9.1 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include <gtest/gtest.h>
#include "vde/mesh/fea_mesh.h"
#include "vde/brep/modeling.h"
#include <cmath>
#include <map>
using namespace vde::mesh;
using namespace vde::brep;
using namespace vde::core;
// ═══════════════════════════════════════════════════════════
// 1. mapped_hex_mesh — 映射法六面体测试
// ═══════════════════════════════════════════════════════════
TEST(MappedHexTest, BoxMappedMesh_NonEmpty) {
auto box = make_box(2.0, 2.0, 2.0);
FaceRegion src;
src.face_indices = {0};
src.name = "bottom";
FaceRegion tgt;
tgt.face_indices = {1};
tgt.name = "top";
auto mesh = mapped_hex_mesh(box, src, tgt, 3);
EXPECT_EQ(mesh.element_type, FEAElementType::Hex8);
}
TEST(MappedHexTest, BoxMappedMesh_ValidConnectivity) {
auto box = make_box(1.0, 1.0, 1.0);
FaceRegion src;
src.face_indices = {0};
FaceRegion tgt;
tgt.face_indices = {1};
auto mesh = mapped_hex_mesh(box, src, tgt, 2);
for (size_t ei = 0; ei < mesh.num_elements(); ++ei) {
auto& e = mesh.elements[ei];
EXPECT_EQ(e.size(), 8u);
for (int vi : e) {
EXPECT_GE(vi, 0);
EXPECT_LT(static_cast<size_t>(vi), mesh.num_vertices());
}
}
}
TEST(MappedHexTest, BoxMappedMesh_LayerCount) {
auto box = make_box(1.0, 1.0, 1.0);
FaceRegion src;
src.face_indices = {0};
FaceRegion tgt;
tgt.face_indices = {1};
int layers = 4;
auto mesh = mapped_hex_mesh(box, src, tgt, layers);
// 顶点数 = 源面顶点 × (layers + 1)
EXPECT_GT(mesh.num_vertices(), 0u);
}
// ═══════════════════════════════════════════════════════════
// 2. submapped_hex_mesh — 子映射法六面体测试
// ═══════════════════════════════════════════════════════════
TEST(SubmappedHexTest, BoxSubmappedMesh_NonEmpty) {
auto box = make_box(1.0, 1.0, 1.0);
auto mesh = submapped_hex_mesh(box);
EXPECT_EQ(mesh.element_type, FEAElementType::Hex8);
EXPECT_GT(mesh.num_vertices(), 0u);
EXPECT_GT(mesh.num_elements(), 0u);
}
TEST(SubmappedHexTest, BoxSubmappedMesh_ValidConnectivity) {
auto box = make_box(2.0, 1.0, 1.0);
auto mesh = submapped_hex_mesh(box);
for (size_t ei = 0; ei < mesh.num_elements(); ++ei) {
auto& e = mesh.elements[ei];
EXPECT_EQ(e.size(), 8u);
for (int vi : e) {
EXPECT_GE(vi, 0);
EXPECT_LT(static_cast<size_t>(vi), mesh.num_vertices());
}
}
}
TEST(SubmappedHexTest, SphereSubmappedMesh_NonTrivial) {
auto sphere = make_sphere(1.0);
auto mesh = submapped_hex_mesh(sphere);
EXPECT_GT(mesh.num_vertices(), 0u);
EXPECT_GT(mesh.num_elements(), 0u);
}
// ═══════════════════════════════════════════════════════════
// 3. multi_block_hex_mesh — 多块结构化六面体测试
// ═══════════════════════════════════════════════════════════
TEST(MultiBlockHexTest, EmptyBlocks_FallbackGrid) {
auto box = make_box(2.0, 2.0, 2.0);
// blocks为空时回退到4×4×4均匀栅格
std::vector<BlockRegion> blocks;
auto mesh = multi_block_hex_mesh(box, blocks);
EXPECT_EQ(mesh.element_type, FEAElementType::Hex8);
EXPECT_GT(mesh.num_vertices(), 0u);
EXPECT_GT(mesh.num_elements(), 0u);
// 4×4×4 grid → 4^3=64 elements
EXPECT_GE(mesh.num_elements(), 64u);
}
TEST(MultiBlockHexTest, SingleBlock_TFILinear) {
auto box = make_box(1.0, 1.0, 1.0);
BlockRegion block;
block.corner_vertices = {
Point3D(0,0,0), Point3D(1,0,0), Point3D(1,1,0), Point3D(0,1,0),
Point3D(0,0,1), Point3D(1,0,1), Point3D(1,1,1), Point3D(0,1,1)
};
block.n_i = 2;
block.n_j = 2;
block.n_k = 2;
std::vector<BlockRegion> blocks = {block};
auto mesh = multi_block_hex_mesh(box, blocks);
EXPECT_GT(mesh.num_vertices(), 0u);
EXPECT_GT(mesh.num_elements(), 0u);
}
TEST(MultiBlockHexTest, MultiBlocks_ValidConnectivity) {
auto box = make_box(2.0, 2.0, 2.0);
BlockRegion b1;
b1.corner_vertices = {
Point3D(0,0,0), Point3D(1,0,0), Point3D(1,1,0), Point3D(0,1,0),
Point3D(0,0,1), Point3D(1,0,1), Point3D(1,1,1), Point3D(0,1,1)
};
b1.n_i = 2; b1.n_j = 2; b1.n_k = 2;
BlockRegion b2;
b2.corner_vertices = {
Point3D(1,0,0), Point3D(2,0,0), Point3D(2,1,0), Point3D(1,1,0),
Point3D(1,0,1), Point3D(2,0,1), Point3D(2,1,1), Point3D(1,1,1)
};
b2.n_i = 2; b2.n_j = 2; b2.n_k = 2;
std::vector<BlockRegion> blocks = {b1, b2};
auto mesh = multi_block_hex_mesh(box, blocks);
for (size_t ei = 0; ei < mesh.num_elements(); ++ei) {
auto& e = mesh.elements[ei];
EXPECT_EQ(e.size(), 8u);
for (int vi : e) {
EXPECT_GE(vi, 0);
EXPECT_LT(static_cast<size_t>(vi), mesh.num_vertices());
}
}
}
// ═══════════════════════════════════════════════════════════
// 4. hex_quality_optimization — 六面体质量优化 + untangling
// ═══════════════════════════════════════════════════════════
TEST(HexQualityOptTest, UnitCube_QualityOptimization) {
// 创建单位立方体六面体网格
FEAMesh mesh;
mesh.element_type = FEAElementType::Hex8;
mesh.vertices = {
{0,0,0}, {1,0,0}, {1,1,0}, {0,1,0},
{0,0,1}, {1,0,1}, {1,1,1}, {0,1,1}
};
mesh.elements.push_back({0,1,2,3,4,5,6,7});
mesh.material_ids.push_back(0);
HexOptimizationParams params;
params.untangle = true;
params.laplacian_smooth = true;
params.max_iterations = 5;
auto result = hex_quality_optimization(mesh, params);
EXPECT_GT(result.initial_quality.total_elements, 0u);
EXPECT_GE(result.final_quality.avg_jacobian, 0.0);
}
TEST(HexQualityOptTest, QualityReport_AfterOptimization) {
auto box = make_box(2.0, 2.0, 2.0);
// 生成结构化六面体网格
std::vector<BlockRegion> blocks;
auto mesh = multi_block_hex_mesh(box, blocks);
auto initial = fea_quality_report(mesh);
EXPECT_GT(initial.total_elements, 0u);
HexOptimizationParams params;
params.laplacian_smooth = true;
params.max_iterations = 10;
auto result = hex_quality_optimization(mesh, params);
EXPECT_EQ(result.final_quality.total_elements, initial.total_elements);
EXPECT_GE(result.final_quality.avg_jacobian, 0.0);
EXPECT_LE(result.final_quality.avg_jacobian, 1.0);
}
TEST(HexQualityOptTest, Untangle_Inverted) {
// 创建一个轻微扭曲的六面体
FEAMesh mesh;
mesh.element_type = FEAElementType::Hex8;
mesh.vertices = {
{0,0,0}, {1,0,0}, {1,1,0}, {0,1,0},
{0,0,1}, {1,0,1}, {0.3,0.3,1}, {0,1,1} // 顶点6向内扭曲
};
mesh.elements.push_back({0,1,2,3,4,5,6,7});
mesh.material_ids.push_back(0);
HexOptimizationParams params;
params.untangle = true;
params.laplacian_smooth = true;
params.optimization_based_smooth = false;
params.max_iterations = 10;
auto result = hex_quality_optimization(mesh, params);
EXPECT_GT(result.final_quality.avg_jacobian, 0.0);
}
// ═══════════════════════════════════════════════════════════
// 5. 综合集成测试
// ═══════════════════════════════════════════════════════════
TEST(HexIntegrationTest, MappedThenOptimize) {
auto box = make_box(2.0, 2.0, 2.0);
FaceRegion src;
src.face_indices = {0};
FaceRegion tgt;
tgt.face_indices = {1};
auto mesh = mapped_hex_mesh(box, src, tgt, 3);
if (mesh.num_elements() == 0) {
GTEST_SKIP() << "mapped_hex_mesh returned empty (no quad faces available)";
}
HexOptimizationParams params;
params.max_iterations = 5;
auto result = hex_quality_optimization(mesh, params);
EXPECT_EQ(result.final_quality.total_elements, mesh.num_elements());
}
TEST(HexIntegrationTest, SubmappedThenOptimize) {
auto box = make_box(1.0, 1.0, 1.0);
auto mesh = submapped_hex_mesh(box);
HexOptimizationParams params;
params.laplacian_smooth = true;
params.max_iterations = 5;
auto result = hex_quality_optimization(mesh, params);
EXPECT_EQ(result.final_quality.total_elements, mesh.num_elements());
}