mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-08-04 00:32:08 +00:00
feat(plugin): add the slicing-pipeline plugin capability
Introduces a plugin capability that runs Python at the seams of Print::process(), letting a plugin read and rewrite slicing state as it is computed. - New slicing_pipeline_plugin config option; selected plugin refs are serialized into the print manifest. - Print gains an injectable hook fired at each pipeline step (posSlice, posPerimeters, posInfill, ...). It is a no-op when unset, fires only on genuine (re)computation, and never on the use-cache path. - orca.slicing submodule: SlicingPipelineCapabilityBase plus a trampoline and a Step enum. Capabilities read the live graph through zero-copy int64 numpy views (contour/holes geometry with unscaled coordinates, flattened toolpath data) and edit it through 2D-geometry mutators with cache-invariant refresh. - GUI dispatcher runs capabilities during slicing under the GIL, turns plugin errors into slicing errors, honors cancellation, and adds the plugin picker. - Ships the InsetEverySlice sample plugin and binding/hook tests.
This commit is contained in:
@@ -50,6 +50,8 @@ using namespace nlohmann;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
Print::SlicingPipelineHookFn Print::s_slicing_pipeline_hook_fn = nullptr;
|
||||
|
||||
template class PrintState<PrintStep, psCount>;
|
||||
template class PrintState<PrintObjectStep, posCount>;
|
||||
|
||||
@@ -277,7 +279,8 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
||||
|| opt_key == "wipe_tower_rotation_angle") {
|
||||
steps.emplace_back(psSkirtBrim);
|
||||
} else if (
|
||||
opt_key == "initial_layer_print_height"
|
||||
opt_key == "slicing_pipeline_plugin"
|
||||
|| opt_key == "initial_layer_print_height"
|
||||
|| opt_key == "nozzle_diameter"
|
||||
|| opt_key == "filament_shrink"
|
||||
|| opt_key == "filament_shrinkage_compensation_z"
|
||||
@@ -2201,6 +2204,11 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
||||
if (time_cost_with_cache)
|
||||
*time_cost_with_cache = 0;
|
||||
|
||||
{
|
||||
const auto* sp = this->config().option<ConfigOptionStrings>("slicing_pipeline_plugin");
|
||||
m_pipeline_plugin_active = s_slicing_pipeline_hook_fn && sp && !sp->values.empty();
|
||||
}
|
||||
|
||||
name_tbb_thread_pool_threads_set_locale();
|
||||
|
||||
//compute the PrintObject with the same geometries
|
||||
@@ -2310,20 +2318,36 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": total object counts %1% in current print, need to slice %2%")%m_objects.size()%need_slicing_objects.size();
|
||||
BOOST_LOG_TRIVIAL(info) << "Starting the slicing process." << log_memory_info();
|
||||
if (!use_cache) {
|
||||
// Fire the SlicingPipeline hook for `obj` iff it just (re)computed `pstep` this pass.
|
||||
auto hook_after = [this](PrintObject* obj, bool was_done, PrintObjectStep pstep, SlicingPipelineStep sstep) {
|
||||
if (m_pipeline_plugin_active && !was_done && obj->is_step_done(pstep))
|
||||
run_pipeline_hook(sstep, obj);
|
||||
};
|
||||
|
||||
// SlicingPipeline: dedicated slice loop so the Slice boundary is hookable before perimeters.
|
||||
for (PrintObject *obj : m_objects) {
|
||||
if (need_slicing_objects.count(obj) != 0) {
|
||||
obj->make_perimeters();
|
||||
}
|
||||
else {
|
||||
if (obj->set_started(posSlice))
|
||||
obj->set_done(posSlice);
|
||||
if (obj->set_started(posPerimeters))
|
||||
obj->set_done(posPerimeters);
|
||||
const bool was_done = obj->is_step_done(posSlice);
|
||||
obj->slice();
|
||||
hook_after(obj, was_done, posSlice, SlicingPipelineStep::Slice);
|
||||
} else {
|
||||
if (obj->set_started(posSlice)) obj->set_done(posSlice); // shared/duplicate — no hook
|
||||
}
|
||||
}
|
||||
for (PrintObject *obj : m_objects) {
|
||||
if (need_slicing_objects.count(obj) != 0) {
|
||||
const bool was_done = obj->is_step_done(posPerimeters);
|
||||
obj->make_perimeters(); // slice() inside is a no-op: posSlice already DONE
|
||||
hook_after(obj, was_done, posPerimeters, SlicingPipelineStep::Perimeters);
|
||||
} else {
|
||||
if (obj->set_started(posPerimeters)) obj->set_done(posPerimeters);
|
||||
}
|
||||
}
|
||||
for (PrintObject *obj : m_objects) {
|
||||
if (need_slicing_objects.count(obj) != 0) {
|
||||
const bool was_done = obj->is_step_done(posEstimateCurledExtrusions);
|
||||
obj->estimate_curled_extrusions();
|
||||
hook_after(obj, was_done, posEstimateCurledExtrusions, SlicingPipelineStep::EstimateCurledExtrusions);
|
||||
}
|
||||
else {
|
||||
if (obj->set_started(posEstimateCurledExtrusions))
|
||||
@@ -2332,7 +2356,9 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
||||
}
|
||||
for (PrintObject *obj : m_objects) {
|
||||
if (need_slicing_objects.count(obj) != 0) {
|
||||
const bool was_done = obj->is_step_done(posInfill);
|
||||
obj->infill();
|
||||
hook_after(obj, was_done, posInfill, SlicingPipelineStep::Infill);
|
||||
}
|
||||
else {
|
||||
if (obj->set_started(posPrepareInfill))
|
||||
@@ -2343,7 +2369,9 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
||||
}
|
||||
for (PrintObject *obj : m_objects) {
|
||||
if (need_slicing_objects.count(obj) != 0) {
|
||||
const bool was_done = obj->is_step_done(posIroning);
|
||||
obj->ironing();
|
||||
hook_after(obj, was_done, posIroning, SlicingPipelineStep::Ironing);
|
||||
}
|
||||
else {
|
||||
if (obj->set_started(posIroning))
|
||||
@@ -2355,13 +2383,22 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
||||
for (PrintObject *obj : m_objects) {
|
||||
bool need_contouring = need_slicing_objects.count(obj) != 0 && obj->need_z_contouring();
|
||||
if (need_contouring) {
|
||||
const bool was_done = obj->is_step_done(posContouring);
|
||||
obj->contour_z();
|
||||
hook_after(obj, was_done, posContouring, SlicingPipelineStep::Contouring);
|
||||
} else {
|
||||
if (obj->set_started(posContouring))
|
||||
obj->set_done(posContouring);
|
||||
}
|
||||
}
|
||||
|
||||
// SlicingPipeline: support runs in the parallel block below; the hook must fire in a
|
||||
// sequential loop afterward. Snapshot per-object done-state just before the parallel_for.
|
||||
std::vector<char> sup_was_done(m_objects.size(), 1);
|
||||
if (m_pipeline_plugin_active)
|
||||
for (size_t i = 0; i < m_objects.size(); ++i)
|
||||
sup_was_done[i] = m_objects[i]->is_step_done(posSupportMaterial) ? 1 : 0;
|
||||
|
||||
tbb::parallel_for(tbb::blocked_range<int>(0, int(m_objects.size())),
|
||||
[this, need_slicing_objects](const tbb::blocked_range<int>& range) {
|
||||
for (int i = range.begin(); i < range.end(); i++) {
|
||||
@@ -2377,9 +2414,17 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
||||
}
|
||||
);
|
||||
|
||||
if (m_pipeline_plugin_active)
|
||||
for (size_t i = 0; i < m_objects.size(); ++i)
|
||||
if (need_slicing_objects.count(m_objects[i]) != 0 && !sup_was_done[i]
|
||||
&& m_objects[i]->is_step_done(posSupportMaterial))
|
||||
run_pipeline_hook(SlicingPipelineStep::SupportMaterial, m_objects[i]);
|
||||
|
||||
for (PrintObject* obj : m_objects) {
|
||||
if (need_slicing_objects.count(obj) != 0) {
|
||||
const bool was_done = obj->is_step_done(posDetectOverhangsForLift);
|
||||
obj->detect_overhangs_for_lift();
|
||||
hook_after(obj, was_done, posDetectOverhangsForLift, SlicingPipelineStep::DetectOverhangsForLift);
|
||||
}
|
||||
else {
|
||||
if (obj->set_started(posDetectOverhangsForLift))
|
||||
@@ -2456,6 +2501,7 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
||||
|
||||
}
|
||||
this->set_done(psWipeTower);
|
||||
if (m_pipeline_plugin_active) run_pipeline_hook(SlicingPipelineStep::WipeTower, nullptr);
|
||||
}
|
||||
|
||||
if (this->has_wipe_tower()) {
|
||||
@@ -2581,6 +2627,7 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
||||
|
||||
this->finalize_first_layer_convex_hull();
|
||||
this->set_done(psSkirtBrim);
|
||||
if (m_pipeline_plugin_active) run_pipeline_hook(SlicingPipelineStep::SkirtBrim, nullptr);
|
||||
|
||||
if (time_cost_with_cache) {
|
||||
end_time = (long long)Slic3r::Utils::get_current_time_utc();
|
||||
@@ -2591,7 +2638,13 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
||||
for (PrintObject *obj : m_objects) {
|
||||
if (((!use_cache)&&(need_slicing_objects.count(obj) != 0))
|
||||
|| (use_cache &&(re_slicing_objects.count(obj) != 0))){
|
||||
const bool was_done = obj->is_step_done(posSimplifyPath);
|
||||
obj->simplify_extrusion_path();
|
||||
// Unlike every other seam (all inside the `if (!use_cache)` block above), this loop is
|
||||
// shared with the use_cache path (re_slicing_objects), so `!use_cache` must be checked
|
||||
// explicitly here to keep hooks from ever firing on cache-loaded (plugin-final) objects.
|
||||
if (!use_cache && m_pipeline_plugin_active && !was_done && obj->is_step_done(posSimplifyPath))
|
||||
run_pipeline_hook(SlicingPipelineStep::SimplifyPath, obj);
|
||||
}
|
||||
else {
|
||||
if (obj->set_started(posSimplifyPath))
|
||||
|
||||
Reference in New Issue
Block a user