diff --git a/sandboxes/orca_inset_plugin_any.py b/sandboxes/orca_inset_plugin_any.py index 784e5d86f7..ebcbc8563a 100644 --- a/sandboxes/orca_inset_plugin_any.py +++ b/sandboxes/orca_inset_plugin_any.py @@ -10,12 +10,12 @@ # /// """Inset Every Slice -- a small, WORKING SlicingPipeline sample plugin. -At Step.Slice, for every layer/region of the sliced object, this shrinks each +At Step.posSlice, for every layer/region of the sliced object, this shrinks each sliced surface by INSET_MM using a real polygon offset (ExPolygon.offset) and writes the result back with SurfaceCollection.set(). After the per-region edits, layer.make_slices() re-derives the layer's merged islands (lslices) so overhang/bridge detection, skirt/brim and support stay coherent with the inset -geometry. At Step.Slice the split slice loop runs make_perimeters() right after +geometry. At Step.posSlice the split slice loop runs make_perimeters() right after the hook, so the change cascades into perimeters, infill and the final G-code -- the toolpath preview shrinks. @@ -35,7 +35,7 @@ class InsetEverySlice(orca.slicing.SlicingPipelineCapabilityBase): return "Inset Every Slice" def execute(self, ctx): - if ctx.step != orca.slicing.Step.Slice or ctx.object is None: + if ctx.step != orca.slicing.Step.posSlice or ctx.object is None: return orca.ExecutionResult.success() # Millimeters -> scaled integer units via the *live* scale (never hardcode 1e6). diff --git a/sandboxes/orca_twistify_plugin_example_any.py b/sandboxes/orca_twistify_plugin_example_any.py index 6dadf6febe..72c746d6b0 100644 --- a/sandboxes/orca_twistify_plugin_example_any.py +++ b/sandboxes/orca_twistify_plugin_example_any.py @@ -17,7 +17,7 @@ # /// """Twistify -- twist/taper/wobble any model at slice time. -At Step.Slice, every layer's sliced surfaces are transformed by a similarity +At Step.posSlice, every layer's sliced surfaces are transformed by a similarity about the object's bounding-box center as a function of Z -- edited IN PLACE through the host geometry classes (ExPolygon.rotate/scale/translate). Each surface is rotated about the center, then (if tapering) translated to the @@ -81,7 +81,7 @@ class Twistify(orca.slicing.SlicingPipelineCapabilityBase): return "Twistify" def execute(self, ctx): - if ctx.step != orca.slicing.Step.Slice or ctx.object is None: + if ctx.step != orca.slicing.Step.posSlice or ctx.object is None: return orca.ExecutionResult.success() p = _params(ctx) diff --git a/src/libslic3r/Print.cpp b/src/libslic3r/Print.cpp index 16e056d3c9..dbbca5ab28 100644 --- a/src/libslic3r/Print.cpp +++ b/src/libslic3r/Print.cpp @@ -2319,7 +2319,7 @@ void Print::process(long long *time_cost_with_cache, bool use_cache) 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) { + auto hook_after = [this](PrintObject* obj, bool was_done, PrintObjectStep pstep, SlicingPipelineStepPlugin sstep) { if (m_pipeline_plugin_active && !was_done && obj->is_step_done(pstep)) run_pipeline_hook(sstep, obj); }; @@ -2329,7 +2329,7 @@ void Print::process(long long *time_cost_with_cache, bool use_cache) if (need_slicing_objects.count(obj) != 0) { const bool was_done = obj->is_step_done(posSlice); obj->slice(); - hook_after(obj, was_done, posSlice, SlicingPipelineStep::Slice); + hook_after(obj, was_done, posSlice, SlicingPipelineStepPlugin::posSlice); // re-snapshot each layer's raw_slices AFTER the Slice hook ran, so the // plugin's mutation becomes the untyped baseline. Without this, a later // perimeter-only re-run (make_perimeters -> restore_untyped_slices) reverts @@ -2349,7 +2349,7 @@ void Print::process(long long *time_cost_with_cache, bool use_cache) 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); + hook_after(obj, was_done, posPerimeters, SlicingPipelineStepPlugin::posPerimeters); } else { if (obj->set_started(posPerimeters)) obj->set_done(posPerimeters); } @@ -2358,7 +2358,7 @@ void Print::process(long long *time_cost_with_cache, bool use_cache) 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); + hook_after(obj, was_done, posEstimateCurledExtrusions, SlicingPipelineStepPlugin::posEstimateCurledExtrusions); } else { if (obj->set_started(posEstimateCurledExtrusions)) @@ -2374,10 +2374,10 @@ void Print::process(long long *time_cost_with_cache, bool use_cache) // is DONE, so this is a mechanical split mirroring the slice/perimeters loop. const bool prepare_was_done = obj->is_step_done(posPrepareInfill); obj->prepare_infill(); - hook_after(obj, prepare_was_done, posPrepareInfill, SlicingPipelineStep::PrepareInfill); + hook_after(obj, prepare_was_done, posPrepareInfill, SlicingPipelineStepPlugin::posPrepareInfill); const bool was_done = obj->is_step_done(posInfill); obj->infill(); - hook_after(obj, was_done, posInfill, SlicingPipelineStep::Infill); + hook_after(obj, was_done, posInfill, SlicingPipelineStepPlugin::posInfill); } else { if (obj->set_started(posPrepareInfill)) @@ -2390,7 +2390,7 @@ void Print::process(long long *time_cost_with_cache, bool use_cache) 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); + hook_after(obj, was_done, posIroning, SlicingPipelineStepPlugin::posIroning); } else { if (obj->set_started(posIroning)) @@ -2404,7 +2404,7 @@ void Print::process(long long *time_cost_with_cache, bool use_cache) if (need_contouring) { const bool was_done = obj->is_step_done(posContouring); obj->contour_z(); - hook_after(obj, was_done, posContouring, SlicingPipelineStep::Contouring); + hook_after(obj, was_done, posContouring, SlicingPipelineStepPlugin::posContouring); } else { if (obj->set_started(posContouring)) obj->set_done(posContouring); @@ -2437,13 +2437,13 @@ void Print::process(long long *time_cost_with_cache, bool use_cache) 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]); + run_pipeline_hook(SlicingPipelineStepPlugin::posSupportMaterial, 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); + hook_after(obj, was_done, posDetectOverhangsForLift, SlicingPipelineStepPlugin::posDetectOverhangsForLift); } else { if (obj->set_started(posDetectOverhangsForLift)) @@ -2520,7 +2520,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 (m_pipeline_plugin_active) run_pipeline_hook(SlicingPipelineStepPlugin::psWipeTower, nullptr); } if (this->has_wipe_tower()) { @@ -2646,7 +2646,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 (m_pipeline_plugin_active) run_pipeline_hook(SlicingPipelineStepPlugin::psSkirtBrim, nullptr); if (time_cost_with_cache) { end_time = (long long)Slic3r::Utils::get_current_time_utc(); @@ -2663,7 +2663,7 @@ void Print::process(long long *time_cost_with_cache, bool use_cache) // 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); + run_pipeline_hook(SlicingPipelineStepPlugin::posSimplifyPath, obj); } else { if (obj->set_started(posSimplifyPath)) diff --git a/src/libslic3r/Print.hpp b/src/libslic3r/Print.hpp index 0402b31614..a3521dcc8e 100644 --- a/src/libslic3r/Print.hpp +++ b/src/libslic3r/Print.hpp @@ -99,6 +99,11 @@ enum PrintObjectStep { posCount, }; +enum class SlicingPipelineStepPlugin { + posSlice, posPerimeters, posEstimateCurledExtrusions, posPrepareInfill, posInfill, posIroning, posContouring, + posSupportMaterial, posDetectOverhangsForLift, posSimplifyPath, psWipeTower, psSkirtBrim +}; + // A PrintRegion object represents a group of volumes to print // sharing the same config (including the same assigned extruder(s)) class PrintRegion @@ -882,11 +887,6 @@ enum FilamentCompatibilityType { InvalidTemperatureRange }; -enum class SlicingPipelineStep { - Slice, Perimeters, EstimateCurledExtrusions, PrepareInfill, Infill, Ironing, Contouring, - SupportMaterial, DetectOverhangsForLift, SimplifyPath, WipeTower, SkirtBrim -}; - // The complete print tray with possibly multiple objects. class Print : public PrintBaseWithState { @@ -896,7 +896,7 @@ private: // Prevents erroneous use by other classes. typedef std::pair PrintObjectInfo; public: - using SlicingPipelineHookFn = std::function; + using SlicingPipelineHookFn = std::function; // Cross-layer injection (mirrors ConfigBase::set_resolve_capability_fn): the GUI/plugin // layer registers a dispatcher; libslic3r stays free of any plugin/Python dependency. static void set_slicing_pipeline_hook_fn(SlicingPipelineHookFn fn) { s_slicing_pipeline_hook_fn = std::move(fn); } @@ -1159,7 +1159,7 @@ private: static SlicingPipelineHookFn s_slicing_pipeline_hook_fn; bool m_pipeline_plugin_active { false }; - void run_pipeline_hook(SlicingPipelineStep step, const PrintObject* object) { + void run_pipeline_hook(SlicingPipelineStepPlugin step, const PrintObject* object) { if (m_pipeline_plugin_active && s_slicing_pipeline_hook_fn) s_slicing_pipeline_hook_fn(*this, object, step); } diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 0ec1a616e5..3150eda1c8 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -3130,7 +3130,7 @@ bool GUI_App::on_init_inner() // cancellation, and convert a plugin failure into a (non-critical) SlicingError so it surfaces as a // slicing-error notification rather than the fatal-crash dialog. Slic3r::Print::set_slicing_pipeline_hook_fn( - [](Slic3r::Print& print, const Slic3r::PrintObject* object, Slic3r::SlicingPipelineStep step) { + [](Slic3r::Print& print, const Slic3r::PrintObject* object, Slic3r::SlicingPipelineStepPlugin step) { const auto* caps = print.config().option("slicing_pipeline_plugin"); // `plugins` is a dynamic-only manifest key (not a static PrintConfig member), so it // must be read from the full/dynamic config -- reading it off print.config() (the @@ -3180,10 +3180,10 @@ bool GUI_App::on_init_inner() // out in Release), so it must NOT be called from a pipeline hook. if (!r.message.empty()) { static const char* const kStepNames[] = { - "Slice", "Perimeters", "EstimateCurledExtrusions", "PrepareInfill", "Infill", - "Ironing", "Contouring", "SupportMaterial", "DetectOverhangsForLift", - "SimplifyPath", "WipeTower", "SkirtBrim" - }; // order must match Slic3r::SlicingPipelineStep + "posSlice", "posPerimeters", "posEstimateCurledExtrusions", "posPrepareInfill", "posInfill", + "posIroning", "posContouring", "posSupportMaterial", "posDetectOverhangsForLift", + "posSimplifyPath", "psWipeTower", "psSkirtBrim" + }; // order must match Slic3r::SlicingPipelineStepPlugin const char* step_name = static_cast(step) < sizeof(kStepNames) / sizeof(kStepNames[0]) ? kStepNames[static_cast(step)] : "Unknown"; BOOST_LOG_TRIVIAL(info) << "Slicing pipeline plugin '" << ref.capability_name diff --git a/src/slic3r/GUI/Tab.cpp b/src/slic3r/GUI/Tab.cpp index a765c6fcdd..fcb5c575ea 100644 --- a/src/slic3r/GUI/Tab.cpp +++ b/src/slic3r/GUI/Tab.cpp @@ -1794,7 +1794,7 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value) // always carries resolved "name;uuid;capability" references that full_config() and save_to_json() // then pass downstream as-is -- no separate rebuild anywhere else. if (const ConfigOptionDef* opt_def = m_config->def()->get(opt_key); - opt_def && opt_def->gui_type == ConfigOptionDef::GUIType::plugin_picker) + opt_def && opt_def->plugin_type != ConfigOptionDef::PluginType::None) m_config->update_plugin_manifest(); if (opt_key == "gcode_flavor" && m_type == Preset::TYPE_PRINTER) { diff --git a/src/slic3r/plugin/PluginHostSlicing.cpp b/src/slic3r/plugin/PluginHostSlicing.cpp index 57aa02962e..01739420f0 100644 --- a/src/slic3r/plugin/PluginHostSlicing.cpp +++ b/src/slic3r/plugin/PluginHostSlicing.cpp @@ -414,7 +414,7 @@ void PluginHostSlicing::RegisterBindings(py::module_& host) layer_region .def_readonly("slices", &LayerRegion::slices, "Sliced, typed surfaces (SurfaceCollection). Edit in place, or replace with " - "slices.set(expolygons, surface_type). At Step.Slice this is the primary mutation " + "slices.set(expolygons, surface_type). At Step.posSlice this is the primary mutation " "target; the split slice loop runs make_perimeters() afterward so edits cascade downstream.") .def_readonly("fill_surfaces", &LayerRegion::fill_surfaces, "Surfaces prepared for infill (SurfaceCollection). Edit in place or via fill_surfaces.set(...).") diff --git a/src/slic3r/plugin/pluginTypes/slicingPipeline/SlicingPipelinePluginCapability.cpp b/src/slic3r/plugin/pluginTypes/slicingPipeline/SlicingPipelinePluginCapability.cpp index 6cc1d1bde9..bd2bca71c7 100644 --- a/src/slic3r/plugin/pluginTypes/slicingPipeline/SlicingPipelinePluginCapability.cpp +++ b/src/slic3r/plugin/pluginTypes/slicingPipeline/SlicingPipelinePluginCapability.cpp @@ -13,19 +13,19 @@ void SlicingPipelinePluginCapability::RegisterBindings(py::module_& module, py:: (void) pluginTypes; // matches gcode/script/printerAgent; Step is a fresh enum below. auto slicing = module.def_submodule("slicing", "Slicing pipeline API (research/experimental)."); - py::enum_(slicing, "Step") - .value("Slice", SlicingPipelineStep::Slice) - .value("Perimeters", SlicingPipelineStep::Perimeters) - .value("EstimateCurledExtrusions", SlicingPipelineStep::EstimateCurledExtrusions) - .value("PrepareInfill", SlicingPipelineStep::PrepareInfill) // after prepare_infill, before make_fills: editing fill_surfaces here CASCADES - .value("Infill", SlicingPipelineStep::Infill) // after make_fills: editing fill_surfaces here does NOT regenerate fills (v1) - .value("Ironing", SlicingPipelineStep::Ironing) - .value("Contouring", SlicingPipelineStep::Contouring) - .value("SupportMaterial", SlicingPipelineStep::SupportMaterial) - .value("DetectOverhangsForLift", SlicingPipelineStep::DetectOverhangsForLift) - .value("SimplifyPath", SlicingPipelineStep::SimplifyPath) // covers all simplify sub-steps - .value("WipeTower", SlicingPipelineStep::WipeTower) - .value("SkirtBrim", SlicingPipelineStep::SkirtBrim) + py::enum_(slicing, "Step") + .value("posSlice", SlicingPipelineStepPlugin::posSlice) + .value("posPerimeters", SlicingPipelineStepPlugin::posPerimeters) + .value("posEstimateCurledExtrusions", SlicingPipelineStepPlugin::posEstimateCurledExtrusions) + .value("posPrepareInfill", SlicingPipelineStepPlugin::posPrepareInfill) // after prepare_infill, before make_fills: editing fill_surfaces here CASCADES + .value("posInfill", SlicingPipelineStepPlugin::posInfill) // after make_fills: editing fill_surfaces here does NOT regenerate fills (v1) + .value("posIroning", SlicingPipelineStepPlugin::posIroning) + .value("posContouring", SlicingPipelineStepPlugin::posContouring) + .value("posSupportMaterial", SlicingPipelineStepPlugin::posSupportMaterial) + .value("posDetectOverhangsForLift", SlicingPipelineStepPlugin::posDetectOverhangsForLift) + .value("posSimplifyPath", SlicingPipelineStepPlugin::posSimplifyPath) // covers all simplify sub-steps + .value("psWipeTower", SlicingPipelineStepPlugin::psWipeTower) + .value("psSkirtBrim", SlicingPipelineStepPlugin::psSkirtBrim) .export_values(); // The read-graph data model (Surface / ExPolygon / the extrusion tree / LayerRegion / diff --git a/src/slic3r/plugin/pluginTypes/slicingPipeline/SlicingPipelinePluginCapability.hpp b/src/slic3r/plugin/pluginTypes/slicingPipeline/SlicingPipelinePluginCapability.hpp index 7f770913e7..d3c30ba92c 100644 --- a/src/slic3r/plugin/pluginTypes/slicingPipeline/SlicingPipelinePluginCapability.hpp +++ b/src/slic3r/plugin/pluginTypes/slicingPipeline/SlicingPipelinePluginCapability.hpp @@ -1,6 +1,6 @@ #pragma once #include "slic3r/plugin/PythonPluginInterface.hpp" -#include "libslic3r/Print.hpp" // SlicingPipelineStep, Print, PrintObject +#include "libslic3r/Print.hpp" // SlicingPipelineStepPlugin, Print, PrintObject #include #include #include @@ -15,7 +15,7 @@ namespace Slic3r { // src/slic3r/plugin/PluginHostSlicing.cpp. struct SlicingPipelineContext { std::string orca_version; - SlicingPipelineStep step { SlicingPipelineStep::Slice }; + SlicingPipelineStepPlugin step { SlicingPipelineStepPlugin::posSlice }; Print* print { nullptr }; // always present when dispatched const PrintObject* object { nullptr }; // null for print-wide steps // read-only per-plugin settings, populated by the dispatcher from the diff --git a/tests/fff_print/test_slicing_pipeline_hook.cpp b/tests/fff_print/test_slicing_pipeline_hook.cpp index fcb1802b8c..1f7db91e4c 100644 --- a/tests/fff_print/test_slicing_pipeline_hook.cpp +++ b/tests/fff_print/test_slicing_pipeline_hook.cpp @@ -19,7 +19,7 @@ TEST_CASE("slicing_pipeline_plugin option exists and defaults empty", "[slicing_ TEST_CASE("slicing pipeline hook setter is a no-op-safe injection", "[slicing_pipeline]") { int calls = 0; Slic3r::Print::set_slicing_pipeline_hook_fn( - [&](Slic3r::Print&, const Slic3r::PrintObject*, Slic3r::SlicingPipelineStep){ ++calls; }); + [&](Slic3r::Print&, const Slic3r::PrintObject*, Slic3r::SlicingPipelineStepPlugin){ ++calls; }); Slic3r::Print::set_slicing_pipeline_hook_fn(nullptr); // reset — must be legal CHECK(calls == 0); } @@ -30,10 +30,10 @@ TEST_CASE("slicing pipeline hook setter is a no-op-safe injection", "[slicing_pi using namespace Slic3r::Test; TEST_CASE("SlicingPipeline hook fires once per step per object in order", "[slicing_pipeline]") { - struct Call { const Slic3r::PrintObject* obj; Slic3r::SlicingPipelineStep step; }; + struct Call { const Slic3r::PrintObject* obj; Slic3r::SlicingPipelineStepPlugin step; }; std::vector calls; Slic3r::Print::set_slicing_pipeline_hook_fn( - [&](Slic3r::Print&, const Slic3r::PrintObject* o, Slic3r::SlicingPipelineStep s){ calls.push_back({o, s}); }); + [&](Slic3r::Print&, const Slic3r::PrintObject* o, Slic3r::SlicingPipelineStepPlugin s){ calls.push_back({o, s}); }); Slic3r::Print print; Slic3r::Model model; Slic3r::DynamicPrintConfig config = Slic3r::DynamicPrintConfig::full_print_config(); @@ -42,7 +42,7 @@ TEST_CASE("SlicingPipeline hook fires once per step per object in order", "[slic print.process(); Slic3r::Print::set_slicing_pipeline_hook_fn(nullptr); - using S = Slic3r::SlicingPipelineStep; + using S = Slic3r::SlicingPipelineStepPlugin; auto count = [&](S s){ return std::count_if(calls.begin(), calls.end(), [&](const Call& c){ return c.step == s; }); }; CHECK(count(S::Slice) == 1); CHECK(count(S::Perimeters) == 1); @@ -97,7 +97,7 @@ TEST_CASE("Inactive hook: process output is byte-identical (no-op hook == unset) if (activate) config.set_key_value("slicing_pipeline_plugin", new Slic3r::ConfigOptionStrings({"probe"})); if (set_noop_hook) - Slic3r::Print::set_slicing_pipeline_hook_fn([](Slic3r::Print&, const Slic3r::PrintObject*, Slic3r::SlicingPipelineStep){}); + Slic3r::Print::set_slicing_pipeline_hook_fn([](Slic3r::Print&, const Slic3r::PrintObject*, Slic3r::SlicingPipelineStepPlugin){}); else Slic3r::Print::set_slicing_pipeline_hook_fn(nullptr); init_print({TestMesh::cube_20x20x20}, print, model, config); @@ -120,7 +120,7 @@ TEST_CASE("Inactive hook: process output is byte-identical (no-op hook == unset) TEST_CASE("Empty option: registered hook is gated off and never fires", "[slicing_pipeline]") { int calls = 0; Slic3r::Print::set_slicing_pipeline_hook_fn( - [&](Slic3r::Print&, const Slic3r::PrintObject*, Slic3r::SlicingPipelineStep){ ++calls; }); + [&](Slic3r::Print&, const Slic3r::PrintObject*, Slic3r::SlicingPipelineStepPlugin){ ++calls; }); Slic3r::Print print; Slic3r::Model model; auto config = Slic3r::DynamicPrintConfig::full_print_config(); // option left EMPTY -> inactive regardless of the registered hook. @@ -138,9 +138,9 @@ TEST_CASE("Empty option: registered hook is gated off and never fires", "[slicin TEST_CASE("Duplicate objects share a slice: Slice hook fires exactly once", "[slicing_pipeline]") { int slice_calls = 0, perim_calls = 0; Slic3r::Print::set_slicing_pipeline_hook_fn( - [&](Slic3r::Print&, const Slic3r::PrintObject*, Slic3r::SlicingPipelineStep s){ - if (s == Slic3r::SlicingPipelineStep::Slice) ++slice_calls; - if (s == Slic3r::SlicingPipelineStep::Perimeters) ++perim_calls; + [&](Slic3r::Print&, const Slic3r::PrintObject*, Slic3r::SlicingPipelineStepPlugin s){ + if (s == Slic3r::SlicingPipelineStepPlugin::posSlice) ++slice_calls; + if (s == Slic3r::SlicingPipelineStepPlugin::posPerimeters) ++perim_calls; }); Slic3r::Print print; Slic3r::Model model; @@ -186,8 +186,8 @@ TEST_CASE("Mutating slices at the Slice boundary cascades downstream", "[slicing auto config = Slic3r::DynamicPrintConfig::full_print_config(); config.set_key_value("slicing_pipeline_plugin", new Slic3r::ConfigOptionStrings({"probe"})); if (inset) Slic3r::Print::set_slicing_pipeline_hook_fn( - [](Slic3r::Print&, const Slic3r::PrintObject* o, Slic3r::SlicingPipelineStep s){ - if (s != Slic3r::SlicingPipelineStep::Slice || !o) return; + [](Slic3r::Print&, const Slic3r::PrintObject* o, Slic3r::SlicingPipelineStepPlugin s){ + if (s != Slic3r::SlicingPipelineStepPlugin::posSlice || !o) return; for (Slic3r::Layer* l : const_cast(o)->layers()) for (Slic3r::LayerRegion* r : l->regions()) { Slic3r::Surfaces in = r->slices.surfaces; @@ -219,7 +219,7 @@ TEST_CASE("Changing slicing_pipeline_plugin invalidates posSlice", "[slicing_pip #include // §3.6 (Twistify design): Twistify's effect is a similarity transform (rotate + uniform -// scale) applied to slices at Step.Slice. This C++ analogue rotates every region's slices a +// scale) applied to slices at Step.posSlice. This C++ analogue rotates every region's slices a // fixed 45 deg about the object's base-footprint center -- the same seam and cascade that // Twistify.py drives through the slices.set() + Layer::make_slices() path. Two end-to-end invariants after // process() confirm the approach: @@ -235,8 +235,8 @@ TEST_CASE("Rotating slices at the Slice boundary cascades (area preserved, bbox auto config = Slic3r::DynamicPrintConfig::full_print_config(); config.set_key_value("slicing_pipeline_plugin", new Slic3r::ConfigOptionStrings({"probe"})); if (rotate) Slic3r::Print::set_slicing_pipeline_hook_fn( - [](Slic3r::Print&, const Slic3r::PrintObject* o, Slic3r::SlicingPipelineStep s){ - if (s != Slic3r::SlicingPipelineStep::Slice || !o) return; + [](Slic3r::Print&, const Slic3r::PrintObject* o, Slic3r::SlicingPipelineStepPlugin s){ + if (s != Slic3r::SlicingPipelineStepPlugin::posSlice || !o) return; auto* obj = const_cast(o); // Twist axis = center of the first sliced layer's footprint (Twistify's anchor). coord_t nx=0, xx=0, ny=0, xy=0; bool seeded=false; @@ -310,8 +310,8 @@ TEST_CASE("Identity round-trip through set_slices is byte-identical", "[slicing_ auto config = Slic3r::DynamicPrintConfig::full_print_config(); config.set_key_value("slicing_pipeline_plugin", new Slic3r::ConfigOptionStrings({"probe"})); // active in both runs Slic3r::Print::set_slicing_pipeline_hook_fn( - [roundtrip](Slic3r::Print&, const Slic3r::PrintObject* o, Slic3r::SlicingPipelineStep s){ - if (!roundtrip || s != Slic3r::SlicingPipelineStep::Slice || !o) return; + [roundtrip](Slic3r::Print&, const Slic3r::PrintObject* o, Slic3r::SlicingPipelineStepPlugin s){ + if (!roundtrip || s != Slic3r::SlicingPipelineStepPlugin::posSlice || !o) return; for (Slic3r::Layer* l : const_cast(o)->layers()) for (Slic3r::LayerRegion* r : l->regions()) { Slic3r::Surfaces in = r->slices.surfaces; // copy current (already-normalized) geometry @@ -359,8 +359,8 @@ static double outer_slices_width(const Slic3r::Print& print) { TEST_CASE("raw_slices captures post-hook geometry so a perimeter re-run keeps the mutation", "[slicing_pipeline]") { using Catch::Matchers::WithinRel; Slic3r::Print::set_slicing_pipeline_hook_fn( - [](Slic3r::Print&, const Slic3r::PrintObject* o, Slic3r::SlicingPipelineStep s){ - if (s != Slic3r::SlicingPipelineStep::Slice || !o) return; + [](Slic3r::Print&, const Slic3r::PrintObject* o, Slic3r::SlicingPipelineStepPlugin s){ + if (s != Slic3r::SlicingPipelineStepPlugin::posSlice || !o) return; for (Slic3r::Layer* l : const_cast(o)->layers()) for (Slic3r::LayerRegion* r : l->regions()) { Slic3r::Surfaces in = r->slices.surfaces; @@ -391,12 +391,12 @@ TEST_CASE("raw_slices captures post-hook geometry so a perimeter re-run keeps th // them, whereas the pre-existing Infill seam fires after the fills are already built (v1 limit). // All three runs register a hook (active path) so the comparison isolates only the mutation. TEST_CASE("fill_surfaces mutation cascades at PrepareInfill but not at Infill", "[slicing_pipeline]") { - auto fill_paths = [](bool shrink, Slic3r::SlicingPipelineStep at) { + auto fill_paths = [](bool shrink, Slic3r::SlicingPipelineStepPlugin at) { Slic3r::Print print; Slic3r::Model model; auto config = Slic3r::DynamicPrintConfig::full_print_config(); config.set_key_value("slicing_pipeline_plugin", new Slic3r::ConfigOptionStrings({"probe"})); Slic3r::Print::set_slicing_pipeline_hook_fn( - [shrink, at](Slic3r::Print&, const Slic3r::PrintObject* o, Slic3r::SlicingPipelineStep s){ + [shrink, at](Slic3r::Print&, const Slic3r::PrintObject* o, Slic3r::SlicingPipelineStepPlugin s){ if (!shrink || s != at || !o) return; for (Slic3r::Layer* l : const_cast(o)->layers()) for (Slic3r::LayerRegion* r : l->regions()) { @@ -417,7 +417,7 @@ TEST_CASE("fill_surfaces mutation cascades at PrepareInfill but not at Infill", Slic3r::Print::set_slicing_pipeline_hook_fn(nullptr); return n; }; - using S = Slic3r::SlicingPipelineStep; + using S = Slic3r::SlicingPipelineStepPlugin; const size_t base = fill_paths(false, S::PrepareInfill); // active hook, no mutation CHECK(base > 0); CHECK(fill_paths(true, S::PrepareInfill) < base); // mutation before make_fills cascades @@ -434,8 +434,8 @@ TEST_CASE("refreshing lslices after a slice mutation makes islands track the geo auto config = Slic3r::DynamicPrintConfig::full_print_config(); config.set_key_value("slicing_pipeline_plugin", new Slic3r::ConfigOptionStrings({"probe"})); Slic3r::Print::set_slicing_pipeline_hook_fn( - [refresh](Slic3r::Print&, const Slic3r::PrintObject* o, Slic3r::SlicingPipelineStep s){ - if (s != Slic3r::SlicingPipelineStep::Slice || !o) return; + [refresh](Slic3r::Print&, const Slic3r::PrintObject* o, Slic3r::SlicingPipelineStepPlugin s){ + if (s != Slic3r::SlicingPipelineStepPlugin::posSlice || !o) return; for (Slic3r::Layer* l : const_cast(o)->layers()) { for (Slic3r::LayerRegion* r : l->regions()) { Slic3r::Surfaces in = r->slices.surfaces;