Compare commits

...

5 Commits

Author SHA1 Message Date
Kris Austin
5c635d5e50 build: scope -Werror to the Clang family so GCC builds again (#15701) 2026-09-14 17:04:03 -03:00
Nopraz
5496883493 fix(profiles): Snapmaker U1 — cap ABS/ASA/PPS bed temps at 100 °C (#15483)
The U1's heated bed tops out at 100 °C, but these profiles requested
105-110 °C, which leads to print errors unless the user modifies the
printer's firmware configuration.

Affected profiles:
- Snapmaker ABS @U1 base (110/105 → 100)
- Snapmaker ASA @U1 base (110 → 100)
- Fiberon ASA-CF08 @Snapmaker U1 base (105 → 100)
- Fiberon PPS-GF20 @Snapmaker U1 base (105 → 100)

Bumps Snapmaker.json to 02.04.00.10.

Co-authored-by: yw4z <ywsyildiz@gmail.com>
2026-09-14 20:44:15 +03:00
Daniel Williams
70247ad298 Extract Layer::choose_ironing_extruder for unit-testable ironing routing (#13467)
* Extract Layer::choose_ironing_extruder for unit-testable ironing routing

The ironing extruder selection in make_ironing() was a 5-line nested
conditional inlined at the top of the loop, with no isolated test
coverage. Pull the gating into a static helper so the routing decision
is unit-testable without spinning up the slicing pipeline.

Pure refactor: the helper preserves the original logic bit-for-bit
(NoIroning -> -1; AllSolid always enabled; TopSurfaces and TopmostOnly
require some top shells or, in spiral mode, more than one bottom shell;
TopmostOnly additionally requires being on the topmost layer; enabled
ironing routes to solid_infill_filament).

Add tests/fff_print/test_choose_ironing_extruder.cpp covering:
- AllSolid regardless of layer position
- TopSurfaces with top_shell_layers > 0
- TopSurfaces with top_shell_layers=0 + spiral mode + bottom_shell_layers>1
- TopmostOnly + topmost layer
- NoIroning short-circuit
- TopSurfaces with top_shell_layers=0 (and not spiral) -> disabled
- TopSurfaces, spiral, but bottom_shell_layers=1 -> disabled
- TopmostOnly on a non-topmost layer -> disabled

* Move ironing routing test into the Fill subsystem file

Rename the test to tests/libslic3r/test_fill.cpp and tag it [Fill] to
match the subsystem it covers, use flat behavioral test cases with
GENERATE for the parameterized ones, and drop the history narration from
the code comments.

* tests: move ironing routing tests into fff_print/test_fill.cpp

Keeps the Fill tests in one file, alongside the existing ironing
rotation-template test.
2026-09-14 09:37:04 -03:00
Lam Wei Lun
ecbe1b1b90 UI Bug fixes and code cleanup for Publish 3MF Dialog (#15690)
# Description
- Fixes an issue on macOS where the modified indicator can be cut-off.
- Remove unused code
2026-09-14 16:57:41 +08:00
Lam Wei Lun
ffb4f192c1 Fix macOS UI issue in publish dialog. Remove item_size helper in TabCtrl and its relevant setter 2026-09-14 14:19:25 +08:00
13 changed files with 136 additions and 75 deletions

View File

@@ -587,10 +587,15 @@ if ((NOT MSVC OR IS_CLANG_CL) AND ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR
add_compile_options(-Wno-${w}) add_compile_options(-Wno-${w})
endforeach () endforeach ()
# Turn everything else into an error. Dependency headers are exempt because the SYSTEM # GCC is not built in CI, so don't throw errors CI won't catch.
# include flag (-imsvc on clang-cl, -isystem elsewhere) keeps their diagnostics out, if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
# apart from GCC's maybe-uninitialized, demoted below. add_compile_options(-Werror=return-type)
else ()
# Turn everything else into an error. Dependency headers are exempt because the
# SYSTEM include flag (-imsvc on clang-cl, -isystem elsewhere) keeps their
# diagnostics out.
add_compile_options(-Werror) add_compile_options(-Werror)
endif ()
# Demoted. Remove a name once its category is cleared on every compiler. # Demoted. Remove a name once its category is cleared on every compiler.
set(warnings_demoted) set(warnings_demoted)
@@ -612,20 +617,6 @@ if ((NOT MSVC OR IS_CLANG_CL) AND ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR
cast-function-type-mismatch cast-function-type-mismatch
) )
endif () endif ()
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
list(APPEND warnings_demoted
# maybe-uninitialized runs after inlining and reports inside boost/variant,
# boost/tuple and the bundled clipper header even with -isystem.
maybe-uninitialized
# array-bounds is reported once, where ConfigOptionVector::set_at inlines
# into OrcaSlicer.cpp on a branch the preceding type test rules out.
array-bounds
# template-id-cdtor is a GCC 14+ warning in the bundled Clipper2 headers.
template-id-cdtor
)
endif ()
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
list(APPEND warnings_demoted list(APPEND warnings_demoted
# enum-constexpr-conversion is a Clang warning that defaults to an error, # enum-constexpr-conversion is a Clang warning that defaults to an error,

View File

@@ -1,6 +1,6 @@
{ {
"name": "Snapmaker", "name": "Snapmaker",
"version": "02.04.00.12", "version": "02.04.00.13",
"force_update": "0", "force_update": "0",
"description": "Snapmaker configurations", "description": "Snapmaker configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -15,13 +15,13 @@
"1" "1"
], ],
"cool_plate_temp": [ "cool_plate_temp": [
"105" "100"
], ],
"cool_plate_temp_initial_layer": [ "cool_plate_temp_initial_layer": [
"105" "100"
], ],
"eng_plate_temp": [ "eng_plate_temp": [
"105" "100"
], ],
"eng_plate_temp_initial_layer": [ "eng_plate_temp_initial_layer": [
"100" "100"
@@ -48,7 +48,7 @@
"Polymaker" "Polymaker"
], ],
"hot_plate_temp": [ "hot_plate_temp": [
"105" "100"
], ],
"hot_plate_temp_initial_layer": [ "hot_plate_temp_initial_layer": [
"100" "100"
@@ -72,7 +72,7 @@
"110.8" "110.8"
], ],
"textured_plate_temp": [ "textured_plate_temp": [
"105" "100"
], ],
"textured_plate_temp_initial_layer": [ "textured_plate_temp_initial_layer": [
"100" "100"

View File

@@ -15,16 +15,16 @@
"1" "1"
], ],
"cool_plate_temp": [ "cool_plate_temp": [
"105" "100"
], ],
"cool_plate_temp_initial_layer": [ "cool_plate_temp_initial_layer": [
"105" "100"
], ],
"eng_plate_temp": [ "eng_plate_temp": [
"105" "100"
], ],
"eng_plate_temp_initial_layer": [ "eng_plate_temp_initial_layer": [
"105" "100"
], ],
"fan_cooling_layer_time": [ "fan_cooling_layer_time": [
"12" "12"
@@ -51,10 +51,10 @@
"Polymaker" "Polymaker"
], ],
"hot_plate_temp": [ "hot_plate_temp": [
"105" "100"
], ],
"hot_plate_temp_initial_layer": [ "hot_plate_temp_initial_layer": [
"105" "100"
], ],
"nozzle_temperature": [ "nozzle_temperature": [
"300" "300"
@@ -81,10 +81,10 @@
"110" "110"
], ],
"textured_plate_temp": [ "textured_plate_temp": [
"105" "100"
], ],
"textured_plate_temp_initial_layer": [ "textured_plate_temp_initial_layer": [
"105" "100"
], ],
"filament_type": [ "filament_type": [
"ABS" "ABS"

View File

@@ -9,10 +9,10 @@
"" ""
], ],
"hot_plate_temp": [ "hot_plate_temp": [
"110" "100"
], ],
"hot_plate_temp_initial_layer": [ "hot_plate_temp_initial_layer": [
"105" "100"
], ],
"overhang_fan_speed": [ "overhang_fan_speed": [
"20" "20"

View File

@@ -9,7 +9,7 @@
"" ""
], ],
"hot_plate_temp": [ "hot_plate_temp": [
"110" "100"
], ],
"hot_plate_temp_initial_layer": [ "hot_plate_temp_initial_layer": [
"100" "100"

View File

@@ -1595,6 +1595,25 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc
return sparse_infill_polylines; return sparse_infill_polylines;
} }
// Returns the filament id (1-based) the region is ironed with, or -1 when the
// region is not ironed. AllSolid always irons. TopSurfaces and TopmostOnly need
// either some top shells or, in spiral mode, more than one bottom shell, and
// TopmostOnly additionally needs the layer to be the topmost one.
int Layer::choose_ironing_extruder(const PrintRegionConfig &cfg,
bool spiral_mode,
bool is_topmost_layer)
{
if (cfg.ironing_type == IroningType::NoIroning)
return -1;
const bool gate = (cfg.ironing_type == IroningType::AllSolid)
|| ((cfg.top_shell_layers > 0 || (spiral_mode && cfg.bottom_shell_layers > 1))
&& (cfg.ironing_type == IroningType::TopSurfaces
|| (cfg.ironing_type == IroningType::TopmostOnly && is_topmost_layer)));
if (!gate)
return -1;
return cfg.top_surface_filament_id;
}
// Create ironing extrusions over top surfaces. // Create ironing extrusions over top surfaces.
void Layer::make_ironing() void Layer::make_ironing()
{ {
@@ -1664,19 +1683,10 @@ void Layer::make_ironing()
if (! layerm->slices.empty()) { if (! layerm->slices.empty()) {
IroningParams ironing_params; IroningParams ironing_params;
const PrintRegionConfig &config = layerm->region().config(); const PrintRegionConfig &config = layerm->region().config();
if (config.ironing_type != IroningType::NoIroning && ironing_params.extruder = Layer::choose_ironing_extruder(
(config.ironing_type == IroningType::AllSolid || config,
((config.top_shell_layers > 0 || (this->object()->print()->config().spiral_mode && config.bottom_shell_layers > 1)) && /*spiral_mode=*/this->object()->print()->config().spiral_mode,
(config.ironing_type == IroningType::TopSurfaces || /*is_topmost_layer=*/layerm->layer()->upper_layer == nullptr);
(config.ironing_type == IroningType::TopmostOnly && layerm->layer()->upper_layer == nullptr))))) {
if (config.outer_wall_filament_id == config.top_surface_filament_id || config.wall_loops == 0) {
// Iron the whole face.
ironing_params.extruder = config.top_surface_filament_id;
} else {
// Iron just the infill.
ironing_params.extruder = config.top_surface_filament_id;
}
}
if (ironing_params.extruder != -1) { if (ironing_params.extruder != -1) {
//TODO just_infill is currently not used. //TODO just_infill is currently not used.
ironing_params.just_infill = false; ironing_params.just_infill = false;

View File

@@ -16,6 +16,7 @@ using LayerPtrs = std::vector<Layer*>;
class LayerRegion; class LayerRegion;
using LayerRegionPtrs = std::vector<LayerRegion*>; using LayerRegionPtrs = std::vector<LayerRegion*>;
class PrintRegion; class PrintRegion;
class PrintRegionConfig;
class PrintObject; class PrintObject;
class Print; class Print;
@@ -200,6 +201,11 @@ public:
FillAdaptive::Octree *support_fill_octree, FillAdaptive::Octree *support_fill_octree,
FillLightning::Generator* lightning_generator) const; FillLightning::Generator* lightning_generator) const;
void make_ironing(); void make_ironing();
// Returns the filament id (1-based) the region is ironed with, or -1 when the
// region is not ironed.
static int choose_ironing_extruder(const PrintRegionConfig &cfg,
bool spiral_mode,
bool is_topmost_layer);
void make_contour_z(const sla::IndexedMesh &mesh); void make_contour_z(const sla::IndexedMesh &mesh);
void export_region_slices_to_svg(const char *path) const; void export_region_slices_to_svg(const char *path) const;

View File

@@ -1037,9 +1037,6 @@ size_t PublishSettingsDialog::section_group_for(Section kind)
section.mixed_tabs = new TabCtrl(section.page, wxID_ANY, wxDefaultPosition, wxDefaultSize, s_tab_style); section.mixed_tabs = new TabCtrl(section.page, wxID_ANY, wxDefaultPosition, wxDefaultSize, s_tab_style);
section.mixed_tabs->SetFont(Label::Body_14); section.mixed_tabs->SetFont(Label::Body_14);
section.mixed_tabs->SetBackgroundColour(GetBackgroundColour()); section.mixed_tabs->SetBackgroundColour(GetBackgroundColour());
// The mixed tabs carry full swatch compositions: give them a touch more room than the
// filament tabs so neighbouring compositions stay distinguishable (must precede AppendItem).
section.mixed_tabs->SetItemSpace(FromDIP(3));
page_sizer->Add(section.mixed_tabs, 0, wxEXPAND | wxTOP, FromDIP(2)); page_sizer->Add(section.mixed_tabs, 0, wxEXPAND | wxTOP, FromDIP(2));
section.mixed_tabs->Hide(); section.mixed_tabs->Hide();
} }

View File

@@ -311,8 +311,11 @@ void Button::render(wxDC& dc)
} }
} }
auto szContent = textSize; auto szContent = textSize;
// Whether the measured content reserved the text/icon gap. macOS measures an empty label
// as 0-high, so the gap is skipped there; the dot must not advance past it in that case.
const bool gap_reserved = szContent.y > 0;
if (icon.bmp().IsOk()) { if (icon.bmp().IsOk()) {
if (szContent.y > 0) { if (gap_reserved) {
//BBS norrow size between text and icon //BBS norrow size between text and icon
if (vertical) if (vertical)
szContent.y += spacing; szContent.y += spacing;
@@ -357,10 +360,10 @@ void Button::render(wxDC& dc)
dc.DrawBitmap(icon.bmp(), pt); dc.DrawBitmap(icon.bmp(), pt);
//BBS norrow size between text and icon //BBS norrow size between text and icon
if (vertical) { if (vertical) {
pt.y += szIcon.y + spacing; pt.y += szIcon.y + (gap_reserved ? spacing : 0);
pt.x = rcContent.x; pt.x = rcContent.x;
} else { } else {
pt.x += szIcon.x + spacing; pt.x += szIcon.x + (gap_reserved ? spacing : 0);
pt.y = rcContent.y; pt.y = rcContent.y;
} }
} }

View File

@@ -99,7 +99,7 @@ int TabCtrl::AppendItem(const wxString& item, int image, int selImage, void* cli
btns.push_back(btn); btns.push_back(btn);
if (btns.size() > 1) if (btns.size() > 1)
sizer->GetItem(sizer->GetItemCount() - 1)->SetMinSize({0, 0}); sizer->GetItem(sizer->GetItemCount() - 1)->SetMinSize({0, 0});
sizer->Add(btn, 0, wxALIGN_CENTER_VERTICAL | wxLEFT | wxRIGHT, item_space); sizer->Add(btn, 0, wxALIGN_CENTER_VERTICAL);
sizer->AddStretchSpacer(1); sizer->AddStretchSpacer(1);
relayout(); relayout();
return btns.size() - 1; return btns.size() - 1;
@@ -256,12 +256,12 @@ void TabCtrl::relayout()
int item = sel + 1; int item = sel + 1;
int first = 0; int first = 0;
for (int i = 0; i < item; ++i) for (int i = 0; i < item; ++i)
offset += btns[i]->GetMinSize().x + item_space * 2; offset += btns[i]->GetMinSize().x;
if (item < btns.size()) if (item < btns.size())
offset += btns[item]->GetMinSize().x + item_space * 2; offset += btns[item]->GetMinSize().x;
int width = GetSize().x; int width = GetSize().x;
for (int i = 0; i < btns.size(); ++i) { for (int i = 0; i < btns.size(); ++i) {
auto size = btns[i]->GetMinSize().x + item_space * 2; auto size = btns[i]->GetMinSize().x;
if (i < sel && offset > width) { if (i < sel && offset > width) {
sizer->Show(i * 2 + 1, false); sizer->Show(i * 2 + 1, false);
sizer->Show(i * 2 + 2, false); sizer->Show(i * 2 + 2, false);
@@ -284,26 +284,17 @@ void TabCtrl::relayout()
if (item >= btns.size()) if (item >= btns.size())
--item; --item;
// Keep spacing 2 ~ 10 TAB_BUTTON_SPACE // Keep spacing 2 ~ 10 TAB_BUTTON_SPACE
int b = GetSize().x - offset - 10 - (item + 1 - first) * item_space * 8; int b = GetSize().x - offset - 10 - (item + 1 - first) * 16;
sizer->GetItem(item * 2 + 2)->SetMinSize({b > 0 ? b : 0, 0}); sizer->GetItem(item * 2 + 2)->SetMinSize({b > 0 ? b : 0, 0});
Layout(); Layout();
} }
void TabCtrl::SetItemSpace(int space)
{
if (space < 0 || space == item_space)
return;
item_space = space;
relayout();
Refresh();
}
int TabCtrl::GetFullSize() const int TabCtrl::GetFullSize() const
{ {
// Mirrors relayout(): a 10px leading spacer plus every button's min width and spacing. // Mirrors relayout(): a 10px leading spacer plus every button's min width.
int width = 10; int width = 10;
for (const Button* btn : btns) for (const Button* btn : btns)
width += btn->GetMinSize().x + item_space * 2; width += btn->GetMinSize().x;
return width; return width;
} }

View File

@@ -14,7 +14,6 @@ class TabCtrl : public StaticBox
int sel = -1; int sel = -1;
wxFont bold; wxFont bold;
int item_space = 2; // space around each button, both sides (SetItemSpace)
public: public:
TabCtrl(wxWindow* parent, wxWindowID id, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0); TabCtrl(wxWindow* parent, wxWindowID id, const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = 0);
@@ -64,10 +63,6 @@ public:
int GetNextVisible(int item) const; int GetNextVisible(int item) const;
bool IsVisible(unsigned int item) const; bool IsVisible(unsigned int item) const;
// Extra space around each tab button (in px on both sides). Defaults to the control-wide
// standard; call before appending items so every button picks it up.
void SetItemSpace(int space);
int GetFullSize() const; int GetFullSize() const;
private: private:

View File

@@ -15,6 +15,7 @@
#include "libslic3r/Geometry.hpp" #include "libslic3r/Geometry.hpp"
#include "libslic3r/Layer.hpp" #include "libslic3r/Layer.hpp"
#include "libslic3r/Print.hpp" #include "libslic3r/Print.hpp"
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/SVG.hpp" #include "libslic3r/SVG.hpp"
#include "libslic3r/libslic3r.h" #include "libslic3r/libslic3r.h"
@@ -676,6 +677,73 @@ TEST_CASE("Ironing follows the solid infill rotation template", "[Fill]")
REQUIRE(compared > int(ironing.size()) / 2); REQUIRE(compared > int(ironing.size()) / 2);
} }
namespace {
PrintRegionConfig ironing_config(IroningType type,
int top_surface_filament_id = 1,
int top_shell_layers = 3,
int bottom_shell_layers = 1)
{
PrintRegionConfig cfg;
cfg.ironing_type.value = type;
cfg.top_surface_filament_id.value = top_surface_filament_id;
cfg.top_shell_layers.value = top_shell_layers;
cfg.bottom_shell_layers.value = bottom_shell_layers;
cfg.outer_wall_filament_id.value = 1;
cfg.wall_loops.value = 2;
return cfg;
}
} // namespace
TEST_CASE("Ironing an all-solid region uses the top surface filament on every layer", "[Fill]")
{
const PrintRegionConfig cfg = ironing_config(IroningType::AllSolid, /*top_surface_filament_id=*/2);
const bool is_topmost_layer = GENERATE(false, true);
CAPTURE(is_topmost_layer);
REQUIRE(Layer::choose_ironing_extruder(cfg, /*spiral_mode=*/false, is_topmost_layer) == 2);
}
TEST_CASE("Ironing top surfaces uses the top surface filament when the region has top shells", "[Fill]")
{
const PrintRegionConfig cfg = ironing_config(IroningType::TopSurfaces,
/*top_surface_filament_id=*/3,
/*top_shell_layers=*/2);
REQUIRE(Layer::choose_ironing_extruder(cfg, /*spiral_mode=*/false, /*is_topmost_layer=*/false) == 3);
}
TEST_CASE("Ironing top surfaces without top shells needs spiral mode and more than one bottom shell", "[Fill]")
{
const PrintRegionConfig one_bottom_shell = ironing_config(IroningType::TopSurfaces,
/*top_surface_filament_id=*/1,
/*top_shell_layers=*/0,
/*bottom_shell_layers=*/1);
const PrintRegionConfig two_bottom_shells = ironing_config(IroningType::TopSurfaces,
/*top_surface_filament_id=*/1,
/*top_shell_layers=*/0,
/*bottom_shell_layers=*/2);
REQUIRE(Layer::choose_ironing_extruder(two_bottom_shells, /*spiral_mode=*/true, /*is_topmost_layer=*/false) == 1);
REQUIRE(Layer::choose_ironing_extruder(one_bottom_shell, /*spiral_mode=*/true, /*is_topmost_layer=*/false) == -1);
REQUIRE(Layer::choose_ironing_extruder(two_bottom_shells, /*spiral_mode=*/false, /*is_topmost_layer=*/false) == -1);
}
TEST_CASE("Ironing the topmost surface only applies to the topmost layer", "[Fill]")
{
const PrintRegionConfig cfg = ironing_config(IroningType::TopmostOnly, /*top_surface_filament_id=*/4);
REQUIRE(Layer::choose_ironing_extruder(cfg, /*spiral_mode=*/false, /*is_topmost_layer=*/true) == 4);
REQUIRE(Layer::choose_ironing_extruder(cfg, /*spiral_mode=*/false, /*is_topmost_layer=*/false) == -1);
}
TEST_CASE("A region with ironing turned off is never ironed", "[Fill]")
{
const PrintRegionConfig cfg = ironing_config(IroningType::NoIroning);
const bool spiral_mode = GENERATE(false, true);
CAPTURE(spiral_mode);
REQUIRE(Layer::choose_ironing_extruder(cfg, spiral_mode, /*is_topmost_layer=*/true) == -1);
}
TEST_CASE("Solid infill direction offsets every layer when no template is set", "[Fill]") TEST_CASE("Solid infill direction offsets every layer when no template is set", "[Fill]")
{ {
auto angles_for = [](int direction) { auto angles_for = [](int direction) {