M7b: thicken-from-surface + surface offset

Two features bridging sheet bodies back to solids and to other sheets:
ThickenSurface feeds a whole sheet shell to MakeThickSolidBySimple (the same
OCCT recipe the face-level Thicken already uses) and appends the result as a
solid; SurfaceOffset offsets a sheet's shell along its normals via
MakeOffsetShape::PerformBySimple, keeping it open. Both refuse a non-sheet
target with a clear error.

Purely additive: two enum values appended to CadFeatureType, reusing the
existing target_body / thicken_thickness / thicken_flip / plane_offset
fields. No new cereal fields, recipe stays v2, golden fixture unchanged
(30773). MCP thicken_surface/surface_offset added as pure additions.
Suite 103 cases / 1520 assertions green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BVzKmX6Y1aEteit1HTXG4Q
This commit is contained in:
Tommaso Bianchi
2026-07-25 09:41:51 +02:00
co-authored by Claude Opus 4.8
parent 01e474e17c
commit 2da75d28ca
4 changed files with 240 additions and 1 deletions
+31
View File
@@ -1041,6 +1041,35 @@ json action_surface_revolve(DesignPanel* panel, const json& params)
return json{{"ok", ok}, {"feature_index", idx}, {"bodies", int(doc.bodies.size())}, {"error", doc.error}};
}
json action_thicken_surface(DesignPanel* panel, const json& params)
{
CadDocument& doc = panel->mcp_doc();
if (doc.bodies.empty()) throw std::runtime_error("no sheet body to thicken");
int bi = target_body_arg(params, doc);
double thickness = params.value("thickness", 2.0);
bool flip = params.value("flip", false);
doc.checkpoint();
int idx = doc.add_thicken_surface(bi, thickness, flip, "ThickenSurface");
bool ok = doc.recompute();
if (!ok) doc.undo();
panel->mcp_after_change();
return json{{"ok", ok}, {"feature_index", idx}, {"bodies", int(doc.bodies.size())}, {"error", doc.error}};
}
json action_surface_offset(DesignPanel* panel, const json& params)
{
CadDocument& doc = panel->mcp_doc();
if (doc.bodies.empty()) throw std::runtime_error("no sheet body to offset");
int bi = target_body_arg(params, doc);
double offset = params.value("offset", 1.0);
doc.checkpoint();
int idx = doc.add_surface_offset(bi, offset, "SurfaceOffset");
bool ok = doc.recompute();
if (!ok) doc.undo();
panel->mcp_after_change();
return json{{"ok", ok}, {"feature_index", idx}, {"bodies", int(doc.bodies.size())}, {"error", doc.error}};
}
json action_draft(DesignPanel* panel, const json& params)
{
if (!params.contains("face")) throw std::runtime_error("draft needs 'face' (id from query_topology)");
@@ -1332,6 +1361,8 @@ std::string handle_on_main(const std::string& method, const json& params, const
if (method == "set_feature_expr") return rpc_result(id, action_set_feature_expr(panel, params));
if (method == "surface_extrude") return rpc_result(id, action_surface_extrude(panel, params));
if (method == "surface_revolve") return rpc_result(id, action_surface_revolve(panel, params));
if (method == "thicken_surface") return rpc_result(id, action_thicken_surface(panel, params));
if (method == "surface_offset") return rpc_result(id, action_surface_offset(panel, params));
return rpc_error(id, -32601, "Unknown method: " + method);
} catch (const Standard_Failure& ex) { // OCCT errors are NOT std::exception
return rpc_error(id, -32000, std::string("OCCT: ") + (ex.GetMessageString() ? ex.GetMessageString() : "failure"));