mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-29 20:01:26 +00:00
Merge branch 'main' into publish_3mf
This commit is contained in:
+72
-7
@@ -370,13 +370,13 @@ if "%use_ninja%" == "ON" (
|
||||
set "using_ninja=ON"
|
||||
)
|
||||
|
||||
call :resolve_clang_cl
|
||||
%repeat_error%
|
||||
|
||||
if "%using_ninja%" == "ON" (
|
||||
if "%use_clang_cl%" == "ON" (
|
||||
REM Bare, so it resolves from the PATH the dev shell just set up, which
|
||||
REM is the clang shipped with Visual Studio. --clang-path names another.
|
||||
set "clang_exe=clang-cl.exe"
|
||||
if not "%clang_path%" == "" set "clang_exe="%clang_path%""
|
||||
set "gen_args=-DCMAKE_C_COMPILER=!clang_exe! -DCMAKE_CXX_COMPILER=!clang_exe!"
|
||||
REM Quoted, because the resolved path has spaces in it.
|
||||
set "gen_args=-DCMAKE_C_COMPILER="!clang_exe!" -DCMAKE_CXX_COMPILER="!clang_exe!""
|
||||
)
|
||||
) else (
|
||||
set "gen_args=-A !arch!"
|
||||
@@ -473,6 +473,7 @@ REM forward slashes, and anything that prints the directory has to show a
|
||||
REM real path rather than one glued onto the repository root.
|
||||
for %%p in ("!build_dir!") do set "build_full=%%~fp"
|
||||
echo Configuration: %build_type%, %arch%
|
||||
if not "%clang_exe%" == "" echo Compiler: %clang_exe%
|
||||
|
||||
set "SIG_FLAG="
|
||||
if defined ORCA_UPDATER_SIG_KEY set "SIG_FLAG=-DORCA_UPDATER_SIG_KEY=%ORCA_UPDATER_SIG_KEY%"
|
||||
@@ -734,6 +735,14 @@ REM worked out the same way in either run.
|
||||
set "slicer_exe=%build_dir%\src\%build_type%\orca-slicer.exe"
|
||||
if "%install_slicer%" == "ON" set "slicer_exe=%build_dir%\OrcaSlicer\orca-slicer.exe"
|
||||
for %%p in ("!slicer_exe!") do set "slicer_full=%%~fp"
|
||||
REM The 2026 generator writes OrcaSlicer.slnx, the releases before it
|
||||
REM OrcaSlicer.sln. A file already there wins, in case an older CMake
|
||||
REM configured the build.
|
||||
set "solution=OrcaSlicer.sln"
|
||||
if "%vs_version%" == "2026" set "solution=OrcaSlicer.slnx"
|
||||
if exist "!build_full!\OrcaSlicer.sln" set "solution=OrcaSlicer.sln"
|
||||
if exist "!build_full!\OrcaSlicer.slnx" set "solution=OrcaSlicer.slnx"
|
||||
|
||||
REM Naming a target builds it and its dependencies, not its dependents,
|
||||
REM so only a full build or the executable's own target relinks.
|
||||
set "linked=ON"
|
||||
@@ -751,14 +760,14 @@ REM worked out the same way in either run.
|
||||
if "%build_deps%" == "ON" echo Dependencies !dep_full!
|
||||
if "%build_slicer%" == "ON" if "%linked%" == "ON" echo OrcaSlicer !slicer_full!
|
||||
if "%build_slicer%" == "ON" if not "%linked%" == "ON" echo Target %slicer_target%
|
||||
if "%build_slicer%" == "ON" if not "%using_ninja%" == "ON" echo Solution %build_full%\OrcaSlicer.sln
|
||||
if "%build_slicer%" == "ON" if not "%using_ninja%" == "ON" echo Solution %build_full%\!solution!
|
||||
if "%pack_deps%" == "ON" if defined bundle echo Bundle !bundle!
|
||||
|
||||
echo.
|
||||
echo Next
|
||||
if "%build_slicer%" == "ON" (
|
||||
if "%linked%" == "ON" echo Run it !slicer_exe!
|
||||
if not "%using_ninja%" == "ON" echo Open in Visual Studio %build_dir%\OrcaSlicer.sln
|
||||
if not "%using_ninja%" == "ON" echo Open in Visual Studio %build_dir%\!solution!
|
||||
if "%linked%" == "ON" echo Rebuild after edits build_win.bat -s!recall! --no-configure
|
||||
if not "%linked%" == "ON" echo Relink the binary build_win.bat -s!recall! --no-configure
|
||||
if "%linked%" == "ON" if "%using_ninja%" == "ON" echo Rebuild one target build_win.bat -s!recall! --no-configure --slicer-target libslic3r
|
||||
@@ -1080,6 +1089,62 @@ REM echo_var <variable>
|
||||
|
||||
exit /b 0
|
||||
|
||||
REM resolve_clang_cl - set clang_exe to the clang-cl a Ninja build uses.
|
||||
REM VsDevCmd appends the Visual Studio LLVM directory to the end of PATH,
|
||||
REM so a standalone LLVM already there shadows it. Name a full path.
|
||||
:resolve_clang_cl
|
||||
set "clang_exe="
|
||||
if not "%using_ninja%" == "ON" exit /b 0
|
||||
if not "%use_clang_cl%" == "ON" exit /b 0
|
||||
REM Only a configure uses it, so -p and --no-configure need none.
|
||||
if "%no_configure%" == "ON" exit /b 0
|
||||
if "%build_deps%%build_slicer%" == "" exit /b 0
|
||||
|
||||
REM --clang-path wins. Forward slashes either way, so CMake does not
|
||||
REM read a backslash as an escape.
|
||||
if not "%clang_path%" == "" (
|
||||
set "clang_exe=%clang_path:\=/%"
|
||||
exit /b 0
|
||||
)
|
||||
|
||||
setlocal
|
||||
|
||||
REM Keyed on the host, not %arch%, because the x64 compiler
|
||||
REM cross-compiles to ARM64.
|
||||
set "llvm_host=x64"
|
||||
if /I "%PROCESSOR_ARCHITECTURE%" == "ARM64" set "llvm_host=ARM64"
|
||||
|
||||
set "found="
|
||||
%VSWHERE% -nologo >nul 2>nul
|
||||
if !errorlevel! == 0 (
|
||||
for /f "tokens=*" %%i in ('%VSWHERE% -nologo -products * -latest -property resolvedInstallationPath') do (
|
||||
if exist "%%i\VC\Tools\Llvm\!llvm_host!\bin\clang-cl.exe" (
|
||||
set "found=%%i\VC\Tools\Llvm\!llvm_host!\bin\clang-cl.exe"
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
REM No clang toolset in Visual Studio. where lists every match, and the
|
||||
REM first is the one PATH would resolve.
|
||||
if "!found!" == "" (
|
||||
for /f "tokens=*" %%i in ('where clang-cl.exe 2^>nul') do (
|
||||
if "!found!" == "" set "found=%%i"
|
||||
)
|
||||
if not "!found!" == "" echo Visual Studio has no clang-cl; using !found! from PATH.
|
||||
)
|
||||
|
||||
if "!found!" == "" (
|
||||
echo No clang-cl found. Add the C++ Clang Compiler component with
|
||||
echo %script_name% --install-vs ide -l
|
||||
echo or name a standalone one with --clang-path.
|
||||
endlocal
|
||||
exit /b 1
|
||||
)
|
||||
|
||||
endlocal & set "clang_exe=%found:\=/%"
|
||||
|
||||
exit /b 0
|
||||
|
||||
REM clean_tree <path> - remove a build tree, refusing anything that is not
|
||||
REM one. Nothing here should ever fire; it is a floor under a bug that
|
||||
REM produced a path far shorter than it looks.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Snapmaker",
|
||||
"version": "02.04.00.09",
|
||||
"version": "02.04.00.10",
|
||||
"force_update": "0",
|
||||
"description": "Snapmaker configurations",
|
||||
"machine_model_list": [
|
||||
|
||||
@@ -184,7 +184,7 @@
|
||||
"nozzle_type": "undefine",
|
||||
"auxiliary_fan": "0",
|
||||
"support_multi_bed_types": "1",
|
||||
"default_bed_type": "4",
|
||||
"default_bed_type": "Textured PEI Plate",
|
||||
"printable_area": [
|
||||
"0.5x1",
|
||||
"270.5x1",
|
||||
|
||||
+70
-10
@@ -85,6 +85,24 @@ foreach ($v in @{ old = '1.11.1'; new = '1.12.0' }.GetEnumerator()) {
|
||||
$ninjaPaths[$v.Key] = "$d;$env:PATH"
|
||||
}
|
||||
|
||||
# A clang-cl earlier on PATH than the Visual Studio one, which is what the
|
||||
# compiler used to resolve to. Nothing runs it; the script only locates it.
|
||||
$clangDir = Join-Path $fixtures 'clang'
|
||||
New-Item -ItemType Directory -Force -Path $clangDir | Out-Null
|
||||
Copy-Item "$env:SystemRoot\System32\where.exe" (Join-Path $clangDir 'clang-cl.exe') -Force
|
||||
$clangOnPath = "$clangDir;$env:PATH"
|
||||
|
||||
# ProgramFiles(x86) is where the script looks for vswhere, so an empty one
|
||||
# stands in for a machine whose Visual Studio has no clang toolset.
|
||||
$noVs = Join-Path $fixtures 'no-vs'
|
||||
New-Item -ItemType Directory -Force -Path $noVs | Out-Null
|
||||
|
||||
# A build directory that already holds a classic solution, for the case where
|
||||
# what is on disk disagrees with what the generator would write.
|
||||
$slnDir = Join-Path $fixtures 'sln'
|
||||
New-Item -ItemType Directory -Force -Path $slnDir | Out-Null
|
||||
Set-Content -Path (Join-Path $slnDir 'OrcaSlicer.sln') -Value '' -Encoding ascii
|
||||
|
||||
# The pack stamp is checked against real dates, so a locale-dependent parse
|
||||
# in the script cannot pass by looking date-shaped. Yesterday is accepted too,
|
||||
# so a run that crosses midnight does not flake.
|
||||
@@ -131,7 +149,36 @@ $cases = @(
|
||||
Contains = @('-G "Ninja Multi-Config"')
|
||||
NotContains = @('clang-cl', '-A x64') }
|
||||
@{ Name = '-l -x builds with clang-cl under Ninja'; Args = @('-d', '-l', '-x')
|
||||
Contains = @('-G "Ninja Multi-Config"', '-DCMAKE_C_COMPILER=clang-cl.exe', '-DCMAKE_CXX_COMPILER=clang-cl.exe') }
|
||||
Contains = @('-G "Ninja Multi-Config"')
|
||||
Match = @('-DCMAKE_C_COMPILER="[^"]+/clang-cl\.exe"', '-DCMAKE_CXX_COMPILER="[^"]+/clang-cl\.exe"') }
|
||||
# PATH order used to decide the compiler. VsDevCmd appends the Visual
|
||||
# Studio LLVM directory to the end of PATH, so a standalone LLVM already
|
||||
# there was resolved instead, and an old one failed the compiler check.
|
||||
@{ Name = 'the compiler is resolved from Visual Studio, not PATH'; Args = @('-s', '-l', '-x')
|
||||
Env = @{ PATH = $clangOnPath }
|
||||
Match = @('^Compiler: .*/VC/Tools/Llvm/[^/]+/bin/clang-cl\.exe$') }
|
||||
@{ Name = 'msvc names no compiler, having resolved none'; Args = @('-s')
|
||||
NotContains = @('Compiler: ') }
|
||||
@{ Name = '-l without -x names none either, the toolset picks it'; Args = @('-s', '-l')
|
||||
NotContains = @('Compiler: ') }
|
||||
# An empty ProgramFiles(x86) puts vswhere out of reach, which is a machine
|
||||
# whose Visual Studio has no clang toolset.
|
||||
@{ Name = 'without a Visual Studio clang the one on PATH is used and named'; Args = @('-s', '-l', '-x')
|
||||
Env = @{ 'ProgramFiles(x86)' = $noVs; PATH = $clangOnPath }
|
||||
Contains = @('Visual Studio has no clang-cl')
|
||||
Match = @('^Compiler: .*/clang/clang-cl\.exe$') }
|
||||
@{ Name = 'no clang-cl anywhere stops before configuring'; Args = @('-s', '-l', '-x'); ExpectExit = 1
|
||||
Env = @{ 'ProgramFiles(x86)' = $noVs; PATH = 'C:\Windows\system32;C:\Windows' }
|
||||
Contains = @('No clang-cl found', '--install-vs ide -l')
|
||||
NotContains = @('cmake -B') }
|
||||
# Only a configure passes the compiler to CMake, so an action that does
|
||||
# not configure resolves none, and cannot start needing one installed.
|
||||
@{ Name = 'packing resolves no compiler'; Args = @('-p', '-l', '-x')
|
||||
Contains = @('Packing the dependencies')
|
||||
NotContains = @('Compiler: ') }
|
||||
@{ Name = '--no-configure resolves none either'; Args = @('-s', '-l', '-x', '--no-configure')
|
||||
Contains = @('cmake --build "build-clang"')
|
||||
NotContains = @('Compiler: ') }
|
||||
@{ Name = '-l alone uses the ClangCL toolset on the VS generator'; Args = @('-d', '-l')
|
||||
Contains = @('-G "Visual Studio', '-T ClangCL')
|
||||
NotContains = @('-DCMAKE_C_COMPILER') }
|
||||
@@ -148,11 +195,13 @@ $cases = @(
|
||||
NotContains = @('clang-cl') }
|
||||
@{ Name = '--msbuild with -l gives the VS generator and the ClangCL toolset'; Args = @('-d', '--msbuild', '-l')
|
||||
Contains = @('-G "Visual Studio', '-T ClangCL') }
|
||||
# A developer with a standalone LLVM points at it; the VS-bundled clang
|
||||
# is what a bare clang-cl.exe resolves to after the dev shell runs.
|
||||
# A developer with a standalone LLVM points at it, and the path is passed
|
||||
# with forward slashes so CMake cannot read a backslash as an escape.
|
||||
@{ Name = '--clang-path names the compiler, quoted for its spaces'; Args = @('-d', '-x', '--clang-path', 'C:\Program Files\LLVM\bin\clang-cl.exe')
|
||||
Contains = @('-DCMAKE_C_COMPILER="C:\Program Files\LLVM\bin\clang-cl.exe"',
|
||||
'-DCMAKE_CXX_COMPILER="C:\Program Files\LLVM\bin\clang-cl.exe"') }
|
||||
Contains = @('-DCMAKE_C_COMPILER="C:/Program Files/LLVM/bin/clang-cl.exe"',
|
||||
'-DCMAKE_CXX_COMPILER="C:/Program Files/LLVM/bin/clang-cl.exe"') }
|
||||
@{ Name = '--clang-path beats the Visual Studio clang'; Args = @('-s', '-x', '--clang-path', 'C:\Program Files\LLVM\bin\clang-cl.exe')
|
||||
Contains = @('Compiler: C:/Program Files/LLVM/bin/clang-cl.exe') }
|
||||
@{ Name = '--clang-path is a clang request on its own'; Args = @('-d', '-x', '--clang-path', 'C:\Program Files\LLVM\bin\clang-cl.exe')
|
||||
Contains = @('deps/build-clang') }
|
||||
@{ Name = '--clang-path needs Ninja to take effect'; Args = @('-d', '--clang-path', 'C:\Program Files\LLVM\bin\clang-cl.exe'); ExpectExit = 1
|
||||
@@ -195,7 +244,8 @@ $cases = @(
|
||||
@{ Name = 'the architecture is matched case-insensitively'; Args = @('-d', '--arch', 'ARM64')
|
||||
Contains = @('-A ARM64', 'deps/build-arm64') }
|
||||
@{ Name = 'arm64 under Ninja has no -A but keeps the arm64 tree'; Args = @('-d', '--arch', 'arm64', '-x', '-l')
|
||||
Contains = @('deps/build-clang-arm64', '-DCMAKE_C_COMPILER=clang-cl.exe')
|
||||
Contains = @('deps/build-clang-arm64')
|
||||
Match = @('-DCMAKE_C_COMPILER="[^"]+/clang-cl\.exe"')
|
||||
NotContains = @('-A ') }
|
||||
|
||||
'build configurations'
|
||||
@@ -699,18 +749,28 @@ $cases = @(
|
||||
Contains = @('Next', 'Rebuild after edits') }
|
||||
|
||||
'pointing at the solution'
|
||||
# The extension follows the generator, so these two pin the release and a
|
||||
# build directory that cannot already hold a solution of either kind.
|
||||
@{ Name = 'the 2026 generator gets the XML solution'; Args = @('-s', '--vs', '2026', '--build-dir', 'D:\tree')
|
||||
Contains = @('Solution D:\tree\OrcaSlicer.slnx', 'Open in Visual Studio D:\tree\OrcaSlicer.slnx') }
|
||||
@{ Name = 'the releases before it get the classic one'; Args = @('-s', '--vs', '2022', '--build-dir', 'D:\tree')
|
||||
Contains = @('Solution D:\tree\OrcaSlicer.sln', 'Open in Visual Studio D:\tree\OrcaSlicer.sln') }
|
||||
@{ Name = 'a solution already on disk wins over the generator'; Args = @('-s', '--vs', '2026', '--build-dir', $slnDir)
|
||||
Match = @('^ Solution .*\\OrcaSlicer\.sln$') }
|
||||
# Extension-agnostic from here: these cases are about the directory, and
|
||||
# the release is whatever is installed.
|
||||
@{ Name = 'the VS generator says where the solution is'; Args = @('-s')
|
||||
Match = @('^ Solution .*\\build\\OrcaSlicer\.sln$') }
|
||||
Match = @('^ Solution .*\\build\\OrcaSlicer\.slnx?$') }
|
||||
@{ Name = 'the solution path follows the configuration'; Args = @('-s', '--config', 'debug')
|
||||
Match = @('^ Solution .*\\build-dbg\\OrcaSlicer\.sln$') }
|
||||
Match = @('^ Solution .*\\build-dbg\\OrcaSlicer\.slnx?$') }
|
||||
@{ Name = 'the solution line survives an install'; Args = @('-s', '-i')
|
||||
Contains = @(' Solution ') }
|
||||
# The path is resolved, not pasted onto the repository root, so it is
|
||||
# right whether --build-dir came absolute or with forward slashes.
|
||||
@{ Name = 'a moved build still prints one real path'; Args = @('-s', '--build-dir', 'out/build/x64-clang')
|
||||
Match = @('^ Solution [A-Za-z]:\\[^/]+\\OrcaSlicer\.sln$') }
|
||||
Match = @('^ Solution [A-Za-z]:\\[^/]+\\OrcaSlicer\.slnx?$') }
|
||||
@{ Name = 'an absolute --build-dir is not glued onto the repo root'; Args = @('-s', '--build-dir', 'D:\tree')
|
||||
Contains = @('Solution D:\tree\OrcaSlicer.sln') }
|
||||
Match = @('^ Solution D:\\tree\\OrcaSlicer\.slnx?$') }
|
||||
)
|
||||
|
||||
function Invoke-BuildScript {
|
||||
|
||||
+1
-1
@@ -7384,7 +7384,7 @@ void CLI::print_help(bool include_print_options, PrinterTechnology printer_techn
|
||||
<< std::endl
|
||||
<< "Print setting priorities:" << std::endl
|
||||
<< "\t1) setting values from the command line (highest priority)"<< std::endl
|
||||
<< "\t2) setting values loaded with --load_settings and --load_filaments" << std::endl
|
||||
<< "\t2) setting values loaded with --load-settings and --load-filaments" << std::endl
|
||||
<< "\t3) setting values loaded from 3mf(lowest priority)" << std::endl;
|
||||
|
||||
/*if (include_print_options) {
|
||||
|
||||
@@ -1715,6 +1715,36 @@ const ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key) co
|
||||
return (it == options.end()) ? nullptr : it->second.get();
|
||||
}
|
||||
|
||||
// ConfigOptionBool(s)::deserialize only understands "1" and "0", but scripts commonly spell CLI
|
||||
// flags as --opt=true or --opt=no. Map the usual spellings onto what deserialize() accepts, per
|
||||
// comma-separated item so vector options keep working, and pass anything else through unchanged
|
||||
// so a genuine typo is still reported as invalid.
|
||||
static std::string normalize_cli_bool_value(const std::string &value)
|
||||
{
|
||||
static const char* true_values[] = { "1", "true", "yes", "on", "enabled" };
|
||||
static const char* false_values[] = { "0", "false", "no", "off", "disabled" };
|
||||
|
||||
auto matches = [](const std::string &item, const char* const* candidates, size_t count) {
|
||||
return std::any_of(candidates, candidates + count, [&item](const char* candidate) { return boost::iequals(item, candidate); });
|
||||
};
|
||||
|
||||
std::string normalized;
|
||||
std::istringstream is(value);
|
||||
std::string item;
|
||||
while (std::getline(is, item, ',')) {
|
||||
boost::trim(item);
|
||||
if (! normalized.empty())
|
||||
normalized += ",";
|
||||
if (matches(item, true_values, std::size(true_values)))
|
||||
normalized += "1";
|
||||
else if (matches(item, false_values, std::size(false_values)))
|
||||
normalized += "0";
|
||||
else
|
||||
normalized += item;
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
bool DynamicConfig::read_cli(int argc, const char* const argv[], t_config_option_keys* extra, t_config_option_keys* keys)
|
||||
{
|
||||
// cache the CLI option => opt_key mapping
|
||||
@@ -1812,17 +1842,32 @@ bool DynamicConfig::read_cli(int argc, const char* const argv[], t_config_option
|
||||
// to the end of the value.
|
||||
if (opt_base->type() == coBools && value.empty())
|
||||
static_cast<ConfigOptionBools*>(opt_base)->values.push_back(!no);
|
||||
else
|
||||
else {
|
||||
// Deserialize any other vector value (ConfigOptionInts, Floats, Percents, Points) the same way
|
||||
// they get deserialized from an .ini file. For ConfigOptionStrings, that means that the C-style unescape
|
||||
// will be applied for values enclosed in quotes, while values non-enclosed in quotes are left to be
|
||||
// unescaped by the calling shell.
|
||||
opt_vector->deserialize(value, true);
|
||||
const std::string vector_value = opt_base->type() == coBools ? normalize_cli_bool_value(value) : value;
|
||||
bool deserialized = false;
|
||||
try {
|
||||
deserialized = opt_vector->deserialize(vector_value, true);
|
||||
} catch (const std::exception &ex) {
|
||||
// e.g. "nil" deserialized into a non-nullable vector option throws instead of
|
||||
// returning false - treat that the same as any other invalid value here.
|
||||
deserialized = false;
|
||||
}
|
||||
if (! deserialized) {
|
||||
boost::nowide::cerr << "Invalid value for option --" << token.c_str() << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (opt_base->type() == coBool) {
|
||||
if (value.empty())
|
||||
static_cast<ConfigOptionBool*>(opt_base)->value = !no;
|
||||
else
|
||||
opt_base->deserialize(value);
|
||||
else if (! opt_base->deserialize(normalize_cli_bool_value(value))) {
|
||||
boost::nowide::cerr << "Invalid value for option --" << token.c_str() << std::endl;
|
||||
return false;
|
||||
}
|
||||
} else if (opt_base->type() == coString) {
|
||||
// Do not unescape single string values, the unescaping is left to the calling shell.
|
||||
static_cast<ConfigOptionString*>(opt_base)->value = value;
|
||||
|
||||
@@ -33,15 +33,6 @@ public:
|
||||
SurfaceFeature(const Vec3d& pt)
|
||||
: m_type{SurfaceFeatureType::Point}, m_pt1{pt} {}
|
||||
|
||||
SurfaceFeature(const SurfaceFeature& sf){
|
||||
this->clone(sf);
|
||||
volume = sf.volume;
|
||||
plane_indices = sf.plane_indices;
|
||||
world_tran = sf.world_tran;
|
||||
world_plane_features = sf.world_plane_features;
|
||||
origin_surface_feature = sf.origin_surface_feature;
|
||||
}
|
||||
|
||||
void clone(const SurfaceFeature &sf)
|
||||
{
|
||||
m_type = sf.get_type();
|
||||
|
||||
@@ -39,7 +39,6 @@ namespace orientation {
|
||||
float height_to_bottom_hull_ratio = 0; // affects stability, the lower the better
|
||||
float unprintability = 0;
|
||||
Eigen::VectorXf areas_cooling;
|
||||
CostItems(CostItems const & other) = default;
|
||||
CostItems() = default;
|
||||
static std::string field_names() {
|
||||
return " overhang, bottom, bothull, contour, A_laf, A_prj, unprintability";
|
||||
|
||||
@@ -195,7 +195,6 @@ public:
|
||||
Point(int64_t x, int32_t y) : Vec2crd(coord_t(x), coord_t(y)) {}
|
||||
Point(int32_t x, int64_t y) : Vec2crd(coord_t(x), coord_t(y)) {}
|
||||
Point(double x, double y) : Vec2crd(coord_t(std::round(x)), coord_t(std::round(y))) {}
|
||||
Point(const Point &rhs) { *this = rhs; }
|
||||
explicit Point(const Vec2d& rhs) : Vec2crd(coord_t(std::round(rhs.x())), coord_t(std::round(rhs.y()))) {}
|
||||
// This constructor allows you to construct Point from Eigen expressions
|
||||
// This constructor has to be implicit (non-explicit) to allow implicit conversion from Eigen expressions.
|
||||
@@ -278,7 +277,6 @@ public:
|
||||
Point3(int32_t x, int32_t y, int32_t z = 0) : Vec3crd(coord_t(x), coord_t(y), coord_t(z)) {}
|
||||
Point3(int64_t x, int64_t y, int64_t z = 0) : Vec3crd(coord_t(x), coord_t(y), coord_t(z)) {}
|
||||
Point3(double x, double y, double z = 0.0) : Vec3crd(coord_t(std::round(x)), coord_t(std::round(y)), coord_t(std::round(z))) {}
|
||||
Point3(const Point3 &rhs) { *this = rhs; }
|
||||
explicit Point3(const Vec2crd& vec2crd, coord_t z = 0) : Vec3crd(vec2crd.x(), vec2crd.y(), z) {}
|
||||
explicit Point3(const Vec3crd &vec3crd) : Vec3crd(vec3crd) {}
|
||||
// This constructor allows you to construct Point from Eigen expressions
|
||||
|
||||
@@ -983,15 +983,19 @@ BedType Preset::get_default_bed_type(PresetBundle* preset_bundle)
|
||||
if (config.has("default_bed_type") && !config.opt_string("default_bed_type").empty()) {
|
||||
try {
|
||||
std::string str_bed_type = config.opt_string("default_bed_type");
|
||||
BedType bed_type;
|
||||
if (ConfigOptionEnum<BedType>::from_string(str_bed_type, bed_type) &&
|
||||
bed_type > btDefault && bed_type < btCount) {
|
||||
return bed_type;
|
||||
}
|
||||
|
||||
// Try parsing as integer first (legacy format)
|
||||
// Try parsing as integer (legacy format)
|
||||
int bed_type_value = atoi(str_bed_type.c_str());
|
||||
if (bed_type_value > 0) {
|
||||
if (bed_type_value > 0 && bed_type_value < BedType::btCount) {
|
||||
return BedType(bed_type_value);
|
||||
}
|
||||
else {
|
||||
|
||||
BOOST_LOG_TRIVIAL(error) << "default_bed_type: invalid bed type: " << str_bed_type;
|
||||
}
|
||||
return BedType::btPEI;
|
||||
|
||||
} catch(...) {
|
||||
|
||||
@@ -856,13 +856,12 @@ public:
|
||||
|
||||
protected:
|
||||
PresetCollection() = default;
|
||||
// Copy constructor and copy operators are not to be used from outside PresetBundle,
|
||||
// as the Profile::vendor points to an instance of VendorProfile stored at parent PresetBundle!
|
||||
PresetCollection(const PresetCollection &other) = default;
|
||||
//BBS: add operator= logic insteadof default
|
||||
// Deleted by the std::recursive_mutex member. PresetBundle copies by assignment.
|
||||
PresetCollection(const PresetCollection &other) = delete;
|
||||
//BBS: hand-written because m_mutex cannot be copy-assigned.
|
||||
PresetCollection& operator=(const PresetCollection &other);
|
||||
// After copying a collection with the default operators above, call this function
|
||||
// to adjust Profile::vendor pointers.
|
||||
// Copying leaves every Preset::vendor pointing into the source bundle's vendor map.
|
||||
// This re-points them at the matching entries in vendors.
|
||||
void update_vendor_ptrs_after_copy(const VendorMap &vendors);
|
||||
|
||||
// Select a preset, if it exists. If it does not exist, select an invalid (-1) index.
|
||||
@@ -1000,7 +999,8 @@ public:
|
||||
bool only_default_printers() const;
|
||||
private:
|
||||
PrinterPresetCollection() = default;
|
||||
PrinterPresetCollection(const PrinterPresetCollection &other) = default;
|
||||
// Deleted along with the base copy constructor.
|
||||
PrinterPresetCollection(const PrinterPresetCollection &other) = delete;
|
||||
PrinterPresetCollection& operator=(const PrinterPresetCollection &other) = default;
|
||||
|
||||
friend class PresetBundle;
|
||||
|
||||
@@ -2941,6 +2941,16 @@ void PresetBundle::load_selections(AppConfig &config, const PresetPreferences& p
|
||||
// If executed due to a Config Wizard update, preferred_printer contains the first newly installed printer, otherwise nullptr.
|
||||
const Preset *preferred_printer = printers.find_system_preset_by_model_and_variant(preferred_selection.printer_model_id, preferred_selection.printer_variant);
|
||||
printers.select_preset_by_name(preferred_printer ? preferred_printer->name : initial_printer_profile_name, true);
|
||||
Preset &selected_printer = printers.get_edited_preset();
|
||||
if (selected_printer.printer_technology() == ptFFF) {
|
||||
BedType bed_type = selected_printer.get_default_bed_type(this);
|
||||
const std::string saved_bed_type = config.get_printer_setting(selected_printer.name, "curr_bed_type");
|
||||
const int saved_bed_type_value = atoi(saved_bed_type.c_str());
|
||||
if (saved_bed_type_value > btDefault && saved_bed_type_value < btCount)
|
||||
bed_type = static_cast<BedType>(saved_bed_type_value);
|
||||
project_config.set_key_value("curr_bed_type", new ConfigOptionEnum<BedType>(bed_type));
|
||||
config.set("curr_bed_type", std::to_string(static_cast<int>(bed_type)));
|
||||
}
|
||||
CNumericLocalesSetter locales_setter;
|
||||
|
||||
// Orca: load from orca_presets
|
||||
|
||||
@@ -11990,13 +11990,11 @@ CLIActionsConfigDef::CLIActionsConfigDef()
|
||||
def = this->add("load_defaultfila", coBool);
|
||||
def->label = L("Load default filaments");
|
||||
def->tooltip = L("Load first filament as default for those not loaded.");
|
||||
def->cli_params = "option";
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("min_save", coBool);
|
||||
def->label = L("Minimum save");
|
||||
def->tooltip = L("Export 3MF with minimum size.");
|
||||
def->cli_params = "option";
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("mtcpp", coInt);
|
||||
@@ -12022,7 +12020,6 @@ CLIActionsConfigDef::CLIActionsConfigDef()
|
||||
def = this->add("normative_check", coBool);
|
||||
def->label = L("Normative check");
|
||||
def->tooltip = L("Check the normative items.");
|
||||
def->cli_params = "option";
|
||||
def->set_default_value(new ConfigOptionBool(true));
|
||||
|
||||
/*def = this->add("help_fff", coBool);
|
||||
@@ -12289,7 +12286,7 @@ CLIMiscConfigDef::CLIMiscConfigDef()
|
||||
def->cli_params = "level";
|
||||
def->set_default_value(new ConfigOptionInt(1));
|
||||
|
||||
def = this->add("logfile", coInt);
|
||||
def = this->add("logfile", coString);
|
||||
def->label = L("Log file");
|
||||
def->tooltip = L("Redirects debug logging to file.\n");
|
||||
def->cli_params = "file";
|
||||
@@ -12337,7 +12334,6 @@ CLIMiscConfigDef::CLIMiscConfigDef()
|
||||
def = this->add("skip_modified_gcodes", coBool);
|
||||
def->label = L("Skip modified G-code in 3MF");
|
||||
def->tooltip = L("Skip the modified G-code in 3MF from printer or filament presets.");
|
||||
def->cli_params = "option";
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("makerlab_name", coString);
|
||||
@@ -12367,14 +12363,12 @@ CLIMiscConfigDef::CLIMiscConfigDef()
|
||||
def = this->add("allow_newer_file", coBool);
|
||||
def->label = L("Allow 3MF with newer version to be sliced");
|
||||
def->tooltip = L("Allow 3MF with newer version to be sliced.");
|
||||
def->cli_params = "option";
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
|
||||
def = this->add("allow_mix_temp", coBool);
|
||||
// internal use only, don't need translation
|
||||
def->label = "Allow filaments with high/low temperature to be printed together";
|
||||
def->tooltip = "Allow filaments with high/low temperature to be printed together.";
|
||||
def->cli_params = "option";
|
||||
def->set_default_value(new ConfigOptionBool(false));
|
||||
}
|
||||
|
||||
|
||||
@@ -44,9 +44,6 @@ struct DrainHole
|
||||
: pos(p), normal(n), radius(r), height(h), failed(fl)
|
||||
{}
|
||||
|
||||
DrainHole(const DrainHole& rhs) :
|
||||
DrainHole(rhs.pos, rhs.normal, rhs.radius, rhs.height, rhs.failed) {}
|
||||
|
||||
bool operator==(const DrainHole &sp) const;
|
||||
|
||||
bool operator!=(const DrainHole &sp) const { return !(sp == (*this)); }
|
||||
|
||||
@@ -1234,10 +1234,6 @@ static void modulate_extrusion_by_overlapping_layers(
|
||||
(fragment_end.is_start ? &polyline.points.front() : &polyline.points.back());
|
||||
}
|
||||
private:
|
||||
ExtrusionPathFragmentEndPointAccessor& operator=(const ExtrusionPathFragmentEndPointAccessor&) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
const std::vector<ExtrusionPathFragment> &m_path_fragments;
|
||||
};
|
||||
const coord_t search_radius = 7;
|
||||
|
||||
@@ -204,8 +204,9 @@ public:
|
||||
clear_nodes();
|
||||
}
|
||||
|
||||
TreeSupportData(TreeSupportData&&) = default;
|
||||
TreeSupportData& operator=(TreeSupportData&&) = default;
|
||||
// Deleted by the tbb::spin_mutex member.
|
||||
TreeSupportData(TreeSupportData&&) = delete;
|
||||
TreeSupportData& operator=(TreeSupportData&&) = delete;
|
||||
|
||||
TreeSupportData(const TreeSupportData&) = delete;
|
||||
TreeSupportData& operator=(const TreeSupportData&) = delete;
|
||||
|
||||
+3
-17
@@ -91,29 +91,15 @@ class CaliPresetInfo
|
||||
{
|
||||
public:
|
||||
int tray_id;
|
||||
int extruder_id;
|
||||
NozzleVolumeType nozzle_volume_type;
|
||||
BedType bed_type;
|
||||
int extruder_id = 0;
|
||||
NozzleVolumeType nozzle_volume_type{nvtStandard};
|
||||
BedType bed_type{btDefault};
|
||||
float nozzle_diameter;
|
||||
int nozzle_pos_id{-1};
|
||||
std::string nozzle_sn;
|
||||
std::string filament_id;
|
||||
std::string setting_id;
|
||||
std::string name;
|
||||
|
||||
CaliPresetInfo &operator=(const CaliPresetInfo &other)
|
||||
{
|
||||
this->tray_id = other.tray_id;
|
||||
this->extruder_id = other.extruder_id;
|
||||
this->nozzle_volume_type = other.nozzle_volume_type;
|
||||
this->nozzle_diameter = other.nozzle_diameter;
|
||||
this->nozzle_pos_id = other.nozzle_pos_id;
|
||||
this->nozzle_sn = other.nozzle_sn;
|
||||
this->filament_id = other.filament_id;
|
||||
this->setting_id = other.setting_id;
|
||||
this->name = other.name;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
struct PrinterCaliInfo
|
||||
|
||||
@@ -675,16 +675,6 @@ public:
|
||||
offset = Vec2d(0, 0);
|
||||
}
|
||||
|
||||
TexturePart(const TexturePart& part) {
|
||||
this->x = part.x;
|
||||
this->y = part.y;
|
||||
this->w = part.w;
|
||||
this->h = part.h;
|
||||
this->offset = part.offset;
|
||||
this->buffer = part.buffer;
|
||||
this->filename = part.filename;
|
||||
this->texture = part.texture;
|
||||
}
|
||||
void update_pos(float xx, float yy, float ww, float hh) {
|
||||
x = xx;
|
||||
y = yy;
|
||||
|
||||
@@ -149,7 +149,10 @@ SavePresetDialog::Item::Item(Preset::Type type, const std::string &suffix, wxBox
|
||||
// Set initial state (unchecked by default)
|
||||
detach_checkbox->SetValue(m_detach);
|
||||
// Bind the checkbox event to update the detach state for this item
|
||||
detach_checkbox->Bind(wxEVT_TOGGLEBUTTON, [this, detach_checkbox](wxCommandEvent&) { m_detach = detach_checkbox->GetValue(); });
|
||||
detach_checkbox->Bind(wxEVT_TOGGLEBUTTON, [this, detach_checkbox](wxCommandEvent& event) {
|
||||
m_detach = detach_checkbox->GetValue();
|
||||
event.Skip(); // Let CheckBox update its bitmap for the new state.
|
||||
});
|
||||
|
||||
detach_label->SetForegroundColour(wxColour("#363636"));
|
||||
|
||||
|
||||
@@ -5131,7 +5131,7 @@ void TabPrinter::build_fff()
|
||||
optgroup->append_single_option_line("adaptive_bed_mesh_margin", "printer_basic_information_adaptive_bed_mesh#mesh-margin");
|
||||
|
||||
optgroup = page->new_optgroup(L("Accessory"), "param_accessory");
|
||||
optgroup->append_single_option_line("nozzle_type", "printer_basic_information_accessory#nozzle-type");
|
||||
optgroup->append_single_option_line("nozzle_type", "printer_basic_information_accessory#nozzle-type", 0);
|
||||
optgroup->append_single_option_line("nozzle_hrc", "printer_basic_information_accessory#nozzle-hrc");
|
||||
optgroup->append_single_option_line("auxiliary_fan", "printer_basic_information_accessory#auxiliary-part-cooling-fan");
|
||||
optgroup->append_single_option_line("fan_direction");
|
||||
|
||||
@@ -828,3 +828,226 @@ SCENARIO("ConfigOptionVector::set_to_index throws on incompatible type", "[Confi
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("read_cli applies valid values and collects non-option arguments", "[Config]") {
|
||||
Slic3r::DynamicPrintConfig config;
|
||||
t_config_option_keys extra, keys;
|
||||
const char* argv[] = {"orca-slicer", "--nozzle-temperature", "210,190", "--reduce-crossing-wall=1", "model.3mf"};
|
||||
REQUIRE(config.read_cli(5, argv, &extra, &keys));
|
||||
REQUIRE(config.opt<ConfigOptionInts>("nozzle_temperature")->values == std::vector<int>{210, 190});
|
||||
REQUIRE(config.opt<ConfigOptionBool>("reduce_crossing_wall")->value);
|
||||
REQUIRE(extra == t_config_option_keys{"model.3mf"});
|
||||
REQUIRE(keys == t_config_option_keys{"nozzle_temperature", "reduce_crossing_wall"});
|
||||
}
|
||||
|
||||
TEST_CASE("read_cli rejects nil for a non-nullable vector option", "[Config]") {
|
||||
Slic3r::DynamicPrintConfig config;
|
||||
t_config_option_keys extra, keys;
|
||||
const char* argv[] = {"orca-slicer", "--nozzle-temperature", "nil"};
|
||||
REQUIRE_FALSE(config.read_cli(3, argv, &extra, &keys));
|
||||
}
|
||||
|
||||
TEST_CASE("read_cli rejects an invalid boolean value", "[Config]") {
|
||||
Slic3r::DynamicPrintConfig config;
|
||||
t_config_option_keys extra, keys;
|
||||
const char* argv[] = {"orca-slicer", "--reduce-crossing-wall=maybe"};
|
||||
REQUIRE_FALSE(config.read_cli(2, argv, &extra, &keys));
|
||||
}
|
||||
|
||||
TEST_CASE("read_cli accepts the common spellings of a boolean value", "[Config]") {
|
||||
const auto [text, expected] = GENERATE(table<const char*, bool>({
|
||||
{"--reduce-crossing-wall=1", true },
|
||||
{"--reduce-crossing-wall=true", true },
|
||||
{"--reduce-crossing-wall=Yes", true },
|
||||
{"--reduce-crossing-wall=on", true },
|
||||
{"--reduce-crossing-wall=enabled", true },
|
||||
{"--reduce-crossing-wall=TRUE", true },
|
||||
{"--reduce-crossing-wall=oN", true },
|
||||
{"--reduce-crossing-wall=0", false},
|
||||
{"--reduce-crossing-wall=false", false},
|
||||
{"--reduce-crossing-wall=No", false},
|
||||
{"--reduce-crossing-wall=off", false},
|
||||
{"--reduce-crossing-wall=disabled", false},
|
||||
{"--reduce-crossing-wall=FALSE", false},
|
||||
{"--reduce-crossing-wall=DiSaBlEd", false},
|
||||
}));
|
||||
|
||||
DYNAMIC_SECTION(text) {
|
||||
Slic3r::DynamicPrintConfig config;
|
||||
t_config_option_keys extra, keys;
|
||||
const char* argv[] = {"orca-slicer", text};
|
||||
REQUIRE(config.read_cli(2, argv, &extra, &keys));
|
||||
REQUIRE(config.opt<ConfigOptionBool>("reduce_crossing_wall")->value == expected);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("read_cli accepts the common boolean spellings inside a bools vector", "[Config]") {
|
||||
Slic3r::DynamicPrintConfig config;
|
||||
t_config_option_keys extra, keys;
|
||||
const char* argv[] = {"orca-slicer", "--filament-soluble=true,no,1"};
|
||||
REQUIRE(config.read_cli(2, argv, &extra, &keys));
|
||||
REQUIRE(config.opt<ConfigOptionBools>("filament_soluble")->values == std::vector<unsigned char>{1, 0, 1});
|
||||
}
|
||||
|
||||
TEST_CASE("read_cli trims whitespace around boolean spellings", "[Config]") {
|
||||
Slic3r::DynamicPrintConfig config;
|
||||
t_config_option_keys extra, keys;
|
||||
const char* argv[] = {"orca-slicer", "--reduce-crossing-wall= true ", "--filament-soluble= true , no ,1"};
|
||||
REQUIRE(config.read_cli(3, argv, &extra, &keys));
|
||||
REQUIRE(config.opt<ConfigOptionBool>("reduce_crossing_wall")->value);
|
||||
REQUIRE(config.opt<ConfigOptionBools>("filament_soluble")->values == std::vector<unsigned char>{1, 0, 1});
|
||||
}
|
||||
|
||||
TEST_CASE("read_cli normalizes boolean spellings when a bools vector is repeated", "[Config]") {
|
||||
Slic3r::DynamicPrintConfig config;
|
||||
t_config_option_keys extra, keys;
|
||||
const char* argv[] = {"orca-slicer", "--filament-soluble=true", "--filament-soluble=off"};
|
||||
REQUIRE(config.read_cli(3, argv, &extra, &keys));
|
||||
REQUIRE(config.opt<ConfigOptionBools>("filament_soluble")->values == std::vector<unsigned char>{1, 0});
|
||||
}
|
||||
|
||||
TEST_CASE("read_cli keeps nil alongside boolean spellings in a nullable bools vector", "[Config]") {
|
||||
Slic3r::DynamicPrintConfig config;
|
||||
t_config_option_keys extra, keys;
|
||||
const char* argv[] = {"orca-slicer", "--enable-overhang-speed=nil,yes,off"};
|
||||
REQUIRE(config.read_cli(2, argv, &extra, &keys));
|
||||
auto* opt = config.opt<ConfigOptionBoolsNullable>("enable_overhang_speed");
|
||||
REQUIRE(opt != nullptr);
|
||||
REQUIRE(opt->values.size() == 3);
|
||||
REQUIRE(opt->is_nil(0));
|
||||
REQUIRE(opt->values[1] == 1);
|
||||
REQUIRE(opt->values[2] == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("read_cli rejects an empty item inside a bools vector", "[Config]") {
|
||||
Slic3r::DynamicPrintConfig config;
|
||||
t_config_option_keys extra, keys;
|
||||
const char* argv[] = {"orca-slicer", "--filament-soluble=true,,1"};
|
||||
REQUIRE_FALSE(config.read_cli(2, argv, &extra, &keys));
|
||||
}
|
||||
|
||||
TEST_CASE("read_cli rejects an unknown spelling next to a valid one in a bools vector", "[Config]") {
|
||||
Slic3r::DynamicPrintConfig config;
|
||||
t_config_option_keys extra, keys;
|
||||
const char* argv[] = {"orca-slicer", "--filament-soluble=true,affirmative"};
|
||||
REQUIRE_FALSE(config.read_cli(2, argv, &extra, &keys));
|
||||
}
|
||||
|
||||
// The normalization lives in read_cli's boolean branches, so options of other types keep the
|
||||
// value verbatim - a path named "on" or a colour named "true" must not turn into "1".
|
||||
TEST_CASE("read_cli leaves boolean spellings alone for non-boolean options", "[Config]") {
|
||||
SECTION("string option") {
|
||||
Slic3r::DynamicPrintAndCLIConfig config;
|
||||
t_config_option_keys extra, keys;
|
||||
const char* argv[] = {"orca-slicer", "--logfile=true"};
|
||||
REQUIRE(config.read_cli(2, argv, &extra, &keys));
|
||||
REQUIRE(config.opt<ConfigOptionString>("logfile")->value == "true");
|
||||
}
|
||||
SECTION("strings vector option") {
|
||||
Slic3r::DynamicPrintConfig config;
|
||||
t_config_option_keys extra, keys;
|
||||
const char* argv[] = {"orca-slicer", "--filament-colour=on;off"};
|
||||
REQUIRE(config.read_cli(2, argv, &extra, &keys));
|
||||
REQUIRE(config.opt<ConfigOptionStrings>("filament_colour")->values == std::vector<std::string>{"on", "off"});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("read_cli treats a bare boolean flag as true without consuming the next argument", "[Config]") {
|
||||
Slic3r::DynamicPrintConfig config;
|
||||
t_config_option_keys extra, keys;
|
||||
const char* argv[] = {"orca-slicer", "--reduce-crossing-wall", "model.3mf"};
|
||||
REQUIRE(config.read_cli(3, argv, &extra, &keys));
|
||||
REQUIRE(config.opt<ConfigOptionBool>("reduce_crossing_wall")->value);
|
||||
REQUIRE(extra == t_config_option_keys{"model.3mf"});
|
||||
}
|
||||
|
||||
TEST_CASE("read_cli rejects an invalid scalar numeric value", "[Config]") {
|
||||
Slic3r::DynamicPrintConfig config;
|
||||
t_config_option_keys extra, keys;
|
||||
const char* argv[] = {"orca-slicer", "--top-shell-layers", "several"};
|
||||
REQUIRE_FALSE(config.read_cli(3, argv, &extra, &keys));
|
||||
}
|
||||
|
||||
TEST_CASE("read_cli appends values when a vector option is repeated", "[Config]") {
|
||||
Slic3r::DynamicPrintConfig config;
|
||||
t_config_option_keys extra, keys;
|
||||
const char* argv[] = {"orca-slicer", "--nozzle-temperature", "210", "--nozzle-temperature", "190,200"};
|
||||
REQUIRE(config.read_cli(5, argv, &extra, &keys));
|
||||
REQUIRE(config.opt<ConfigOptionInts>("nozzle_temperature")->values == std::vector<int>{210, 190, 200});
|
||||
// the key is recorded once, on first use
|
||||
REQUIRE(keys == t_config_option_keys{"nozzle_temperature"});
|
||||
}
|
||||
|
||||
TEST_CASE("read_cli parses a bools vector given in the --flag=values form", "[Config]") {
|
||||
Slic3r::DynamicPrintConfig config;
|
||||
t_config_option_keys extra, keys;
|
||||
const char* argv[] = {"orca-slicer", "--filament-soluble=1,0,1"};
|
||||
REQUIRE(config.read_cli(2, argv, &extra, &keys));
|
||||
REQUIRE(config.opt<ConfigOptionBools>("filament_soluble")->values == std::vector<unsigned char>{1, 0, 1});
|
||||
}
|
||||
|
||||
TEST_CASE("read_cli rejects an invalid value inside a bools vector", "[Config]") {
|
||||
Slic3r::DynamicPrintConfig config;
|
||||
t_config_option_keys extra, keys;
|
||||
const char* argv[] = {"orca-slicer", "--filament-soluble=1,maybe"};
|
||||
REQUIRE_FALSE(config.read_cli(2, argv, &extra, &keys));
|
||||
}
|
||||
|
||||
TEST_CASE("read_cli appends true for a bare bools vector flag", "[Config]") {
|
||||
Slic3r::DynamicPrintConfig config;
|
||||
t_config_option_keys extra, keys;
|
||||
const char* argv[] = {"orca-slicer", "--filament-soluble"};
|
||||
REQUIRE(config.read_cli(2, argv, &extra, &keys));
|
||||
REQUIRE(config.opt<ConfigOptionBools>("filament_soluble")->values == std::vector<unsigned char>{1});
|
||||
}
|
||||
|
||||
TEST_CASE("read_cli splits a strings vector on semicolons and unescapes quoted items", "[Config]") {
|
||||
Slic3r::DynamicPrintConfig config;
|
||||
t_config_option_keys extra, keys;
|
||||
const char* argv[] = {"orca-slicer", "--filament-colour", "#FF0000;\"a\\nb\";#00FF00"};
|
||||
REQUIRE(config.read_cli(3, argv, &extra, &keys));
|
||||
auto& values = config.opt<ConfigOptionStrings>("filament_colour")->values;
|
||||
REQUIRE(values == std::vector<std::string>{"#FF0000", "a\nb", "#00FF00"});
|
||||
}
|
||||
|
||||
TEST_CASE("read_cli rejects a strings vector with an unterminated quote", "[Config]") {
|
||||
Slic3r::DynamicPrintConfig config;
|
||||
t_config_option_keys extra, keys;
|
||||
const char* argv[] = {"orca-slicer", "--filament-colour", "\"oops"};
|
||||
REQUIRE_FALSE(config.read_cli(3, argv, &extra, &keys));
|
||||
}
|
||||
|
||||
TEST_CASE("read_cli parses a points vector in the NxM coordinate form", "[Config]") {
|
||||
Slic3r::DynamicPrintConfig config;
|
||||
t_config_option_keys extra, keys;
|
||||
const char* argv[] = {"orca-slicer", "--printable-area", "0x0,200x0,200x200,0x200"};
|
||||
REQUIRE(config.read_cli(3, argv, &extra, &keys));
|
||||
auto& points = config.opt<ConfigOptionPoints>("printable_area")->values;
|
||||
REQUIRE(points.size() == 4);
|
||||
REQUIRE_THAT(points[1].x(), Catch::Matchers::WithinAbs(200.0, 1e-9));
|
||||
REQUIRE_THAT(points[1].y(), Catch::Matchers::WithinAbs(0.0, 1e-9));
|
||||
REQUIRE_THAT(points[3].x(), Catch::Matchers::WithinAbs(0.0, 1e-9));
|
||||
REQUIRE_THAT(points[3].y(), Catch::Matchers::WithinAbs(200.0, 1e-9));
|
||||
}
|
||||
|
||||
// logfile is a CLI-only option, so it needs the config type whose def pulls in cli_misc_config_def.
|
||||
TEST_CASE("read_cli stores the log file path as a string", "[Config]") {
|
||||
Slic3r::DynamicPrintAndCLIConfig config;
|
||||
t_config_option_keys extra, keys;
|
||||
const char* argv[] = {"orca-slicer", "--logfile", "orca.log"};
|
||||
REQUIRE(config.read_cli(3, argv, &extra, &keys));
|
||||
REQUIRE(config.opt<ConfigOptionString>("logfile")->value == "orca.log");
|
||||
}
|
||||
|
||||
TEST_CASE("read_cli accepts nil entries for a nullable vector option", "[Config]") {
|
||||
Slic3r::DynamicPrintConfig config;
|
||||
t_config_option_keys extra, keys;
|
||||
const char* argv[] = {"orca-slicer", "--filament-retraction-length", "nil,2.5"};
|
||||
REQUIRE(config.read_cli(3, argv, &extra, &keys));
|
||||
auto* opt = config.opt<ConfigOptionFloatsNullable>("filament_retraction_length");
|
||||
REQUIRE(opt != nullptr);
|
||||
REQUIRE(opt->values.size() == 2);
|
||||
REQUIRE(opt->is_nil(0));
|
||||
REQUIRE_FALSE(opt->is_nil(1));
|
||||
REQUIRE_THAT(opt->values[1], Catch::Matchers::WithinAbs(2.5, 1e-9));
|
||||
}
|
||||
|
||||
@@ -220,6 +220,44 @@ TEST_CASE("Printer extruder count tolerates missing nozzle diameter", "[Preset][
|
||||
CHECK(bundle.get_printer_extruder_count() == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("Selected printer uses its default or saved bed type", "[Preset][Bundle]")
|
||||
{
|
||||
PresetBundle bundle;
|
||||
Preset& printer = add_inmemory_preset(bundle.printers, "Test Printer");
|
||||
printer.is_system = true;
|
||||
printer.config.option<ConfigOptionString>("printer_model")->value = "TEST-MODEL";
|
||||
printer.config.option<ConfigOptionString>("printer_variant")->value = "0.4";
|
||||
printer.config.option<ConfigOptionString>("default_bed_type")->value = "Engineering Plate";
|
||||
|
||||
AppConfig app_config;
|
||||
app_config.set("curr_bed_type", std::to_string(static_cast<int>(btPTE)));
|
||||
PresetBundle::PresetPreferences preferred_selection;
|
||||
BedType expected_bed_type;
|
||||
|
||||
SECTION("New printer uses its symbolic default") {
|
||||
expected_bed_type = btEP;
|
||||
preferred_selection = {"TEST-MODEL", "0.4"};
|
||||
}
|
||||
SECTION("Re-enabled printer uses its saved selection") {
|
||||
expected_bed_type = btPC;
|
||||
preferred_selection = {"TEST-MODEL", "0.4"};
|
||||
app_config.set_printer_setting("Test Printer", "curr_bed_type",
|
||||
std::to_string(static_cast<int>(expected_bed_type)));
|
||||
}
|
||||
SECTION("Existing printer keeps its saved selection after presets reload") {
|
||||
expected_bed_type = btPCT;
|
||||
app_config.set("presets", PRESET_PRINTER_NAME, "Test Printer");
|
||||
app_config.set_printer_setting("Test Printer", "curr_bed_type",
|
||||
std::to_string(static_cast<int>(expected_bed_type)));
|
||||
}
|
||||
|
||||
bundle.load_selections(app_config, preferred_selection);
|
||||
bundle.export_selections(app_config);
|
||||
|
||||
CHECK(bundle.project_config.opt_enum<BedType>("curr_bed_type") == expected_bed_type);
|
||||
CHECK(app_config.get_printer_setting("Test Printer", "curr_bed_type") == std::to_string(static_cast<int>(expected_bed_type)));
|
||||
}
|
||||
|
||||
TEST_CASE("find_preset resolves a system preset's renamed_from", "[Preset][Rename]")
|
||||
{
|
||||
RenameTestCollection coll;
|
||||
|
||||
Reference in New Issue
Block a user