mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-18 22:42:37 +00:00
M5a: hole standards library — counterbore/countersink + ISO/ANSI table
Extend CadFeatureType::Hole (no new enum value) with a style flag (simple/counterbore/countersink) and the matching geometry: a coaxial shallow cylinder cut for counterbores, a cone-frustum cut for countersinks. A file-local hole_std_lookup() resolves screw designations (ISO 273/4762/ 10642 metric M3–M10 + common ANSI unified) into clearance/cbore/csink dims; add_hole_standard() fills the feature from it, add_hole_styled() takes them explicitly. Six serialized fields appended to both symmetric cereal lists (recipe version stays 2, golden fixture regenerated 28161->29525). MCP: hole_styled, hole_standard. 4 new [CadDocument][hole] tests; suite 83/1351. 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
e19e51b150
commit
0bbf22ceae
@@ -24,6 +24,7 @@
|
||||
#include <gp_Pln.hxx>
|
||||
#include <TopTools_ListOfShape.hxx>
|
||||
#include <BRepPrimAPI_MakeCylinder.hxx>
|
||||
#include <BRepPrimAPI_MakeCone.hxx>
|
||||
#include <BRepCheck_Analyzer.hxx>
|
||||
#include <BRepLib.hxx>
|
||||
#include <BRepAdaptor_Curve.hxx>
|
||||
@@ -575,6 +576,72 @@ int CadDocument::add_hole(double diameter, double depth, bool through,
|
||||
return int(features.size()) - 1;
|
||||
}
|
||||
|
||||
// Hole standards lookup table (representative ISO 273 medium / ISO 4762 / ANSI unified).
|
||||
struct HoleStdEntry { const char* desig; double clearance; double cbore_d; double cbore_depth; double csink_d; };
|
||||
static const HoleStdEntry kHoleStdTable[] = {
|
||||
{"M3", 3.4, 6.0, 3.4, 6.3},
|
||||
{"M4", 4.5, 8.0, 4.4, 8.4},
|
||||
{"M5", 5.5, 10.0, 5.4, 10.4},
|
||||
{"M6", 6.6, 11.0, 6.8, 12.6},
|
||||
{"M8", 9.0, 15.0, 8.8, 17.3},
|
||||
{"M10", 11.0, 18.0, 11.0, 20.0},
|
||||
{"#6-32", 3.7, 8.8, 4.2, 8.7},
|
||||
{"#8-32", 4.4, 9.9, 5.1, 10.2},
|
||||
{"1/4-20", 6.9, 14.4, 7.2, 14.7},
|
||||
{"5/16-18", 8.8, 17.0, 8.2, 17.3},
|
||||
{"3/8-16", 10.5, 19.6, 9.5, 19.8},
|
||||
};
|
||||
static bool hole_std_lookup(const std::string& desig, double& clearance,
|
||||
double& cbore_d, double& cbore_depth, double& csink_d)
|
||||
{
|
||||
for (const auto& e : kHoleStdTable) {
|
||||
if (e.desig == desig) {
|
||||
clearance = e.clearance;
|
||||
cbore_d = e.cbore_d;
|
||||
cbore_depth = e.cbore_depth;
|
||||
csink_d = e.csink_d;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int CadDocument::add_hole_styled(double diameter, double depth, bool through,
|
||||
double x, double y, const SketchPlane& plane, int style,
|
||||
double cbore_diameter, double cbore_depth,
|
||||
double csink_diameter, double csink_angle,
|
||||
const std::string& standard, const std::string& name)
|
||||
{
|
||||
CadFeature f;
|
||||
f.type = CadFeatureType::Hole;
|
||||
f.name = name;
|
||||
f.plane = plane;
|
||||
f.hole_diameter = diameter;
|
||||
f.hole_depth = depth;
|
||||
f.hole_through = through;
|
||||
f.hole_x = x;
|
||||
f.hole_y = y;
|
||||
f.hole_style = style;
|
||||
f.hole_cbore_diameter = cbore_diameter;
|
||||
f.hole_cbore_depth = cbore_depth;
|
||||
f.hole_csink_diameter = csink_diameter;
|
||||
f.hole_csink_angle = csink_angle;
|
||||
f.hole_standard = standard;
|
||||
features.push_back(f);
|
||||
return int(features.size()) - 1;
|
||||
}
|
||||
|
||||
int CadDocument::add_hole_standard(const std::string& designation, int style, bool through,
|
||||
double depth, double x, double y,
|
||||
const SketchPlane& plane, const std::string& name)
|
||||
{
|
||||
double clearance, cbore_d, cbore_depth, csink_d;
|
||||
if (!hole_std_lookup(designation, clearance, cbore_d, cbore_depth, csink_d))
|
||||
throw std::runtime_error("unknown hole standard \"" + designation + "\"");
|
||||
return add_hole_styled(clearance, depth, through, x, y, plane, style,
|
||||
cbore_d, cbore_depth, csink_d, 90, designation, name);
|
||||
}
|
||||
|
||||
int CadDocument::add_thread(double radius, double pitch, double height, double depth,
|
||||
bool internal, double x, double y, const SketchPlane& plane,
|
||||
const std::string& name)
|
||||
@@ -1749,6 +1816,27 @@ void CadDocument::apply_feature(TopoDS_Shape& result, bool& have_body,
|
||||
BRepAlgoAPI_Cut cut(result, tool);
|
||||
if (!cut.IsDone()) throw std::runtime_error("hole cut failed");
|
||||
result = cut.Shape();
|
||||
|
||||
// Enlarge the entry for a screw head (style 1 = counterbore, 2 = countersink).
|
||||
if (f.hole_style == 1 && f.hole_cbore_diameter > f.hole_diameter
|
||||
&& f.hole_cbore_depth > 1e-6) {
|
||||
gp_Ax2 cbax(o, n);
|
||||
TopoDS_Shape cb = BRepPrimAPI_MakeCylinder(cbax, f.hole_cbore_diameter * 0.5,
|
||||
f.hole_cbore_depth).Shape();
|
||||
BRepAlgoAPI_Cut cbc(result, cb);
|
||||
if (cbc.IsDone() && !cbc.Shape().IsNull()) result = cbc.Shape();
|
||||
} else if (f.hole_style == 2 && f.hole_csink_diameter > f.hole_diameter) {
|
||||
const double Rmaj = f.hole_csink_diameter * 0.5;
|
||||
const double Rmin = f.hole_diameter * 0.5;
|
||||
const double half = f.hole_csink_angle * 0.5 * M_PI / 180.0;
|
||||
const double h = (Rmaj - Rmin) / std::tan(half);
|
||||
if (h > 1e-6) {
|
||||
gp_Ax2 csax(o, n);
|
||||
TopoDS_Shape cs = BRepPrimAPI_MakeCone(csax, Rmaj, Rmin, h).Shape();
|
||||
BRepAlgoAPI_Cut csc(result, cs);
|
||||
if (csc.IsDone() && !csc.Shape().IsNull()) result = csc.Shape();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CadFeatureType::Thread: {
|
||||
|
||||
@@ -118,6 +118,15 @@ struct CadFeature {
|
||||
double hole_x{0}; // position on the plane (plane u/x axis)
|
||||
double hole_y{0}; // position on the plane (plane v/y axis)
|
||||
|
||||
// Hole standards library (extends the plain bore above).
|
||||
// hole_style: 0 = simple, 1 = counterbore, 2 = countersink.
|
||||
int hole_style{0};
|
||||
double hole_cbore_diameter{0}; // counterbore cylinder diameter (mm), style==1
|
||||
double hole_cbore_depth{0}; // counterbore depth from the entry face (mm), style==1
|
||||
double hole_csink_diameter{0}; // countersink major diameter at entry face (mm), style==2
|
||||
double hole_csink_angle{90}; // countersink included angle (degrees), style==2
|
||||
std::string hole_standard; // provenance only, e.g. "M6" / "1/4-20"; not used by geometry
|
||||
|
||||
// Thread params (helical thread about the plane normal at a positioned point)
|
||||
double thread_radius{5}; // nominal cylinder radius
|
||||
double thread_pitch{2}; // axial advance per turn
|
||||
@@ -297,7 +306,9 @@ struct CadFeature {
|
||||
thicken_face, thicken_thickness, thicken_flip,
|
||||
cut_face_body, cut_face,
|
||||
project_source_body, project_edges, project_face,
|
||||
delete_faces);
|
||||
delete_faces,
|
||||
hole_style, hole_cbore_diameter, hole_cbore_depth,
|
||||
hole_csink_diameter, hole_csink_angle, hole_standard);
|
||||
}
|
||||
template<class Archive>
|
||||
void load(Archive& ar) {
|
||||
@@ -329,7 +340,9 @@ struct CadFeature {
|
||||
thicken_face, thicken_thickness, thicken_flip,
|
||||
cut_face_body, cut_face,
|
||||
project_source_body, project_edges, project_face,
|
||||
delete_faces);
|
||||
delete_faces,
|
||||
hole_style, hole_cbore_diameter, hole_cbore_depth,
|
||||
hole_csink_diameter, hole_csink_angle, hole_standard);
|
||||
imported_solid = brep_from_string(brep);
|
||||
}
|
||||
};
|
||||
@@ -410,6 +423,14 @@ public:
|
||||
int add_hole(double diameter, double depth, bool through,
|
||||
double x, double y, const SketchPlane& plane,
|
||||
const std::string& name);
|
||||
int add_hole_styled(double diameter, double depth, bool through,
|
||||
double x, double y, const SketchPlane& plane, int style,
|
||||
double cbore_diameter, double cbore_depth,
|
||||
double csink_diameter, double csink_angle,
|
||||
const std::string& standard, const std::string& name);
|
||||
int add_hole_standard(const std::string& designation, int style, bool through,
|
||||
double depth, double x, double y,
|
||||
const SketchPlane& plane, const std::string& name);
|
||||
int add_thread(double radius, double pitch, double height, double depth,
|
||||
bool internal, double x, double y, const SketchPlane& plane,
|
||||
const std::string& name);
|
||||
|
||||
@@ -153,6 +153,31 @@ json describe_tools()
|
||||
json{{"name", "y"}, {"type", "number"}, {"unit", "mm"}, {"default", 0}},
|
||||
json{{"name", "plane"}, {"type", "string"}, {"enum", json::array({"XY", "XZ", "YZ"})}, {"default", "XY"}},
|
||||
})}},
|
||||
json{{"name", "hole_styled"}, {"summary", "Drill a hole with optional counterbore (style=1) or countersink (style=2) at (x,y) on a plane."},
|
||||
{"params", json::array({
|
||||
json{{"name", "diameter"}, {"type", "number"}, {"unit", "mm"}, {"default", 5}, {"min", 0.01}},
|
||||
json{{"name", "depth"}, {"type", "number"}, {"unit", "mm"}, {"default", 10}, {"min", 0.01}},
|
||||
json{{"name", "through"}, {"type", "boolean"}, {"default", true}},
|
||||
json{{"name", "x"}, {"type", "number"}, {"unit", "mm"}, {"default", 0}},
|
||||
json{{"name", "y"}, {"type", "number"}, {"unit", "mm"}, {"default", 0}},
|
||||
json{{"name", "plane"}, {"type", "string"}, {"enum", json::array({"XY", "XZ", "YZ"})}, {"default", "XY"}},
|
||||
json{{"name", "style"}, {"type", "integer"}, {"default", 0}, {"description", "0=simple, 1=counterbore, 2=countersink"}},
|
||||
json{{"name", "cbore_diameter"}, {"type", "number"}, {"unit", "mm"}, {"default", 0}},
|
||||
json{{"name", "cbore_depth"}, {"type", "number"}, {"unit", "mm"}, {"default", 0}},
|
||||
json{{"name", "csink_diameter"}, {"type", "number"}, {"unit", "mm"}, {"default", 0}},
|
||||
json{{"name", "csink_angle"}, {"type", "number"}, {"unit", "deg"}, {"default", 90}},
|
||||
json{{"name", "standard"}, {"type", "string"}, {"default", ""}, {"description", "provenance designation, e.g. M6"}},
|
||||
})}},
|
||||
json{{"name", "hole_standard"}, {"summary", "Drill a standard clearance hole (ISO 273 / ANSI) at (x,y) on a plane. style: 0=simple, 1=counterbore, 2=countersink."},
|
||||
{"params", json::array({
|
||||
json{{"name", "designation"}, {"type", "string"}, {"description", "e.g. M6, 1/4-20"}},
|
||||
json{{"name", "style"}, {"type", "integer"}, {"default", 0}},
|
||||
json{{"name", "through"}, {"type", "boolean"}, {"default", true}},
|
||||
json{{"name", "depth"}, {"type", "number"}, {"unit", "mm"}, {"default", 10}, {"min", 0.01}},
|
||||
json{{"name", "x"}, {"type", "number"}, {"unit", "mm"}, {"default", 0}},
|
||||
json{{"name", "y"}, {"type", "number"}, {"unit", "mm"}, {"default", 0}},
|
||||
json{{"name", "plane"}, {"type", "string"}, {"enum", json::array({"XY", "XZ", "YZ"})}, {"default", "XY"}},
|
||||
})}},
|
||||
json{{"name", "boolean"}, {"summary", "Combine two bodies: union | subtract (tool from target) | intersect."},
|
||||
{"params", json::array({
|
||||
json{{"name", "op"}, {"type", "string"}, {"enum", json::array({"union", "subtract", "intersect"})}, {"default", "subtract"}},
|
||||
@@ -811,6 +836,57 @@ json action_hole(DesignPanel* panel, const json& params)
|
||||
return json{{"ok", ok}, {"hole_index", h}, {"bodies", int(doc.bodies.size())}, {"error", doc.error}};
|
||||
}
|
||||
|
||||
json action_hole_styled(DesignPanel* panel, const json& params)
|
||||
{
|
||||
const double dia = params.value("diameter", 5.0);
|
||||
const double depth = params.value("depth", 10.0);
|
||||
const bool thru = params.value("through", true);
|
||||
const double x = params.value("x", 0.0), y = params.value("y", 0.0);
|
||||
const int style = params.value("style", 0);
|
||||
const double cbore_diameter = params.value("cbore_diameter", 0.0);
|
||||
const double cbore_depth = params.value("cbore_depth", 0.0);
|
||||
const double csink_diameter = params.value("csink_diameter", 0.0);
|
||||
const double csink_angle = params.value("csink_angle", 90.0);
|
||||
const std::string standard = params.value("standard", std::string(""));
|
||||
if (dia <= 0) throw std::runtime_error("diameter must be > 0");
|
||||
CadDocument& doc = panel->mcp_doc();
|
||||
if (doc.bodies.empty()) throw std::runtime_error("no body to drill");
|
||||
SketchPlane pl = plane_from(params, doc);
|
||||
doc.checkpoint();
|
||||
int h = doc.add_hole_styled(dia, depth, thru, x, y, pl, style,
|
||||
cbore_diameter, cbore_depth,
|
||||
csink_diameter, csink_angle, standard, "Hole");
|
||||
bool ok = doc.recompute();
|
||||
if (!ok) doc.undo();
|
||||
panel->mcp_after_change();
|
||||
return json{{"ok", ok}, {"hole_index", h}, {"bodies", int(doc.bodies.size())}, {"error", doc.error}};
|
||||
}
|
||||
|
||||
json action_hole_standard(DesignPanel* panel, const json& params)
|
||||
{
|
||||
const std::string desig = params.value("designation", std::string(""));
|
||||
if (desig.empty()) throw std::runtime_error("designation is required");
|
||||
const int style = params.value("style", 0);
|
||||
const bool thru = params.value("through", true);
|
||||
const double depth = params.value("depth", 10.0);
|
||||
const double x = params.value("x", 0.0), y = params.value("y", 0.0);
|
||||
CadDocument& doc = panel->mcp_doc();
|
||||
if (doc.bodies.empty()) throw std::runtime_error("no body to drill");
|
||||
SketchPlane pl = plane_from(params, doc);
|
||||
doc.checkpoint();
|
||||
try {
|
||||
int h = doc.add_hole_standard(desig, style, thru, depth, x, y, pl, "Hole");
|
||||
bool ok = doc.recompute();
|
||||
if (!ok) doc.undo();
|
||||
panel->mcp_after_change();
|
||||
return json{{"ok", ok}, {"hole_index", h}, {"bodies", int(doc.bodies.size())}, {"error", doc.error}};
|
||||
} catch (const std::exception& ex) {
|
||||
doc.undo();
|
||||
panel->mcp_after_change();
|
||||
return json{{"ok", false}, {"error", ex.what()}};
|
||||
}
|
||||
}
|
||||
|
||||
json action_boolean(DesignPanel* panel, const json& params)
|
||||
{
|
||||
BooleanMode m = bool_from(params.value("op", std::string("subtract")));
|
||||
@@ -1101,6 +1177,8 @@ std::string handle_on_main(const std::string& method, const json& params, const
|
||||
if (method == "fillet") return rpc_result(id, action_fillet(panel, params));
|
||||
if (method == "chamfer") return rpc_result(id, action_chamfer(panel, params));
|
||||
if (method == "hole") return rpc_result(id, action_hole(panel, params));
|
||||
if (method == "hole_styled") return rpc_result(id, action_hole_styled(panel, params));
|
||||
if (method == "hole_standard") return rpc_result(id, action_hole_standard(panel, params));
|
||||
if (method == "boolean") return rpc_result(id, action_boolean(panel, params));
|
||||
if (method == "pattern") return rpc_result(id, action_pattern(panel, params));
|
||||
if (method == "shell") return rpc_result(id, action_shell(panel, params));
|
||||
|
||||
Reference in New Issue
Block a user