Files
ViewDesignEngine/tests/brep/test_measure.cpp
T
茂之钳 1e196473bc
CI / Build & Test (push) Failing after 39s
CI / Release Build (push) Failing after 29s
Build & Test / build-and-test (push) Has been cancelled
Build & Test / python-bindings (push) Has been cancelled
feat(v4.3): BOM (bill of materials) — flat merged, name-sorted
- BomEntry struct: name, quantity, volume, total_volume
- generate_bom(): recursive assembly traversal, same-name merging
- 5 new tests, 26/26 passing
2026-07-25 07:14:27 +00:00

227 lines
8.3 KiB
C++

#include <gtest/gtest.h>
#include "vde/brep/brep.h"
#include "vde/brep/modeling.h"
#include "vde/brep/measure.h"
#include "vde/brep/assembly.h"
#include <cmath>
using namespace vde::brep;
using namespace vde::core;
// ═══════════════════════════════════════════════════════════
// Volume measurements
// ═══════════════════════════════════════════════════════════
TEST(MeasureTest, Volume_UnitBox) {
auto box = make_box(2, 2, 2);
double vol = volume(box);
EXPECT_NEAR(vol, 8.0, 0.2);
}
TEST(MeasureTest, Volume_LargeBox) {
auto box = make_box(10, 5, 3);
double vol = volume(box);
EXPECT_NEAR(vol, 150.0, 1.0);
}
// ═══════════════════════════════════════════════════════════
// Surface area measurements
// ═══════════════════════════════════════════════════════════
TEST(MeasureTest, SurfaceArea_UnitBox) {
auto box = make_box(2, 2, 2);
double area = surface_area(box);
EXPECT_NEAR(area, 24.0, 0.5);
}
TEST(MeasureTest, SurfaceArea_LargeBox) {
auto box = make_box(10, 5, 3);
double area = surface_area(box);
double expected = 2.0 * (10*5 + 10*3 + 5*3);
EXPECT_NEAR(area, expected, 2.0);
}
// ═══════════════════════════════════════════════════════════
// Centroid measurements
// ═══════════════════════════════════════════════════════════
TEST(MeasureTest, Centroid_BoxAtOrigin) {
auto box = make_box(2, 2, 2);
auto c = centroid(box);
EXPECT_NEAR(c.x(), 0.0, 0.01);
EXPECT_NEAR(c.y(), 0.0, 0.01);
EXPECT_NEAR(c.z(), 0.0, 0.01);
}
// ═══════════════════════════════════════════════════════════
// Distance measurements
// ═══════════════════════════════════════════════════════════
TEST(MeasureTest, Distance_SeparatedBoxes) {
auto box1 = make_box(1, 1, 1);
auto box2 = make_box(1, 1, 1);
double d = distance(box1, box2);
EXPECT_NEAR(d, 0.0, 0.1);
}
TEST(MeasureTest, Distance_OverlappingBoxes) {
auto box1 = make_box(2, 2, 2);
auto box2 = make_box(2, 2, 2);
double d = distance(box1, box2);
EXPECT_NEAR(d, 0.0, 0.1);
}
TEST(MeasureTest, Volume_NonNegative) {
auto box = make_box(2, 2, 2);
EXPECT_GE(volume(box), 0.0);
}
TEST(MeasureTest, SurfaceArea_NonNegative) {
auto box = make_box(2, 2, 2);
EXPECT_GE(surface_area(box), 0.0);
}
TEST(MeasureTest, Distance_NonNegative) {
auto box1 = make_box(1, 1, 1);
auto box2 = make_box(1, 1, 1);
EXPECT_GE(distance(box1, box2), 0.0);
}
// ═══════════════════════════════════════════════════════════
// Mass properties
// ═══════════════════════════════════════════════════════════
TEST(MassPropertiesTest, Box_Volume) {
auto box = make_box(2, 2, 2);
auto mp = mass_properties(box);
EXPECT_NEAR(mp.volume, 8.0, 1.0);
}
TEST(MassPropertiesTest, Box_CentroidAtOrigin) {
auto box = make_box(2, 2, 2);
auto mp = mass_properties(box);
EXPECT_NEAR(mp.centroid.x(), 0.0, 0.3);
EXPECT_NEAR(mp.centroid.y(), 0.0, 0.3);
EXPECT_NEAR(mp.centroid.z(), 0.0, 0.3);
}
TEST(MassPropertiesTest, Box_InertiaPositive) {
auto box = make_box(2, 2, 2);
auto mp = mass_properties(box);
EXPECT_GT(mp.inertia(0,0), 0.0);
EXPECT_GT(mp.inertia(1,1), 0.0);
EXPECT_GT(mp.inertia(2,2), 0.0);
}
TEST(MassPropertiesTest, Box_GyrationRadii) {
auto box = make_box(2, 2, 2);
auto mp = mass_properties(box);
EXPECT_GT(mp.gyration_radii.x(), 0.0);
EXPECT_GT(mp.gyration_radii.y(), 0.0);
EXPECT_GT(mp.gyration_radii.z(), 0.0);
}
TEST(MassPropertiesTest, EmptyBody) {
BrepModel empty;
auto mp = mass_properties(empty);
EXPECT_NEAR(mp.volume, 0.0, 1e-10);
}
// ═══════════════════════════════════════════════════════════
// Clearance (gap analysis)
// ═══════════════════════════════════════════════════════════
TEST(ClearanceTest, SeparatedBoxes) {
auto box1 = make_box(1, 1, 1);
auto box2 = make_box(1, 1, 1);
double c = clearance(box1, box2);
EXPECT_GE(c, 0.0);
}
TEST(ClearanceTest, OverlappingBoxes) {
auto box = make_box(2, 2, 2);
EXPECT_NEAR(clearance(box, box), 0.0, 1e-6);
}
// ═══════════════════════════════════════════════════════════
// Assembly mass properties
// ═══════════════════════════════════════════════════════════
TEST(AssemblyMassTest, SinglePart) {
Assembly assy("test");
assy.root.add_part("box", make_box(2, 2, 2));
auto mp = assembly_mass_properties(assy.root);
EXPECT_NEAR(mp.volume, 8.0, 1.0);
}
TEST(AssemblyMassTest, TwoPartsCombined) {
Assembly assy("test");
assy.root.add_part("box1", make_box(2, 2, 2));
assy.root.add_part("box2", make_box(2, 2, 2));
auto mp = assembly_mass_properties(assy.root);
EXPECT_NEAR(mp.volume, 16.0, 2.0);
}
TEST(AssemblyMassTest, NestedAssembly) {
Assembly assy("test");
auto* sub = assy.root.add_subassembly("sub");
sub->add_part("box", make_box(2, 2, 2));
assy.root.add_part("box2", make_box(2, 2, 2));
auto mp = assembly_mass_properties(assy.root);
EXPECT_NEAR(mp.volume, 16.0, 2.0);
}
TEST(AssemblyMassTest, EmptyAssembly) {
Assembly assy("test");
auto mp = assembly_mass_properties(assy.root);
EXPECT_NEAR(mp.volume, 0.0, 1e-10);
}
// ═══════════════════════════════════════════════════════════
// BOM (Bill of Materials)
// ═══════════════════════════════════════════════════════════
TEST(BomTest, SinglePart) {
Assembly assy("test");
assy.root.add_part("bracket", make_box(2, 2, 2));
auto bom = generate_bom(assy.root);
ASSERT_EQ(bom.size(), 1u);
EXPECT_EQ(bom[0].name, "bracket");
EXPECT_EQ(bom[0].quantity, 1);
}
TEST(BomTest, MultipleSameParts) {
Assembly assy("test");
assy.root.add_part("bolt", make_box(1, 1, 1));
assy.root.add_part("bolt", make_box(1, 1, 1));
assy.root.add_part("bolt", make_box(1, 1, 1));
auto bom = generate_bom(assy.root);
ASSERT_EQ(bom.size(), 1u);
EXPECT_EQ(bom[0].quantity, 3);
}
TEST(BomTest, DifferentParts) {
Assembly assy("test");
assy.root.add_part("base", make_box(4, 4, 1));
assy.root.add_part("pillar", make_cylinder(1, 8));
assy.root.add_part("top", make_box(4, 4, 1));
auto bom = generate_bom(assy.root);
ASSERT_EQ(bom.size(), 3u);
}
TEST(BomTest, NestedBom) {
Assembly assy("test");
auto* sub = assy.root.add_subassembly("sub_assy");
sub->add_part("pin", make_cylinder(0.5, 2));
sub->add_part("pin", make_cylinder(0.5, 2));
assy.root.add_part("frame", make_box(5, 5, 1));
auto bom = generate_bom(assy.root);
EXPECT_GE(bom.size(), 2u);
}
TEST(BomTest, EmptyAssembly) {
Assembly assy("test");
auto bom = generate_bom(assy.root);
EXPECT_TRUE(bom.empty());
}