Rib: accept a Project feature as its sketch ref

Rib guarded with `sk.type != CadFeatureType::Sketch`, while every other
sketch consumer — Extrude, SurfaceExtrude, SurfaceRevolve, the loft paths —
tests `!= Sketch && != Project`. A Project feature carries a plane and Line
entities, which is all a rib reads, so the guard blocked "project a body
edge, then rib along it" for no stated reason.

The picker in the Design tab offered Sketch features only, so it is widened
to match: a kernel that accepts Project refs and a GUI that never lists them
would have left the path unreachable anyway.

Worth recording for whoever hits this next: Rib also needs a sketch carrying
EXPLICIT entities. A parametric Rectangle sketch (add_sketch with
width/height) has an empty entities vector — build_sketch_wire synthesises
its profile on demand — so rib_entity 0 is out of range there and it fails
with "rib: bad entity". That is why Rib could not be driven headlessly at
all before this change; a Project feature is now the one programmatic way to
produce a ribbable line.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Tommaso Bianchi
2026-07-26 07:48:16 +02:00
co-authored by Claude Opus 5
parent 1cb80f7f9f
commit b25335e1b3
3 changed files with 32 additions and 2 deletions
+4 -1
View File
@@ -2273,7 +2273,10 @@ void CadDocument::apply_feature(TopoDS_Shape& result, bool& have_body,
if (f.rib_sketch_ref < 0 || f.rib_sketch_ref >= (int)features.size())
throw std::runtime_error("rib: bad sketch ref");
const CadFeature& sk = features[f.rib_sketch_ref];
if (sk.type != CadFeatureType::Sketch)
// Project counts as sketch-like here exactly as it does for Extrude, SurfaceExtrude
// and SurfaceRevolve: it carries plane + Line entities, which is all a rib reads.
// Ribbing along a projected body edge is otherwise unreachable.
if (sk.type != CadFeatureType::Sketch && sk.type != CadFeatureType::Project)
throw std::runtime_error("rib: ref is not a sketch");
if (f.rib_entity < 0 || f.rib_entity >= (int)sk.entities.size())
throw std::runtime_error("rib: bad entity");
+4 -1
View File
@@ -596,7 +596,10 @@ DesignPanel::DesignPanel(wxWindow* parent)
for (int i = 0; i < int(m_doc.features.size()); ++i) {
// 3-arg Append: ComboBox's own Append(text, bitmap) hides
// wxItemContainer's (text, void*) — see the Sweep picker.
if (m_doc.features[i].type == CadFeatureType::Sketch)
// Project features too: they carry Line entities the kernel ribs from
// just like a drawn sketch, so a projected body edge is a valid path.
if (m_doc.features[i].type == CadFeatureType::Sketch ||
m_doc.features[i].type == CadFeatureType::Project)
m_rib_sketch->Append(wxString::FromUTF8(m_doc.features[i].name), wxNullBitmap,
reinterpret_cast<void*>(intptr_t(i)));
}
+24
View File
@@ -4048,6 +4048,30 @@ TEST_CASE("rib adds material to a box", "[CadDocument][rib]")
REQUIRE_FALSE(bad.recompute());
}
TEST_CASE("rib accepts a Project feature as its sketch ref", "[CadDocument][rib]")
{
// Project carries plane + Line entities, which is all a rib reads. Every other consumer
// (Extrude, SurfaceExtrude, SurfaceRevolve) already accepts it; Rib used to reject it,
// which made "project a body edge, then rib along it" unreachable.
CadDocument doc;
int sk = doc.add_sketch(SketchShape::Rectangle, SketchPlane::XY(), 40, 40, 10, "Box");
doc.add_extrude(sk, 10.0, false, BooleanMode::New, "Extrude");
REQUIRE(doc.recompute());
double Vbox = double(doc.display_mesh.volume());
// Project the whole body onto XY: the 4 top and 4 bottom edges survive as Lines.
int proj = doc.add_project_edges(0, {}, -1, SketchPlane::XY(), "Proj");
REQUIRE(proj >= 0);
REQUIRE(doc.recompute());
REQUIRE(doc.features[proj].entities.size() == 8);
int fi = doc.add_rib(proj, 0, 3.0, 12.0, 0, "RibFromProjection");
REQUIRE(fi >= 0);
REQUIRE(doc.recompute());
REQUIRE(doc.error.empty());
REQUIRE(double(doc.display_mesh.volume()) > Vbox);
}
TEST_CASE("rib non-line entity rejected safely", "[CadDocument][rib]")
{