mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-28 03:11:47 +00:00
CAD: a sheet body reports no volume, instead of a confident wrong one
mass_properties on an open shell returned volume 96000 with an inertia diagonal of [-4.2e7, -4.2e7, -6.9e7] for a 60x60x40 four-walled box — negative principal moments, which no real body can have. BRepGProp::VolumeProperties integrates the divergence theorem over whatever faces exist; on an open shell that is not a volume at all, and the old code hid the only obvious tell by taking std::abs() of the mass. "valid: true" then asserted the number was trustworthy. This matters because mass_properties is what an agent or a user reaches for to confirm a cut removed the right material. Silent nonsense there means the check passes on garbage. MassProps gains is_solid. For a sheet we compute surface area only — that stays exact — and report volume 0 with the inertia left zeroed. The MCP verb returns is_solid plus a note saying volume and inertia are not defined for an open shell; the GUI's Mass command says "sheet body — N cm² of surface, no volume" rather than quoting material that is not there. Verified on the rig: the sheet now returns volume 0.0, surface_area 9600.0 (exactly 4 x 60 x 40), is_solid false. The solid controls are unchanged and exact — a 60 mm cube reports 216000.0 and 21600.0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
1545fb7946
commit
9013f530fa
@@ -470,6 +470,21 @@ GeometryEngine::MassProps GeometryEngine::mass_properties(const TopoDS_Shape& sh
|
|||||||
MassProps p;
|
MassProps p;
|
||||||
if (shape.IsNull()) return p;
|
if (shape.IsNull()) return p;
|
||||||
try {
|
try {
|
||||||
|
// A sheet body (open shell, no solid) encloses nothing, and BRepGProp::VolumeProperties
|
||||||
|
// integrates the divergence theorem over whatever faces exist — on an open shell that is
|
||||||
|
// not a volume at all. It came back as 96000 with an inertia diagonal of
|
||||||
|
// [-4.2e7, -4.2e7, -6.9e7] for a 60x60x40 four-walled box: negative principal moments,
|
||||||
|
// which no real body can have. The old code then hid the only obvious tell by taking
|
||||||
|
// std::abs() of the mass. Report the honest answer instead — surface area is still
|
||||||
|
// meaningful, so this is not a failure, just not a solid.
|
||||||
|
p.is_solid = TopExp_Explorer(shape, TopAbs_SOLID).More();
|
||||||
|
if (!p.is_solid) {
|
||||||
|
GProp_GProps sonly;
|
||||||
|
BRepGProp::SurfaceProperties(shape, sonly);
|
||||||
|
p.surface_area = sonly.Mass();
|
||||||
|
p.valid = true; // the area IS trustworthy; volume/inertia stay zero
|
||||||
|
return p;
|
||||||
|
}
|
||||||
GProp_GProps vprops;
|
GProp_GProps vprops;
|
||||||
BRepGProp::VolumeProperties(shape, vprops);
|
BRepGProp::VolumeProperties(shape, vprops);
|
||||||
double mass = vprops.Mass();
|
double mass = vprops.Mass();
|
||||||
|
|||||||
@@ -100,6 +100,9 @@ public:
|
|||||||
Vec3d center_of_mass{Vec3d::Zero()};
|
Vec3d center_of_mass{Vec3d::Zero()};
|
||||||
std::array<double, 9> inertia{};
|
std::array<double, 9> inertia{};
|
||||||
bool valid{false};
|
bool valid{false};
|
||||||
|
// False for a sheet body (an open shell with no solid). Volume and inertia are then
|
||||||
|
// meaningless and are reported as zero; surface_area stays meaningful. See the .cpp.
|
||||||
|
bool is_solid{false};
|
||||||
};
|
};
|
||||||
static MassProps mass_properties(const TopoDS_Shape& shape);
|
static MassProps mass_properties(const TopoDS_Shape& shape);
|
||||||
|
|
||||||
|
|||||||
@@ -5239,6 +5239,19 @@ void DesignPanel::on_mass_properties()
|
|||||||
wxString name = wxString::Format(_L("Body %d"), m_sel_solid_body + 1);
|
wxString name = wxString::Format(_L("Body %d"), m_sel_solid_body + 1);
|
||||||
if (!m_doc.bodies[m_sel_solid_body].name.empty())
|
if (!m_doc.bodies[m_sel_solid_body].name.empty())
|
||||||
name = wxString::FromUTF8(m_doc.bodies[m_sel_solid_body].name);
|
name = wxString::FromUTF8(m_doc.bodies[m_sel_solid_body].name);
|
||||||
|
if (!mp.is_solid) {
|
||||||
|
// Sheet body: quoting a volume here would be inventing material that is not there.
|
||||||
|
m_status->SetForegroundColour(wxNullColour);
|
||||||
|
set_status(wxString::Format(_L("%s: sheet body — %.2f cm² of surface, no volume"),
|
||||||
|
name, mp.surface_area / 100.0));
|
||||||
|
m_status->Refresh();
|
||||||
|
wxMessageBox(wxString::Format(_L("%s\n\nSheet body (open shell)\nSurface area: %.2f cm²\n\n"
|
||||||
|
"A sheet encloses no material, so it has no volume. "
|
||||||
|
"Thicken it into a solid to get one."),
|
||||||
|
name, mp.surface_area / 100.0),
|
||||||
|
_L("Mass properties"), wxOK, this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
m_status->SetForegroundColour(wxNullColour);
|
m_status->SetForegroundColour(wxNullColour);
|
||||||
set_status(wxString::Format(_L("%s: %.3f cm³, %.2f cm²"),
|
set_status(wxString::Format(_L("%s: %.3f cm³, %.2f cm²"),
|
||||||
name, mp.volume / 1000.0, mp.surface_area / 100.0));
|
name, mp.volume / 1000.0, mp.surface_area / 100.0));
|
||||||
|
|||||||
@@ -556,11 +556,23 @@ json mass_properties(DesignPanel* panel, const json& params)
|
|||||||
const TopoDS_Shape& shape = body_shape(panel, params);
|
const TopoDS_Shape& shape = body_shape(panel, params);
|
||||||
auto mp = GeometryEngine::mass_properties(shape);
|
auto mp = GeometryEngine::mass_properties(shape);
|
||||||
if (!mp.valid) throw std::runtime_error("mass properties could not be computed (null/empty shape)");
|
if (!mp.valid) throw std::runtime_error("mass properties could not be computed (null/empty shape)");
|
||||||
|
// A sheet body has no volume and no inertia. Report the area and say so, rather than
|
||||||
|
// returning numbers a caller would reasonably treat as a material check.
|
||||||
|
if (!mp.is_solid)
|
||||||
|
return json{
|
||||||
|
{"surface_area", mp.surface_area},
|
||||||
|
{"volume", 0.0},
|
||||||
|
{"is_solid", false},
|
||||||
|
{"valid", mp.valid},
|
||||||
|
{"note", "sheet body (open shell): it encloses no material, so volume and inertia "
|
||||||
|
"are not defined; surface_area is exact"},
|
||||||
|
};
|
||||||
return json{
|
return json{
|
||||||
{"volume", mp.volume},
|
{"volume", mp.volume},
|
||||||
{"surface_area", mp.surface_area},
|
{"surface_area", mp.surface_area},
|
||||||
{"center_of_mass", json::array({mp.center_of_mass.x(), mp.center_of_mass.y(), mp.center_of_mass.z()})},
|
{"center_of_mass", json::array({mp.center_of_mass.x(), mp.center_of_mass.y(), mp.center_of_mass.z()})},
|
||||||
{"inertia", mp.inertia},
|
{"inertia", mp.inertia},
|
||||||
|
{"is_solid", true},
|
||||||
{"valid", mp.valid},
|
{"valid", mp.valid},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user