mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-16 21:42:43 +00:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b0e190e64 | ||
|
|
8effa27f4a | ||
|
|
72774e5398 |
@@ -85,6 +85,15 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
leg="${{ runner.os }}-${{ inputs.arch || 'amd64' }}${{ runner.os == 'Windows' && format('-{0}', inputs.compiler) || '' }}"
|
leg="${{ runner.os }}-${{ inputs.arch || 'amd64' }}${{ runner.os == 'Windows' && format('-{0}', inputs.compiler) || '' }}"
|
||||||
|
# clang-cl refuses a precompiled header from another cl.exe build and ccache
|
||||||
|
# does not hash that build, so each one gets its own cache. The build number
|
||||||
|
# is read from cl.exe itself; the toolset directory keeps its name across patches.
|
||||||
|
if [ "${{ runner.os }}" = Windows ]; then
|
||||||
|
vswhere='/c/Program Files (x86)/Microsoft Visual Studio/Installer/vswhere.exe'
|
||||||
|
toolset=$(tr -d '\r\n' < "$("$vswhere" -latest -products '*' -find 'VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt' | tr -d '\r')")
|
||||||
|
cl=$("$vswhere" -latest -products '*' -find 'VC\Tools\MSVC\'"$toolset"'\**\cl.exe' | tr -d '\r' | head -1)
|
||||||
|
leg="$leg-vc$("$cl" 2>&1 | grep -o -E 'Version [0-9.]+' | cut -d' ' -f2)"
|
||||||
|
fi
|
||||||
echo "CCACHE_LEG=$leg" >> "$GITHUB_ENV"
|
echo "CCACHE_LEG=$leg" >> "$GITHUB_ENV"
|
||||||
echo "CCACHE_ENTRY=ccache-$leg-${{ github.run_id }}-${{ github.run_attempt }}" >> "$GITHUB_ENV"
|
echo "CCACHE_ENTRY=ccache-$leg-${{ github.run_id }}-${{ github.run_attempt }}" >> "$GITHUB_ENV"
|
||||||
|
|
||||||
|
|||||||
Vendored
+8
@@ -27,8 +27,15 @@ endif ()
|
|||||||
# Boost.Container's bundled dlmalloc passes int* where the Win32 Interlocked API
|
# Boost.Container's bundled dlmalloc passes int* where the Win32 Interlocked API
|
||||||
# takes volatile long*; cl compiles that with a warning, clang errors out.
|
# takes volatile long*; cl compiles that with a warning, clang errors out.
|
||||||
set(_boost_c_flags_line "")
|
set(_boost_c_flags_line "")
|
||||||
|
set(_boost_cxx_flags_line "")
|
||||||
if (MSVC AND CMAKE_C_COMPILER_ID STREQUAL "Clang")
|
if (MSVC AND CMAKE_C_COMPILER_ID STREQUAL "Clang")
|
||||||
set(_boost_c_flags_line "-DCMAKE_C_FLAGS:STRING=-Wno-incompatible-pointer-types")
|
set(_boost_c_flags_line "-DCMAKE_C_FLAGS:STRING=-Wno-incompatible-pointer-types")
|
||||||
|
# The Visual Studio generator applies only the link language's flags to a
|
||||||
|
# project, and boost_container links as C++, so its C file never sees
|
||||||
|
# CMAKE_C_FLAGS. The C++ flags reach every file; keep CMake's defaults.
|
||||||
|
if (CMAKE_GENERATOR MATCHES "Visual Studio")
|
||||||
|
set(_boost_cxx_flags_line "-DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS} -Wno-incompatible-pointer-types")
|
||||||
|
endif ()
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
orcaslicer_add_cmake_project(Boost
|
orcaslicer_add_cmake_project(Boost
|
||||||
@@ -46,6 +53,7 @@ orcaslicer_add_cmake_project(Boost
|
|||||||
"${_context_arch_line}"
|
"${_context_arch_line}"
|
||||||
"${_context_impl_line}"
|
"${_context_impl_line}"
|
||||||
"${_boost_c_flags_line}"
|
"${_boost_c_flags_line}"
|
||||||
|
"${_boost_cxx_flags_line}"
|
||||||
)
|
)
|
||||||
|
|
||||||
set(DEP_Boost_DEPENDS ZLIB)
|
set(DEP_Boost_DEPENDS ZLIB)
|
||||||
|
|||||||
Vendored
+5
@@ -184,6 +184,11 @@ function(orcaslicer_add_cmake_project projectname)
|
|||||||
|
|
||||||
if (_dep_msvc_gen)
|
if (_dep_msvc_gen)
|
||||||
set(_gen CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}")
|
set(_gen CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}")
|
||||||
|
# The toolset picks the compiler here, not the CMAKE_<LANG>_COMPILER
|
||||||
|
# forwarded below, so without it a clang-cl superbuild builds with cl.
|
||||||
|
if (CMAKE_GENERATOR_TOOLSET)
|
||||||
|
list(APPEND _gen CMAKE_GENERATOR_TOOLSET "${CMAKE_GENERATOR_TOOLSET}")
|
||||||
|
endif ()
|
||||||
else()
|
else()
|
||||||
set(_gen "")
|
set(_gen "")
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
Vendored
+3
@@ -7,4 +7,7 @@ orcaslicer_add_cmake_project(Draco
|
|||||||
${_options}
|
${_options}
|
||||||
URL https://github.com/google/draco/archive/refs/tags/1.5.7.zip
|
URL https://github.com/google/draco/archive/refs/tags/1.5.7.zip
|
||||||
URL_HASH SHA256=27b72ba2d5ff3d0a9814ad40d4cb88f8dc89a35491c0866d952473f8f9416b77
|
URL_HASH SHA256=27b72ba2d5ff3d0a9814ad40d4cb88f8dc89a35491c0866d952473f8f9416b77
|
||||||
|
CMAKE_ARGS
|
||||||
|
# The encoder and decoder tools duplicate draco.lib; see deps-windows.cmake.
|
||||||
|
"${DEP_LLD_FORCE_MULTIPLE}"
|
||||||
)
|
)
|
||||||
Vendored
+2
@@ -8,6 +8,8 @@ orcaslicer_add_cmake_project(NLopt
|
|||||||
-DNLOPT_GUILE:BOOL=OFF
|
-DNLOPT_GUILE:BOOL=OFF
|
||||||
-DNLOPT_SWIG:BOOL=OFF
|
-DNLOPT_SWIG:BOOL=OFF
|
||||||
-DNLOPT_TESTS:BOOL=OFF
|
-DNLOPT_TESTS:BOOL=OFF
|
||||||
|
# testopt is built regardless of NLOPT_TESTS; see deps-windows.cmake.
|
||||||
|
"${DEP_LLD_FORCE_MULTIPLE}"
|
||||||
)
|
)
|
||||||
|
|
||||||
if (MSVC)
|
if (MSVC)
|
||||||
|
|||||||
Vendored
+6
@@ -80,6 +80,12 @@ ExternalProject_Add(dep_OpenSSL
|
|||||||
INSTALL_COMMAND ${_install_cmd}
|
INSTALL_COMMAND ${_install_cmd}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (CMAKE_GENERATOR MATCHES "Visual Studio")
|
||||||
|
# OpenSSL builds with cl, but MSBuild runs nmake in this project's toolset
|
||||||
|
# environment, and ClangCL's puts clang's headers first. Use the default.
|
||||||
|
set_target_properties(dep_OpenSSL PROPERTIES VS_PLATFORM_TOOLSET "$(DefaultPlatformToolset)")
|
||||||
|
endif ()
|
||||||
|
|
||||||
ExternalProject_Add_Step(dep_OpenSSL install_cmake_files
|
ExternalProject_Add_Step(dep_OpenSSL install_cmake_files
|
||||||
DEPENDEES install
|
DEPENDEES install
|
||||||
|
|
||||||
|
|||||||
Vendored
+9
@@ -42,6 +42,15 @@ else ()
|
|||||||
message(FATAL_ERROR "Unsupported OS architecture: ${DEPS_ARCH}")
|
message(FATAL_ERROR "Unsupported OS architecture: ${DEPS_ARCH}")
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
# Draco's tools and NLopt's testopt compile sources that are also in their
|
||||||
|
# static library. MSBuild passes the library before the objects and lld-link
|
||||||
|
# resolves as it goes, so the library's copy wins and the object then reads as
|
||||||
|
# a duplicate. Nothing uses those executables, so let lld keep the first one.
|
||||||
|
set(DEP_LLD_FORCE_MULTIPLE "")
|
||||||
|
if (CMAKE_GENERATOR MATCHES "Visual Studio" AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||||
|
set(DEP_LLD_FORCE_MULTIPLE "-DCMAKE_EXE_LINKER_FLAGS:STRING=${CMAKE_EXE_LINKER_FLAGS} /FORCE:MULTIPLE")
|
||||||
|
endif ()
|
||||||
|
|
||||||
if (${DEP_DEBUG})
|
if (${DEP_DEBUG})
|
||||||
set(DEP_BOOST_DEBUG "debug")
|
set(DEP_BOOST_DEBUG "debug")
|
||||||
else ()
|
else ()
|
||||||
|
|||||||
@@ -2735,6 +2735,28 @@ void ToolOrdering::enforce_mixed_component_order()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Declared in ToolOrdering.hpp (exposed for unit testing).
|
||||||
|
std::vector<unsigned int> parse_cyclic_order(const std::string& str, unsigned int number_of_extruders)
|
||||||
|
{
|
||||||
|
std::vector<unsigned int> order;
|
||||||
|
for (const std::string& token : split_string(str, ',')) {
|
||||||
|
try {
|
||||||
|
size_t pos = 0;
|
||||||
|
int filament = std::stoi(token, &pos); // stoi skips leading whitespace by itself
|
||||||
|
// stoi stops at the first non-digit, so "2x" would parse as 2. Require the whole token to be
|
||||||
|
// consumed (bar trailing whitespace) to drop it like any other garbage.
|
||||||
|
if (token.find_first_not_of(" \t\r\n", pos) != std::string::npos)
|
||||||
|
continue;
|
||||||
|
if (filament >= 1 && (unsigned int)filament <= number_of_extruders
|
||||||
|
&& std::find(order.begin(), order.end(), (unsigned int)(filament - 1)) == order.end())
|
||||||
|
order.emplace_back((unsigned int)(filament - 1));
|
||||||
|
} catch (const std::exception&) {
|
||||||
|
// Not a number, ignore it.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return order;
|
||||||
|
}
|
||||||
|
|
||||||
void ToolOrdering::reorder_extruders_for_minimum_flush_volume(bool reorder_first_layer)
|
void ToolOrdering::reorder_extruders_for_minimum_flush_volume(bool reorder_first_layer)
|
||||||
{
|
{
|
||||||
const PrintConfig* print_config = m_print_config_ptr;
|
const PrintConfig* print_config = m_print_config_ptr;
|
||||||
@@ -2832,11 +2854,41 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume(bool reorder_first
|
|||||||
const bool use_cyclic_ordering =
|
const bool use_cyclic_ordering =
|
||||||
(print_config->toolchange_ordering == ToolChangeOrderingType::Cyclic);
|
(print_config->toolchange_ordering == ToolChangeOrderingType::Cyclic);
|
||||||
|
|
||||||
|
// By default the first layer keeps its adhesion-optimized order (and any custom first layer
|
||||||
|
// sequence); the cyclic sequence is only forced onto it when the user opts in.
|
||||||
|
const bool cyclic_first_layer = use_cyclic_ordering && print_config->toolchange_cyclic_first_layer.value;
|
||||||
|
|
||||||
|
// Optional user defined cyclic sequence, given as 1-based filament numbers ("3,2,1,4"). Filaments
|
||||||
|
// missing from it keep their ascending order after the listed ones, so a partial or bogus entry
|
||||||
|
// still yields the default cyclic order.
|
||||||
|
const std::vector<unsigned int> cyclic_order =
|
||||||
|
use_cyclic_ordering ? parse_cyclic_order(print_config->toolchange_cyclic_order.value, number_of_extruders)
|
||||||
|
: std::vector<unsigned int>();
|
||||||
|
|
||||||
|
// Reorder a layer's filaments (0-based) for cyclic ordering: ascending by default, or following the
|
||||||
|
// user defined sequence when one was given. Filaments absent from the sequence keep ascending order
|
||||||
|
// after the listed ones.
|
||||||
|
auto apply_cyclic_order = [&cyclic_order](std::vector<unsigned int>& filaments) {
|
||||||
|
std::sort(filaments.begin(), filaments.end());
|
||||||
|
if (!cyclic_order.empty())
|
||||||
|
std::stable_sort(filaments.begin(), filaments.end(), [&cyclic_order](unsigned int lhs, unsigned int rhs) {
|
||||||
|
auto rank = [&cyclic_order](unsigned int filament) {
|
||||||
|
return size_t(std::find(cyclic_order.begin(), cyclic_order.end(), filament) - cyclic_order.begin());
|
||||||
|
};
|
||||||
|
return rank(lhs) < rank(rhs);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// other_layers_seq: the layer_idx and extruder_idx are base on 1
|
// other_layers_seq: the layer_idx and extruder_idx are base on 1
|
||||||
auto get_custom_seq = [&other_layers_seqs, &reorder_first_layer, &first_layer_filaments, &layer_filaments, use_cyclic_ordering](int layer_idx, std::vector<int>& out_seq) -> bool {
|
auto get_custom_seq = [&other_layers_seqs, &reorder_first_layer, &first_layer_filaments, &layer_filaments, use_cyclic_ordering, cyclic_first_layer, &apply_cyclic_order](int layer_idx, std::vector<int>& out_seq) -> bool {
|
||||||
if (!reorder_first_layer && layer_idx == 0) {
|
if (!reorder_first_layer && layer_idx == 0) {
|
||||||
out_seq.resize(first_layer_filaments.size());
|
// The first layer tool order is already decided (adhesion-optimized, plus any custom first
|
||||||
std::transform(first_layer_filaments.begin(), first_layer_filaments.end(), out_seq.begin(), [](auto item) {return item + 1; });
|
// layer sequence). Only override it with the cyclic sequence when the user opted in.
|
||||||
|
std::vector<unsigned int> ordered = first_layer_filaments;
|
||||||
|
if (cyclic_first_layer)
|
||||||
|
apply_cyclic_order(ordered);
|
||||||
|
out_seq.resize(ordered.size());
|
||||||
|
std::transform(ordered.begin(), ordered.end(), out_seq.begin(), [](auto item) {return int(item) + 1; });
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
for (size_t idx = other_layers_seqs.size() - 1; idx != size_t(-1); --idx) {
|
for (size_t idx = other_layers_seqs.size() - 1; idx != size_t(-1); --idx) {
|
||||||
@@ -2847,9 +2899,12 @@ void ToolOrdering::reorder_extruders_for_minimum_flush_volume(bool reorder_first
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (use_cyclic_ordering && layer_idx >= 0 && size_t(layer_idx) < layer_filaments.size()) {
|
// Skip the first layer here (layer_idx == 0 only reaches this point on the reorder_first_layer
|
||||||
|
// path) unless the user asked for cyclic order on it, so it keeps the default flush ordering.
|
||||||
|
if (use_cyclic_ordering && layer_idx >= 0 && (layer_idx != 0 || cyclic_first_layer)
|
||||||
|
&& size_t(layer_idx) < layer_filaments.size()) {
|
||||||
std::vector<unsigned int> ordered = layer_filaments[size_t(layer_idx)];
|
std::vector<unsigned int> ordered = layer_filaments[size_t(layer_idx)];
|
||||||
std::sort(ordered.begin(), ordered.end());
|
apply_cyclic_order(ordered);
|
||||||
out_seq.resize(ordered.size());
|
out_seq.resize(ordered.size());
|
||||||
std::transform(ordered.begin(), ordered.end(), out_seq.begin(), [](auto item) { return int(item) + 1; });
|
std::transform(ordered.begin(), ordered.end(), out_seq.begin(), [](auto item) { return int(item) + 1; });
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -417,6 +417,11 @@ private:
|
|||||||
int most_used_extruder;
|
int most_used_extruder;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Parse the user defined cyclic toolchange sequence ("3,2 , 1 , 4") into 0-based filament indices.
|
||||||
|
// Out-of-range entries, duplicates and non-numeric tokens are dropped, so a partially valid string
|
||||||
|
// still orders the filaments it does name. Exposed for unit testing.
|
||||||
|
std::vector<unsigned int> parse_cyclic_order(const std::string& str, unsigned int number_of_extruders);
|
||||||
|
|
||||||
} // namespace SLic3r
|
} // namespace SLic3r
|
||||||
|
|
||||||
#endif /* slic3r_ToolOrdering_hpp_ */
|
#endif /* slic3r_ToolOrdering_hpp_ */
|
||||||
|
|||||||
@@ -1320,6 +1320,8 @@ static std::vector<std::string> s_Preset_print_options{
|
|||||||
"wipe_tower_extra_flow",
|
"wipe_tower_extra_flow",
|
||||||
"single_extruder_multi_material_priming",
|
"single_extruder_multi_material_priming",
|
||||||
"toolchange_ordering",
|
"toolchange_ordering",
|
||||||
|
"toolchange_cyclic_order",
|
||||||
|
"toolchange_cyclic_first_layer",
|
||||||
"wipe_tower_rotation_angle",
|
"wipe_tower_rotation_angle",
|
||||||
"tree_support_branch_distance_organic",
|
"tree_support_branch_distance_organic",
|
||||||
"tree_support_branch_diameter_organic",
|
"tree_support_branch_diameter_organic",
|
||||||
|
|||||||
@@ -360,6 +360,8 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
|||||||
|| opt_key == "other_layers_print_sequence"
|
|| opt_key == "other_layers_print_sequence"
|
||||||
|| opt_key == "other_layers_print_sequence_nums"
|
|| opt_key == "other_layers_print_sequence_nums"
|
||||||
|| opt_key == "toolchange_ordering"
|
|| opt_key == "toolchange_ordering"
|
||||||
|
|| opt_key == "toolchange_cyclic_order"
|
||||||
|
|| opt_key == "toolchange_cyclic_first_layer"
|
||||||
|| opt_key == "extruder_ams_count"
|
|| opt_key == "extruder_ams_count"
|
||||||
|| opt_key == "extruder_nozzle_stats"
|
|| opt_key == "extruder_nozzle_stats"
|
||||||
|| opt_key == "filament_map_mode"
|
|| opt_key == "filament_map_mode"
|
||||||
|
|||||||
@@ -6700,6 +6700,34 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->enum_labels.emplace_back(L("Cyclic"));
|
def->enum_labels.emplace_back(L("Cyclic"));
|
||||||
def->set_default_value(new ConfigOptionEnum<ToolChangeOrderingType>(ToolChangeOrderingType::Default));
|
def->set_default_value(new ConfigOptionEnum<ToolChangeOrderingType>(ToolChangeOrderingType::Default));
|
||||||
|
|
||||||
|
def = this->add("toolchange_cyclic_order", coString);
|
||||||
|
def->label = L("Cyclic order");
|
||||||
|
def->category = L("Advanced");
|
||||||
|
def->tooltip = L(
|
||||||
|
"Custom filament sequence used by the cyclic toolchange ordering, as filament numbers separated by commas (e.g. \"3,2,1,4\").\n"
|
||||||
|
"Each layer prints its filaments following this sequence; filaments not listed are printed last, in ascending order.\n"
|
||||||
|
"Leave empty to cycle through the filaments in ascending order."
|
||||||
|
);
|
||||||
|
def->mode = comExpert;
|
||||||
|
def->set_default_value(new ConfigOptionString(""));
|
||||||
|
|
||||||
|
def = this->add("toolchange_cyclic_first_layer", coBool);
|
||||||
|
def->label = L("Apply cyclic order to first layer");
|
||||||
|
def->category = L("Advanced");
|
||||||
|
def->tooltip = L(
|
||||||
|
"Applies the cyclic toolchange order to the first layer as well.\n"
|
||||||
|
"By default this is disabled, because the first layer is instead ordered for the best bed "
|
||||||
|
"adhesion: filaments that print small, fragile first-layer features are printed last, so the "
|
||||||
|
"following tool changes and travel moves are less likely to knock those weakly anchored parts "
|
||||||
|
"loose. This first-layer order also honors a custom first layer filament sequence when one is set. "
|
||||||
|
"The cyclic order's benefit (extra tool changes give each layer more time to cool) does not apply "
|
||||||
|
"to the first layer, which is printed slowly and hot for adhesion.\n"
|
||||||
|
"Enable this only if you need the exact same tool sequence on every layer, including the first, at "
|
||||||
|
"the cost of that adhesion optimization."
|
||||||
|
);
|
||||||
|
def->mode = comExpert;
|
||||||
|
def->set_default_value(new ConfigOptionBool(false));
|
||||||
|
|
||||||
def = this->add("slice_closing_radius", coFloat);
|
def = this->add("slice_closing_radius", coFloat);
|
||||||
def->label = L("Slice gap closing radius");
|
def->label = L("Slice gap closing radius");
|
||||||
def->category = L("Quality");
|
def->category = L("Quality");
|
||||||
|
|||||||
@@ -1627,6 +1627,8 @@ PRINT_CONFIG_CLASS_DEFINE(
|
|||||||
((ConfigOptionBool, manual_filament_change))
|
((ConfigOptionBool, manual_filament_change))
|
||||||
((ConfigOptionBool, single_extruder_multi_material_priming))
|
((ConfigOptionBool, single_extruder_multi_material_priming))
|
||||||
((ConfigOptionEnum<ToolChangeOrderingType>, toolchange_ordering))
|
((ConfigOptionEnum<ToolChangeOrderingType>, toolchange_ordering))
|
||||||
|
((ConfigOptionString, toolchange_cyclic_order))
|
||||||
|
((ConfigOptionBool, toolchange_cyclic_first_layer))
|
||||||
((ConfigOptionBool, wipe_tower_no_sparse_layers))
|
((ConfigOptionBool, wipe_tower_no_sparse_layers))
|
||||||
((ConfigOptionString, change_filament_gcode))
|
((ConfigOptionString, change_filament_gcode))
|
||||||
((ConfigOptionString, change_extrusion_role_gcode))
|
((ConfigOptionString, change_extrusion_role_gcode))
|
||||||
|
|||||||
@@ -1055,6 +1055,10 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in
|
|||||||
|
|
||||||
toggle_line("single_extruder_multi_material_priming", !bSEMM && have_prime_tower && supports_wipe_tower_2);
|
toggle_line("single_extruder_multi_material_priming", !bSEMM && have_prime_tower && supports_wipe_tower_2);
|
||||||
|
|
||||||
|
bool use_cyclic_ordering = config->opt_enum<ToolChangeOrderingType>("toolchange_ordering") == ToolChangeOrderingType::Cyclic;
|
||||||
|
toggle_line("toolchange_cyclic_order", use_cyclic_ordering);
|
||||||
|
toggle_line("toolchange_cyclic_first_layer", use_cyclic_ordering);
|
||||||
|
|
||||||
toggle_line("prime_volume",have_prime_tower && (!purge_in_primetower || !bSEMM));
|
toggle_line("prime_volume",have_prime_tower && (!purge_in_primetower || !bSEMM));
|
||||||
|
|
||||||
for (auto el : {"flush_into_infill", "flush_into_support", "flush_into_objects"})
|
for (auto el : {"flush_into_infill", "flush_into_support", "flush_into_objects"})
|
||||||
|
|||||||
@@ -3026,6 +3026,8 @@ void TabPrint::build()
|
|||||||
optgroup = page->new_optgroup(L("Advanced"), L"advanced");
|
optgroup = page->new_optgroup(L("Advanced"), L"advanced");
|
||||||
optgroup->append_single_option_line("interlocking_beam", "multimaterial_settings_advanced#interlocking-beam");
|
optgroup->append_single_option_line("interlocking_beam", "multimaterial_settings_advanced#interlocking-beam");
|
||||||
optgroup->append_single_option_line("toolchange_ordering", "multimaterial_settings_advanced#toolchange-ordering");
|
optgroup->append_single_option_line("toolchange_ordering", "multimaterial_settings_advanced#toolchange-ordering");
|
||||||
|
optgroup->append_single_option_line("toolchange_cyclic_order", "multimaterial_settings_advanced#toolchange-order");
|
||||||
|
optgroup->append_single_option_line("toolchange_cyclic_first_layer", "multimaterial_settings_advanced#toolchange-order");
|
||||||
optgroup->append_single_option_line("interface_shells", "multimaterial_settings_advanced#interface-shells");
|
optgroup->append_single_option_line("interface_shells", "multimaterial_settings_advanced#interface-shells");
|
||||||
optgroup->append_single_option_line("mmu_segmented_region_max_width", "multimaterial_settings_advanced#maximum-width-of-segmented-region");
|
optgroup->append_single_option_line("mmu_segmented_region_max_width", "multimaterial_settings_advanced#maximum-width-of-segmented-region");
|
||||||
optgroup->append_single_option_line("mmu_segmented_region_interlocking_depth", "multimaterial_settings_advanced#interlocking-depth-of-segmented-region");
|
optgroup->append_single_option_line("mmu_segmented_region_interlocking_depth", "multimaterial_settings_advanced#interlocking-depth-of-segmented-region");
|
||||||
|
|||||||
@@ -1025,3 +1025,41 @@ TEST_CASE("Selector slicing keeps the result valid across re-apply", "[Print][H2
|
|||||||
REQUIRE(status != PrintBase::APPLY_STATUS_INVALIDATED);
|
REQUIRE(status != PrintBase::APPLY_STATUS_INVALIDATED);
|
||||||
REQUIRE(print.is_step_done(psSlicingFinished));
|
REQUIRE(print.is_step_done(psSlicingFinished));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("parse_cyclic_order parses user cyclic toolchange sequences", "[ToolOrdering][Cyclic]")
|
||||||
|
{
|
||||||
|
// Filament numbers are 1-based in the UI; the parser returns 0-based indices.
|
||||||
|
SECTION("well-formed sequence") {
|
||||||
|
REQUIRE(parse_cyclic_order("3,2,1,4", 4) == std::vector<unsigned int>({2, 1, 0, 3}));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("surrounding whitespace is tolerated") {
|
||||||
|
REQUIRE(parse_cyclic_order(" 3 , 2 ,1, 4 ", 4) == std::vector<unsigned int>({2, 1, 0, 3}));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("out-of-range and non-positive entries are dropped") {
|
||||||
|
// 0 is below the 1-based range, 5 is above it for a 4-filament setup, -1 is invalid.
|
||||||
|
REQUIRE(parse_cyclic_order("0,5,-1,2", 4) == std::vector<unsigned int>({1}));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("duplicates keep only the first occurrence") {
|
||||||
|
REQUIRE(parse_cyclic_order("2,2,1,2", 4) == std::vector<unsigned int>({1, 0}));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("garbage tokens are ignored") {
|
||||||
|
REQUIRE(parse_cyclic_order("3,abc,,2,x1", 4) == std::vector<unsigned int>({2, 1}));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("tokens that only start with a number are ignored") {
|
||||||
|
// "2x" must be dropped rather than parsed as filament 2.
|
||||||
|
REQUIRE(parse_cyclic_order("3,2x,1", 4) == std::vector<unsigned int>({2, 0}));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("empty string yields an empty order") {
|
||||||
|
REQUIRE(parse_cyclic_order("", 4).empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("a partial sequence only names the filaments it lists") {
|
||||||
|
REQUIRE(parse_cyclic_order("3,1", 4) == std::vector<unsigned int>({2, 0}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user