feat(v4.3): BOM (bill of materials) — flat merged, name-sorted
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

- BomEntry struct: name, quantity, volume, total_volume
- generate_bom(): recursive assembly traversal, same-name merging
- 5 new tests, 26/26 passing
This commit is contained in:
茂之钳
2026-07-25 07:14:27 +00:00
parent 3d435a4801
commit 1e196473bc
3 changed files with 111 additions and 70 deletions
+48
View File
@@ -176,3 +176,51 @@ TEST(AssemblyMassTest, EmptyAssembly) {
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());
}