M4: delete-face direct edit — remove faces and heal via OCCT defeaturing

New CadFeatureType::DeleteFace: removes a set of global face ids from target_body
and heals the gap via BRepAlgoAPI_Defeaturing (TKBO, already linked), mirroring the
Shell/Draft body-modifying pattern. delete_faces appended to both symmetric cereal
lists (recipe version stays 2, golden fixture regenerated 27913->28161). MCP
delete_face method (pure additions). 3 new [CadDocument][deleteface] tests: remove a
fillet face restores the sharp-box volume, bad index fails safely, round-trip.
Full kernel suite green (79 cases, 1309 asserts).

Move-face / replace-face deferred to snaporca-3c4 / snaporca-tc6 (no clean shipping
OCCT direct-modeling primitive; need research, and replace-face depends on M7 surfaces).

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 03:28:25 +02:00
co-authored by Claude Opus 4.8
parent 65caa2ea6e
commit e19e51b150
5 changed files with 179 additions and 10 deletions
+29
View File
@@ -13,6 +13,7 @@
#include <BRepAlgoAPI_Fuse.hxx>
#include <BRepAlgoAPI_Cut.hxx>
#include <BRepAlgoAPI_Common.hxx>
#include <BRepAlgoAPI_Defeaturing.hxx>
#include <BRepAlgoAPI_BooleanOperation.hxx>
#include <BRepOffsetAPI_MakePipe.hxx>
#include <BRepOffsetAPI_MakePipeShell.hxx>
@@ -680,6 +681,18 @@ int CadDocument::add_shell(double thickness, int face, int target_body, const st
return int(features.size()) - 1;
}
int CadDocument::add_delete_face(int target_body, const std::vector<int>& faces,
const std::string& name)
{
CadFeature f;
f.type = CadFeatureType::DeleteFace;
f.name = name;
f.target_body = target_body;
f.delete_faces = faces;
features.push_back(f);
return int(features.size()) - 1;
}
int CadDocument::add_draft(double angle, int face, int target_body, const std::string& name)
{
CadFeature f;
@@ -1873,6 +1886,22 @@ void CadDocument::apply_feature(TopoDS_Shape& result, bool& have_body,
if (result.IsNull()) throw std::runtime_error("draft produced no geometry");
break;
}
case CadFeatureType::DeleteFace: {
if (!have_body) throw std::runtime_error("delete_face needs a body");
if (f.delete_faces.empty()) throw std::runtime_error("delete_face needs at least one face");
BRepAlgoAPI_Defeaturing df;
df.SetShape(result);
for (int fi : f.delete_faces) {
TopoDS_Face fc = GeometryEngine::face_by_index(result, fi);
if (fc.IsNull()) throw std::runtime_error("delete_face: face not found");
df.AddFaceToRemove(fc);
}
df.Build();
if (!df.IsDone()) throw std::runtime_error("delete_face failed");
result = df.Shape();
if (result.IsNull()) throw std::runtime_error("delete_face produced no geometry");
break;
}
}
}
+11 -3
View File
@@ -17,7 +17,7 @@
namespace Slic3r {
enum class CadFeatureType { Sketch, Extrude, Fillet, Chamfer, Hole, Thread, Shell, Revolve, Sweep, Pattern, Plane, Loft, Draft, Import, Boolean, Cut, Mirror, Axis, CoordSys, Helix, Transform, Thicken, Project };
enum class CadFeatureType { Sketch, Extrude, Fillet, Chamfer, Hole, Thread, Shell, Revolve, Sweep, Pattern, Plane, Loft, Draft, Import, Boolean, Cut, Mirror, Axis, CoordSys, Helix, Transform, Thicken, Project, DeleteFace };
enum class SketchShape { Rectangle, Circle };
enum class PlaneType { Offset, Angle, Midplane, Tangent, TwoEdges, Coincident };
enum class AxisType { TwoPoints, FaceNormal, CylinderCenterline, PlaneIntersection, AlongEdge };
@@ -263,6 +263,10 @@ struct CadFeature {
std::vector<int> project_edges; // global edge ids to project; empty => use project_face
int project_face{-1}; // if project_edges empty, project every edge of this face
// Direct edit: faces to remove (global face indices into target_body's shape),
// healed via BRepAlgoAPI_Defeaturing.
std::vector<int> delete_faces;
template<class Archive>
void save(Archive& ar) const {
std::string brep = (type == CadFeatureType::Import) ? brep_to_string(imported_solid) : std::string();
@@ -292,7 +296,8 @@ struct CadFeature {
xf_translate, xf_axis, xf_pivot, xf_angle_deg, xf_copy,
thicken_face, thicken_thickness, thicken_flip,
cut_face_body, cut_face,
project_source_body, project_edges, project_face);
project_source_body, project_edges, project_face,
delete_faces);
}
template<class Archive>
void load(Archive& ar) {
@@ -323,7 +328,8 @@ struct CadFeature {
xf_translate, xf_axis, xf_pivot, xf_angle_deg, xf_copy,
thicken_face, thicken_thickness, thicken_flip,
cut_face_body, cut_face,
project_source_body, project_edges, project_face);
project_source_body, project_edges, project_face,
delete_faces);
imported_solid = brep_from_string(brep);
}
};
@@ -446,6 +452,8 @@ public:
// Offset face `face` of `target_body` by `thickness` along its normal, producing a new
// thin solid appended as a new body. flip=true offsets against the normal.
int add_thicken(int target_body, int face, double thickness, bool flip, const std::string& name);
int add_delete_face(int target_body, const std::vector<int>& faces,
const std::string& name);
// Datum plane: derived from base (0=XY/1=XZ/2=YZ/3+N=Nth earlier datum), offset
// along its normal, optional tilt about a base axis. Produces no solid.
int add_plane(int base, double offset, double angle_tilt, int axis,