From 44d5a22a32ab9037a87bc95f028536a4088ac613 Mon Sep 17 00:00:00 2001 From: Hanif Koh Date: Wed, 23 Sep 2026 01:44:39 +0800 Subject: [PATCH] Add a Shell-Only Drag Mode That Keeps What Can Be Seen From Outside A third mode of the drag preference: the reduced set keeps every role but the sparse infill, internal solid infill, internal bridge infill and gap fill, which lie under the skins or between the walls, on every layer. Walls, top and bottom surfaces, bridges, supports and the prime tower are drawn whole, so from outside the print looks as it does at rest, and the two end layers of the visible range stay whole as in every mode. The roles come from the slicer, so the classification is exact wherever the slicer's is. What a role cannot tell apart is the inside of the prime tower or of a support from its outside; both are kept whole. A profile with no top or bottom shell layers exposes infill, which this mode hides. --- docs/HLSD/gcode-preview-dragging.md | 11 +++++++++-- src/libslic3r/AppConfig.cpp | 2 +- src/libvgcode/include/Types.hpp | 2 ++ src/libvgcode/src/ViewerImpl.cpp | 24 ++++++++++++++++++++++-- src/libvgcode/src/ViewerImpl.hpp | 2 +- src/slic3r/GUI/GCodeViewer.cpp | 2 ++ src/slic3r/GUI/GCodeViewer.hpp | 2 +- src/slic3r/GUI/Preferences.cpp | 6 ++++-- 8 files changed, 42 insertions(+), 9 deletions(-) diff --git a/docs/HLSD/gcode-preview-dragging.md b/docs/HLSD/gcode-preview-dragging.md index d2935f84e9..b35f8f0559 100644 --- a/docs/HLSD/gcode-preview-dragging.md +++ b/docs/HLSD/gcode-preview-dragging.md @@ -9,8 +9,8 @@ let go. | Preference | Values | Effect | |---|---|---| -| `preview_reduced_detail_mode` | `off`, `solid`, `layers`, `outer_walls` | what is drawn while dragging | -| `preview_reduced_detail_layer_stride` | 1–20 | one layer in every N is kept by the toolpath modes | +| `preview_reduced_detail_mode` | `off`, `solid`, `layers`, `outer_walls`, `shell` | what is drawn while dragging | +| `preview_reduced_detail_layer_stride` | 1–20 | one layer in every N is kept by the skip-layers and outer-walls modes | libvgcode (`src/libvgcode`) builds and binds the reduced toolpath set, `GCodeViewer` maps the preferences onto it and draws the solid model, and `GLCanvas3D` decides when the user is dragging. @@ -35,6 +35,13 @@ are the faces the range cuts open, and the top is what the user is looking at. - `LayersOnly` (`layers`) keeps every role of one layer in every stride. - `OuterWallsOnly` (`outer_walls`) keeps the outer and overhang perimeters of one layer in every stride. The prime tower and supports have other roles and are left out. +- `ShellOnly` (`shell`) keeps every layer, and every role but the sparse infill, internal solid + infill, internal bridge infill and gap fill, which lie under the skins or between the walls. + Walls, top and bottom surfaces, bridges, supports and the prime tower are drawn whole, so from + outside the print looks as it does at rest, and the stride does not apply. The roles come from + the slicer, so the classification is exact wherever the slicer's is; what it cannot express is + the inside of the prime tower and of a support, which share one role with their outside and are + kept whole. A profile with no top or bottom shell layers exposes infill, which this mode hides. ## The solid model diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index 699efad7f5..67ca58076a 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -208,7 +208,7 @@ void AppConfig::set_defaults() // what the preview draws while the user drags it, and one layer in how many the toolpath modes keep { const std::string mode = get("preview_reduced_detail_mode"); - if (mode != "off" && mode != "solid" && mode != "layers" && mode != "outer_walls") + if (mode != "off" && mode != "solid" && mode != "layers" && mode != "outer_walls" && mode != "shell") set("preview_reduced_detail_mode", "off"); int stride = 4; try { diff --git a/src/libvgcode/include/Types.hpp b/src/libvgcode/include/Types.hpp index ee4d555ffe..7d423a9a30 100644 --- a/src/libvgcode/include/Types.hpp +++ b/src/libvgcode/include/Types.hpp @@ -172,6 +172,8 @@ enum class EReducedDetailMode : uint8_t LayersOnly, // one layer in every stride, outer walls only OuterWallsOnly, + // every layer, every role but the infill under the skins and the gap fill: what can be seen from outside + ShellOnly, COUNT }; diff --git a/src/libvgcode/src/ViewerImpl.cpp b/src/libvgcode/src/ViewerImpl.cpp index 2d33f64d31..413543e6e1 100644 --- a/src/libvgcode/src/ViewerImpl.cpp +++ b/src/libvgcode/src/ViewerImpl.cpp @@ -17,6 +17,7 @@ #include #include #include +#include namespace libvgcode { @@ -1194,17 +1195,34 @@ void ViewerImpl::load(GCodeInputData&& gcode_data) } #ifndef ENABLE_OPENGL_ES +static bool is_outer_wall(EGCodeExtrusionRole role) +{ + return role == EGCodeExtrusionRole::ExternalPerimeter || role == EGCodeExtrusionRole::OverhangPerimeter; +} + +// the roles that lie under a skin or between walls, never seen from outside the print +static bool is_hidden_in_shell(EGCodeExtrusionRole role) +{ + return role == EGCodeExtrusionRole::InternalInfill || + role == EGCodeExtrusionRole::SolidInfill || + role == EGCodeExtrusionRole::InternalBridgeInfill || + role == EGCodeExtrusionRole::GapFill; +} + bool ViewerImpl::reduced_set_keeps(const PathVertex& v) const { switch (m_settings.reduced_detail_mode) { case EReducedDetailMode::OuterWallsOnly: - return v.role == EGCodeExtrusionRole::ExternalPerimeter || v.role == EGCodeExtrusionRole::OverhangPerimeter; + return is_outer_wall(v.role); + case EReducedDetailMode::ShellOnly: + return !is_hidden_in_shell(v.role); default: return true; } } #endif // ENABLE_OPENGL_ES + void ViewerImpl::update_enabled_entities() { if (m_vertices.empty()) @@ -1222,6 +1240,8 @@ void ViewerImpl::update_enabled_entities() std::vector enabled_segments_reduced; std::vector enabled_options_reduced; const Interval& layers_range = m_layers.get_view_range(); + // the shell mode draws every layer, the other toolpath modes one in every stride + const bool shell_reduced = reduced_mode == EReducedDetailMode::ShellOnly; #endif // ENABLE_OPENGL_ES Interval range = m_view_range.get_visible(); @@ -1276,7 +1296,7 @@ void ViewerImpl::update_enabled_entities() const bool end_layer = v.layer_id == layers_range[0] || v.layer_id == layers_range[1]; if (end_layer) (v.is_option() ? enabled_options_reduced : enabled_segments_reduced).push_back(static_cast(i)); - else if (reduced_mode != EReducedDetailMode::EndLayersOnly && (v.layer_id % layer_stride) == 0) { + else if (reduced_mode != EReducedDetailMode::EndLayersOnly && (shell_reduced || (v.layer_id % layer_stride) == 0)) { if (v.is_option()) enabled_options_reduced.push_back(static_cast(i)); else if (!v.is_extrusion() || reduced_set_keeps(v)) diff --git a/src/libvgcode/src/ViewerImpl.hpp b/src/libvgcode/src/ViewerImpl.hpp index 50e502d653..fee8c2a09d 100644 --- a/src/libvgcode/src/ViewerImpl.hpp +++ b/src/libvgcode/src/ViewerImpl.hpp @@ -537,7 +537,7 @@ private: // The set the next draw reads from: the reduced one while dragging, if one is built. bool use_reduced_set() const { return m_settings.reduced_detail && m_settings.reduced_detail_mode != EReducedDetailMode::Off; } - // Whether an extrusion segment belongs to the reduced set under the current mode + // Whether the extrusion segment starting at vertex v belongs to the reduced set under the current mode bool reduced_set_keeps(const PathVertex& v) const; struct ActiveSet { diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 0ef4a07e92..7878e1b1ff 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -2069,6 +2069,8 @@ libvgcode::EReducedDetailMode GCodeViewer::reduced_detail_mode_from_string(const return libvgcode::EReducedDetailMode::LayersOnly; if (mode == "outer_walls") return libvgcode::EReducedDetailMode::OuterWallsOnly; + if (mode == "shell") + return libvgcode::EReducedDetailMode::ShellOnly; return libvgcode::EReducedDetailMode::Off; } diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index e6428e7615..8242c1cf33 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -382,7 +382,7 @@ public: // while the user drags the camera or a slider, draw the reduced set, if the preference asks for one void set_interacting(bool interacting); bool is_reduced_detail() const { return m_viewer.is_reduced_detail(); } - // the preference's string value: "off", "solid", "layers" or "outer_walls" + // the preference's string value: "off", "solid", "layers", "outer_walls" or "shell" void set_reduced_detail_mode(const std::string& mode); void set_reduced_detail_layer_stride(unsigned int value); diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp index 6846e25f8e..af4546d51f 100644 --- a/src/slic3r/GUI/Preferences.cpp +++ b/src/slic3r/GUI/Preferences.cpp @@ -2065,10 +2065,12 @@ void PreferencesDialog::create_items() "with its bottom and top layers drawn as toolpaths. Supports are not shown, and negative volumes are not cut out.\n" "Skip layers: the toolpaths of one layer in every N, set below.\n" "Outer walls: only the outer walls of one layer in every N. The prime tower and supports are left out.\n" + "Shell only: every layer without its sparse infill, internal solid infill and gap fill, which lie under the walls and skins. " + "Walls, top and bottom surfaces, bridges, supports and the prime tower are drawn whole, so the print looks the same from outside.\n" "The bottom and top of the visible layer range are always drawn whole."), "preview_reduced_detail_mode", - {_L("Off"), _L("Solid model"), _L("Skip layers"), _L("Outer walls")}, - {"off", "solid", "layers", "outer_walls"}, + {_L("Off"), _L("Solid model"), _L("Skip layers"), _L("Outer walls"), _L("Shell only")}, + {"off", "solid", "layers", "outer_walls", "shell"}, // apply the new mode immediately to the currently loaded preview [this](std::string value) { if (m_reduced_detail_layer_stride_input)