mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-22 00:12:34 +00:00
M5b: rib — thin stiffening wall grown from an open sketch line
New CadFeatureType::Rib (appended). A straight open Line entity in a sketch is offset ±thickness/2 along its in-plane perpendicular into a thin rectangle, extruded rib_depth along the sketch-plane normal, and fused to the target body. Line-only for now (ponytail; polyline/arc ribs are a later extension) — a non-line entity fails cleanly at recompute. Four serialized fields (rib_sketch_ref/rib_entity/rib_thickness/rib_depth) appended to both symmetric cereal lists (version stays 2, golden fixture regenerated 29525->30269). MCP: rib. 3 new [CadDocument][rib] tests; suite 86/1376. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BVzKmX6Y1aEteit1HTXG4Q
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
0bbf22ceae
commit
a606dfe00a
@@ -3809,6 +3809,112 @@ TEST_CASE("golden recipe v1 still deserialises", "[CadDocument]")
|
||||
}
|
||||
}
|
||||
|
||||
// --- Rib tests (M5b) ---
|
||||
|
||||
TEST_CASE("rib adds material to a box", "[CadDocument][rib]")
|
||||
{
|
||||
using Catch::Matchers::WithinRel;
|
||||
using Catch::Matchers::WithinAbs;
|
||||
|
||||
CadDocument doc;
|
||||
|
||||
// Build a box: 40x40x10 extruded on XY -> z=[0,10]
|
||||
int sk_box = doc.add_sketch(SketchShape::Rectangle, SketchPlane::XY(),
|
||||
40, 40, 10, "Box");
|
||||
doc.add_extrude(sk_box, 10.0, false, BooleanMode::New, "Extrude");
|
||||
REQUIRE(doc.recompute());
|
||||
REQUIRE(doc.error.empty());
|
||||
double Vbox = double(doc.display_mesh.volume());
|
||||
REQUIRE(Vbox > 0.0);
|
||||
|
||||
// Sketch a single open Line across the box footprint, on the same XY plane.
|
||||
// Line from (5,20) to (35,20), centred in Y but off-centre in X.
|
||||
std::vector<SketchEntity> ents = {
|
||||
{SketchEntity::Type::Line, Vec2d(5, 20), Vec2d(35, 20)},
|
||||
};
|
||||
int sk_rib = doc.add_sketch_entities(ents, SketchPlane::XY(), "RibLine");
|
||||
REQUIRE(sk_rib >= 0);
|
||||
|
||||
int fi = doc.add_rib(sk_rib, 0, 3.0, 12.0, 0, "Rib");
|
||||
REQUIRE(fi >= 0);
|
||||
REQUIRE(doc.features[fi].type == CadFeatureType::Rib);
|
||||
|
||||
bool ok = doc.recompute();
|
||||
REQUIRE(ok);
|
||||
REQUIRE(doc.error.empty());
|
||||
// The rib fused extra material -> volume must be strictly larger.
|
||||
double Vrib = double(doc.display_mesh.volume());
|
||||
REQUIRE(Vrib > Vbox);
|
||||
|
||||
// A rib with a bad sketch ref must fail cleanly, not crash.
|
||||
CadDocument bad;
|
||||
bad.add_sketch(SketchShape::Rectangle, SketchPlane::XY(), 20, 20, 10, "Box");
|
||||
bad.add_extrude(0, 10.0, false, BooleanMode::New, "E");
|
||||
REQUIRE(bad.recompute());
|
||||
bad.add_rib(999, 0, 3.0, 10.0, 0, "BadRib");
|
||||
REQUIRE_FALSE(bad.recompute());
|
||||
}
|
||||
|
||||
TEST_CASE("rib non-line entity rejected safely", "[CadDocument][rib]")
|
||||
{
|
||||
using Catch::Matchers::Contains;
|
||||
|
||||
CadDocument doc;
|
||||
int sk = doc.add_sketch(SketchShape::Rectangle, SketchPlane::XY(), 20, 20, 10, "Box");
|
||||
doc.add_extrude(sk, 10.0, false, BooleanMode::New, "E");
|
||||
REQUIRE(doc.recompute());
|
||||
|
||||
// Sketch with a Circle entity (not a Line)
|
||||
std::vector<SketchEntity> ents = {
|
||||
{SketchEntity::Type::Circle, Vec2d(0, 0), Vec2d(0, 0), Vec2d(0, 0), 5.0},
|
||||
};
|
||||
int sk2 = doc.add_sketch_entities(ents, SketchPlane::XY(), "CircleSketch");
|
||||
doc.add_rib(sk2, 0, 2.0, 10.0, 0, "BadRib");
|
||||
|
||||
REQUIRE_FALSE(doc.recompute());
|
||||
REQUIRE_FALSE(doc.error.empty());
|
||||
REQUIRE_THAT(doc.error, Catch::Matchers::Contains("rib"));
|
||||
}
|
||||
|
||||
TEST_CASE("rib round-trip serialization", "[CadDocument][rib]")
|
||||
{
|
||||
using Catch::Matchers::WithinAbs;
|
||||
|
||||
CadDocument doc;
|
||||
int sk = doc.add_sketch(SketchShape::Rectangle, SketchPlane::XY(), 40, 40, 10, "Box");
|
||||
doc.add_extrude(sk, 10.0, false, BooleanMode::New, "E");
|
||||
REQUIRE(doc.recompute());
|
||||
|
||||
std::vector<SketchEntity> ents = {
|
||||
{SketchEntity::Type::Line, Vec2d(5, 20), Vec2d(35, 20)},
|
||||
};
|
||||
int sk2 = doc.add_sketch_entities(ents, SketchPlane::XY(), "RibLine");
|
||||
doc.add_rib(sk2, 0, 3.0, 12.0, 0, "Rib");
|
||||
REQUIRE(doc.recompute());
|
||||
REQUIRE(doc.error.empty());
|
||||
|
||||
size_t nb = doc.bodies.size();
|
||||
double Vdoc = double(doc.display_mesh.volume());
|
||||
|
||||
auto blob = doc.serialize_recipe();
|
||||
REQUIRE_FALSE(blob.empty());
|
||||
|
||||
CadDocument fresh;
|
||||
REQUIRE(fresh.deserialize_recipe(blob));
|
||||
REQUIRE(fresh.bodies.size() == nb);
|
||||
double Vfresh = double(fresh.display_mesh.volume());
|
||||
REQUIRE_THAT(Vfresh, WithinAbs(Vdoc, 1e-6));
|
||||
|
||||
// Find the rib feature and check its fields survived.
|
||||
const CadFeature* rf = nullptr;
|
||||
for (const auto& f : fresh.features) {
|
||||
if (f.type == CadFeatureType::Rib) { rf = &f; break; }
|
||||
}
|
||||
REQUIRE(rf != nullptr);
|
||||
REQUIRE_THAT(rf->rib_thickness, WithinAbs(3.0, 1e-9));
|
||||
REQUIRE_THAT(rf->rib_depth, WithinAbs(12.0, 1e-9));
|
||||
}
|
||||
|
||||
// --- Bridge tests (M3c) ---
|
||||
|
||||
TEST_CASE("bridge two collinear lines", "[CadDocument][bridge]")
|
||||
|
||||
Reference in New Issue
Block a user