Files
ViewDesignEngine/tests/brep/test_draft_analysis.cpp
T

169 lines
7.4 KiB
C++
Raw Normal View History

#include <gtest/gtest.h>
#include "vde/brep/brep.h"
#include "vde/brep/modeling.h"
#include "vde/brep/draft_analysis.h"
#include <cmath>
using namespace vde::brep;
using namespace vde::core;
// ═══════════════════════════════════════════════════════════
// Draft Angle — Single Face
// ═══════════════════════════════════════════════════════════
TEST(DraftAnalysisTest, DraftAngle_BoxTopFace) {
auto box = make_box(2, 2, 2); // centered at origin, faces at ±1
// Top face (z = 1): normal = (0, 0, 1)
// Pull direction (0, 0, 1): face normal aligned → angle ≈ 0
double angle = draft_angle(box, 0, Vector3D(0, 0, 1));
EXPECT_NEAR(std::abs(angle), 0.0, 0.1);
}
TEST(DraftAnalysisTest, DraftAngle_BoxSideFace) {
auto box = make_box(2, 2, 2);
// Side face normal is perpendicular to pull direction (0,0,1)
// Draft angle ≈ 90° (π/2)
double angle = draft_angle(box, 0, Vector3D(0, 0, 1));
// Side face: normal.x = ±1, dot(pull) ≈ 0 → angle ≈ π/2
// We just verify it's finite and reasonable
EXPECT_TRUE(std::isfinite(angle));
}
TEST(DraftAnalysisTest, DraftAngle_CylinderTop) {
auto cyl = make_cylinder(2, 6, 32);
// Top cap face normal ≈ (0, 0, 1), pull direction (0, 0, 1) → angle ≈ 0
double angle = draft_angle(cyl, 0, Vector3D(0, 0, 1));
EXPECT_NEAR(std::abs(angle), 0.0, 0.15);
}
TEST(DraftAnalysisTest, DraftAngle_Sphere) {
auto sphere = make_sphere(2, 16, 16);
// Sphere has varying normals; pull dir (0,0,1) → angle varies
double angle = draft_angle(sphere, 0, Vector3D(0, 0, 1));
EXPECT_TRUE(std::isfinite(angle));
}
// ═══════════════════════════════════════════════════════════
// Full Model Analysis
// ═══════════════════════════════════════════════════════════
TEST(DraftAnalysisTest, AnalyzeDraft_Box) {
auto box = make_box(2, 2, 2);
auto result = analyze_draft(box, Vector3D(0, 0, 1), 0.01745); // 1° min
EXPECT_EQ(result.faces.size(), box.num_faces());
EXPECT_GE(result.positive_count + result.negative_count +
result.zero_draft_count + result.undercut_count, 1);
EXPECT_TRUE(std::isfinite(result.area_weighted_angle));
}
TEST(DraftAnalysisTest, AnalyzeDraft_PullUp) {
auto box = make_box(2, 2, 2);
// Pull direction (0,0,1): top = zero draft, sides = positive/negative depending on normal
auto result = analyze_draft(box, Vector3D(0, 0, 1));
EXPECT_GE(result.faces.size(), 1u);
// Check that classifications sum to face count
int total = result.positive_count + result.negative_count +
result.zero_draft_count + result.undercut_count;
EXPECT_EQ(total, static_cast<int>(result.faces.size()));
}
TEST(DraftAnalysisTest, AnalyzeDraft_PullSide) {
auto box = make_box(2, 2, 2);
auto result = analyze_draft(box, Vector3D(1, 0, 0));
EXPECT_GE(result.faces.size(), 1u);
}
TEST(DraftAnalysisTest, AnalyzeDraft_EmptyBody) {
BrepModel empty;
auto result = analyze_draft(empty, Vector3D(0, 0, 1));
EXPECT_EQ(result.faces.size(), 0u);
EXPECT_TRUE(result.is_moldable());
}
TEST(DraftAnalysisTest, AnalyzeDraft_MinAngleLarger) {
auto box = make_box(2, 2, 2);
// With a very large min angle (45°), most faces should be classified as zero draft
auto result = analyze_draft(box, Vector3D(0, 0, 1), M_PI / 4.0);
EXPECT_GE(result.zero_draft_count, 0);
}
// ═══════════════════════════════════════════════════════════
// Classification
// ═══════════════════════════════════════════════════════════
TEST(DraftAnalysisTest, Classification_Positive) {
auto box = make_box(2, 2, 2);
auto result = analyze_draft(box, Vector3D(0, 0, 1), 0.001);
// At least some faces should be classified
bool has_positive = result.positive_count > 0;
bool has_negative = result.negative_count > 0;
bool has_zero = result.zero_draft_count > 0;
EXPECT_TRUE(has_positive || has_negative || has_zero);
}
TEST(DraftAnalysisTest, HasUndercut_SimpleBox) {
auto box = make_box(2, 2, 2);
auto result = analyze_draft(box, Vector3D(0, 0, 1));
// Simple box along Z should be moldable (no undercut)
EXPECT_TRUE(result.is_moldable());
}
// ═══════════════════════════════════════════════════════════
// Report
// ═══════════════════════════════════════════════════════════
TEST(DraftAnalysisTest, Report_NotEmpty) {
auto box = make_box(2, 2, 2);
auto result = analyze_draft(box, Vector3D(0, 0, 1));
auto report = draft_report(result);
EXPECT_FALSE(report.empty());
EXPECT_NE(report.find("Draft Analysis Report"), std::string::npos);
EXPECT_NE(report.find("Moldability"), std::string::npos);
}
// ═══════════════════════════════════════════════════════════
// Angle Distribution
// ═══════════════════════════════════════════════════════════
TEST(DraftAnalysisTest, AngleDistribution_NotEmpty) {
auto box = make_box(2, 2, 2);
auto result = analyze_draft(box, Vector3D(0, 0, 1));
auto dist = result.angle_distribution(10);
EXPECT_GT(dist.size(), 0u);
}
TEST(DraftAnalysisTest, AngleDistribution_ZeroBins) {
auto box = make_box(2, 2, 2);
auto result = analyze_draft(box, Vector3D(0, 0, 1));
auto dist = result.angle_distribution(0);
EXPECT_EQ(dist.size(), 0u);
}
// ═══════════════════════════════════════════════════════════
// Draft Face Creation (stub — returns false)
// ═══════════════════════════════════════════════════════════
TEST(DraftAnalysisTest, CreateDraftFace_Stub) {
auto box = make_box(2, 2, 2);
// create_draft_face is a stub (needs mutable surface access)
bool ok = create_draft_face(box, 0, Vector3D(0, 0, 1), 0.1);
// Stub returns false (not yet implemented at topology level)
EXPECT_FALSE(ok);
}
TEST(DraftAnalysisTest, ApplyDraft_EmptyList) {
auto box = make_box(2, 2, 2);
int count = apply_draft(box, {}, Vector3D(0, 0, 1), 0.1);
EXPECT_EQ(count, 0);
}
TEST(DraftAnalysisTest, ApplyDraft_Stub) {
auto box = make_box(2, 2, 2);
int count = apply_draft(box, {0, 1}, Vector3D(0, 0, 1), 0.1);
// create_draft_face returns false → count = 0
EXPECT_EQ(count, 0);
}