Files
OrcaSlicer/src/slic3r/Utils/PaintCLI.cpp
T
HanifKoh 84657ff11e Add Missing Includes Across the Remaining Sources and Tests (#16071)
* Ignore Clipper, libpng, mcut and Boost.Polygon Internals in clang-tidy

Each only works through a wrapper or umbrella header: libslic3r/clipper.hpp or clipper_z.hpp configure Clipper before including it, png.h pulls in libpng's config headers, and Boost.Polygon's headers only compile through polygon.hpp or voronoi.hpp.

* Ignore minilzo's Config Headers in clang-tidy

lzoconf.h and lzodefs.h are internal to minilzo.h, which is what the code includes.

* Add Missing Includes Across the Remaining Sources and Tests

Covers src/slic3r/Utils, src/slic3r/plugin, src/slic3r/Config, src/libvgcode, src/dev-utils, src/OrcaSlicer.cpp and tests/, the directories left after src/slic3r/GUI and src/libslic3r. Generated with clang-tidy misc-include-cleaner. libvgcode's own headers are included by relative path as in the rest of that library, and Catch2 and pybind11 with angle brackets as elsewhere in the repo.

* Make the GUI and Test Headers Compile on Their Own

Each now includes, or forward-declares, what it uses instead of relying on what its includers happened to include first. Headers that only compile on one platform, or that nothing built includes, are left alone.

* Keep Windows and nanosvg Setup Ahead of the Added Includes

OrcaSlicer.cpp and several tests set _WIN32_WINNT, WIN32_LEAN_AND_MEAN or NOMINMAX before including Windows.h, and the profile validator defines NANOSVG_IMPLEMENTATION before any libslic3r header. The added includes had landed above those blocks, which broke the Windows build.

* Add the GUI Includes the First Pass Missed

Covers headers that only became editable once they compiled on their own, and wx symbols whose suggested header changed as the clang-tidy ignore list grew after the src/slic3r/GUI pass.

* Keep the Added Test Includes Below the NOMINMAX Guard

test_marchingsquares.cpp and test_texture_displacement.cpp had includes inside #ifndef NOMINMAX, which the tests inherit as defined on Windows from libslic3r, so those were skipped there. .clang-tidy also ignores the MSVC STL and UCRT internals, Boost.Multiprecision's fwd.hpp and CPython's Windows include directory, as in #16068.
2026-10-03 13:45:21 +08:00

209 lines
7.0 KiB
C++

// PaintCLI.cpp — CLI paint-inspection primitives. See PaintCLI.hpp.
#include "PaintCLI.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/TriangleMesh.hpp"
#include "libslic3r/TriangleSelector.hpp"
#include "libslic3r/Point.hpp"
#include "libslic3r/BoundingBox.hpp"
#include <cstddef>
#include <nlohmann/json.hpp>
#include <cmath>
#include <ostream>
#include <string>
#include <utility>
#include <vector>
namespace Slic3r {
namespace PaintCLI {
namespace {
using json = nlohmann::json;
double its_surface_area(const indexed_triangle_set &its)
{
double total = 0.0;
for (const stl_triangle_vertex_indices &t : its.indices) {
const Vec3f &a = its.vertices[t(0)];
const Vec3f &b = its.vertices[t(1)];
const Vec3f &c = its.vertices[t(2)];
total += 0.5 * (b - a).cross(c - a).norm();
}
return total;
}
// Bbox over triangle-referenced vertices only. get_facets_strict() returns
// an itset with the full source vertex list — using bounding_box() on it
// would report the whole mesh's bbox even when only a few facets are painted.
BoundingBoxf3 its_referenced_bbox(const indexed_triangle_set &its)
{
BoundingBoxf3 bb;
bool first = true;
for (const stl_triangle_vertex_indices &t : its.indices) {
for (int k = 0; k < 3; ++k) {
const Vec3d v = its.vertices[t(k)].cast<double>();
if (first) { bb.min = bb.max = v; first = false; }
else bb.merge(v);
}
}
return bb;
}
json vec3_to_json(const Vec3d &v)
{
return json::array({ v.x(), v.y(), v.z() });
}
json bbox_to_json(const BoundingBoxf3 &bb)
{
return {
{ "min", vec3_to_json(bb.min) },
{ "max", vec3_to_json(bb.max) },
{ "size", vec3_to_json(Vec3d(bb.max - bb.min)) },
};
}
// One (layer, state) row — empty ones are omitted at the caller level.
json state_entry(const std::string &label, const indexed_triangle_set &its)
{
return {
{ "state", label },
{ "facets", its.indices.size() },
{ "area_mm2", its_surface_area(its) },
{ "bbox", bbox_to_json(its_referenced_bbox(its)) },
};
}
// Iterate the states relevant to one FacetsAnnotation kind, collecting
// non-empty entries. Empty layer → {"empty": true}. `n_facets_out` is the
// running total of painted facets — bumped for the summary.
json inspect_layer(const ModelVolume &mv, const FacetsAnnotation &fa,
const std::vector<std::pair<EnforcerBlockerType, std::string>> &states,
size_t &n_facets_out)
{
if (fa.empty())
return { { "empty", true } };
json entries = json::array();
for (const auto &st : states) {
if (!fa.has_facets(mv, st.first))
continue;
indexed_triangle_set its = fa.get_facets_strict(mv, st.first);
if (its.indices.empty())
continue;
n_facets_out += its.indices.size();
entries.push_back(state_entry(st.second, its));
}
return {
{ "empty", entries.empty() },
{ "states", std::move(entries) },
};
}
const std::vector<std::pair<EnforcerBlockerType, std::string>> &supports_states()
{
static const std::vector<std::pair<EnforcerBlockerType, std::string>> s = {
{ EnforcerBlockerType::ENFORCER, "ENFORCER" },
{ EnforcerBlockerType::BLOCKER, "BLOCKER" },
};
return s;
}
const std::vector<std::pair<EnforcerBlockerType, std::string>> &fuzzy_states()
{
// FUZZY_SKIN is an enum alias for ENFORCER; the layer is single-state.
static const std::vector<std::pair<EnforcerBlockerType, std::string>> s = {
{ EnforcerBlockerType::FUZZY_SKIN, "FUZZY_SKIN" },
};
return s;
}
const std::vector<std::pair<EnforcerBlockerType, std::string>> &mmu_states()
{
static std::vector<std::pair<EnforcerBlockerType, std::string>> s = []{
std::vector<std::pair<EnforcerBlockerType, std::string>> v;
for (int i = 1; i <= int(EnforcerBlockerType::ExtruderMax); ++i)
v.emplace_back(EnforcerBlockerType(i), "extruder_" + std::to_string(i));
return v;
}();
return s;
}
} // namespace
void inspect_to_json(const Model &model, const std::vector<std::string> &source_paths,
std::ostream &out)
{
json root;
root["sources"] = source_paths;
root["frame"] = "mesh_local";
root["note"] = "Coordinates are mesh-local (each volume's own frame). "
"Paint gizmos operate in this frame.";
json objects = json::array();
size_t total_objects = 0, total_volumes = 0, total_painted = 0, total_facets = 0;
for (size_t oi = 0; oi < model.objects.size(); ++oi) {
const ModelObject *mo = model.objects[oi];
if (!mo) continue;
++total_objects;
json obj;
obj["index"] = oi;
obj["name"] = mo->name;
json volumes = json::array();
for (size_t vi = 0; vi < mo->volumes.size(); ++vi) {
const ModelVolume *mv = mo->volumes[vi];
if (!mv) continue;
++total_volumes;
const indexed_triangle_set &its = mv->mesh().its;
json vol;
vol["index"] = vi;
vol["name"] = mv->name;
vol["n_facets"] = its.indices.size();
vol["is_model_part"] = mv->is_model_part();
vol["bbox_mesh_local"] = bbox_to_json(bounding_box(its));
size_t vol_painted = 0;
json paints;
paints["supports"] = inspect_layer(*mv, mv->supported_facets,
supports_states(), vol_painted);
paints["seam"] = inspect_layer(*mv, mv->seam_facets,
supports_states(), vol_painted);
paints["mmu_segmentation"] = inspect_layer(*mv, mv->mmu_segmentation_facets,
mmu_states(), vol_painted);
paints["fuzzy_skin"] = inspect_layer(*mv, mv->fuzzy_skin_facets,
fuzzy_states(), vol_painted);
vol["paints"] = std::move(paints);
vol["painted_facets_total"] = vol_painted;
if (vol_painted > 0) ++total_painted;
total_facets += vol_painted;
volumes.push_back(std::move(vol));
}
obj["volumes"] = std::move(volumes);
objects.push_back(std::move(obj));
}
root["objects"] = std::move(objects);
root["summary"] = {
{ "objects", total_objects },
{ "volumes", total_volumes },
{ "volumes_with_paint", total_painted },
{ "painted_facets_total", total_facets },
};
// Object names and file paths are arbitrary bytes, and dump() throws on invalid
// UTF-8 by default. Replace such sequences with U+FFFD so the output is always
// valid JSON rather than an exception out of the CLI.
out << root.dump(2, ' ', false, json::error_handler_t::replace) << std::endl;
}
} // namespace PaintCLI
} // namespace Slic3r