Merge branch 'main' into feature/filament_id

This commit is contained in:
SoftFever
2026-09-06 22:34:06 +08:00
46 changed files with 876 additions and 252 deletions
-27
View File
@@ -95,33 +95,6 @@ else ()
add_compile_definitions("$<$<CONFIG:Release>:WXINSPECTOR_DISABLE>") add_compile_definitions("$<$<CONFIG:Release>:WXINSPECTOR_DISABLE>")
endif () endif ()
find_package(Git)
if(DEFINED ENV{git_commit_hash} AND NOT "$ENV{git_commit_hash}" STREQUAL "")
message(STATUS "Specified git commit hash: $ENV{git_commit_hash}")
if(GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
# Convert the given hash to short hash
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-parse --short "$ENV{git_commit_hash}"
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_COMMIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
else()
# No .git directory (e.g., Flatpak sandbox) — truncate directly
string(SUBSTRING "$ENV{git_commit_hash}" 0 7 GIT_COMMIT_HASH)
endif()
add_definitions("-DGIT_COMMIT_HASH=\"${GIT_COMMIT_HASH}\"")
elseif(GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git")
# Check current Git commit hash
execute_process(
COMMAND ${GIT_EXECUTABLE} log -1 --format=%h
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE GIT_COMMIT_HASH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
add_definitions("-DGIT_COMMIT_HASH=\"${GIT_COMMIT_HASH}\"")
endif()
if(DEFINED ENV{SLIC3R_STATIC}) if(DEFINED ENV{SLIC3R_STATIC})
set(SLIC3R_STATIC_INITIAL $ENV{SLIC3R_STATIC}) set(SLIC3R_STATIC_INITIAL $ENV{SLIC3R_STATIC})
else() else()
+73 -8
View File
@@ -370,13 +370,13 @@ if "%use_ninja%" == "ON" (
set "using_ninja=ON" set "using_ninja=ON"
) )
call :resolve_clang_cl
%repeat_error%
if "%using_ninja%" == "ON" ( if "%using_ninja%" == "ON" (
if "%use_clang_cl%" == "ON" ( if "%use_clang_cl%" == "ON" (
REM Bare, so it resolves from the PATH the dev shell just set up, which REM Quoted, because the resolved path has spaces in it.
REM is the clang shipped with Visual Studio. --clang-path names another. set "gen_args=-DCMAKE_C_COMPILER="!clang_exe!" -DCMAKE_CXX_COMPILER="!clang_exe!""
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!"
) )
) else ( ) else (
set "gen_args=-A !arch!" 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. REM real path rather than one glued onto the repository root.
for %%p in ("!build_dir!") do set "build_full=%%~fp" for %%p in ("!build_dir!") do set "build_full=%%~fp"
echo Configuration: %build_type%, %arch% echo Configuration: %build_type%, %arch%
if not "%clang_exe%" == "" echo Compiler: %clang_exe%
set "SIG_FLAG=" set "SIG_FLAG="
if defined ORCA_UPDATER_SIG_KEY set "SIG_FLAG=-DORCA_UPDATER_SIG_KEY=%ORCA_UPDATER_SIG_KEY%" 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" set "slicer_exe=%build_dir%\src\%build_type%\orca-slicer.exe"
if "%install_slicer%" == "ON" set "slicer_exe=%build_dir%\OrcaSlicer\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" 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 Naming a target builds it and its dependencies, not its dependents,
REM so only a full build or the executable's own target relinks. REM so only a full build or the executable's own target relinks.
set "linked=ON" 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_deps%" == "ON" echo Dependencies !dep_full!
if "%build_slicer%" == "ON" if "%linked%" == "ON" echo OrcaSlicer !slicer_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 "%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! if "%pack_deps%" == "ON" if defined bundle echo Bundle !bundle!
echo. echo.
echo Next echo Next
if "%build_slicer%" == "ON" ( if "%build_slicer%" == "ON" (
if "%linked%" == "ON" echo Run it !slicer_exe! 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 "%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 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 if "%linked%" == "ON" if "%using_ninja%" == "ON" echo Rebuild one target build_win.bat -s!recall! --no-configure --slicer-target libslic3r
@@ -866,7 +875,7 @@ REM get_str_len <string> -> length in %ret%
echo ORCA_DEPS_CMAKE_ARGS Extra arguments for the deps configure echo ORCA_DEPS_CMAKE_ARGS Extra arguments for the deps configure
echo ORCA_SLICER_CMAKE_ARGS Extra arguments for the slicer configure echo ORCA_SLICER_CMAKE_ARGS Extra arguments for the slicer configure
echo ORCA_UPDATER_SIG_KEY Update signing key baked into the slicer echo ORCA_UPDATER_SIG_KEY Update signing key baked into the slicer
echo git_commit_hash Revision to stamp, so a commit does not rebuild everything echo git_commit_hash Revision to stamp into the build, as CI does
echo NINJA_STATUS Ninja progress format, if you want your own echo NINJA_STATUS Ninja progress format, if you want your own
echo debugscript Set to ON to trace this script echo debugscript Set to ON to trace this script
echo. echo.
@@ -1080,6 +1089,62 @@ REM echo_var <variable>
exit /b 0 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 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 one. Nothing here should ever fire; it is a floor under a bug that
REM produced a path far shorter than it looks. REM produced a path far shorter than it looks.
+9 -7
View File
@@ -15,7 +15,13 @@ if(WIN32)
# See https://github.com/python/cpython/issues/153438 # See https://github.com/python/cpython/issues/153438
# Patch from https://github.com/python/cpython/pull/153608 # Patch from https://github.com/python/cpython/pull/153608
# This patch has not been merged to 3.12 yet so we need to apply it manually # This patch has not been merged to 3.12 yet so we need to apply it manually
set(_patch_cmd git init && ${PATCH_CMD} ${CMAKE_CURRENT_LIST_DIR}/01-windows-nuget.patch) #
# Without core.autocrlf=false the patched find_python.bat comes out LF and
# cmd.exe cannot find its goto labels.
set(_patch_cmd git init
&& ${GIT_EXECUTABLE} -c core.autocrlf=false apply --verbose
--ignore-space-change --whitespace=fix
${CMAKE_CURRENT_LIST_DIR}/01-windows-nuget.patch)
if(MSVC_VERSION EQUAL 1800) if(MSVC_VERSION EQUAL 1800)
set(_python_platform_toolset v120) set(_python_platform_toolset v120)
@@ -53,12 +59,9 @@ if(WIN32)
set(_python_pcbuild_output_dir win32) set(_python_pcbuild_output_dir win32)
endif() endif()
# pybind11 undefines _DEBUG around Python.h so a debug build links the
# release python3xx.lib; Py_DEBUG could not load release plugin modules.
set(_python_pcbuild_config Release) set(_python_pcbuild_config Release)
set(_python_layout_debug OFF)
if(DEFINED DEP_DEBUG AND DEP_DEBUG)
set(_python_pcbuild_config Debug)
set(_python_layout_debug ON)
endif()
# CPython's PCbuild needs a 64-bit-hosted toolchain: find_msbuild.bat picks the # CPython's PCbuild needs a 64-bit-hosted toolchain: find_msbuild.bat picks the
# 32-bit Bin\MSBuild.exe, whose x86 cl.exe/link.exe run out of address space # 32-bit Bin\MSBuild.exe, whose x86 cl.exe/link.exe run out of address space
@@ -101,7 +104,6 @@ if(WIN32)
-DPYTHON_BUILD_DIR=<SOURCE_DIR>/PCbuild/${_python_pcbuild_output_dir} -DPYTHON_BUILD_DIR=<SOURCE_DIR>/PCbuild/${_python_pcbuild_output_dir}
-DPYTHON_DEST_DIR=${DESTDIR}/libpython -DPYTHON_DEST_DIR=${DESTDIR}/libpython
-DPYTHON_LAYOUT_ARCH=${_python_layout_arch} -DPYTHON_LAYOUT_ARCH=${_python_layout_arch}
-DPYTHON_DEBUG=${_python_layout_debug}
-P ${CMAKE_CURRENT_LIST_DIR}/stage_windows.cmake -P ${CMAKE_CURRENT_LIST_DIR}/stage_windows.cmake
) )
elseif(APPLE) elseif(APPLE)
+1 -15
View File
@@ -9,9 +9,6 @@ foreach(_var PYTHON_SOURCE_DIR PYTHON_BUILD_DIR PYTHON_DEST_DIR PYTHON_LAYOUT_AR
endforeach() endforeach()
set(_python_exe "${PYTHON_BUILD_DIR}/python.exe") set(_python_exe "${PYTHON_BUILD_DIR}/python.exe")
if(PYTHON_DEBUG)
set(_python_exe "${PYTHON_BUILD_DIR}/python_d.exe")
endif()
if(NOT EXISTS "${_python_exe}") if(NOT EXISTS "${_python_exe}")
message(FATAL_ERROR "Built Python executable not found: ${_python_exe}") message(FATAL_ERROR "Built Python executable not found: ${_python_exe}")
@@ -49,21 +46,10 @@ endif()
set(_required_files set(_required_files
"${PYTHON_DEST_DIR}/Lib/encodings/__init__.py" "${PYTHON_DEST_DIR}/Lib/encodings/__init__.py"
"${PYTHON_DEST_DIR}/include/Python.h" "${PYTHON_DEST_DIR}/include/Python.h"
)
if(PYTHON_DEBUG)
list(APPEND _required_files
"${PYTHON_DEST_DIR}/python_d.exe"
"${PYTHON_DEST_DIR}/python${_python_abi}_d.dll"
"${PYTHON_DEST_DIR}/libs/python${_python_abi}_d.lib"
)
else()
list(APPEND _required_files
"${PYTHON_DEST_DIR}/python.exe" "${PYTHON_DEST_DIR}/python.exe"
"${PYTHON_DEST_DIR}/python${_python_abi}.dll" "${PYTHON_DEST_DIR}/python${_python_abi}.dll"
"${PYTHON_DEST_DIR}/libs/python${_python_abi}.lib" "${PYTHON_DEST_DIR}/libs/python${_python_abi}.lib"
) )
endif()
foreach(_required_file IN LISTS _required_files) foreach(_required_file IN LISTS _required_files)
if(NOT EXISTS "${_required_file}") if(NOT EXISTS "${_required_file}")
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "Snapmaker", "name": "Snapmaker",
"version": "02.04.00.10", "version": "02.04.00.11",
"force_update": "0", "force_update": "0",
"description": "Snapmaker configurations", "description": "Snapmaker configurations",
"machine_model_list": [ "machine_model_list": [
@@ -184,7 +184,7 @@
"nozzle_type": "undefine", "nozzle_type": "undefine",
"auxiliary_fan": "0", "auxiliary_fan": "0",
"support_multi_bed_types": "1", "support_multi_bed_types": "1",
"default_bed_type": "4", "default_bed_type": "Textured PEI Plate",
"printable_area": [ "printable_area": [
"0.5x1", "0.5x1",
"270.5x1", "270.5x1",
+70 -10
View File
@@ -85,6 +85,24 @@ foreach ($v in @{ old = '1.11.1'; new = '1.12.0' }.GetEnumerator()) {
$ninjaPaths[$v.Key] = "$d;$env:PATH" $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 # 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, # in the script cannot pass by looking date-shaped. Yesterday is accepted too,
# so a run that crosses midnight does not flake. # so a run that crosses midnight does not flake.
@@ -131,7 +149,36 @@ $cases = @(
Contains = @('-G "Ninja Multi-Config"') Contains = @('-G "Ninja Multi-Config"')
NotContains = @('clang-cl', '-A x64') } NotContains = @('clang-cl', '-A x64') }
@{ Name = '-l -x builds with clang-cl under Ninja'; Args = @('-d', '-l', '-x') @{ 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') @{ Name = '-l alone uses the ClangCL toolset on the VS generator'; Args = @('-d', '-l')
Contains = @('-G "Visual Studio', '-T ClangCL') Contains = @('-G "Visual Studio', '-T ClangCL')
NotContains = @('-DCMAKE_C_COMPILER') } NotContains = @('-DCMAKE_C_COMPILER') }
@@ -148,11 +195,13 @@ $cases = @(
NotContains = @('clang-cl') } NotContains = @('clang-cl') }
@{ Name = '--msbuild with -l gives the VS generator and the ClangCL toolset'; Args = @('-d', '--msbuild', '-l') @{ Name = '--msbuild with -l gives the VS generator and the ClangCL toolset'; Args = @('-d', '--msbuild', '-l')
Contains = @('-G "Visual Studio', '-T ClangCL') } Contains = @('-G "Visual Studio', '-T ClangCL') }
# A developer with a standalone LLVM points at it; the VS-bundled clang # A developer with a standalone LLVM points at it, and the path is passed
# is what a bare clang-cl.exe resolves to after the dev shell runs. # 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') @{ 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"', Contains = @('-DCMAKE_C_COMPILER="C:/Program Files/LLVM/bin/clang-cl.exe"',
'-DCMAKE_CXX_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') @{ 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') } 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 @{ 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') @{ Name = 'the architecture is matched case-insensitively'; Args = @('-d', '--arch', 'ARM64')
Contains = @('-A ARM64', 'deps/build-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') @{ 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 ') } NotContains = @('-A ') }
'build configurations' 'build configurations'
@@ -699,18 +749,28 @@ $cases = @(
Contains = @('Next', 'Rebuild after edits') } Contains = @('Next', 'Rebuild after edits') }
'pointing at the solution' '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') @{ 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') @{ 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') @{ Name = 'the solution line survives an install'; Args = @('-s', '-i')
Contains = @(' Solution ') } Contains = @(' Solution ') }
# The path is resolved, not pasted onto the repository root, so it is # The path is resolved, not pasted onto the repository root, so it is
# right whether --build-dir came absolute or with forward slashes. # 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') @{ 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') @{ 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 { function Invoke-BuildScript {
+12 -1
View File
@@ -33,7 +33,8 @@ if (SLIC3R_GUI)
set (wxWidgets_CONFIG_OPTIONS "--toolkit=gtk${SLIC3R_GTK}") set (wxWidgets_CONFIG_OPTIONS "--toolkit=gtk${SLIC3R_GTK}")
find_package(wxWidgets 3.3 REQUIRED COMPONENTS base core adv html gl aui net media webview) find_package(wxWidgets 3.3 REQUIRED COMPONENTS base core adv html gl aui net media webview)
else () else ()
find_package(wxWidgets 3.3 CONFIG REQUIRED COMPONENTS html adv gl core base webview aui net media) # propgrid is required by wxInspector.
find_package(wxWidgets 3.3 CONFIG REQUIRED COMPONENTS html adv gl core base webview aui net media propgrid)
endif () endif ()
if(UNIX) if(UNIX)
@@ -90,6 +91,16 @@ if (SLIC3R_GUI)
# list(REMOVE_ITEM wxWidgets_LIBRARIES oleacc) # list(REMOVE_ITEM wxWidgets_LIBRARIES oleacc)
find_package(wxInspector REQUIRED) find_package(wxInspector REQUIRED)
# wxInspector's exported interface names the release wxWidgets import
# libraries, which a Debug build cannot link. wx is linked above instead.
get_target_property(_wxinspector_interface wxInspector::wxInspector INTERFACE_LINK_LIBRARIES)
if (_wxinspector_interface)
list(FILTER _wxinspector_interface EXCLUDE REGEX "wx(base|msw)3[0-9]u[_.]")
set_target_properties(wxInspector::wxInspector PROPERTIES
INTERFACE_LINK_LIBRARIES "${_wxinspector_interface}")
endif ()
list(APPEND wxWidgets_LIBRARIES "wxInspector::wxInspector") list(APPEND wxWidgets_LIBRARIES "wxInspector::wxInspector")
message(STATUS "wx libs: ${wxWidgets_LIBRARIES}") message(STATUS "wx libs: ${wxWidgets_LIBRARIES}")
+71 -16
View File
@@ -1797,8 +1797,9 @@ int CLI::run(int argc, char **argv)
old_printable_area = config.option<ConfigOptionPoints>("printable_area", true)->values; old_printable_area = config.option<ConfigOptionPoints>("printable_area", true)->values;
old_exclude_area = config.option<ConfigOptionPoints>("bed_exclude_area", true)->values; old_exclude_area = config.option<ConfigOptionPoints>("bed_exclude_area", true)->values;
if (old_printable_area.size() >= 4) { if (old_printable_area.size() >= 4) {
old_printable_width = (int)(old_printable_area[2].x() - old_printable_area[0].x()); BoundingBoxf old_printable_bbox(old_printable_area);
old_printable_depth = (int)(old_printable_area[2].y() - old_printable_area[0].y()); old_printable_width = static_cast<int>(old_printable_bbox.size().x());
old_printable_depth = static_cast<int>(old_printable_bbox.size().y());
} }
old_printable_height = (int)(config.opt_float("printable_height")); old_printable_height = (int)(config.opt_float("printable_height"));
@@ -2347,8 +2348,9 @@ int CLI::run(int argc, char **argv)
Pointfs orig_printable_area; Pointfs orig_printable_area;
orig_printable_area = config.option<ConfigOptionPoints>("printable_area", true)->values; orig_printable_area = config.option<ConfigOptionPoints>("printable_area", true)->values;
if (orig_printable_area.size() >= 4) { if (orig_printable_area.size() >= 4) {
orig_printable_width = (int)(orig_printable_area[2].x() - orig_printable_area[0].x()); BoundingBoxf orig_printable_bbox(orig_printable_area);
orig_printable_depth = (int)(orig_printable_area[2].y() - orig_printable_area[0].y()); orig_printable_width = static_cast<int>(orig_printable_bbox.size().x());
orig_printable_depth = static_cast<int>(orig_printable_bbox.size().y());
} }
orig_printable_height = (int)(config.opt_float("printable_height")); orig_printable_height = (int)(config.opt_float("printable_height"));
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(":%1%, check printable size: old_printable_width=%2%, orig_printable_width=%3%, old_printable_depth=%4%, orig_printable_depth=%5%, old_printable_height=%6%, orig_printable_height=%7%") BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(":%1%, check printable size: old_printable_width=%2%, orig_printable_width=%3%, old_printable_depth=%4%, orig_printable_depth=%5%, old_printable_height=%6%, orig_printable_height=%7%")
@@ -3142,7 +3144,25 @@ int CLI::run(int argc, char **argv)
std::vector<int> old_variant_counts(filament_count, 1), new_variant_counts; std::vector<int> old_variant_counts(filament_count, 1), new_variant_counts;
ConfigOptionInts* filament_self_index_opt = m_print_config.option<ConfigOptionInts>("filament_self_index"); ConfigOptionInts* filament_self_index_opt = m_print_config.option<ConfigOptionInts>("filament_self_index");
if (!filament_self_index_opt) { bool need_regenerate_self_index = !filament_self_index_opt;
if (filament_self_index_opt) {
// a filament_self_index carried over from a stale project can disagree with the
// current filament_count. old_start_indice/old_variant_counts below are sized to
// filament_count and walked with 1-based group indices, so an index above
// filament_count overruns old_start_indice[++k], and a non-positive first index
// writes old_variant_counts[-1] - both heap corruption.
int max_self_index = 0, min_self_index = 1;
for (int v : filament_self_index_opt->values) {
max_self_index = std::max(max_self_index, v);
min_self_index = std::min(min_self_index, v);
}
if (max_self_index > filament_count || min_self_index < 1) {
BOOST_LOG_TRIVIAL(warning) << boost::format("filament_self_index range [%1%, %2%] is invalid for filament_count %3%, regenerating")
% min_self_index % max_self_index % filament_count;
need_regenerate_self_index = true;
}
}
if (need_regenerate_self_index) {
filament_self_index_opt = m_print_config.option<ConfigOptionInts>("filament_self_index", true); filament_self_index_opt = m_print_config.option<ConfigOptionInts>("filament_self_index", true);
std::vector<int>& filament_self_indice = filament_self_index_opt->values; std::vector<int>& filament_self_indice = filament_self_index_opt->values;
filament_self_indice.resize(filament_count); filament_self_indice.resize(filament_count);
@@ -3736,6 +3756,8 @@ int CLI::run(int argc, char **argv)
double height_to_lid = m_print_config.opt_float("extruder_clearance_height_to_lid"); double height_to_lid = m_print_config.opt_float("extruder_clearance_height_to_lid");
double height_to_rod = m_print_config.opt_float("extruder_clearance_height_to_rod"); double height_to_rod = m_print_config.opt_float("extruder_clearance_height_to_rod");
double clearance_radius = m_print_config.opt_float("extruder_clearance_radius"); double clearance_radius = m_print_config.opt_float("extruder_clearance_radius");
double nozzle_height = m_print_config.opt_float("nozzle_height");
Vec2d align_center = m_print_config.option<ConfigOptionPoint>("best_object_pos")->value;
int shared_printable_width = 0, shared_printable_depth = 0, shared_printable_height = 0, shared_center_x = 0, shared_center_y = 0; int shared_printable_width = 0, shared_printable_depth = 0, shared_printable_height = 0, shared_center_x = 0, shared_center_y = 0;
//double plate_stride; //double plate_stride;
std::string bed_texture; std::string bed_texture;
@@ -3746,8 +3768,11 @@ int CLI::run(int argc, char **argv)
if (m_print_config.opt<ConfigOptionFloatsNullable>("extruder_printable_height")) { if (m_print_config.opt<ConfigOptionFloatsNullable>("extruder_printable_height")) {
current_extruder_print_heights = m_print_config.opt<ConfigOptionFloatsNullable>("extruder_printable_height")->values; current_extruder_print_heights = m_print_config.opt<ConfigOptionFloatsNullable>("extruder_printable_height")->values;
} }
current_printable_width = current_printable_area[2].x() - current_printable_area[0].x(); {
current_printable_depth = current_printable_area[2].y() - current_printable_area[0].y(); BoundingBoxf current_printable_bbox(current_printable_area);
current_printable_width = static_cast<int>(current_printable_bbox.size().x());
current_printable_depth = static_cast<int>(current_printable_bbox.size().y());
}
current_printable_height = print_height; current_printable_height = print_height;
if (old_printable_width == 0) if (old_printable_width == 0)
old_printable_width = current_printable_width; old_printable_width = current_printable_width;
@@ -3948,6 +3973,11 @@ int CLI::run(int argc, char **argv)
ConfigOptionFloats *wipe_x_option = dynamic_cast<ConfigOptionFloats *>(print_config.option("wipe_tower_x")); ConfigOptionFloats *wipe_x_option = dynamic_cast<ConfigOptionFloats *>(print_config.option("wipe_tower_x"));
ConfigOptionFloats *wipe_y_option = dynamic_cast<ConfigOptionFloats *>(print_config.option("wipe_tower_y")); ConfigOptionFloats *wipe_y_option = dynamic_cast<ConfigOptionFloats *>(print_config.option("wipe_tower_y"));
// get_at() silently clamps an out-of-range index to entry 0 - make the reuse visible
if (static_cast<size_t>(plate_index) >= wipe_x_option->values.size() || static_cast<size_t>(plate_index) >= wipe_y_option->values.size()) {
BOOST_LOG_TRIVIAL(warning) << boost::format("plate %1%: wipe_tower_x/y only has %2%/%3% entries, reusing entry 0's position")
%(plate_index+1) %wipe_x_option->values.size() %wipe_y_option->values.size();
}
plate_obj_size_info.wipe_x = wipe_x_option->get_at(plate_index); plate_obj_size_info.wipe_x = wipe_x_option->get_at(plate_index);
plate_obj_size_info.wipe_y = wipe_y_option->get_at(plate_index); plate_obj_size_info.wipe_y = wipe_y_option->get_at(plate_index);
@@ -4143,8 +4173,9 @@ int CLI::run(int argc, char **argv)
temp_extruder_print_heights = config.option<ConfigOptionFloatsNullable>("extruder_printable_height", true)->values; temp_extruder_print_heights = config.option<ConfigOptionFloatsNullable>("extruder_printable_height", true)->values;
if (temp_printable_area.size() >= 4) { if (temp_printable_area.size() >= 4) {
printer_plate.printable_width = (int)(temp_printable_area[2].x() - temp_printable_area[0].x()); BoundingBoxf temp_printable_bbox(temp_printable_area);
printer_plate.printable_depth = (int)(temp_printable_area[2].y() - temp_printable_area[0].y()); printer_plate.printable_width = static_cast<int>(temp_printable_bbox.size().x());
printer_plate.printable_depth = static_cast<int>(temp_printable_bbox.size().y());
printer_plate.printable_height = (int)(config.opt_float("printable_height")); printer_plate.printable_height = (int)(config.opt_float("printable_height"));
} }
if (temp_exclude_area.size() >= 4) { if (temp_exclude_area.size() >= 4) {
@@ -4792,6 +4823,8 @@ int CLI::run(int argc, char **argv)
arrange_cfg.clearance_height_to_rod = height_to_rod; arrange_cfg.clearance_height_to_rod = height_to_rod;
arrange_cfg.clearance_height_to_lid = height_to_lid; arrange_cfg.clearance_height_to_lid = height_to_lid;
arrange_cfg.clearance_radius = clearance_radius; arrange_cfg.clearance_radius = clearance_radius;
arrange_cfg.nozzle_height = nozzle_height;
arrange_cfg.align_center = align_center;
arrange_cfg.printable_height = print_height; arrange_cfg.printable_height = print_height;
arrange_cfg.min_obj_distance = 0; arrange_cfg.min_obj_distance = 0;
if (arrange_cfg.is_seq_print) { if (arrange_cfg.is_seq_print) {
@@ -5170,7 +5203,8 @@ int CLI::run(int argc, char **argv)
Vec3d wipe_tower_size = cur_plate->estimate_wipe_tower_size(m_print_config, w, v, new_extruder_count, filaments_cnt, false, enable_wrapping); Vec3d wipe_tower_size = cur_plate->estimate_wipe_tower_size(m_print_config, w, v, new_extruder_count, filaments_cnt, false, enable_wrapping);
Vec3d plate_origin = cur_plate->get_origin(); Vec3d plate_origin = cur_plate->get_origin();
int plate_width, plate_depth, plate_height; int plate_width, plate_depth;
double plate_height;
partplate_list.get_plate_size(plate_width, plate_depth, plate_height); partplate_list.get_plate_size(plate_width, plate_depth, plate_height);
float depth = wipe_tower_size(1); float depth = wipe_tower_size(1);
float margin = 15.f, wp_brim_width = 0.f; float margin = 15.f, wp_brim_width = 0.f;
@@ -5242,6 +5276,8 @@ int CLI::run(int argc, char **argv)
arrange_cfg.clearance_height_to_rod = height_to_rod; arrange_cfg.clearance_height_to_rod = height_to_rod;
arrange_cfg.clearance_height_to_lid = height_to_lid; arrange_cfg.clearance_height_to_lid = height_to_lid;
arrange_cfg.clearance_radius = clearance_radius; arrange_cfg.clearance_radius = clearance_radius;
arrange_cfg.nozzle_height = nozzle_height;
arrange_cfg.align_center = align_center;
arrange_cfg.printable_height = print_height; arrange_cfg.printable_height = print_height;
arrange_cfg.min_obj_distance = 0; arrange_cfg.min_obj_distance = 0;
if (arrange_cfg.is_seq_print) { if (arrange_cfg.is_seq_print) {
@@ -6501,7 +6537,6 @@ int CLI::run(int argc, char **argv)
bool need_create_thumbnail_group = false, need_create_no_light_group = false, need_create_top_group = false; bool need_create_thumbnail_group = false, need_create_no_light_group = false, need_create_top_group = false;
// get type and color for platedata // get type and color for platedata
auto* filament_types = dynamic_cast<const ConfigOptionStrings*>(m_print_config.option("filament_type"));
const ConfigOptionStrings* filament_color = dynamic_cast<const ConfigOptionStrings *>(m_print_config.option("filament_colour")); const ConfigOptionStrings* filament_color = dynamic_cast<const ConfigOptionStrings *>(m_print_config.option("filament_colour"));
auto* filament_id = dynamic_cast<const ConfigOptionStrings*>(m_print_config.option("filament_ids")); auto* filament_id = dynamic_cast<const ConfigOptionStrings*>(m_print_config.option("filament_ids"));
const ConfigOptionFloats* nozzle_diameter_option = dynamic_cast<const ConfigOptionFloats *>(m_print_config.option("nozzle_diameter")); const ConfigOptionFloats* nozzle_diameter_option = dynamic_cast<const ConfigOptionFloats *>(m_print_config.option("nozzle_diameter"));
@@ -6534,10 +6569,11 @@ int CLI::run(int argc, char **argv)
plate_data->nozzle_diameters = nozzle_diameter_str; plate_data->nozzle_diameters = nozzle_diameter_str;
for (auto it = plate_data->slice_filaments_info.begin(); it != plate_data->slice_filaments_info.end(); it++) { for (auto it = plate_data->slice_filaments_info.begin(); it != plate_data->slice_filaments_info.end(); it++) {
// get_at() on an empty vector option is UB - these can be unpopulated on a from-scratch slice
std::string display_filament_type; std::string display_filament_type;
it->type = m_print_config.get_filament_type(display_filament_type, it->id); it->type = m_print_config.get_filament_type(display_filament_type, it->id);
it->color = filament_color ? filament_color->get_at(it->id) : "#FFFFFF"; it->color = (filament_color && !filament_color->values.empty()) ? filament_color->get_at(it->id) : "#FFFFFF";
it->filament_id = filament_id?filament_id->get_at(it->id):""; it->filament_id = (filament_id && !filament_id->values.empty()) ? filament_id->get_at(it->id) : "";
#ifdef SLIC3R_GUI #ifdef SLIC3R_GUI
if (is_bbl_printer) if (is_bbl_printer)
it->filament_id = bbl_agent.from_orca_filament_id(it->filament_id); it->filament_id = bbl_agent.from_orca_filament_id(it->filament_id);
@@ -7333,6 +7369,10 @@ bool CLI::setup(int argc, char **argv)
m_config.option(optdef.first, true); m_config.option(optdef.first, true);
set_data_dir(m_config.opt_string("datadir")); set_data_dir(m_config.opt_string("datadir"));
if (!data_dir().empty() && !boost::filesystem::exists(data_dir())) {
boost::nowide::cerr << "Could not create data directory: " << data_dir() << std::endl;
return false;
}
//FIXME Validating at this stage most likely does not make sense, as the config is not fully initialized yet. //FIXME Validating at this stage most likely does not make sense, as the config is not fully initialized yet.
if (!validity.empty()) { if (!validity.empty()) {
@@ -7406,7 +7446,7 @@ void CLI::print_help(bool include_print_options, PrinterTechnology printer_techn
<< std::endl << std::endl
<< "Print setting priorities:" << std::endl << "Print setting priorities:" << std::endl
<< "\t1) setting values from the command line (highest priority)"<< 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; << "\t3) setting values loaded from 3mf(lowest priority)" << std::endl;
/*if (include_print_options) { /*if (include_print_options) {
@@ -7445,6 +7485,10 @@ bool CLI::export_models(IO::ExportFormat format, std::string path_dir)
for (ModelObject* model_object : model.objects) for (ModelObject* model_object : model.objects)
{ {
const std::string path = this->output_filepath(*model_object, index++, format, path_dir); const std::string path = this->output_filepath(*model_object, index++, format, path_dir);
if (path.empty()) {
boost::nowide::cerr << "Could not create output directory for STL export" << std::endl;
return false;
}
success = Slic3r::store_stl(path.c_str(), model_object, true); success = Slic3r::store_stl(path.c_str(), model_object, true);
if (success) if (success)
BOOST_LOG_TRIVIAL(info) << "Model successfully exported to " << path << std::endl; BOOST_LOG_TRIVIAL(info) << "Model successfully exported to " << path << std::endl;
@@ -7570,8 +7614,19 @@ std::string CLI::output_filepath(const ModelObject &object, unsigned int index,
output_path = subdir + "/"+file_name; output_path = subdir + "/"+file_name;
boost::filesystem::path subdir_path(subdir); boost::filesystem::path subdir_path(subdir);
if (!boost::filesystem::exists(subdir_path)) if (!boost::filesystem::exists(subdir_path)) {
boost::filesystem::create_directory(subdir_path); try {
boost::filesystem::create_directories(subdir_path);
} catch (const boost::filesystem::filesystem_error &ex) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": failed to create output directory " << subdir_path.string() << ": " << ex.what();
}
if (!boost::filesystem::exists(subdir_path)) {
// Directory creation failed and won't succeed on a retry (same path, same cause) -
// signal failure now instead of letting every object in the model repeat the same
// doomed attempt and fail with a less specific "export failed" error later.
return std::string();
}
}
return output_path; return output_path;
} }
+2 -1
View File
@@ -9,6 +9,7 @@
#include <boost/format.hpp> #include <boost/format.hpp>
#include <mutex> #include <mutex>
#include "git_commit_hash.h"
#include "libslic3r_version.h" #include "libslic3r_version.h"
static std::string g_log_folder; static std::string g_log_folder;
@@ -39,7 +40,7 @@ CBaseException::CBaseException(HANDLE hProcess, WORD wPID, LPCTSTR lpSymbolPath,
output_file->open(log_filename, std::ios::out | std::ios::app); output_file->open(log_filename, std::ios::out | std::ios::app);
// Output app build info in crash log so we could look for the correct PDB files // Output app build info in crash log so we could look for the correct PDB files
OutputString(_T("%s\n\n"), _T(SLIC3R_APP_NAME " " SoftFever_VERSION " Build " GIT_COMMIT_HASH)); OutputString(_T("%s\n\n"), _T(SLIC3R_APP_NAME " " SoftFever_VERSION " Build " GIT_COMMIT_HASH GIT_COMMIT_SUFFIX));
} }
} }
+7 -3
View File
@@ -13,7 +13,6 @@ BuildVolume::BuildVolume(const std::vector<Vec2d> &printable_area, const double
: m_bed_shape(printable_area), m_max_print_height(printable_height), m_extruder_shapes(extruder_areas), m_extruder_printable_height(extruder_printable_heights) : m_bed_shape(printable_area), m_max_print_height(printable_height), m_extruder_shapes(extruder_areas), m_extruder_printable_height(extruder_printable_heights)
{ {
assert(printable_height >= 0); assert(printable_height >= 0);
//assert(extruder_printable_heights.size() == extruder_areas.size());
m_polygon = Polygon::new_scale(printable_area); m_polygon = Polygon::new_scale(printable_area);
assert(m_polygon.is_counter_clockwise()); assert(m_polygon.is_counter_clockwise());
@@ -86,6 +85,9 @@ BuildVolume::BuildVolume(const std::vector<Vec2d> &printable_area, const double
m_shared_volume.data[2] = m_bboxf.max.x(); m_shared_volume.data[2] = m_bboxf.max.x();
m_shared_volume.data[3] = m_bboxf.max.y(); m_shared_volume.data[3] = m_bboxf.max.y();
m_shared_volume.zs[1] = m_bboxf.max.z(); m_shared_volume.zs[1] = m_bboxf.max.z();
if (extruder_printable_heights.size() < m_extruder_shapes.size())
BOOST_LOG_TRIVIAL(warning) << boost::format("extruder_printable_height has only %1% entries but extruder_printable_area has %2%, falling back to the bed printable_height for the missing ones")
% extruder_printable_heights.size() % m_extruder_shapes.size();
for (unsigned int index = 0; index < m_extruder_shapes.size(); index++) for (unsigned int index = 0; index < m_extruder_shapes.size(); index++)
{ {
std::vector<Vec2d>& extruder_shape = m_extruder_shapes[index]; std::vector<Vec2d>& extruder_shape = m_extruder_shapes[index];
@@ -100,7 +102,9 @@ BuildVolume::BuildVolume(const std::vector<Vec2d> &printable_area, const double
return; return;
} }
if ((extruder_shape == printable_area)&&(extruder_printable_heights[index] == printable_height)) { const double extruder_height = index < extruder_printable_heights.size() ? extruder_printable_heights[index] : printable_height;
if ((extruder_shape == printable_area)&&(extruder_height == printable_height)) {
extruder_volume.same_with_bed = true; extruder_volume.same_with_bed = true;
extruder_volume.type = m_type; extruder_volume.type = m_type;
extruder_volume.bbox = m_bbox; extruder_volume.bbox = m_bbox;
@@ -113,7 +117,7 @@ BuildVolume::BuildVolume(const std::vector<Vec2d> &printable_area, const double
double poly_area = poly.area(); double poly_area = poly.area();
extruder_volume.bbox = get_extents(poly); extruder_volume.bbox = get_extents(poly);
BoundingBoxf temp_bboxf = get_extents(extruder_shape); BoundingBoxf temp_bboxf = get_extents(extruder_shape);
extruder_volume.bboxf = BoundingBoxf3{ to_3d(temp_bboxf.min, 0.), to_3d(temp_bboxf.max, extruder_printable_heights[index]) }; extruder_volume.bboxf = BoundingBoxf3{ to_3d(temp_bboxf.min, 0.), to_3d(temp_bboxf.max, extruder_height) };
if (extruder_shape.size() >= 4 && std::abs((poly_area - double(extruder_volume.bbox.size().x()) * double(extruder_volume.bbox.size().y()))) < sqr(SCALED_EPSILON)) if (extruder_shape.size() >= 4 && std::abs((poly_area - double(extruder_volume.bbox.size().x()) * double(extruder_volume.bbox.size().y()))) < sqr(SCALED_EPSILON))
{ {
+51 -5
View File
@@ -1049,7 +1049,8 @@ int ConfigBase::load_from_json(const std::string &file, ConfigSubstitutionContex
std::vector<std::string>& different_settings = this->option<ConfigOptionStrings>("different_settings_to_system", true)->values; std::vector<std::string>& different_settings = this->option<ConfigOptionStrings>("different_settings_to_system", true)->values;
size_t size = different_settings.size(); size_t size = different_settings.size();
if (size == 0) { if (size == 0) {
size = this->option<ConfigOptionStrings>("filament_settings_id")->values.size() + 2; const auto *filament_ids = this->option<ConfigOptionStrings>("filament_settings_id");
size = (filament_ids ? filament_ids->values.size() : 0) + 2;
different_settings.resize(size); different_settings.resize(size);
} }
@@ -1715,6 +1716,36 @@ const ConfigOption* DynamicConfig::optptr(const t_config_option_key &opt_key) co
return (it == options.end()) ? nullptr : it->second.get(); 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) 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 // cache the CLI option => opt_key mapping
@@ -1812,17 +1843,32 @@ bool DynamicConfig::read_cli(int argc, const char* const argv[], t_config_option
// to the end of the value. // to the end of the value.
if (opt_base->type() == coBools && value.empty()) if (opt_base->type() == coBools && value.empty())
static_cast<ConfigOptionBools*>(opt_base)->values.push_back(!no); static_cast<ConfigOptionBools*>(opt_base)->values.push_back(!no);
else else {
// Deserialize any other vector value (ConfigOptionInts, Floats, Percents, Points) the same way // 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 // 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 // will be applied for values enclosed in quotes, while values non-enclosed in quotes are left to be
// unescaped by the calling shell. // 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) { } else if (opt_base->type() == coBool) {
if (value.empty()) if (value.empty())
static_cast<ConfigOptionBool*>(opt_base)->value = !no; static_cast<ConfigOptionBool*>(opt_base)->value = !no;
else else if (! opt_base->deserialize(normalize_cli_bool_value(value))) {
opt_base->deserialize(value); boost::nowide::cerr << "Invalid value for option --" << token.c_str() << std::endl;
return false;
}
} else if (opt_base->type() == coString) { } else if (opt_base->type() == coString) {
// Do not unescape single string values, the unescaping is left to the calling shell. // Do not unescape single string values, the unescaping is left to the calling shell.
static_cast<ConfigOptionString*>(opt_base)->value = value; static_cast<ConfigOptionString*>(opt_base)->value = value;
-9
View File
@@ -33,15 +33,6 @@ public:
SurfaceFeature(const Vec3d& pt) SurfaceFeature(const Vec3d& pt)
: m_type{SurfaceFeatureType::Point}, m_pt1{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) void clone(const SurfaceFeature &sf)
{ {
m_type = sf.get_type(); m_type = sf.get_type();
-1
View File
@@ -39,7 +39,6 @@ namespace orientation {
float height_to_bottom_hull_ratio = 0; // affects stability, the lower the better float height_to_bottom_hull_ratio = 0; // affects stability, the lower the better
float unprintability = 0; float unprintability = 0;
Eigen::VectorXf areas_cooling; Eigen::VectorXf areas_cooling;
CostItems(CostItems const & other) = default;
CostItems() = default; CostItems() = default;
static std::string field_names() { static std::string field_names() {
return " overhang, bottom, bothull, contour, A_laf, A_prj, unprintability"; return " overhang, bottom, bothull, contour, A_laf, A_prj, unprintability";
+1 -1
View File
@@ -2127,7 +2127,7 @@ void PerimeterGenerator::process_no_bridge(Surfaces& all_surfaces, coord_t perim
bridgeable_filtered = union_ex(offset_ex(remaining, perimeter_spacing), bridgeable_filtered); bridgeable_filtered = union_ex(offset_ex(remaining, perimeter_spacing), bridgeable_filtered);
bridgeable_filtered = offset_ex(bridgeable_filtered, -perimeter_spacing); bridgeable_filtered = offset_ex(bridgeable_filtered, -perimeter_spacing);
bridgeable_filtered = diff_ex(bridgeable_filtered, remaining, ApplySafetyOffset::Yes); bridgeable_filtered = diff_ex(bridgeable_filtered, remaining, ApplySafetyOffset::Yes);
bridgeable_filtered = opening_ex(bridgeable_filtered, perimeter_spacing); // filter noise from the diff_ex bridgeable_filtered = opening_ex(bridgeable_filtered, ext_perimeter_width / 2); // filter noise from the diff_ex
bridgeable_filtered = offset_ex(bridgeable_filtered, perimeter_spacing); // restore the size to the original bridgeable area bridgeable_filtered = offset_ex(bridgeable_filtered, perimeter_spacing); // restore the size to the original bridgeable area
// Safety measure: Keep the bridge mask from intruding deeper into the // Safety measure: Keep the bridge mask from intruding deeper into the
// supported anchor region than the explicit anchor overlap. // supported anchor region than the explicit anchor overlap.
-2
View File
@@ -195,7 +195,6 @@ public:
Point(int64_t x, int32_t y) : Vec2crd(coord_t(x), coord_t(y)) {} 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(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(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()))) {} 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 allows you to construct Point from Eigen expressions
// This constructor has to be implicit (non-explicit) to allow implicit conversion 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(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(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(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 Vec2crd& vec2crd, coord_t z = 0) : Vec3crd(vec2crd.x(), vec2crd.y(), z) {}
explicit Point3(const Vec3crd &vec3crd) : Vec3crd(vec3crd) {} explicit Point3(const Vec3crd &vec3crd) : Vec3crd(vec3crd) {}
// This constructor allows you to construct Point from Eigen expressions // This constructor allows you to construct Point from Eigen expressions
+8 -4
View File
@@ -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()) { if (config.has("default_bed_type") && !config.opt_string("default_bed_type").empty()) {
try { try {
std::string str_bed_type = config.opt_string("default_bed_type"); 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()); 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); return BedType(bed_type_value);
} }
else {
BOOST_LOG_TRIVIAL(error) << "default_bed_type: invalid bed type: " << str_bed_type; BOOST_LOG_TRIVIAL(error) << "default_bed_type: invalid bed type: " << str_bed_type;
}
return BedType::btPEI; return BedType::btPEI;
} catch(...) { } catch(...) {
+7 -7
View File
@@ -840,13 +840,12 @@ public:
protected: protected:
PresetCollection() = default; PresetCollection() = default;
// Copy constructor and copy operators are not to be used from outside PresetBundle, // Deleted by the std::recursive_mutex member. PresetBundle copies by assignment.
// as the Profile::vendor points to an instance of VendorProfile stored at parent PresetBundle! PresetCollection(const PresetCollection &other) = delete;
PresetCollection(const PresetCollection &other) = default; //BBS: hand-written because m_mutex cannot be copy-assigned.
//BBS: add operator= logic insteadof default
PresetCollection& operator=(const PresetCollection &other); PresetCollection& operator=(const PresetCollection &other);
// After copying a collection with the default operators above, call this function // Copying leaves every Preset::vendor pointing into the source bundle's vendor map.
// to adjust Profile::vendor pointers. // This re-points them at the matching entries in vendors.
void update_vendor_ptrs_after_copy(const VendorMap &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. // Select a preset, if it exists. If it does not exist, select an invalid (-1) index.
@@ -984,7 +983,8 @@ public:
bool only_default_printers() const; bool only_default_printers() const;
private: private:
PrinterPresetCollection() = default; 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; PrinterPresetCollection& operator=(const PrinterPresetCollection &other) = default;
friend class PresetBundle; friend class PresetBundle;
+10
View File
@@ -2920,6 +2920,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. // 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); 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); 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; CNumericLocalesSetter locales_setter;
// Orca: load from orca_presets // Orca: load from orca_presets
+10 -8
View File
@@ -9865,7 +9865,15 @@ std::string DynamicPrintConfig::get_filament_type(std::string &displayed_filamen
auto* filament_type = dynamic_cast<const ConfigOptionStrings*>(this->option("filament_type")); auto* filament_type = dynamic_cast<const ConfigOptionStrings*>(this->option("filament_type"));
auto* filament_is_support = dynamic_cast<const ConfigOptionBools*>(this->option("filament_is_support")); auto* filament_is_support = dynamic_cast<const ConfigOptionBools*>(this->option("filament_is_support"));
if (!filament_type) // get_at() on an empty vector option is undefined behavior (.front() of an empty vector),
// and e.g. filament_id is never populated on a CLI from-scratch slice - treat an empty
// option the same as a missing one.
if (filament_id && filament_id->values.empty())
filament_id = nullptr;
if (filament_is_support && filament_is_support->values.empty())
filament_is_support = nullptr;
if (!filament_type || filament_type->values.empty())
return ""; return "";
if (!filament_is_support) { if (!filament_is_support) {
@@ -11990,13 +11998,11 @@ CLIActionsConfigDef::CLIActionsConfigDef()
def = this->add("load_defaultfila", coBool); def = this->add("load_defaultfila", coBool);
def->label = L("Load default filaments"); def->label = L("Load default filaments");
def->tooltip = L("Load first filament as default for those not loaded."); def->tooltip = L("Load first filament as default for those not loaded.");
def->cli_params = "option";
def->set_default_value(new ConfigOptionBool(false)); def->set_default_value(new ConfigOptionBool(false));
def = this->add("min_save", coBool); def = this->add("min_save", coBool);
def->label = L("Minimum save"); def->label = L("Minimum save");
def->tooltip = L("Export 3MF with minimum size."); def->tooltip = L("Export 3MF with minimum size.");
def->cli_params = "option";
def->set_default_value(new ConfigOptionBool(false)); def->set_default_value(new ConfigOptionBool(false));
def = this->add("mtcpp", coInt); def = this->add("mtcpp", coInt);
@@ -12022,7 +12028,6 @@ CLIActionsConfigDef::CLIActionsConfigDef()
def = this->add("normative_check", coBool); def = this->add("normative_check", coBool);
def->label = L("Normative check"); def->label = L("Normative check");
def->tooltip = L("Check the normative items."); def->tooltip = L("Check the normative items.");
def->cli_params = "option";
def->set_default_value(new ConfigOptionBool(true)); def->set_default_value(new ConfigOptionBool(true));
/*def = this->add("help_fff", coBool); /*def = this->add("help_fff", coBool);
@@ -12289,7 +12294,7 @@ CLIMiscConfigDef::CLIMiscConfigDef()
def->cli_params = "level"; def->cli_params = "level";
def->set_default_value(new ConfigOptionInt(1)); def->set_default_value(new ConfigOptionInt(1));
def = this->add("logfile", coInt); def = this->add("logfile", coString);
def->label = L("Log file"); def->label = L("Log file");
def->tooltip = L("Redirects debug logging to file.\n"); def->tooltip = L("Redirects debug logging to file.\n");
def->cli_params = "file"; def->cli_params = "file";
@@ -12337,7 +12342,6 @@ CLIMiscConfigDef::CLIMiscConfigDef()
def = this->add("skip_modified_gcodes", coBool); def = this->add("skip_modified_gcodes", coBool);
def->label = L("Skip modified G-code in 3MF"); 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->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->set_default_value(new ConfigOptionBool(false));
def = this->add("makerlab_name", coString); def = this->add("makerlab_name", coString);
@@ -12367,14 +12371,12 @@ CLIMiscConfigDef::CLIMiscConfigDef()
def = this->add("allow_newer_file", coBool); def = this->add("allow_newer_file", coBool);
def->label = L("Allow 3MF with newer version to be sliced"); def->label = L("Allow 3MF with newer version to be sliced");
def->tooltip = 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->set_default_value(new ConfigOptionBool(false));
def = this->add("allow_mix_temp", coBool); def = this->add("allow_mix_temp", coBool);
// internal use only, don't need translation // internal use only, don't need translation
def->label = "Allow filaments with high/low temperature to be printed together"; 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->tooltip = "Allow filaments with high/low temperature to be printed together.";
def->cli_params = "option";
def->set_default_value(new ConfigOptionBool(false)); def->set_default_value(new ConfigOptionBool(false));
} }
-3
View File
@@ -44,9 +44,6 @@ struct DrainHole
: pos(p), normal(n), radius(r), height(h), failed(fl) : 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;
bool operator!=(const DrainHole &sp) const { return !(sp == (*this)); } bool operator!=(const DrainHole &sp) const { return !(sp == (*this)); }
+12 -10
View File
@@ -65,11 +65,13 @@ std::pair<SupportGeneratorLayersPtr, SupportGeneratorLayersPtr> generate_interfa
const bool smooth_supports = support_params.support_style != smsGrid; const bool smooth_supports = support_params.support_style != smsGrid;
SupportGeneratorLayersPtr &interface_layers = base_and_interface_layers.first; SupportGeneratorLayersPtr &interface_layers = base_and_interface_layers.first;
SupportGeneratorLayersPtr &base_interface_layers = base_and_interface_layers.second; SupportGeneratorLayersPtr &base_interface_layers = base_and_interface_layers.second;
// The user-facing interface layer counts include the contact layer. Internally, // Contacts printed separately consume one requested interface layer. Organic
// contact layers are generated separately, so only the remaining layers are // bottom contacts are projection seeds and are not printed separately.
// projected into intermediate interface/base-interface layers here. const bool organic_tree = support_params.support_style == smsTreeOrganic;
const size_t num_top_interface_layers = support_params.has_top_contacts ? support_params.num_top_interface_layers - 1 : 0; const size_t num_top_interface_layers = support_params.has_top_contacts ?
const size_t num_bottom_interface_layers = support_params.has_bottom_contacts ? support_params.num_bottom_interface_layers - 1 : 0; support_params.num_top_interface_layers - 1 : 0;
const size_t num_bottom_interface_layers = support_params.has_bottom_contacts ?
support_params.num_bottom_interface_layers - (organic_tree ? 0 : 1) : 0;
const size_t num_top_base_interface_layers = std::min(support_params.num_top_base_interface_layers, num_top_interface_layers); const size_t num_top_base_interface_layers = std::min(support_params.num_top_base_interface_layers, num_top_interface_layers);
const size_t num_bottom_base_interface_layers = std::min(support_params.num_bottom_base_interface_layers, num_bottom_interface_layers); const size_t num_bottom_base_interface_layers = std::min(support_params.num_bottom_base_interface_layers, num_bottom_interface_layers);
const size_t num_top_interface_layers_only = num_top_interface_layers - num_top_base_interface_layers; const size_t num_top_interface_layers_only = num_top_interface_layers - num_top_base_interface_layers;
@@ -1234,10 +1236,6 @@ static void modulate_extrusion_by_overlapping_layers(
(fragment_end.is_start ? &polyline.points.front() : &polyline.points.back()); (fragment_end.is_start ? &polyline.points.front() : &polyline.points.back());
} }
private: private:
ExtrusionPathFragmentEndPointAccessor& operator=(const ExtrusionPathFragmentEndPointAccessor&) {
return *this;
}
const std::vector<ExtrusionPathFragment> &m_path_fragments; const std::vector<ExtrusionPathFragment> &m_path_fragments;
}; };
const coord_t search_radius = 7; const coord_t search_radius = 7;
@@ -1656,6 +1654,9 @@ void generate_support_toolpaths(
if (top_contact_layer.could_merge(interface_layer) && ! raft_layer) if (top_contact_layer.could_merge(interface_layer) && ! raft_layer)
top_contact_layer.merge(std::move(interface_layer)); top_contact_layer.merge(std::move(interface_layer));
} }
// Orca: Organic bottom contacts are projection seeds, not same-layer toolpaths.
// Do not merge them into another same-layer support region.
if (!organic_tree) {
if (!bottom_interfaces && support_params.can_merge_support_regions) { if (!bottom_interfaces && support_params.can_merge_support_regions) {
if (base_layer.could_merge(bottom_contact_layer)) if (base_layer.could_merge(bottom_contact_layer))
base_layer.merge(std::move(bottom_contact_layer)); base_layer.merge(std::move(bottom_contact_layer));
@@ -1671,7 +1672,7 @@ void generate_support_toolpaths(
bottom_contact_layer.set_polygons_to_extrude( bottom_contact_layer.set_polygons_to_extrude(
diff(bottom_contact_layer.polygons_to_extrude(), top_contact_layer.polygons_to_extrude())); diff(bottom_contact_layer.polygons_to_extrude(), top_contact_layer.polygons_to_extrude()));
} }
} else if (bottom_contact_layer.could_merge(interface_layer) && ! organic_tree) { } else if (bottom_contact_layer.could_merge(interface_layer)) {
const bool interface_layer_is_bottom = interface_layer.layer->layer_type == SupporLayerType::BottomInterface; const bool interface_layer_is_bottom = interface_layer.layer->layer_type == SupporLayerType::BottomInterface;
if (bottom_interfaces && interface_layer_is_bottom) { if (bottom_interfaces && interface_layer_is_bottom) {
bottom_contact_layer.merge(std::move(interface_layer)); bottom_contact_layer.merge(std::move(interface_layer));
@@ -1680,6 +1681,7 @@ void generate_support_toolpaths(
diff(bottom_contact_layer.polygons_to_extrude(), interface_layer.polygons_to_extrude())); diff(bottom_contact_layer.polygons_to_extrude(), interface_layer.polygons_to_extrude()));
} }
} }
}
// Orca: For organic trees the support-material regions are generated from // Orca: For organic trees the support-material regions are generated from
// expanded wall polygons. With zero top Z gap and separate interface material, // expanded wall polygons. With zero top Z gap and separate interface material,
+1 -1
View File
@@ -32,7 +32,7 @@ namespace Slic3r::TreeSupport3D
using namespace std::literals; using namespace std::literals;
// or warning // or warning
// had to use a define beacuse the macro processing inside macro BOOST_LOG_TRIVIAL() // had to use a define because the macro processing inside macro BOOST_LOG_TRIVIAL()
#define error_level_not_in_cache debug #define error_level_not_in_cache debug
//FIXME Machine border is currently ignored. //FIXME Machine border is currently ignored.
+3 -2
View File
@@ -204,8 +204,9 @@ public:
clear_nodes(); clear_nodes();
} }
TreeSupportData(TreeSupportData&&) = default; // Deleted by the tbb::spin_mutex member.
TreeSupportData& operator=(TreeSupportData&&) = default; TreeSupportData(TreeSupportData&&) = delete;
TreeSupportData& operator=(TreeSupportData&&) = delete;
TreeSupportData(const TreeSupportData&) = delete; TreeSupportData(const TreeSupportData&) = delete;
TreeSupportData& operator=(const TreeSupportData&) = delete; TreeSupportData& operator=(const TreeSupportData&) = delete;
+3 -17
View File
@@ -91,29 +91,15 @@ class CaliPresetInfo
{ {
public: public:
int tray_id; int tray_id;
int extruder_id; int extruder_id = 0;
NozzleVolumeType nozzle_volume_type; NozzleVolumeType nozzle_volume_type{nvtStandard};
BedType bed_type; BedType bed_type{btDefault};
float nozzle_diameter; float nozzle_diameter;
int nozzle_pos_id{-1}; int nozzle_pos_id{-1};
std::string nozzle_sn; std::string nozzle_sn;
std::string filament_id; std::string filament_id;
std::string setting_id; std::string setting_id;
std::string name; 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 struct PrinterCaliInfo
-3
View File
@@ -5,9 +5,6 @@
#define SLIC3R_APP_KEY "@SLIC3R_APP_KEY@" #define SLIC3R_APP_KEY "@SLIC3R_APP_KEY@"
#define SLIC3R_VERSION "@SLIC3R_VERSION@" #define SLIC3R_VERSION "@SLIC3R_VERSION@"
#define SoftFever_VERSION "@SoftFever_VERSION@" #define SoftFever_VERSION "@SoftFever_VERSION@"
#ifndef GIT_COMMIT_HASH
#define GIT_COMMIT_HASH "0000000" // 0000000 means uninitialized
#endif
#define SLIC3R_BUILD_ID "@SLIC3R_BUILD_ID@" #define SLIC3R_BUILD_ID "@SLIC3R_BUILD_ID@"
//#define SLIC3R_RC_VERSION "@SLIC3R_VERSION@" //#define SLIC3R_RC_VERSION "@SLIC3R_VERSION@"
#define BBL_INTERNAL_TESTING @BBL_INTERNAL_TESTING@ #define BBL_INTERNAL_TESTING @BBL_INTERNAL_TESTING@
+5 -1
View File
@@ -310,7 +310,11 @@ void set_data_dir(const std::string &dir)
{ {
g_data_dir = dir; g_data_dir = dir;
if (!g_data_dir.empty() && !boost::filesystem::exists(g_data_dir)) { if (!g_data_dir.empty() && !boost::filesystem::exists(g_data_dir)) {
boost::filesystem::create_directory(g_data_dir); try {
boost::filesystem::create_directories(g_data_dir);
} catch (const boost::filesystem::filesystem_error &ex) {
BOOST_LOG_TRIVIAL(error) << "set_data_dir: failed to create data directory " << g_data_dir << ": " << ex.what();
}
} }
} }
+14
View File
@@ -63,6 +63,8 @@ set(SLIC3R_GUI_SOURCES
GUI/BitmapComboBox.hpp GUI/BitmapComboBox.hpp
GUI/BonjourDialog.cpp GUI/BonjourDialog.cpp
GUI/BonjourDialog.hpp GUI/BonjourDialog.hpp
GUI/BuildCommit.cpp
GUI/BuildCommit.hpp
GUI/CrealityDiscoveryDialog.cpp GUI/CrealityDiscoveryDialog.cpp
GUI/CrealityDiscoveryDialog.hpp GUI/CrealityDiscoveryDialog.hpp
GUI/calib_dlg.cpp GUI/calib_dlg.cpp
@@ -845,6 +847,18 @@ source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SLIC3R_GUI_SOURCES})
encoding_check(libslic3r_gui) encoding_check(libslic3r_gui)
# Only BuildCommit.cpp includes the generated header, plus BaseException.cpp on
# Windows. Both build into libslic3r_gui, so the header only has to exist before
# that target builds.
set(_git_commit_hash_header "${CMAKE_CURRENT_BINARY_DIR}/git_commit_hash.h")
add_custom_target(git_commit_hash_header
BYPRODUCTS "${_git_commit_hash_header}"
COMMAND ${CMAKE_COMMAND}
"-DSOURCE_DIR=${CMAKE_SOURCE_DIR}"
"-DOUT_FILE=${_git_commit_hash_header}"
-P "${CMAKE_CURRENT_LIST_DIR}/GitCommitHash.cmake"
COMMENT "Resolving the git commit hash")
add_dependencies(libslic3r_gui git_commit_hash_header)
if(APPLE AND CMAKE_VERSION VERSION_GREATER_EQUAL "4.0") if(APPLE AND CMAKE_VERSION VERSION_GREATER_EQUAL "4.0")
set(_opengl_link_lib "") set(_opengl_link_lib "")
+2 -1
View File
@@ -3,6 +3,7 @@
#include "libslic3r/Utils.hpp" #include "libslic3r/Utils.hpp"
#include "libslic3r/Color.hpp" #include "libslic3r/Color.hpp"
#include "BuildCommit.hpp"
#include "GUI.hpp" #include "GUI.hpp"
#include "GUI_App.hpp" #include "GUI_App.hpp"
#include "MainFrame.hpp" #include "MainFrame.hpp"
@@ -245,7 +246,7 @@ AboutDialog::AboutDialog()
vesizer->Add(0, 0, 1, wxEXPAND, FromDIP(5)); vesizer->Add(0, 0, 1, wxEXPAND, FromDIP(5));
auto version_string = std::string(SoftFever_VERSION); // _L("Orca Slicer ") + " " + std::string(SoftFever_VERSION); auto version_string = std::string(SoftFever_VERSION); // _L("Orca Slicer ") + " " + std::string(SoftFever_VERSION);
wxStaticText* version = new wxStaticText(this, wxID_ANY, version_string.c_str(), wxDefaultPosition, wxDefaultSize); wxStaticText* version = new wxStaticText(this, wxID_ANY, version_string.c_str(), wxDefaultPosition, wxDefaultSize);
wxStaticText* credits_string = new wxStaticText(this, wxID_ANY, wxString::Format("Build %s", std::string(GIT_COMMIT_HASH)), wxDefaultPosition, wxDefaultSize); wxStaticText* credits_string = new wxStaticText(this, wxID_ANY, wxString::Format("Build %s", build_commit_label), wxDefaultPosition, wxDefaultSize);
credits_string->SetFont(_build_string_font); credits_string->SetFont(_build_string_font);
wxFont version_font = GetFont(); wxFont version_font = GetFont();
version_font = version_font.Scaled(1.85f); // SetPointSize(20) not works on macOS because it uses a 72 PPI reference version_font = version_font.Scaled(1.85f); // SetPointSize(20) not works on macOS because it uses a 72 PPI reference
+9
View File
@@ -0,0 +1,9 @@
#include "BuildCommit.hpp"
#include "git_commit_hash.h"
namespace Slic3r { namespace GUI {
const char *const build_commit_hash = GIT_COMMIT_HASH;
const char *const build_commit_label = GIT_COMMIT_HASH GIT_COMMIT_SUFFIX;
}} // namespace Slic3r::GUI
+15
View File
@@ -0,0 +1,15 @@
#pragma once
// Read these rather than including git_commit_hash.h, which changes with every
// commit and rebuilds everything that includes it.
namespace Slic3r { namespace GUI {
// The commit alone, safe to use in a commit URL.
extern const char *const build_commit_hash;
// The same, with "-dirty" when the build had uncommitted changes. Use this
// wherever the build is shown to a person.
extern const char *const build_commit_label;
}} // namespace Slic3r::GUI
+2 -1
View File
@@ -9,6 +9,7 @@
#include "slic3r/GUI/TaskManager.hpp" #include "slic3r/GUI/TaskManager.hpp"
#include "format.hpp" #include "format.hpp"
#include "libslic3r_version.h" #include "libslic3r_version.h"
#include "BuildCommit.hpp"
#include "Downloader.hpp" #include "Downloader.hpp"
#include <boost/chrono/duration.hpp> #include <boost/chrono/duration.hpp>
#include <boost/log/detail/native_typeof.hpp> #include <boost/log/detail/native_typeof.hpp>
@@ -2580,7 +2581,7 @@ void GUI_App::init_app_config()
set_log_path_and_level(log_filename, 3); set_log_path_and_level(log_filename, 3);
#endif #endif
BOOST_LOG_TRIVIAL(info) << boost::format("gui mode, Current OrcaSlicer Version %1% build %2%") % SoftFever_VERSION % GIT_COMMIT_HASH; BOOST_LOG_TRIVIAL(info) << boost::format("gui mode, Current OrcaSlicer Version %1% build %2%") % SoftFever_VERSION % build_commit_label;
//BBS: remove GCodeViewer as seperate APP logic //BBS: remove GCodeViewer as seperate APP logic
if (!app_config) if (!app_config)
+3 -3
View File
@@ -494,13 +494,13 @@ void MediaPlayCtrl::ToggleStream()
DownloadProgressDialog2(MediaPlayCtrl *ctrl) : DownloadProgressDialog(_L("Downloading Virtual Camera Tools")), ctrl(ctrl) {} DownloadProgressDialog2(MediaPlayCtrl *ctrl) : DownloadProgressDialog(_L("Downloading Virtual Camera Tools")), ctrl(ctrl) {}
struct UpgradeNetworkJob2 : UpgradeNetworkJob struct UpgradeNetworkJob2 : UpgradeNetworkJob
{ {
UpgradeNetworkJob2(std::shared_ptr<ProgressIndicator> pri) : UpgradeNetworkJob() { UpgradeNetworkJob2() {
name = "cameratools"; name = "cameratools";
package_name = "camera_tools.zip"; package_name = "camera_tools.zip";
} }
}; };
std::shared_ptr<UpgradeNetworkJob> make_job(std::shared_ptr<ProgressIndicator> pri) std::unique_ptr<UpgradeNetworkJob> make_job() override
{ return std::make_shared<UpgradeNetworkJob2>(pri); } { return std::make_unique<UpgradeNetworkJob2>(); }
void on_finish() override void on_finish() override
{ {
ctrl->CallAfter([ctrl = this->ctrl] { ctrl->ToggleStream(); }); ctrl->CallAfter([ctrl = this->ctrl] { ctrl->ToggleStream(); });
+10 -11
View File
@@ -157,7 +157,7 @@ PartPlate::PartPlate()
init(); init();
} }
PartPlate::PartPlate(PartPlateList *partplate_list, Vec3d origin, int width, int depth, int height, Plater* platerObj, Model* modelObj, bool printable, PrinterTechnology tech) PartPlate::PartPlate(PartPlateList *partplate_list, Vec3d origin, int width, int depth, double height, Plater* platerObj, Model* modelObj, bool printable, PrinterTechnology tech)
:m_partplate_list(partplate_list), m_plater(platerObj), m_model(modelObj), printer_technology(tech), m_origin(origin), m_width(width), m_depth(depth), m_height(height), m_printable(printable) :m_partplate_list(partplate_list), m_plater(platerObj), m_model(modelObj), printer_technology(tech), m_origin(origin), m_width(width), m_depth(depth), m_height(height), m_printable(printable)
{ {
init(); init();
@@ -1757,9 +1757,7 @@ std::vector<int> PartPlate::get_extruders_under_cli(bool conside_custom_gcode, D
else else
obj_support = glb_support; obj_support = glb_support;
if (!obj_support) if (obj_support) {
continue;
int obj_support_intf_extr = 0; int obj_support_intf_extr = 0;
const ConfigOption* support_intf_extr_opt = object->config.option("support_interface_filament"); const ConfigOption* support_intf_extr_opt = object->config.option("support_interface_filament");
if (support_intf_extr_opt != nullptr) if (support_intf_extr_opt != nullptr)
@@ -1777,6 +1775,7 @@ std::vector<int> PartPlate::get_extruders_under_cli(bool conside_custom_gcode, D
plate_extruders.push_back(obj_support_extr); plate_extruders.push_back(obj_support_extr);
else if (glb_support_extr != 0) else if (glb_support_extr != 0)
plate_extruders.push_back(glb_support_extr); plate_extruders.push_back(glb_support_extr);
}
int obj_outer_wall_extr = 0; int obj_outer_wall_extr = 0;
if (const ConfigOption* wall_opt = object->config.option("outer_wall_filament_id"); wall_opt != nullptr) if (const ConfigOption* wall_opt = object->config.option("outer_wall_filament_id"); wall_opt != nullptr)
@@ -2473,7 +2472,7 @@ void PartPlate::clear(bool clear_sliced_result)
/* size and position related functions*/ /* size and position related functions*/
//set position and size //set position and size
void PartPlate::set_pos_and_size(Vec3d& origin, int width, int depth, int height, bool with_instance_move, bool do_clear) void PartPlate::set_pos_and_size(Vec3d& origin, int width, int depth, double height, bool with_instance_move, bool do_clear)
{ {
bool size_changed = false; //size changed means the machine changed bool size_changed = false; //size changed means the machine changed
bool pos_changed = false; bool pos_changed = false;
@@ -2772,10 +2771,10 @@ bool PartPlate::check_outside(int obj_id, int instance_id, BoundingBoxf3* boundi
if (instance_box.min.z() < SINKING_Z_THRESHOLD) { if (instance_box.min.z() < SINKING_Z_THRESHOLD) {
// Orca: For sinking object, we use a more expensive algorithm so part below build plate won't be considered // Orca: For sinking object, we use a more expensive algorithm so part below build plate won't be considered
// m_plater is null in CLI mode. // m_height mirrors the printer's printable height and is set in CLI mode too, unlike m_plater.
if (m_plater && plate_box.intersects(instance_box)) { if (plate_box.intersects(instance_box)) {
// TODO: FIXME: this does not take exclusion area into account // TODO: FIXME: this does not take exclusion area into account
const BuildVolume build_volume(get_shape(), m_plater->build_volume().printable_height(), m_extruder_areas, m_extruder_heights); const BuildVolume build_volume(get_shape(), m_height, m_extruder_areas, m_extruder_heights);
const auto state = instance->calc_print_volume_state(build_volume); const auto state = instance->calc_print_volume_state(build_volume);
outside = state == ModelInstancePVS_Partly_Outside; outside = state == ModelInstancePVS_Partly_Outside;
} }
@@ -4099,7 +4098,7 @@ void PartPlate::on_filament_deleted(int filament_count, int filament_id)
/* PartPlate List related functions*/ /* PartPlate List related functions*/
PartPlateList::PartPlateList(int width, int depth, int height, Plater* platerObj, Model* modelObj, PrinterTechnology tech) PartPlateList::PartPlateList(int width, int depth, double height, Plater* platerObj, Model* modelObj, PrinterTechnology tech)
:m_plate_width(width), m_plate_depth(depth), m_plate_height(height), m_plater(platerObj), m_model(modelObj), printer_technology(tech), :m_plate_width(width), m_plate_depth(depth), m_plate_height(height), m_plater(platerObj), m_model(modelObj), printer_technology(tech),
unprintable_plate(this, Vec3d(0.0 + width * (1. + LOGICAL_PART_PLATE_GAP), 0.0, 0.0), width, depth, height, platerObj, modelObj, false, tech) unprintable_plate(this, Vec3d(0.0 + width * (1. + LOGICAL_PART_PLATE_GAP), 0.0, 0.0), width, depth, height, platerObj, modelObj, false, tech)
{ {
@@ -4544,7 +4543,7 @@ void PartPlateList::set_default_wipe_tower_pos_for_plate(int plate_idx, bool ini
} }
//this may be happened after machine changed //this may be happened after machine changed
void PartPlateList::reset_size(int width, int depth, int height, bool reload_objects, bool update_shapes) void PartPlateList::reset_size(int width, int depth, double height, bool reload_objects, bool update_shapes)
{ {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(":before size: plate_width %1%, plate_depth %2%, plate_height %3%") % m_plate_width % m_plate_depth % m_plate_height; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(":before size: plate_width %1%, plate_depth %2%, plate_height %3%") % m_plate_width % m_plate_depth % m_plate_height;
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(":after size: plate_width %1%, plate_depth %2%, plate_height %3%") % width % depth % height; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(":after size: plate_width %1%, plate_depth %2%, plate_height %3%") % width % depth % height;
@@ -6699,7 +6698,7 @@ int PartPlateList::load_gcode_files()
//BoundingBoxf3 print_volume = m_plate_list[i]->get_bounding_box(false); //BoundingBoxf3 print_volume = m_plate_list[i]->get_bounding_box(false);
//print_volume.max(2) = this->m_plate_height; //print_volume.max(2) = this->m_plate_height;
//print_volume.min(2) = -1e10; //print_volume.min(2) = -1e10;
m_model->update_print_volume_state({m_plate_list[i]->get_shape(), (double)this->m_plate_height, m_plate_list[i]->get_extruder_areas(), m_plate_list[i]->get_extruder_heights() }); m_model->update_print_volume_state({m_plate_list[i]->get_shape(), this->m_plate_height, m_plate_list[i]->get_extruder_areas(), m_plate_list[i]->get_extruder_heights() });
if (!m_plate_list[i]->load_gcode_from_file(m_plate_list[i]->m_gcode_path_from_3mf)) if (!m_plate_list[i]->load_gcode_from_file(m_plate_list[i]->m_gcode_path_from_3mf))
ret ++; ret ++;
+7 -17
View File
@@ -96,7 +96,7 @@ private:
Vec3d m_origin; Vec3d m_origin;
int m_width; int m_width;
int m_depth; int m_depth;
int m_height; double m_height;
float m_height_to_lid; float m_height_to_lid;
float m_height_to_rod; float m_height_to_rod;
bool m_printable; bool m_printable;
@@ -227,7 +227,7 @@ public:
static void load_render_colors(); static void load_render_colors();
PartPlate(); PartPlate();
PartPlate(PartPlateList *partplate_list, Vec3d origin, int width, int depth, int height, Plater* platerObj, Model* modelObj, bool printable=true, PrinterTechnology tech = ptFFF); PartPlate(PartPlateList *partplate_list, Vec3d origin, int width, int depth, double height, Plater* platerObj, Model* modelObj, bool printable=true, PrinterTechnology tech = ptFFF);
~PartPlate(); ~PartPlate();
bool operator<(PartPlate&) const; bool operator<(PartPlate&) const;
@@ -328,7 +328,7 @@ public:
Vec3d get_center_origin(); Vec3d get_center_origin();
/* size and position related functions*/ /* size and position related functions*/
//set position and size //set position and size
void set_pos_and_size(Vec3d& origin, int width, int depth, int height, bool with_instance_move, bool do_clear = true); void set_pos_and_size(Vec3d& origin, int width, int depth, double height, bool with_instance_move, bool do_clear = true);
// BBS // BBS
Vec2d get_size() const { return Vec2d(m_width, m_depth); } Vec2d get_size() const { return Vec2d(m_width, m_depth); }
@@ -590,7 +590,7 @@ class PartPlateList : public ObjectBase
int m_plate_width; int m_plate_width;
int m_plate_depth; int m_plate_depth;
int m_plate_height; double m_plate_height;
float m_height_to_lid; float m_height_to_lid;
float m_height_to_rod; float m_height_to_rod;
@@ -675,16 +675,6 @@ public:
offset = Vec2d(0, 0); 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) { void update_pos(float xx, float yy, float ww, float hh) {
x = xx; x = xx;
y = yy; y = yy;
@@ -708,12 +698,12 @@ public:
static bool is_load_cali_texture; static bool is_load_cali_texture;
static bool is_load_extruder_only_area_textures; static bool is_load_extruder_only_area_textures;
PartPlateList(int width, int depth, int height, Plater* platerObj, Model* modelObj, PrinterTechnology tech = ptFFF); PartPlateList(int width, int depth, double height, Plater* platerObj, Model* modelObj, PrinterTechnology tech = ptFFF);
PartPlateList(Plater* platerObj, Model* modelObj, PrinterTechnology tech = ptFFF); PartPlateList(Plater* platerObj, Model* modelObj, PrinterTechnology tech = ptFFF);
~PartPlateList(); ~PartPlateList();
//this may be happened after machine changed //this may be happened after machine changed
void reset_size(int width, int depth, int height, bool reload_objects = true, bool update_shapes = false); void reset_size(int width, int depth, double height, bool reload_objects = true, bool update_shapes = false);
//clear all the instances in the plate, but keep the plates //clear all the instances in the plate, but keep the plates
void clear(bool delete_plates = false, bool release_print_list = false, bool except_locked = false, int plate_index = -1); void clear(bool delete_plates = false, bool release_print_list = false, bool except_locked = false, int plate_index = -1);
//clear all the instances in the plate, and delete the plates, only keep the first default plate //clear all the instances in the plate, and delete the plates, only keep the first default plate
@@ -727,7 +717,7 @@ public:
//get the plate stride //get the plate stride
double plate_stride_x(); double plate_stride_x();
double plate_stride_y(); double plate_stride_y();
void get_plate_size(int& width, int& depth, int& height) { void get_plate_size(int& width, int& depth, double& height) {
width = m_plate_width; width = m_plate_width;
depth = m_plate_depth; depth = m_plate_depth;
height = m_plate_height; height = m_plate_height;
+7 -5
View File
@@ -3988,17 +3988,17 @@ void Sidebar::update_mixed_filament_list()
p->m_panel_mixed_warning->Show(false); p->m_panel_mixed_warning->Show(false);
// Show/dismiss 3D canvas notification for broken mixed filaments // Show/dismiss 3D canvas notification for broken mixed filaments
auto* notify = plater->get_notification_manager();
GLCanvas3D* view3d_canvas = plater->get_view3D_canvas3D();
if(view3d_canvas && view3d_canvas->is_initialized() && notify){
if (has_mixed && !broken_set.empty()) { if (has_mixed && !broken_set.empty()) {
auto* notify = wxGetApp().plater()->get_notification_manager();
if (notify)
notify->push_notification(NotificationType::BBLMixedFilamentBroken, notify->push_notification(NotificationType::BBLMixedFilamentBroken,
NotificationManager::NotificationLevel::ErrorNotificationLevel, NotificationManager::NotificationLevel::ErrorNotificationLevel,
_u8L("Mixed filament has invalid or mismatched components. Please re-edit affected entries.")); _u8L("Mixed filament has invalid or mismatched components. Please re-edit affected entries."));
} else { } else {
auto* notify = wxGetApp().plater()->get_notification_manager();
if (notify)
notify->close_notification_of_type(NotificationType::BBLMixedFilamentBroken); notify->close_notification_of_type(NotificationType::BBLMixedFilamentBroken);
} }
}
if (has_mixed) { if (has_mixed) {
auto* left_col = p->m_sizer_mixed_filaments->GetItem(size_t(0))->GetSizer(); auto* left_col = p->m_sizer_mixed_filaments->GetItem(size_t(0))->GetSizer();
@@ -8278,7 +8278,8 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
bool dlg_cont = true; bool dlg_cont = true;
bool is_user_cancel = false; bool is_user_cancel = false;
bool translate_old = false; bool translate_old = false;
int current_width = 0, current_depth = 0, current_height = 0, project_filament_count = 1; int current_width = 0, current_depth = 0, project_filament_count = 1;
double current_height = 0;
if (input_files.empty()) if (input_files.empty())
return std::vector<size_t>(); return std::vector<size_t>();
@@ -16272,6 +16273,7 @@ void Plater::calib_retraction(const Calib_Params& params)
obj->config.set_key_value("wall_sequence", new ConfigOptionEnum<WallSequence>(WallSequence::InnerOuter)); obj->config.set_key_value("wall_sequence", new ConfigOptionEnum<WallSequence>(WallSequence::InnerOuter));
obj->config.set_key_value("overhang_reverse", new ConfigOptionBool(false)); obj->config.set_key_value("overhang_reverse", new ConfigOptionBool(false));
obj->config.set_key_value("precise_z_height", new ConfigOptionBool(false)); obj->config.set_key_value("precise_z_height", new ConfigOptionBool(false));
obj->config.set_key_value("seam_slope_type", new ConfigOptionEnum<SeamScarfType>(SeamScarfType::None));
changed_objects({ 0 }); changed_objects({ 0 });
+4 -1
View File
@@ -149,7 +149,10 @@ SavePresetDialog::Item::Item(Preset::Type type, const std::string &suffix, wxBox
// Set initial state (unchecked by default) // Set initial state (unchecked by default)
detach_checkbox->SetValue(m_detach); detach_checkbox->SetValue(m_detach);
// Bind the checkbox event to update the detach state for this item // 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")); detach_label->SetForegroundColour(wxColour("#363636"));
+1 -1
View File
@@ -5130,7 +5130,7 @@ void TabPrinter::build_fff()
optgroup->append_single_option_line("adaptive_bed_mesh_margin", "printer_basic_information_adaptive_bed_mesh#mesh-margin"); 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 = 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("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("auxiliary_fan", "printer_basic_information_accessory#auxiliary-part-cooling-fan");
optgroup->append_single_option_line("fan_direction"); optgroup->append_single_option_line("fan_direction");
+4 -3
View File
@@ -1,6 +1,7 @@
#include "TroubleshootDialog.hpp" #include "TroubleshootDialog.hpp"
#include "I18N.hpp" #include "I18N.hpp"
#include "BuildCommit.hpp"
#include "GUI.hpp" #include "GUI.hpp"
#include "GUI_App.hpp" #include "GUI_App.hpp"
#include "MainFrame.hpp" #include "MainFrame.hpp"
@@ -137,9 +138,9 @@ TroubleshootDialog::TroubleshootDialog()
version->SetFont(version_font); version->SetFont(version_font);
version->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#363636"))); version->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#363636")));
auto build = new Button(this, wxString(GIT_COMMIT_HASH)); auto build = new Button(this, wxString(build_commit_label));
build->SetStyle(ButtonStyle::Regular, ButtonType::Window); build->SetStyle(ButtonStyle::Regular, ButtonType::Window);
auto hash_url = "https://github.com/OrcaSlicer/OrcaSlicer/commit/" + wxString(GIT_COMMIT_HASH); auto hash_url = "https://github.com/OrcaSlicer/OrcaSlicer/commit/" + wxString(build_commit_hash);
build->SetToolTip(hash_url); build->SetToolTip(hash_url);
build->Bind(wxEVT_BUTTON, [hash_url](wxCommandEvent &e) { build->Bind(wxEVT_BUTTON, [hash_url](wxCommandEvent &e) {
wxLaunchDefaultBrowser(hash_url); wxLaunchDefaultBrowser(hash_url);
@@ -371,7 +372,7 @@ wxString TroubleshootDialog::GetSysInfoAll()
{ {
wxString info; wxString info;
info += "Version : " + wxString(SoftFever_VERSION) + "\n" info += "Version : " + wxString(SoftFever_VERSION) + "\n"
+ "Build : " + wxString(GIT_COMMIT_HASH) + "\n" + "Build : " + wxString(build_commit_label) + "\n"
+ "Package : " + GetPackageType() + "\n" + "Package : " + GetPackageType() + "\n"
+ "Platform : " + GetOSinfo() + "\n" + "Platform : " + GetOSinfo() + "\n"
+ "Processor : " + GetCPUinfo() + "\n" + "Processor : " + GetCPUinfo() + "\n"
+51
View File
@@ -0,0 +1,51 @@
# Writes GIT_COMMIT_HASH and GIT_COMMIT_SUFFIX into a generated header.
# GIT_COMMIT_SUFFIX is "-dirty" for a build with uncommitted changes, and empty
# otherwise.
#
# A custom target runs this at the start of every build, which picks up a new
# commit without a reconfigure. The header is rewritten only when the value
# changes.
#
# Inputs: SOURCE_DIR, OUT_FILE.
find_package(Git QUIET)
set(HASH "")
set(SUFFIX "")
if (DEFINED ENV{git_commit_hash} AND NOT "$ENV{git_commit_hash}" STREQUAL "")
if (GIT_FOUND AND EXISTS "${SOURCE_DIR}/.git")
execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --short "$ENV{git_commit_hash}"
WORKING_DIRECTORY ${SOURCE_DIR} OUTPUT_VARIABLE HASH OUTPUT_STRIP_TRAILING_WHITESPACE)
else ()
# No .git directory (e.g. Flatpak sandbox) - truncate directly
string(SUBSTRING "$ENV{git_commit_hash}" 0 7 HASH)
endif ()
elseif (GIT_FOUND AND EXISTS "${SOURCE_DIR}/.git")
execute_process(COMMAND ${GIT_EXECUTABLE} log -1 --format=%h
WORKING_DIRECTORY ${SOURCE_DIR} OUTPUT_VARIABLE HASH OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${GIT_EXECUTABLE} diff --quiet HEAD
WORKING_DIRECTORY ${SOURCE_DIR} RESULT_VARIABLE DIRTY ERROR_QUIET)
if (DIRTY EQUAL 1)
set(SUFFIX "-dirty")
endif ()
endif ()
if (NOT HASH)
set(HASH "0000000") # uninitialized
endif ()
message(STATUS "Build commit: ${HASH}${SUFFIX}")
string(CONCAT CONTENT
"#pragma once\n"
"#define GIT_COMMIT_HASH \"${HASH}\"\n"
"#define GIT_COMMIT_SUFFIX \"${SUFFIX}\"\n")
set(OLD "")
if (EXISTS "${OUT_FILE}")
file(READ "${OUT_FILE}" OLD)
endif ()
if (NOT OLD STREQUAL CONTENT)
file(WRITE "${OUT_FILE}" "${CONTENT}")
endif ()
+2 -1
View File
@@ -1,7 +1,8 @@
#ifndef slic3r_PluginAuditManager_hpp_ #ifndef slic3r_PluginAuditManager_hpp_
#define slic3r_PluginAuditManager_hpp_ #define slic3r_PluginAuditManager_hpp_
#include <Python.h> // Via pybind11 so this file requests the same python3xx.lib as everything else.
#include <pybind11/conduit/wrap_include_python_h.h>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <string> #include <string>
+2 -1
View File
@@ -1,7 +1,8 @@
#ifndef slic3r_PythonInterpreter_hpp_ #ifndef slic3r_PythonInterpreter_hpp_
#define slic3r_PythonInterpreter_hpp_ #define slic3r_PythonInterpreter_hpp_
#include <Python.h> // Via pybind11 so this file requests the same python3xx.lib as everything else.
#include <pybind11/conduit/wrap_include_python_h.h>
#include <pytypedefs.h> #include <pytypedefs.h>
#include <atomic> #include <atomic>
#include <functional> #include <functional>
+1
View File
@@ -8,6 +8,7 @@ add_executable(${_TEST_NAME}_tests
test_arachne_walls.cpp test_arachne_walls.cpp
test_arrange.cpp test_arrange.cpp
test_bambu_networking.cpp test_bambu_networking.cpp
test_buildvolume.cpp
test_calib.cpp test_calib.cpp
test_clipper_offset.cpp test_clipper_offset.cpp
test_clipper_utils.cpp test_clipper_utils.cpp
+40
View File
@@ -0,0 +1,40 @@
#include <catch2/catch_all.hpp>
#include "libslic3r/BuildVolume.hpp"
using namespace Slic3r;
static std::vector<Vec2d> rect_area(double w, double d)
{
return { { 0., 0. }, { w, 0. }, { w, d }, { 0., d } };
}
// extruder_printable_height and extruder_printable_area are independent config options, so a
// profile can leave the heights short. BuildVolume must not index past the end of the heights.
TEST_CASE("BuildVolume falls back to the bed height when extruder_printable_height is short", "[BuildVolume]")
{
const std::vector<Vec2d> bed = rect_area(200., 200.);
const std::vector<std::vector<Vec2d>> areas = { rect_area(200., 200.), rect_area(100., 200.) };
const std::vector<double> heights = { 180. };
const BuildVolume build_volume(bed, 250., areas, heights);
REQUIRE(build_volume.get_extruder_area_count() == 2);
// The extruder with a height of its own keeps it, and differs from the bed, so it gets its own volume.
CHECK_THAT(build_volume.get_extruder_area_volume(0).bboxf.max.z(), Catch::Matchers::WithinAbs(180., 1e-6));
// The extruder without one falls back to the bed's printable_height instead of reading out of range.
CHECK_THAT(build_volume.get_extruder_area_volume(1).bboxf.max.z(), Catch::Matchers::WithinAbs(250., 1e-6));
}
TEST_CASE("BuildVolume keeps per-extruder heights when both vectors match", "[BuildVolume]")
{
const std::vector<Vec2d> bed = rect_area(200., 200.);
const std::vector<std::vector<Vec2d>> areas = { rect_area(120., 200.), rect_area(100., 200.) };
const std::vector<double> heights = { 180., 200.5 };
const BuildVolume build_volume(bed, 250., areas, heights);
REQUIRE(build_volume.get_extruder_area_count() == 2);
CHECK_THAT(build_volume.get_extruder_area_volume(0).bboxf.max.z(), Catch::Matchers::WithinAbs(180., 1e-6));
CHECK_THAT(build_volume.get_extruder_area_volume(1).bboxf.max.z(), Catch::Matchers::WithinAbs(200.5, 1e-6));
}
+263
View File
@@ -828,3 +828,266 @@ 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));
}
// get_at() returns values.front() for an out-of-range index, so calling it on an empty vector
// option is UB. filament_id and filament_is_support are unpopulated on a CLI from-scratch slice.
TEST_CASE("get_filament_type treats empty vector options as absent", "[Config][Filament]")
{
DynamicPrintConfig config;
std::string displayed;
SECTION("an empty filament_type yields no type at all")
{
config.set_key_value("filament_type", new ConfigOptionStrings());
REQUIRE(config.get_filament_type(displayed, 0) == "");
}
SECTION("an empty filament_is_support falls back to the plain filament type")
{
config.set_key_value("filament_type", new ConfigOptionStrings({"PETG"}));
config.set_key_value("filament_is_support", new ConfigOptionBools());
REQUIRE(config.get_filament_type(displayed, 0) == "PETG");
REQUIRE(displayed == "PETG");
}
SECTION("a support filament with an empty filament_id resolves from the type alone")
{
config.set_key_value("filament_type", new ConfigOptionStrings({"PLA"}));
config.set_key_value("filament_is_support", new ConfigOptionBools({true}));
config.set_key_value("filament_id", new ConfigOptionStrings());
REQUIRE(config.get_filament_type(displayed, 0) == "PLA-S");
REQUIRE(displayed == "Sup.PLA");
}
SECTION("a populated filament_id still selects the support type by id")
{
config.set_key_value("filament_type", new ConfigOptionStrings({"PETG"}));
config.set_key_value("filament_is_support", new ConfigOptionBools({true}));
config.set_key_value("filament_id", new ConfigOptionStrings({"GFS00"}));
REQUIRE(config.get_filament_type(displayed, 0) == "PLA-S");
REQUIRE(displayed == "Sup.PLA");
}
}
@@ -185,6 +185,44 @@ TEST_CASE("Printer extruder count tolerates missing nozzle diameter", "[Preset][
CHECK(bundle.get_printer_extruder_count() == 2); 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]") TEST_CASE("find_preset resolves a system preset's renamed_from", "[Preset][Rename]")
{ {
RenameTestCollection coll; RenameTestCollection coll;