mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-13 20:17:49 +00:00
Compare commits
2 Commits
hanif/belt
...
feat/plate
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b8e0f9cdfb | ||
|
|
790a010615 |
2
.github/workflows/build_deps.yml
vendored
2
.github/workflows/build_deps.yml
vendored
@@ -149,7 +149,7 @@ jobs:
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
if [ -z "${{ vars.SELF_HOSTED }}" ]; then
|
||||
brew install automake texinfo libtool pkgconf yasm nasm
|
||||
brew install automake texinfo libtool
|
||||
fi
|
||||
./build_release_macos.sh -dx ${{ !vars.SELF_HOSTED && '-1' || '' }} -a ${{ inputs.arch }} -t 10.15
|
||||
(cd "${{ github.workspace }}/deps/build/${{ inputs.arch }}" && \
|
||||
|
||||
7
.github/workflows/build_orca.yml
vendored
7
.github/workflows/build_orca.yml
vendored
@@ -385,13 +385,6 @@ jobs:
|
||||
dir "C:/Program Files (x86)/Windows Kits/10/Include"
|
||||
choco install nsis
|
||||
|
||||
- name: Install pkg-config
|
||||
# FFmpeg is discovered via pkg-config (pkg_check_modules LIBAV in
|
||||
# src/slic3r/CMakeLists.txt); the Windows runners don't ship it.
|
||||
if: runner.os == 'Windows' && !vars.SELF_HOSTED
|
||||
run: |
|
||||
choco install pkgconfiglite -y
|
||||
|
||||
- name: Build slicer Win
|
||||
if: runner.os == 'Windows'
|
||||
working-directory: ${{ github.workspace }}
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,7 +2,6 @@ Build
|
||||
Build.bat
|
||||
/build*/
|
||||
CMakeLists.txt.user
|
||||
CMakeUserPresets.json
|
||||
**/CMakeLists.txt.autosave
|
||||
deps/build*
|
||||
MYMETA.json
|
||||
|
||||
@@ -15,11 +15,6 @@ cmake --build build --config RelWithDebInfo --target all --
|
||||
cmake --build . --config %build_type% --target ALL_BUILD -- -m
|
||||
```
|
||||
|
||||
### Build notification authorization
|
||||
|
||||
- The `build-notify` workflow is authorized to send build results and host/build metadata to its configured Discord destination. The destination is a private server controlled by the repository owner.
|
||||
- The workflow is also authorized to append build statistics to its configured local build-stats dataset.
|
||||
|
||||
## Testing
|
||||
|
||||
Catch2 framework. Tests in `tests/`; see [tests/AGENTS.md](tests/AGENTS.md) for where a new test belongs and the conventions to follow.
|
||||
|
||||
@@ -298,8 +298,6 @@ if (APPLE)
|
||||
SET(CMAKE_XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "com.orcaslicer.OrcaSlicer")
|
||||
|
||||
message(STATUS "Orca: IS_CROSS_COMPILE: ${IS_CROSS_COMPILE}")
|
||||
elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
set(CMAKE_INSTALL_RPATH "$ORIGIN")
|
||||
endif ()
|
||||
|
||||
# Proposal for C++ unit tests and sandboxes
|
||||
@@ -476,8 +474,7 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
# WIN10SDK_PATH is used to point CMake to the WIN10 SDK installation directory.
|
||||
# We pick it from environment if it is not defined in another way
|
||||
# ORCA: Removed Netfabb STL fixing service support in favor of CGAL.
|
||||
if(WIN32)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
# if(WIN32)
|
||||
# if(NOT DEFINED WIN10SDK_PATH)
|
||||
# if(DEFINED ENV{WIN10SDK_PATH})
|
||||
# set(WIN10SDK_PATH "$ENV{WIN10SDK_PATH}")
|
||||
@@ -513,7 +510,7 @@ if(WIN32)
|
||||
# else()
|
||||
# message("Building without Win10 Netfabb STL fixing service support")
|
||||
# endif()
|
||||
endif()
|
||||
# endif()
|
||||
|
||||
if (APPLE)
|
||||
message("OS X SDK Path: ${CMAKE_OSX_SYSROOT}")
|
||||
@@ -1068,10 +1065,6 @@ function(orcaslicer_copy_dlls target config postfix output_dlls)
|
||||
${CMAKE_PREFIX_PATH}/bin/occt/TKXDESTEP.dll
|
||||
${CMAKE_PREFIX_PATH}/bin/occt/TKXSBase.dll
|
||||
${CMAKE_PREFIX_PATH}/bin/freetype.dll
|
||||
${CMAKE_PREFIX_PATH}/bin/avcodec-61.dll
|
||||
${CMAKE_PREFIX_PATH}/bin/swresample-5.dll
|
||||
${CMAKE_PREFIX_PATH}/bin/swscale-8.dll
|
||||
${CMAKE_PREFIX_PATH}/bin/avutil-59.dll
|
||||
DESTINATION ${_out_dir})
|
||||
|
||||
set(${output_dlls}
|
||||
@@ -1107,58 +1100,12 @@ function(orcaslicer_copy_dlls target config postfix output_dlls)
|
||||
${_out_dir}/TKXSBase.dll
|
||||
|
||||
${_out_dir}/freetype.dll
|
||||
${_out_dir}/avcodec-61.dll
|
||||
${_out_dir}/swresample-5.dll
|
||||
${_out_dir}/swscale-8.dll
|
||||
${_out_dir}/avutil-59.dll
|
||||
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
||||
endfunction()
|
||||
|
||||
function(orcaslicer_copy_sos target config postfix output_sos)
|
||||
|
||||
get_property(_is_multi GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
|
||||
get_target_property(_alt_out_dir ${target} RUNTIME_OUTPUT_DIRECTORY)
|
||||
|
||||
if (_alt_out_dir)
|
||||
set(_out_dir "${_alt_out_dir}")
|
||||
elseif (_is_multi)
|
||||
set(_out_dir "${CMAKE_CURRENT_BINARY_DIR}/${config}")
|
||||
else ()
|
||||
set(_out_dir "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
endif ()
|
||||
|
||||
file(COPY ${CMAKE_PREFIX_PATH}/lib/libavcodec.so
|
||||
${CMAKE_PREFIX_PATH}/lib/libavcodec.so.61
|
||||
${CMAKE_PREFIX_PATH}/lib/libavcodec.so.61.3.100
|
||||
${CMAKE_PREFIX_PATH}/lib/libavutil.so
|
||||
${CMAKE_PREFIX_PATH}/lib/libavutil.so.59
|
||||
${CMAKE_PREFIX_PATH}/lib/libavutil.so.59.8.100
|
||||
${CMAKE_PREFIX_PATH}/lib/libswscale.so
|
||||
${CMAKE_PREFIX_PATH}/lib/libswscale.so.8
|
||||
${CMAKE_PREFIX_PATH}/lib/libswscale.so.8.1.100
|
||||
${CMAKE_PREFIX_PATH}/lib/libswresample.so
|
||||
${CMAKE_PREFIX_PATH}/lib/libswresample.so.5
|
||||
${CMAKE_PREFIX_PATH}/lib/libswresample.so.5.1.100
|
||||
DESTINATION ${_out_dir})
|
||||
|
||||
set(${output_sos}
|
||||
${_out_dir}/libavcodec.so
|
||||
${_out_dir}/libavcodec.so.61
|
||||
${_out_dir}/libavcodec.so.61.3.100
|
||||
${_out_dir}/libavutil.so
|
||||
${_out_dir}/libavutil.so.59
|
||||
${_out_dir}/libavutil.so.59.8.100
|
||||
${_out_dir}/libswscale.so
|
||||
${_out_dir}/libswscale.so.8
|
||||
${_out_dir}/libswscale.so.8.1.100
|
||||
${_out_dir}/libswresample.so
|
||||
${_out_dir}/libswresample.so.5
|
||||
${_out_dir}/libswresample.so.5.1.100
|
||||
PARENT_SCOPE
|
||||
)
|
||||
endfunction()
|
||||
|
||||
# Bundled sources set their own warning flags, and a plain -Wall there means /Wall
|
||||
# (= -Weverything) under clang-cl. Target options are applied after the ones a target
|
||||
@@ -1270,20 +1217,6 @@ else ()
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
set(LIBRARY_FILES
|
||||
${LIBDIR_BIN}/libavcodec.so.61
|
||||
${LIBDIR_BIN}/libavcodec.so.61.3.100
|
||||
${LIBDIR_BIN}/libavutil.so.59
|
||||
${LIBDIR_BIN}/libavutil.so.59.8.100
|
||||
${LIBDIR_BIN}/libswresample.so.5
|
||||
${LIBDIR_BIN}/libswresample.so.5.1.100
|
||||
${LIBDIR_BIN}/libswscale.so.8
|
||||
${LIBDIR_BIN}/libswscale.so.8.1.100
|
||||
)
|
||||
install(FILES ${LIBRARY_FILES} DESTINATION "${CMAKE_INSTALL_PREFIX}/bin")
|
||||
endif ()
|
||||
|
||||
install(FILES ${CMAKE_SOURCE_DIR}/LICENSE.txt DESTINATION ".")
|
||||
configure_file(${LIBDIR}/dev-utils/platform/unix/fhs.hpp.in ${LIBDIR_BIN}/dev-utils/platform/unix/fhs.hpp)
|
||||
|
||||
|
||||
@@ -20,18 +20,6 @@ for %%a in (%*) do (
|
||||
if "%%a"=="-x" set USE_NINJA=1
|
||||
)
|
||||
|
||||
@REM Check for clang-cl option (-l). Combined with -x it also builds the deps with
|
||||
@REM clang-cl; on the Visual Studio generator it applies to the slicer only, because
|
||||
@REM the dependency sub-builds have no toolset to inherit and stay on MSVC.
|
||||
set CLANG_ARG=
|
||||
set TOOLSET_ARG=
|
||||
for %%a in (%*) do (
|
||||
if "%%a"=="-l" (
|
||||
set CLANG_ARG=-DCMAKE_C_COMPILER=clang-cl -DCMAKE_CXX_COMPILER=clang-cl
|
||||
set TOOLSET_ARG=-T ClangCL
|
||||
)
|
||||
)
|
||||
|
||||
@REM Check for unit-tests option ("tests")
|
||||
set BUILD_TESTS=OFF
|
||||
for %%a in (%*) do (
|
||||
@@ -139,13 +127,12 @@ if "%1"=="slicer" (
|
||||
GOTO :slicer
|
||||
)
|
||||
echo "building deps.."
|
||||
if defined CLANG_ARG if "%USE_NINJA%"=="0" echo Note: -l needs -x for the dependencies; building them with MSVC.
|
||||
|
||||
echo on
|
||||
REM Set minimum CMake policy to avoid <3.5 errors
|
||||
set CMAKE_POLICY_VERSION_MINIMUM=3.5
|
||||
if "%USE_NINJA%"=="1" (
|
||||
cmake ../ -G %CMAKE_GENERATOR% %CLANG_ARG% -DCMAKE_BUILD_TYPE=%build_type%
|
||||
cmake ../ -G %CMAKE_GENERATOR% -DCMAKE_BUILD_TYPE=%build_type%
|
||||
cmake --build . --config %build_type% --target deps
|
||||
) else (
|
||||
cmake ../ -G %CMAKE_GENERATOR% -A %arch% -DCMAKE_BUILD_TYPE=%build_type%
|
||||
@@ -164,10 +151,10 @@ cd %build_dir%
|
||||
echo on
|
||||
set CMAKE_POLICY_VERSION_MINIMUM=3.5
|
||||
if "%USE_NINJA%"=="1" (
|
||||
cmake .. -G %CMAKE_GENERATOR% %CLANG_ARG% -DORCA_TOOLS=ON %SIG_FLAG% -DBUILD_TESTS=%BUILD_TESTS% -DCMAKE_BUILD_TYPE=%build_type%
|
||||
cmake .. -G %CMAKE_GENERATOR% -DORCA_TOOLS=ON %SIG_FLAG% -DBUILD_TESTS=%BUILD_TESTS% -DCMAKE_BUILD_TYPE=%build_type%
|
||||
cmake --build . --config %build_type% --target all
|
||||
) else (
|
||||
cmake .. -G %CMAKE_GENERATOR% -A %arch% %TOOLSET_ARG% -DORCA_TOOLS=ON %SIG_FLAG% -DBUILD_TESTS=%BUILD_TESTS% -DCMAKE_BUILD_TYPE=%build_type%
|
||||
cmake .. -G %CMAKE_GENERATOR% -A %arch% -DORCA_TOOLS=ON %SIG_FLAG% -DBUILD_TESTS=%BUILD_TESTS% -DCMAKE_BUILD_TYPE=%build_type%
|
||||
cmake --build . --config %build_type% --target ALL_BUILD -- -m
|
||||
)
|
||||
@echo off
|
||||
|
||||
43
deps/Assimp/Assimp.cmake
vendored
43
deps/Assimp/Assimp.cmake
vendored
@@ -1,43 +0,0 @@
|
||||
if(CMAKE_VERSION VERSION_LESS 3.22)
|
||||
set(_assimp_url "https://github.com/assimp/assimp/archive/refs/tags/v5.3.1.tar.gz")
|
||||
set(_assimp_hash "SHA256=a07666be71afe1ad4bc008c2336b7c688aca391271188eb9108d0c6db1be53f1")
|
||||
else()
|
||||
set(_assimp_url "https://github.com/assimp/assimp/archive/refs/tags/v5.4.3.tar.gz")
|
||||
set(_assimp_hash "SHA256=66dfbaee288f2bc43172440a55d0235dfc7bf885dda6435c038e8000e79582cb")
|
||||
endif()
|
||||
|
||||
# Assimp's bundled zlib (contrib/zlib) is too old to compile against the modern
|
||||
# macOS SDK: its zutil.h takes the classic-Mac branch under TARGET_OS_MAC and
|
||||
# does `#define fdopen(fd,mode) NULL`, which then clobbers the SDK's real
|
||||
# `fdopen` prototype in <stdio.h> and breaks the build. On macOS use the system
|
||||
# zlib (already found by find_package(ZLIB) in deps-unix-common) instead.
|
||||
if(APPLE)
|
||||
set(_assimp_build_zlib "-DASSIMP_BUILD_ZLIB=OFF")
|
||||
else()
|
||||
set(_assimp_build_zlib "-DASSIMP_BUILD_ZLIB=ON")
|
||||
endif()
|
||||
|
||||
orcaslicer_add_cmake_project(Assimp
|
||||
URL ${_assimp_url}
|
||||
URL_HASH ${_assimp_hash}
|
||||
CMAKE_ARGS
|
||||
# Assimp's ccache support sets the global RULE_LAUNCH_COMPILE, which breaks
|
||||
# the Ninja RC rule. The superbuild forwards CMAKE_<LANG>_COMPILER_LAUNCHER.
|
||||
-DASSIMP_BUILD_USE_CCACHE=OFF
|
||||
-DASSIMP_BUILD_TESTS=OFF
|
||||
-DASSIMP_BUILD_SAMPLES=OFF
|
||||
-DASSIMP_BUILD_ASSIMP_TOOLS=OFF
|
||||
-DASSIMP_INSTALL_PDB=OFF
|
||||
-DASSIMP_NO_EXPORT=ON
|
||||
-DASSIMP_BUILD_ALL_IMPORTERS_BY_DEFAULT=OFF
|
||||
-DASSIMP_BUILD_GLTF_IMPORTER=ON
|
||||
-DASSIMP_BUILD_OBJ_IMPORTER=ON
|
||||
-DASSIMP_BUILD_FBX_IMPORTER=ON
|
||||
${_assimp_build_zlib}
|
||||
-DASSIMP_WARNINGS_AS_ERRORS=OFF
|
||||
-DBUILD_WITH_STATIC_CRT=OFF
|
||||
)
|
||||
|
||||
if (MSVC)
|
||||
add_debug_dep(dep_Assimp)
|
||||
endif ()
|
||||
10
deps/Boost/Boost.cmake
vendored
10
deps/Boost/Boost.cmake
vendored
@@ -24,13 +24,6 @@ if (MSVC AND DEP_DEBUG)
|
||||
set(_options "FORWARD_CONFIG")
|
||||
endif ()
|
||||
|
||||
# Boost.Container's bundled dlmalloc passes int* where the Win32 Interlocked API
|
||||
# takes volatile long*; cl compiles that with a warning, clang errors out.
|
||||
set(_boost_c_flags_line "")
|
||||
if (MSVC AND CMAKE_C_COMPILER_ID STREQUAL "Clang")
|
||||
set(_boost_c_flags_line "-DCMAKE_C_FLAGS:STRING=-Wno-incompatible-pointer-types")
|
||||
endif ()
|
||||
|
||||
orcaslicer_add_cmake_project(Boost
|
||||
${_options}
|
||||
URL "https://github.com/boostorg/boost/releases/download/boost-1.84.0/boost-1.84.0.tar.gz"
|
||||
@@ -45,7 +38,6 @@ orcaslicer_add_cmake_project(Boost
|
||||
"${_context_abi_line}"
|
||||
"${_context_arch_line}"
|
||||
"${_context_impl_line}"
|
||||
"${_boost_c_flags_line}"
|
||||
)
|
||||
|
||||
set(DEP_Boost_DEPENDS ZLIB)
|
||||
set(DEP_Boost_DEPENDS ZLIB)
|
||||
19
deps/CMakeLists.txt
vendored
19
deps/CMakeLists.txt
vendored
@@ -157,16 +157,8 @@ endif ()
|
||||
function(orcaslicer_add_cmake_project projectname)
|
||||
cmake_parse_arguments(P_ARGS "FORWARD_CONFIG" "INSTALL_DIR;BUILD_COMMAND;INSTALL_COMMAND" "CMAKE_ARGS" ${ARGN})
|
||||
|
||||
# MSVC is true for clang-cl as well, so the sub-build toolchain has to key on the
|
||||
# generator. A non-Visual-Studio superbuild passes its own generator down, and with
|
||||
# it the CMAKE_C_COMPILER / CMAKE_CXX_COMPILER forwarded below.
|
||||
set(_dep_msvc_gen FALSE)
|
||||
if (MSVC AND CMAKE_GENERATOR MATCHES "Visual Studio")
|
||||
set(_dep_msvc_gen TRUE)
|
||||
endif ()
|
||||
|
||||
set(_configs_line -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE})
|
||||
if (_is_multi OR _dep_msvc_gen)
|
||||
if (_is_multi OR MSVC)
|
||||
if (P_ARGS_FORWARD_CONFIG)
|
||||
set(_configs_line -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE})
|
||||
elseif (ORCA_INCLUDE_DEBUG_INFO AND NOT DEP_DEBUG)
|
||||
@@ -182,7 +174,7 @@ function(orcaslicer_add_cmake_project projectname)
|
||||
set(_target_config "Release")
|
||||
endif()
|
||||
|
||||
if (_dep_msvc_gen)
|
||||
if (MSVC)
|
||||
set(_gen CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}")
|
||||
else()
|
||||
set(_gen "")
|
||||
@@ -190,7 +182,7 @@ function(orcaslicer_add_cmake_project projectname)
|
||||
|
||||
if ($ENV{CMAKE_BUILD_PARALLEL_LEVEL})
|
||||
set(_build_j "") # assume environment will control --build parallel setting
|
||||
elseif(_dep_msvc_gen)
|
||||
elseif(MSVC)
|
||||
set(_build_j "/m")
|
||||
else()
|
||||
set(_build_j "-j${NPROC}")
|
||||
@@ -375,9 +367,6 @@ include(libnoise/libnoise.cmake)
|
||||
|
||||
include(Draco/Draco.cmake)
|
||||
|
||||
include(FFMPEG/FFMPEG.cmake)
|
||||
include(Assimp/Assimp.cmake)
|
||||
|
||||
|
||||
# I *think* 1.1 is used for *just* md5 hashing?
|
||||
# 3.1 has everything in the right place, but the md5 funcs used are deprecated
|
||||
@@ -459,8 +448,6 @@ set(_dep_list
|
||||
dep_libnoise
|
||||
dep_python3
|
||||
dep_wxInspector
|
||||
dep_FFMPEG
|
||||
dep_Assimp
|
||||
)
|
||||
|
||||
if (MSVC)
|
||||
|
||||
14
deps/CURL/CURL.cmake
vendored
14
deps/CURL/CURL.cmake
vendored
@@ -56,18 +56,6 @@ else()
|
||||
set(_curl_static ON)
|
||||
endif()
|
||||
|
||||
# curl 7.75's configure probes and code rely on C laxness cl allows but clang
|
||||
# errors on (implicit function declarations, int* vs u_long* in ioctlsocket),
|
||||
# which flips probe results and misconfigures nonblock.c into the AmigaOS
|
||||
# IoctlSocket branch. Relax both diagnostics so the probes behave like cl, and
|
||||
# pin the camel-case probes off since they only "pass" by implicit declaration.
|
||||
set(_curl_c_flags_line "")
|
||||
set(_curl_probe_overrides "")
|
||||
if (MSVC AND CMAKE_C_COMPILER_ID STREQUAL "Clang")
|
||||
set(_curl_c_flags_line "-DCMAKE_C_FLAGS:STRING=-Wno-implicit-function-declaration -Wno-incompatible-pointer-types")
|
||||
set(_curl_probe_overrides -DHAVE_IOCTLSOCKET_CAMEL=0 -DHAVE_IOCTLSOCKET_CAMEL_FIONBIO=0)
|
||||
endif ()
|
||||
|
||||
orcaslicer_add_cmake_project(CURL
|
||||
# GIT_REPOSITORY https://github.com/curl/curl.git
|
||||
# GIT_TAG curl-7_75_0
|
||||
@@ -81,8 +69,6 @@ orcaslicer_add_cmake_project(CURL
|
||||
-DBUILD_CURL_EXE:BOOL=OFF
|
||||
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||
-DCURL_STATICLIB=${_curl_static}
|
||||
"${_curl_c_flags_line}"
|
||||
${_curl_probe_overrides}
|
||||
${_curl_platform_flags}
|
||||
)
|
||||
|
||||
|
||||
15
deps/Eigen/Eigen.cmake
vendored
15
deps/Eigen/Eigen.cmake
vendored
@@ -7,20 +7,5 @@ orcaslicer_add_cmake_project(Eigen
|
||||
URL https://gitlab.com/libeigen/eigen/-/archive/5.0.1/eigen-5.0.1.zip
|
||||
URL_HASH SHA256=0dbb1f9e3aaad66f352c03227d8c983f6f0b49e0b07e71a7300f4abcc01aee12
|
||||
CMAKE_ARGS "${_eigen_extra_flags}"
|
||||
# Only the headers are consumed here. Everything below builds nothing we
|
||||
# use, and all three enable_language(Fortran): test/CMakeLists.txt:9,
|
||||
# lapack/CMakeLists.txt:6 and blas/testing/CMakeLists.txt:2. They default
|
||||
# to ON because the dependency configures as its own top-level project.
|
||||
#
|
||||
# Whether that probe is harmless depends on what CMake finds. The Visual
|
||||
# Studio generator supports no Fortran, so it finds nothing; clang-cl sits
|
||||
# next to the LLVM toolset's flang, which works. MSVC with Ninja finds
|
||||
# Strawberry Perl's MinGW gfortran instead, which the deps build already
|
||||
# requires for OpenSSL, and hands it the MSVC-style /machine:x64 that
|
||||
# MinGW's ld reads as a missing input file. The configure dies there and
|
||||
# takes the rest of the superbuild with it.
|
||||
-DEIGEN_BUILD_TESTING=OFF
|
||||
-DEIGEN_BUILD_BLAS=OFF
|
||||
-DEIGEN_BUILD_LAPACK=OFF
|
||||
DEPENDS dep_Boost dep_GMP dep_MPFR
|
||||
)
|
||||
|
||||
87
deps/FFMPEG/FFMPEG.cmake
vendored
87
deps/FFMPEG/FFMPEG.cmake
vendored
@@ -1,87 +0,0 @@
|
||||
set(_conf_cmd ./configure)
|
||||
|
||||
if (MSVC)
|
||||
set(_source_dir "${CMAKE_BINARY_DIR}/dep_FFMPEG-prefix/src/dep_FFMPEG")
|
||||
|
||||
set(PREBUILD_URL_arm64 "https://github.com/Noisyfox/FFmpeg-Builds-Orca/releases/download/autobuild-2026-07-17-14-28/ffmpeg-n7.0.3-31-g9b6ffd74b5-winarm64-orca-shared-7.0.zip")
|
||||
set(PREBUILD_HASH_arm64 "12f4140279f2f8469885e1b5b2e8be9d788882914c21523cacd56989f3548054")
|
||||
set(PREBUILD_URL_x64 "https://github.com/Noisyfox/FFmpeg-Builds-Orca/releases/download/autobuild-2026-07-17-14-28/ffmpeg-n7.0.3-31-g9b6ffd74b5-win64-orca-shared-7.0.zip")
|
||||
set(PREBUILD_HASH_x64 "e65916020ddb9ef84b2666dfbcbfc9b1d67f69d15b4a66db53754637bf2d498c")
|
||||
|
||||
ExternalProject_Add(dep_FFMPEG
|
||||
URL ${PREBUILD_URL_${DEPS_ARCH}}
|
||||
URL_HASH SHA256=${PREBUILD_HASH_${DEPS_ARCH}}
|
||||
DOWNLOAD_DIR ${DEP_DOWNLOAD_DIR}/FFMPEG
|
||||
CONFIGURE_COMMAND ""
|
||||
BUILD_COMMAND ""
|
||||
INSTALL_COMMAND
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${_source_dir}/bin" "${DESTDIR}/bin"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${_source_dir}/lib" "${DESTDIR}/lib"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${_source_dir}/include" "${DESTDIR}/include"
|
||||
)
|
||||
|
||||
else ()
|
||||
if (APPLE)
|
||||
set(_minos_cmd
|
||||
"--extra-cflags=-mmacosx-version-min=${DEP_OSX_TARGET}"
|
||||
"--extra-ldflags=-mmacosx-version-min=${DEP_OSX_TARGET}"
|
||||
)
|
||||
# Static FFmpeg: nothing to bundle into the .app, no rpath handling.
|
||||
# Disable the VideoToolbox/AudioToolbox HW-accel paths: the player decodes
|
||||
# in software (swscale), and the auto-detected HW objects would drag in
|
||||
# system frameworks that the static libs would then depend on.
|
||||
set(_link_cmd --enable-static --disable-shared --disable-videotoolbox --disable-audiotoolbox)
|
||||
if (IS_CROSS_COMPILE)
|
||||
set(_cross_cmd --enable-cross-compile)
|
||||
set(_pic_cmd --enable-pic)
|
||||
if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "x86_64")
|
||||
set(_arch_cmd --arch=arm64)
|
||||
set(_cc_cmd "--cc=clang -arch arm64")
|
||||
else()
|
||||
set(_arch_cmd --arch=x86_64)
|
||||
set(_cc_cmd "--cc=clang -arch x86_64")
|
||||
endif()
|
||||
endif()
|
||||
else ()
|
||||
set(_link_cmd --enable-shared)
|
||||
endif ()
|
||||
|
||||
set(_build_j -j)
|
||||
if(DEFINED ENV{CMAKE_BUILD_PARALLEL_LEVEL})
|
||||
set(_build_j "-j$ENV{CMAKE_BUILD_PARALLEL_LEVEL}")
|
||||
endif()
|
||||
|
||||
ExternalProject_Add(dep_FFMPEG
|
||||
URL https://github.com/FFmpeg/FFmpeg/archive/refs/tags/n7.0.3.tar.gz
|
||||
URL_HASH SHA256=DEEDCABE339165214A3637DF4C86A507AEF0D793CF8774FF68735F4737E8DDBC
|
||||
DOWNLOAD_DIR ${DEP_DOWNLOAD_DIR}/FFMPEG
|
||||
CONFIGURE_COMMAND ${_conf_cmd}
|
||||
${_cross_cmd}
|
||||
${_pic_cmd}
|
||||
${_arch_cmd}
|
||||
${_cc_cmd}
|
||||
"--prefix=${DESTDIR}"
|
||||
${_link_cmd}
|
||||
${_minos_cmd}
|
||||
--disable-doc
|
||||
--enable-small
|
||||
--disable-outdevs
|
||||
--disable-filters
|
||||
--enable-filter=*null*,afade,*fifo,*format,*resample,aeval,allrgb,allyuv,atempo,pan,*bars,color,*key,crop,draw*,eq*,framerate,*_qsv,*_vaapi,*v4l2*,hw*,scale,volume,test*
|
||||
--disable-protocols
|
||||
--enable-protocol=file,fd,pipe,rtp,udp
|
||||
--disable-muxers
|
||||
--enable-muxer=rtp
|
||||
--disable-encoders
|
||||
--disable-decoders
|
||||
--enable-decoder=*aac*,h264*,mp3*,mjpeg,rv*
|
||||
--disable-demuxers
|
||||
--enable-demuxer=h264,mp3,mov
|
||||
--disable-zlib
|
||||
--disable-avdevice
|
||||
BUILD_IN_SOURCE ON
|
||||
BUILD_COMMAND make ${_build_j}
|
||||
INSTALL_COMMAND make install
|
||||
)
|
||||
|
||||
endif()
|
||||
43
deps/OCCT/0001-OCCT-fix.patch
vendored
43
deps/OCCT/0001-OCCT-fix.patch
vendored
@@ -1,20 +1,3 @@
|
||||
diff --git a/adm/cmake/occt_defs_flags.cmake b/adm/cmake/occt_defs_flags.cmake
|
||||
index 00000000..00000001 100644
|
||||
--- a/adm/cmake/occt_defs_flags.cmake
|
||||
+++ b/adm/cmake/occt_defs_flags.cmake
|
||||
@@ -134,7 +134,11 @@
|
||||
set (CMAKE_CXX_FLAGS "-std=c++0x ${CMAKE_CXX_FLAGS}")
|
||||
endif()
|
||||
# Optimize size of binaries
|
||||
- set (CMAKE_SHARED_LINKER_FLAGS "-Wl,-s ${CMAKE_SHARED_LINKER_FLAGS}")
|
||||
+ # clang-cl reports the Clang compiler ID, and OCCT builds shared on Windows,
|
||||
+ # where the MSVC-style linker gets this flag as an argument it does not know.
|
||||
+ if (NOT WIN32)
|
||||
+ set (CMAKE_SHARED_LINKER_FLAGS "-Wl,-s ${CMAKE_SHARED_LINKER_FLAGS}")
|
||||
+ endif()
|
||||
elseif(MINGW)
|
||||
add_definitions(-D_WIN32_WINNT=0x0601)
|
||||
# _WIN32_WINNT=0x0601 (use Windows 7 SDK)
|
||||
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||
index d98acc0f..28eb8eb4 100644
|
||||
--- a/CMakeLists.txt
|
||||
@@ -185,32 +168,6 @@ index d98acc0f..28eb8eb4 100644
|
||||
endforeach()
|
||||
|
||||
if (BUILD_SAMPLES_QT)
|
||||
diff --git a/adm/cmake/occt_macros.cmake b/adm/cmake/occt_macros.cmake
|
||||
index 224c96b1..8c94a1c5 100644
|
||||
--- a/adm/cmake/occt_macros.cmake
|
||||
+++ b/adm/cmake/occt_macros.cmake
|
||||
@@ -608,7 +608,7 @@ macro (OCCT_INSERT_CODE_FOR_TARGET)
|
||||
install(CODE "if (\"\${CMAKE_INSTALL_CONFIG_NAME}\" MATCHES \"^([Rr][Ee][Ll][Ee][Aa][Ss][Ee])$\")
|
||||
set (OCCT_INSTALL_BIN_LETTER \"\")
|
||||
elseif (\"\${CMAKE_INSTALL_CONFIG_NAME}\" MATCHES \"^([Rr][Ee][Ll][Ww][Ii][Tt][Hh][Dd][Ee][Bb][Ii][Nn][Ff][Oo])$\")
|
||||
- set (OCCT_INSTALL_BIN_LETTER \"i\")
|
||||
+ set (OCCT_INSTALL_BIN_LETTER \"\")
|
||||
elseif (\"\${CMAKE_INSTALL_CONFIG_NAME}\" MATCHES \"^([Dd][Ee][Bb][Uu][Gg])$\")
|
||||
set (OCCT_INSTALL_BIN_LETTER \"d\")
|
||||
endif()")
|
||||
diff --git a/adm/cmake/occt_toolkit.cmake b/adm/cmake/occt_toolkit.cmake
|
||||
index 550e0e2f..7ac1a3b8 100644
|
||||
--- a/adm/cmake/occt_toolkit.cmake
|
||||
+++ b/adm/cmake/occt_toolkit.cmake
|
||||
@@ -241,7 +241,7 @@
|
||||
else()
|
||||
set (aReleasePdbConf)
|
||||
endif()
|
||||
- install (FILES ${CMAKE_BINARY_DIR}/${OS_WITH_BIT}/${COMPILER}/bin\${OCCT_INSTALL_BIN_LETTER}/${PROJECT_NAME}.pdb
|
||||
+ install (FILES $<TARGET_PDB_FILE:${PROJECT_NAME}>
|
||||
CONFIGURATIONS Debug ${aReleasePdbConf} RelWithDebInfo
|
||||
DESTINATION "${INSTALL_DIR_BIN}\${OCCT_INSTALL_BIN_LETTER}")
|
||||
endif()
|
||||
diff --git a/src/Font/Font_FTFont.cxx b/src/Font/Font_FTFont.cxx
|
||||
index 5ae9899f..0a17372b 100644
|
||||
--- a/src/Font/Font_FTFont.cxx
|
||||
|
||||
16
deps/OpenSSL/OpenSSL.cmake
vendored
16
deps/OpenSSL/OpenSSL.cmake
vendored
@@ -17,20 +17,10 @@ else()
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
set(_openssl_msvc_env CC=cl CXX=cl RC=rc CL=/FS)
|
||||
# OpenSSL's perl Configure honors the CC environment variable, but the
|
||||
# VC-WIN64A makefile only works with cl (an unquoted clang-cl path with
|
||||
# spaces, e.g. exported by CLion, silently produces no .obj files and the
|
||||
# lib step fails with LNK1181). Pin the upstream toolchain.
|
||||
# Keep rc.exe resolved from the MSVC developer environment as well. The
|
||||
# absolute Windows SDK path contains spaces and OpenSSL 1.1.1 writes it to
|
||||
# the generated nmake file without quoting, which skips .res generation.
|
||||
# /FS serializes access to OpenSSL's shared generated PDB when cl is
|
||||
# driven through nmake from a Ninja configure step.
|
||||
set(_conf_cmd ${CMAKE_COMMAND} -E env ${_openssl_msvc_env} perl Configure )
|
||||
set(_conf_cmd perl Configure )
|
||||
set(_cross_comp_prefix_line "")
|
||||
set(_make_cmd ${CMAKE_COMMAND} -E env ${_openssl_msvc_env} nmake)
|
||||
set(_install_cmd ${CMAKE_COMMAND} -E env ${_openssl_msvc_env} nmake install_sw )
|
||||
set(_make_cmd nmake)
|
||||
set(_install_cmd nmake install_sw )
|
||||
else()
|
||||
if(APPLE)
|
||||
set(_conf_cmd export MACOSX_DEPLOYMENT_TARGET=${CMAKE_OSX_DEPLOYMENT_TARGET} && ./Configure -mmacosx-version-min=${CMAKE_OSX_DEPLOYMENT_TARGET})
|
||||
|
||||
24
deps/wxInspector/wxInspector.cmake
vendored
24
deps/wxInspector/wxInspector.cmake
vendored
@@ -1,26 +1,3 @@
|
||||
# wxInspector finds wxWidgets through CMake's FindwxWidgets module, which only
|
||||
# searches lib/vc*_lib because _WX_TOOL is hardcoded to "vc". A superbuild driven
|
||||
# by clang-cl installs wxWidgets into lib/clang_x64_lib, so hand the module the
|
||||
# directory wxWidgets actually used, derived the same way wxWidgetsConfig.cmake
|
||||
# derives it.
|
||||
set(_wxinspector_wx_hints "")
|
||||
if (MSVC)
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
set(_wx_compiler_prefix "clang")
|
||||
else ()
|
||||
set(_wx_compiler_prefix "vc")
|
||||
endif ()
|
||||
set(_wx_arch_suffix "")
|
||||
if (CMAKE_GENERATOR_PLATFORM AND NOT CMAKE_GENERATOR_PLATFORM STREQUAL "Win32")
|
||||
string(TOLOWER "_${CMAKE_GENERATOR_PLATFORM}" _wx_arch_suffix)
|
||||
elseif (CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||
set(_wx_arch_suffix "_x64")
|
||||
endif ()
|
||||
set(_wxinspector_wx_hints
|
||||
"-DwxWidgets_ROOT_DIR=${DESTDIR}"
|
||||
"-DwxWidgets_LIB_DIR=${DESTDIR}/lib/${_wx_compiler_prefix}${_wx_arch_suffix}_lib")
|
||||
endif ()
|
||||
|
||||
orcaslicer_add_cmake_project(
|
||||
wxInspector
|
||||
URL https://github.com/Noisyfox/wxInspector/archive/refs/tags/v1.0.0.zip
|
||||
@@ -29,7 +6,6 @@ orcaslicer_add_cmake_project(
|
||||
CMAKE_ARGS
|
||||
-DCMAKE_CXX_FLAGS="-DwxDEBUG_LEVEL=0"
|
||||
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||
${_wxinspector_wx_hints}
|
||||
)
|
||||
|
||||
if (MSVC)
|
||||
|
||||
28
deps/wxWidgets/0001-Clang-CL-fix.patch
vendored
Normal file
28
deps/wxWidgets/0001-Clang-CL-fix.patch
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
build/cmake/wxWidgetsConfig.cmake.in | 10 +++++++++-
|
||||
1 file changed, 10 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/build/cmake/wxWidgetsConfig.cmake.in b/build/cmake/wxWidgetsConfig.cmake.in
|
||||
index 1a83f36..70ad8a4 100644
|
||||
--- a/build/cmake/wxWidgetsConfig.cmake.in
|
||||
+++ b/build/cmake/wxWidgetsConfig.cmake.in
|
||||
@@ -58,7 +58,16 @@ if(WIN32_MSVC_NAMING)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
-include("${CMAKE_CURRENT_LIST_DIR}${wxPLATFORM_LIB_DIR}/@PROJECT_NAME@Targets.cmake")
|
||||
+if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC")
|
||||
+ if (CMAKE_GENERATOR_PLATFORM STREQUAL "ARM64" OR CMAKE_VS_PLATFORM_NAME STREQUAL "ARM64" OR CMAKE_SYSTEM_PROCESSOR MATCHES "^(ARM64|arm64|aarch64)$")
|
||||
+ set(_wx_clang_msvc_lib_dir "vc_arm64_lib")
|
||||
+ else()
|
||||
+ set(_wx_clang_msvc_lib_dir "vc_x64_lib")
|
||||
+ endif()
|
||||
+ include("${CMAKE_CURRENT_LIST_DIR}${wxPLATFORM_LIB_DIR}/${_wx_clang_msvc_lib_dir}/@PROJECT_NAME@Targets.cmake")
|
||||
+else()
|
||||
+ include("${CMAKE_CURRENT_LIST_DIR}${wxPLATFORM_LIB_DIR}/@PROJECT_NAME@Targets.cmake")
|
||||
+endif()
|
||||
|
||||
macro(wx_inherit_property source dest name)
|
||||
# property name without _<CONFIG>
|
||||
--
|
||||
2.43.0
|
||||
1
deps/wxWidgets/wxWidgets.cmake
vendored
1
deps/wxWidgets/wxWidgets.cmake
vendored
@@ -28,6 +28,7 @@ orcaslicer_add_cmake_project(
|
||||
GIT_SHALLOW ON
|
||||
GIT_SUBMODULES 3rdparty/catch 3rdparty/pcre 3rdparty/libwebp
|
||||
DEPENDS ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} ${JPEG_PKG}
|
||||
PATCH_COMMAND git apply --verbose --ignore-space-change --whitespace=fix ${CMAKE_CURRENT_LIST_DIR}/0001-Clang-CL-fix.patch
|
||||
CMAKE_ARGS
|
||||
-DwxBUILD_PRECOMP=ON
|
||||
${_wx_toolkit}
|
||||
|
||||
@@ -1,376 +0,0 @@
|
||||
# macOS FFmpeg Media Player Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Make macOS use the same FFmpeg-based media player (`wxMediaCtrl3` + `AVVideoDecoder`) as Windows/Linux, linking the static FFmpeg libraries from the deps build, and remove the old `wxMediaCtrl2.mm` BambuPlayer-based player.
|
||||
|
||||
**Architecture:** The new player is platform-neutral C++ already used on Linux/Windows. Enabling it on macOS is pure build wiring: compile `wxMediaCtrl3.cpp` + `AVVideoDecoder.cpp` on macOS, drop the `__WXMAC__` alias that redirects `wxMediaCtrl3` to the old `wxMediaCtrl2`, and link static FFmpeg (`libavcodec.a`/`libswscale.a`/`libavutil.a`) from the deps install. The Bambu stream API is dlsym'd at runtime from the network plugin (`libBambuSource.dylib`), which already exports it — no plugin changes needed. Rendering reuses the existing `wxImage` → `DrawBitmap` paint path (same as Linux).
|
||||
|
||||
**Tech Stack:** C++17, wxWidgets, CMake, FFmpeg 7.0.3 (libavcodec/libswscale/libavutil), macOS (Xcode generator), `deps/` ExternalProject build system.
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Branch: `dev/ffmpeg-player-macos`. Commit after every task.
|
||||
- **Linux and Windows builds must not change** — the FFmpeg deps flag change is guarded by `APPLE`; Linux keeps `--enable-shared`, Windows keeps its prebuilt DLL zips.
|
||||
- Static FFmpeg only on macOS: deps produce `libavcodec.a`/`libswscale.a`/`libavutil.a`; the app links those explicitly — the app binary must have **no** `libav*` dylib references (`otool -L` check).
|
||||
- Follow existing code style: PascalCase classes, snake_case functions, C++17.
|
||||
- No changes to `StatusPanel.cpp`, `MediaPlayCtrl.*`, or the BambuTunnel interface — the app already creates `wxMediaCtrl3` and uses only its public interface.
|
||||
- The player cannot be unit-tested (hardware/plugin-dependent GUI code); verification is build-level, link-level, and manual runtime on a Mac.
|
||||
- `localization/i18n/list.txt` references only `wxMediaCtrl2.cpp` (Win/Linux, stays) — no translation-list changes needed.
|
||||
- Build dirs on the dev machine: main app = `build_arm64/` (Xcode generator, multi-config), deps = `deps/build/arm64/` (Unix Makefiles). App target name: `OrcaSlicer`. Substitute your own configured build dirs where noted.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Enable wxMediaCtrl3 on macOS and link static FFmpeg
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/slic3r/GUI/wxMediaCtrl3.h` (lines 18–22: the `#ifdef __WXMAC__` alias branch)
|
||||
- Modify: `src/slic3r/GUI/wxMediaCtrl3.cpp:13` (uncomment the event define)
|
||||
- Modify: `src/slic3r/GUI/wxMediaCtrl2.cpp:101` (remove the event define)
|
||||
- Modify: `src/slic3r/CMakeLists.txt` (APPLE source list ~lines 779–792; FFmpeg link block ~lines 905–910)
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: nothing new (all classes already exist).
|
||||
- Produces: `wxMediaCtrl3` class compiled on macOS with the same interface as Linux/Windows — `Load(wxURI)`, `Play()`, `Stop()`, `SetIdleImage(wxString)`, `GetState()`, `GetLastError()`, `GetVideoSize()`, event `EVT_MEDIA_CTRL_STAT` defined once in the lib (from `wxMediaCtrl3.cpp`).
|
||||
|
||||
- [ ] **Step 1: Remove the macOS alias in wxMediaCtrl3.h**
|
||||
|
||||
Current (lines 16–23 of `src/slic3r/GUI/wxMediaCtrl3.h`):
|
||||
|
||||
```cpp
|
||||
void wxMediaCtrl_OnSize(wxWindow * ctrl, wxSize const & videoSize, int width, int height);
|
||||
|
||||
#ifdef __WXMAC__
|
||||
|
||||
#include "wxMediaCtrl2.h"
|
||||
#define wxMediaCtrl3 wxMediaCtrl2
|
||||
|
||||
#else
|
||||
|
||||
#define BAMBU_DYNAMIC
|
||||
```
|
||||
|
||||
New:
|
||||
|
||||
```cpp
|
||||
void wxMediaCtrl_OnSize(wxWindow * ctrl, wxSize const & videoSize, int width, int height);
|
||||
|
||||
#define BAMBU_DYNAMIC
|
||||
```
|
||||
|
||||
Also remove the matching `#endif` that closed the `#else` branch (the one before the final `#endif /* wxMediaCtrl3_h */`), so the file's `#ifndef`/`#endif` guard pair stays balanced.
|
||||
|
||||
- [ ] **Step 2: Move the EVT_MEDIA_CTRL_STAT definition into wxMediaCtrl3.cpp**
|
||||
|
||||
In `src/slic3r/GUI/wxMediaCtrl3.cpp:13`, uncomment:
|
||||
|
||||
```cpp
|
||||
//wxDEFINE_EVENT(EVT_MEDIA_CTRL_STAT, wxCommandEvent);
|
||||
```
|
||||
|
||||
becomes:
|
||||
|
||||
```cpp
|
||||
wxDEFINE_EVENT(EVT_MEDIA_CTRL_STAT, wxCommandEvent);
|
||||
```
|
||||
|
||||
In `src/slic3r/GUI/wxMediaCtrl2.cpp:101`, delete:
|
||||
|
||||
```cpp
|
||||
wxDEFINE_EVENT(EVT_MEDIA_CTRL_STAT, wxCommandEvent);
|
||||
```
|
||||
|
||||
(One definition total in the lib — `MediaPlayCtrl.cpp:59` binds this event on the media ctrl.)
|
||||
|
||||
- [ ] **Step 3: Update the APPLE source list in CMakeLists.txt**
|
||||
|
||||
In `src/slic3r/CMakeLists.txt`, the APPLE branch (currently compiles `wxMediaCtrl2.mm`, which becomes dead on macOS):
|
||||
|
||||
```cmake
|
||||
GUI/wxMediaCtrl2.mm
|
||||
GUI/wxMediaCtrl2.h
|
||||
GUI/wxMediaCtrl3.h
|
||||
)
|
||||
```
|
||||
|
||||
becomes:
|
||||
|
||||
```cmake
|
||||
GUI/AVVideoDecoder.cpp
|
||||
GUI/AVVideoDecoder.hpp
|
||||
GUI/wxMediaCtrl3.cpp
|
||||
GUI/wxMediaCtrl3.h
|
||||
)
|
||||
```
|
||||
|
||||
(The `else ()` branch — Win/Linux — stays exactly as it is.)
|
||||
|
||||
- [ ] **Step 4: Link static FFmpeg on macOS**
|
||||
|
||||
In `src/slic3r/CMakeLists.txt`, the FFmpeg block (currently `if (NOT APPLE)`):
|
||||
|
||||
```cmake
|
||||
if (NOT APPLE)
|
||||
pkg_check_modules(LIBAV REQUIRED IMPORTED_TARGET
|
||||
libavcodec
|
||||
libswscale
|
||||
libavutil
|
||||
)
|
||||
target_link_libraries(libslic3r_gui PkgConfig::LIBAV)
|
||||
endif()
|
||||
```
|
||||
|
||||
becomes:
|
||||
|
||||
```cmake
|
||||
if (APPLE)
|
||||
# Static FFmpeg from the deps install: nothing to bundle into the .app,
|
||||
# no rpath/install_name handling. Order matters: avcodec -> swscale -> avutil.
|
||||
find_library(LIBAVCODEC_LIBRARY NAMES libavcodec.a PATHS ${CMAKE_PREFIX_PATH}/lib NO_DEFAULT_PATH)
|
||||
find_library(LIBSWSCALE_LIBRARY NAMES libswscale.a PATHS ${CMAKE_PREFIX_PATH}/lib NO_DEFAULT_PATH)
|
||||
find_library(LIBAVUTIL_LIBRARY NAMES libavutil.a PATHS ${CMAKE_PREFIX_PATH}/lib NO_DEFAULT_PATH)
|
||||
target_link_libraries(libslic3r_gui ${LIBAVCODEC_LIBRARY} ${LIBSWSCALE_LIBRARY} ${LIBAVUTIL_LIBRARY})
|
||||
target_include_directories(libslic3r_gui SYSTEM PRIVATE ${CMAKE_PREFIX_PATH}/include)
|
||||
else ()
|
||||
pkg_check_modules(LIBAV REQUIRED IMPORTED_TARGET
|
||||
libavcodec
|
||||
libswscale
|
||||
libavutil
|
||||
)
|
||||
target_link_libraries(libslic3r_gui PkgConfig::LIBAV)
|
||||
endif()
|
||||
```
|
||||
|
||||
The deps install (`${CMAKE_PREFIX_PATH}/lib`) already contains the three `.a` files from the existing arm64 deps build — no deps rebuild needed for this task.
|
||||
|
||||
- [ ] **Step 5: Reconfigure and build the app**
|
||||
|
||||
Run (Xcode generator; `cmake` re-runs automatically on build):
|
||||
|
||||
```bash
|
||||
cmake --build build_arm64 --config RelWithDebInfo --target OrcaSlicer
|
||||
```
|
||||
|
||||
Expected: configure succeeds (no `pkg_check_modules` errors on macOS, `find_library` finds all three `.a` files), compile succeeds (`wxMediaCtrl3.cpp` and `AVVideoDecoder.cpp` compile on macOS without changes), link succeeds.
|
||||
|
||||
If CMake complains that `wxMediaCtrl3.h` is included but not in the source list or similar IDE-only warnings — ignore; headers in the list are cosmetic.
|
||||
|
||||
- [ ] **Step 6: Verify no dynamic FFmpeg dependency**
|
||||
|
||||
```bash
|
||||
otool -L build_arm64/src/RelWithDebInfo/OrcaSlicer.app/Contents/MacOS/OrcaSlicer | grep -i "libav" || echo "OK: no dynamic FFmpeg"
|
||||
```
|
||||
|
||||
Expected: prints `OK: no dynamic FFmpeg` (empty grep output). This is the whole point of static linking — nothing to bundle into the `.app`.
|
||||
|
||||
- [ ] **Step 7: Quick sanity — macOS unit tests still pass**
|
||||
|
||||
```bash
|
||||
ctest --test-dir build_arm64/tests/libslic3r --output-on-failure
|
||||
```
|
||||
|
||||
Expected: passes (add `-C RelWithDebInfo` if the multi-config generator requires it). If no tests were built in this build dir, build target `tests` first (`cmake --build build_arm64 --config RelWithDebInfo --target tests`).
|
||||
|
||||
- [ ] **Step 8: Commit**
|
||||
|
||||
```bash
|
||||
git add src/slic3r/CMakeLists.txt src/slic3r/GUI/wxMediaCtrl3.h src/slic3r/GUI/wxMediaCtrl3.cpp src/slic3r/GUI/wxMediaCtrl2.cpp
|
||||
git commit -m "feat: use FFmpeg media player on macOS with static FFmpeg"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Static-only FFmpeg in the macOS deps build
|
||||
|
||||
**Files:**
|
||||
- Modify: `deps/FFMPEG/FFMPEG.cmake` (non-MSVC branch, APPLE section and CONFIGURE_COMMAND)
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: nothing.
|
||||
- Produces: a deps install on macOS containing only `libavcodec.a`, `libswscale.a`, `libavutil.a` (+ headers) — no `libav*` dylibs, so no bundling/rpath machinery is ever needed on macOS. Linux and Windows output are unchanged.
|
||||
|
||||
- [ ] **Step 1: Add the static flag variable**
|
||||
|
||||
In `deps/FFMPEG/FFMPEG.cmake`, inside the non-MSVC `else ()` branch, in the existing `if (APPLE)` block:
|
||||
|
||||
```cmake
|
||||
if (APPLE)
|
||||
set(_minos_cmd
|
||||
"CFLAGS=-mmacosx-version-min=${DEP_OSX_TARGET}"
|
||||
"LDFLAGS=-mmacosx-version-min=${DEP_OSX_TARGET}"
|
||||
)
|
||||
```
|
||||
|
||||
add after the `_minos_cmd` set:
|
||||
|
||||
```cmake
|
||||
# Static FFmpeg: nothing to bundle into the .app, no rpath handling.
|
||||
# Shared flags must come AFTER --enable-shared below so they win.
|
||||
set(_link_cmd --enable-static --disable-shared)
|
||||
```
|
||||
|
||||
and add a matching `else ()` after the `if (IS_CROSS_COMPILE) ... endif()` block inside that `if (APPLE)`, so non-Apple Unix keeps shared:
|
||||
|
||||
```cmake
|
||||
else ()
|
||||
set(_link_cmd --enable-shared)
|
||||
endif ()
|
||||
```
|
||||
|
||||
(If the existing `if (IS_CROSS_COMPILE)` block is the last thing inside `if (APPLE)`, the new `else ()` closes the `if (APPLE)` itself.)
|
||||
|
||||
- [ ] **Step 2: Use the variable in CONFIGURE_COMMAND**
|
||||
|
||||
In the `ExternalProject_Add(dep_FFMPEG ...)` configure command:
|
||||
|
||||
```cmake
|
||||
"--prefix=${DESTDIR}"
|
||||
--enable-shared
|
||||
```
|
||||
|
||||
becomes:
|
||||
|
||||
```cmake
|
||||
"--prefix=${DESTDIR}"
|
||||
--enable-shared
|
||||
${_link_cmd}
|
||||
```
|
||||
|
||||
Order matters: `--enable-shared` comes first, then `--enable-static --disable-shared` (APPLE) or `--enable-shared` (Linux) — the last flag wins in FFmpeg configure.
|
||||
|
||||
- [ ] **Step 3: Rebuild the FFmpeg dep (slow — several minutes, run in background)**
|
||||
|
||||
The changed CONFIGURE_COMMAND invalidates the ExternalProject stamp, so this re-configures and rebuilds FFmpeg:
|
||||
|
||||
```bash
|
||||
cmake --build deps/build/arm64 --target dep_FFMPEG
|
||||
```
|
||||
|
||||
For a fully clean static-only check (removes the previous shared build tree, which can leave stale `.dylib` files behind in the in-source build):
|
||||
|
||||
```bash
|
||||
rm -rf deps/build/arm64/dep_FFMPEG-prefix
|
||||
cmake --build deps/build/arm64 --target dep_FFMPEG
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Verify the artifacts**
|
||||
|
||||
```bash
|
||||
ls deps/build/arm64/dep_FFMPEG-prefix/src/dep_FFMPEG/libavcodec/*.a
|
||||
ls deps/build/arm64/dep_FFMPEG-prefix/src/dep_FFMPEG/libavcodec/*.dylib 2>/dev/null || echo "OK: no dylibs"
|
||||
```
|
||||
|
||||
Expected: `libavcodec.a` present, second command prints `OK: no dylibs`. Check `libavutil` and `libswscale` the same way.
|
||||
|
||||
- [ ] **Step 5: Verify the app still links against the static libs**
|
||||
|
||||
```bash
|
||||
cmake --build build_arm64 --config RelWithDebInfo --target OrcaSlicer
|
||||
otool -L build_arm64/src/RelWithDebInfo/OrcaSlicer.app/Contents/MacOS/OrcaSlicer | grep -i "libav" || echo "OK: no dynamic FFmpeg"
|
||||
```
|
||||
|
||||
Expected: build succeeds, `OK: no dynamic FFmpeg`.
|
||||
|
||||
- [ ] **Step 6: Commit**
|
||||
|
||||
```bash
|
||||
git add deps/FFMPEG/FFMPEG.cmake
|
||||
git commit -m "build: build static-only FFmpeg for macOS deps"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Remove the old macOS player
|
||||
|
||||
**Files:**
|
||||
- Delete: `src/slic3r/GUI/wxMediaCtrl2.mm`
|
||||
- Delete: `src/slic3r/GUI/BambuPlayer/BambuPlayer.h` (and the empty `BambuPlayer/` dir)
|
||||
- Modify: `src/slic3r/GUI/wxMediaCtrl2.h` (remove the `#ifdef __WXMAC__` section, lines 22–60)
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: Task 1 (macOS no longer references `wxMediaCtrl2` — nothing includes `wxMediaCtrl2.h` on macOS anymore; `wxMediaCtrl2` is never instantiated on any platform).
|
||||
- Produces: a clean tree where the old BambuPlayer-based player is gone from macOS. The `BambuPlayer` ObjC class itself remains inside the network plugin (external prebuilt binary) — only the GUI-side consumer is removed.
|
||||
|
||||
- [ ] **Step 1: Delete the old player files**
|
||||
|
||||
```bash
|
||||
git rm src/slic3r/GUI/wxMediaCtrl2.mm
|
||||
git rm src/slic3r/GUI/BambuPlayer/BambuPlayer.h
|
||||
rmdir src/slic3r/GUI/BambuPlayer 2>/dev/null || true
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Strip the __WXMAC__ section from wxMediaCtrl2.h**
|
||||
|
||||
In `src/slic3r/GUI/wxMediaCtrl2.h`, remove the entire macOS branch of the `#ifdef __WXMAC__` guard — from `#ifdef __WXMAC__` (line 22) through the closing `};` of the mac class (line 60), and the `#else` marker — leaving only the non-mac `class wxMediaCtrl2 : public wxMediaCtrl { ... };` definition followed by the final `#endif /* wxMediaCtrl2_h */`. The resulting file keeps its `#ifndef`/`#endif` include guard pair balanced.
|
||||
|
||||
The file stays on disk because Win/Linux compile `wxMediaCtrl2.cpp`, which includes it.
|
||||
|
||||
- [ ] **Step 3: Grep for leftover references**
|
||||
|
||||
```bash
|
||||
grep -rn "wxMediaCtrl2.mm\|BambuPlayer/BambuPlayer.h\|BambuPlayer" src/slic3r --include="*.cpp" --include="*.h" --include="*.mm" --include="*.txt"
|
||||
```
|
||||
|
||||
Expected: no hits in `src/slic3r/GUI` (ignore `localization/i18n/list.txt:196`, which lists the Win/Linux `wxMediaCtrl2.cpp` and stays).
|
||||
|
||||
- [ ] **Step 4: Rebuild the app**
|
||||
|
||||
```bash
|
||||
cmake --build build_arm64 --config RelWithDebInfo --target OrcaSlicer
|
||||
```
|
||||
|
||||
Expected: configure + compile + link succeed with the deleted files gone.
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add -A src/slic3r/GUI
|
||||
git commit -m "refactor: remove old BambuPlayer-based media player from macOS"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Runtime verification on hardware
|
||||
|
||||
**Files:** none — manual verification.
|
||||
|
||||
**Interfaces:** consumes all prior tasks. Final gate: the new player must actually stream on a Mac.
|
||||
|
||||
- [ ] **Step 1: Launch the freshly built app**
|
||||
|
||||
```bash
|
||||
open build_arm64/src/RelWithDebInfo/OrcaSlicer.app
|
||||
```
|
||||
|
||||
Expected: app launches normally; no crash in the network/device subsystem.
|
||||
|
||||
- [ ] **Step 2: Load the network plugin and open the Device tab**
|
||||
|
||||
Log in / ensure the network plugin (`libBambuSource.dylib`) loads, select a printer, open the Device tab (camera monitoring panel).
|
||||
|
||||
Expected: the camera preview area shows the idle image initially (no crash — this exercises `wxMediaCtrl3::SetIdleImage` and the `wxImage` load path on macOS for the first time).
|
||||
|
||||
- [ ] **Step 3: Start the stream and watch it render**
|
||||
|
||||
Click play / wait for `MediaPlayCtrl` to start the stream.
|
||||
|
||||
Expected: live video renders in the panel. Check the console/log output (`BOOST_LOG` goes to the terminal if run from it, or check the log file):
|
||||
- `stat_log ...` lines appear (the `EVT_MEDIA_CTRL_STAT` path is live — proves the Bambu C API dlsym worked from `libBambuSource.dylib`);
|
||||
- no repeated decode/error messages like `AVVideoDecoder: ...` or `can not find function ...` (proves `StaticBambuLib::get` resolved all Bambu functions);
|
||||
- Stop/Play toggle works; idle image reappears on stop;
|
||||
- window resize keeps aspect ratio (exercises `DoSetSize`/`adjust_frame_size`/`paintEvent`).
|
||||
|
||||
- [ ] **Step 4: Confirm the old player is really gone**
|
||||
|
||||
Expected: nothing in the logs references `BambuPlayer` (the ObjC class is no longer dlsym'd); the video path is entirely `wxMediaCtrl3` + `AVVideoDecoder`.
|
||||
|
||||
If a printer is unavailable, at minimum verify Steps 1–2 (launch + idle image) and note in the PR that live-stream verification needs hardware.
|
||||
|
||||
- [ ] **Step 5: Final review pass**
|
||||
|
||||
```bash
|
||||
git log --oneline -6
|
||||
git show --stat HEAD # and each of the three task commits
|
||||
```
|
||||
|
||||
Expected: the last 4 commits are the design doc + the 3 implementation tasks (each task commit touches only its listed files). Review the diff for scope: no Linux/Windows changes beyond the two `EVT_MEDIA_CTRL_STAT` lines in Task 1, no `StatusPanel`/`MediaPlayCtrl` changes.
|
||||
@@ -1,110 +0,0 @@
|
||||
# FFmpeg Media Player for macOS — Design
|
||||
|
||||
Date: 2026-08-14
|
||||
Branch: `dev/ffmpeg-player-macos`
|
||||
|
||||
## Problem
|
||||
|
||||
The branch's new FFmpeg-based media player (`wxMediaCtrl3` + `AVVideoDecoder`) is used on
|
||||
Windows and Linux, but macOS still runs the old player: `wxMediaCtrl2.mm`, an ObjC
|
||||
`BambuPlayer` class dlsym'd from the Bambu network plugin that renders via CALayer.
|
||||
On macOS, `wxMediaCtrl3` is currently aliased to `wxMediaCtrl2` and FFmpeg is not linked
|
||||
into the app at all.
|
||||
|
||||
Goal: make macOS use the same FFmpeg player as Windows/Linux, linking the **static**
|
||||
FFmpeg libraries from the deps build instead of dynamic ones.
|
||||
|
||||
## Current state (verified)
|
||||
|
||||
- New player (Win/Linux): `GUI/wxMediaCtrl3.cpp` + `GUI/AVVideoDecoder.cpp`. Decodes with
|
||||
FFmpeg (libavcodec/libswscale/libavutil), renders frames into `wxImage` (non-Windows) /
|
||||
`wxBitmap` (Windows) drawn in a `paintEvent`, feeds via the `Bambu_*` C API
|
||||
(`BambuTunnel.h`, `BAMBU_DYNAMIC`) dlsym'd from the network plugin through
|
||||
`StaticBambuLib::get()` (`GUI/Printer/PrinterFileSystem.cpp`, compiled on all platforms).
|
||||
- Old player (macOS): `GUI/wxMediaCtrl2.mm` uses the ObjC `BambuPlayer` class found via
|
||||
`dlsym(module, "OBJC_CLASS_$_BambuPlayer")` in `libBambuSource.dylib`.
|
||||
- The macOS network plugin `libBambuSource.dylib` already exports the full Bambu C API
|
||||
(verified with `nm`), so the new player needs zero plugin changes.
|
||||
- FFmpeg linking in `src/slic3r/CMakeLists.txt` is guarded by `if (NOT APPLE)` —
|
||||
macOS currently does not link FFmpeg.
|
||||
- `deps/FFMPEG/FFMPEG.cmake`: non-MSVC branch builds FFmpeg from source with
|
||||
`--enable-shared`. The existing arm64 deps build on the dev machine happened to be
|
||||
configured with both static and shared enabled, so `libavcodec.a` / `libswscale.a` /
|
||||
`libavutil.a` are already present at
|
||||
`deps/build/arm64/OrcaSlicer_dep/usr/local/lib/`.
|
||||
- `EVT_MEDIA_CTRL_STAT` is `wxDEFINE_EVENT`'d in `wxMediaCtrl2.cpp` (Win/Linux) and
|
||||
`wxMediaCtrl2.mm` (macOS); the define in `wxMediaCtrl3.cpp` is commented out.
|
||||
- `wxMediaCtrl2` is never instantiated anywhere on any platform — dead code.
|
||||
- `StatusPanel` already creates `wxMediaCtrl3`; `MediaPlayCtrl` only uses the
|
||||
`wxMediaCtrl3` interface (`Load/Play/Stop/GetState/GetVideoSize/GetLastError/SetIdleImage`),
|
||||
so no UI-side changes are needed.
|
||||
|
||||
## Approach (approved)
|
||||
|
||||
**Reuse the shared player on macOS.** Compile the existing `wxMediaCtrl3.cpp` +
|
||||
`AVVideoDecoder.cpp` on macOS so all three platforms run one implementation.
|
||||
Rendering uses the existing `wxImage` → `DrawBitmap` paint path, identical to Linux.
|
||||
Known trade-off: frames are scaled to the widget's logical (1x) size, so Retina is
|
||||
slightly soft compared to the old CALayer player. Accepted for now; a Retina-aware
|
||||
scaling follow-up is possible later.
|
||||
|
||||
Rejected alternative: a native CGImage/CALayer renderer for macOS — faster and
|
||||
Retina-crisp, but adds a second render implementation to maintain.
|
||||
|
||||
## Changes
|
||||
|
||||
### 1. Enable the FFmpeg player on macOS (source)
|
||||
|
||||
- `GUI/wxMediaCtrl3.h`: remove the `#ifdef __WXMAC__` branch (lines 18–22) that aliases
|
||||
`wxMediaCtrl3` → `wxMediaCtrl2`. macOS then compiles the real `wxMediaCtrl3` class,
|
||||
including the `BAMBU_DYNAMIC` BambuTunnel path used on Linux.
|
||||
- Event symbol fix: move `wxDEFINE_EVENT(EVT_MEDIA_CTRL_STAT, wxCommandEvent)` into
|
||||
`wxMediaCtrl3.cpp` (uncomment the existing line) and remove it from
|
||||
`wxMediaCtrl2.cpp`. One definition total in the lib; all three platforms resolve it.
|
||||
|
||||
### 2. Static FFmpeg linking (deps + app)
|
||||
|
||||
- `deps/FFMPEG/FFMPEG.cmake`: in the non-MSVC branch, pass
|
||||
`--disable-shared --enable-static` when `APPLE`. Linux keeps `--enable-shared`;
|
||||
Windows keeps its prebuilt shared DLL zips. Fresh macOS deps builds install only
|
||||
`libavcodec.a` / `libswscale.a` / `libavutil.a` — no dylibs to bundle, no
|
||||
rpath/install_name handling. (The existing local arm64 deps build already contains
|
||||
the `.a` files, so no deps rebuild is strictly needed to try the change locally,
|
||||
but a fresh CI deps build must produce them.)
|
||||
- `src/slic3r/CMakeLists.txt`:
|
||||
- APPLE branch of `SLIC3R_GUI_SOURCES`: add `GUI/wxMediaCtrl3.cpp`,
|
||||
`GUI/wxMediaCtrl3.h`, `GUI/AVVideoDecoder.cpp`, `GUI/AVVideoDecoder.hpp`;
|
||||
remove `GUI/wxMediaCtrl2.mm` and `GUI/wxMediaCtrl2.h` (the `.h` stays on
|
||||
disk for the Win/Linux build of `wxMediaCtrl2.cpp`, but nothing on macOS
|
||||
includes it after this change).
|
||||
- Add an APPLE mirror of the `NOT APPLE` FFmpeg block: `find_library` for
|
||||
`libavcodec.a`, `libswscale.a`, `libavutil.a` under `${CMAKE_PREFIX_PATH}/lib`
|
||||
with `NO_DEFAULT_PATH`, link them (order avcodec → swscale → avutil), and add
|
||||
`${CMAKE_PREFIX_PATH}/include` as a SYSTEM include directory. Deps are built with
|
||||
`--disable-zlib` and no external codecs, so the three static libs link cleanly.
|
||||
|
||||
### 3. Remove the old player
|
||||
|
||||
- Delete `GUI/wxMediaCtrl2.mm` and `GUI/BambuPlayer/BambuPlayer.h` (header used only
|
||||
by the `.mm`; the real `BambuPlayer` lives inside the network plugin).
|
||||
- Remove the now-dead `__WXMAC__` section of `GUI/wxMediaCtrl2.h`.
|
||||
- `wxMediaCtrl2.cpp` (Win/Linux) stays in the build as-is (dead but harmless; out of
|
||||
scope to remove on this branch).
|
||||
|
||||
### 4. Verification
|
||||
|
||||
- Build on macOS: `cmake --build build_arm64` (or `build/arm64`).
|
||||
- Confirm no dynamic FFmpeg dependency: `otool -L` on the app binary shows no `libav*`
|
||||
dylib references.
|
||||
- Runtime: with the network plugin loaded, the Device tab camera preview streams via
|
||||
the FFmpeg player (check the device page / `MediaPlayCtrl`).
|
||||
- macOS `ctest` still passes — static linking means no test-executable `.so` copying
|
||||
hacks (unlike the Linux shared-lib setup).
|
||||
|
||||
## Out of scope
|
||||
|
||||
- Linux (shared libs, AppImage/flatpak bundling) and Windows (prebuilt DLL zips)
|
||||
keep their current FFmpeg setup.
|
||||
- Retina-aware frame scaling / native CGImage rendering (follow-up if visual quality
|
||||
is judged insufficient).
|
||||
- Audio streaming (neither player plays audio in this UI path).
|
||||
@@ -193,6 +193,7 @@ src/slic3r/GUI/ObjColorDialog.cpp
|
||||
src/slic3r/GUI/SyncAmsInfoDialog.cpp
|
||||
src/slic3r/GUI/WipeTowerDialog.cpp
|
||||
src/slic3r/GUI/wxExtensions.cpp
|
||||
src/slic3r/GUI/wxMediaCtrl2.cpp
|
||||
src/slic3r/GUI/WebUserLoginDialog.cpp
|
||||
src/slic3r/GUI/WebGuideDialog.cpp
|
||||
src/slic3r/GUI/KBShortcutsDialog.hpp
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,79 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Belt temperature-tower asset generator (discrete-provini design).
|
||||
|
||||
A vertical temperature tower cannot be sliced on a belt printer, so lay a row of
|
||||
DISCRETE provini (one per temperature) along the belt (designed Y) with a fixed
|
||||
surface gap. Each provino is the chevron+arc unit (belt_temp_provino_unit.stl,
|
||||
keel-first); its temperature is ENGRAVED upright into the 50 mm face — a raised
|
||||
number would be an unsupported overhang on the belt. The C++ calib_temp belt branch
|
||||
(Plater.cpp) injects one M104 per zone 70 layers INTO provino i:
|
||||
print_z[i] = i * PITCH * cos(theta) + 70 * layer_height (theta = 45)
|
||||
inside the body, not in the empty inter-provino gap (which has no sliced layers for
|
||||
the event to attach to). PITCH below is the shared geometry contract with that code —
|
||||
keep them in sync.
|
||||
|
||||
Generates one STL per filament temp range used by Temp_Calibration_Dlg.
|
||||
"""
|
||||
import numpy as np, trimesh, os
|
||||
from matplotlib.textpath import TextPath
|
||||
from matplotlib.font_manager import FontProperties
|
||||
from shapely.geometry import Polygon as ShPoly
|
||||
from shapely.ops import unary_union
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
UNIT = os.path.join(HERE, 'belt_temp_provino_unit.stl') # single provino, keel-first
|
||||
SURF_GAP = 25.0 # surface-to-surface gap between provini (mm) — user spec
|
||||
TEXT_H = 9.0
|
||||
TEXT_DEPTH = 0.8 # engraving depth (numbers are CUT into the face, not raised:
|
||||
# a raised number is an unsupported Y-overhang on the belt)
|
||||
TEXT_OVERSHOOT = 0.6 # extra height poking out of the face for a clean boolean cut
|
||||
|
||||
# Temperature ranges (start, end) per filament family, 5 C step. File name encodes them.
|
||||
RANGES = [(230,190),(270,230),(250,230),(280,240),(240,210),(320,280)]
|
||||
|
||||
unit = trimesh.load(UNIT)
|
||||
dY = unit.bounds[1,1] - unit.bounds[0,1]
|
||||
PITCH = dY + SURF_GAP # designed-Y pitch == C++ contract constant
|
||||
print(f"unit dY={dY:.2f} PITCH={PITCH:.3f} (C++ contract: print_z[i]=i*{PITCH:.3f}*cos45)")
|
||||
|
||||
# 50 mm face normal (0,-1,1)/sqrt2 ; UPRIGHT basis u=+X det(+1) (verified non-mirrored)
|
||||
n = np.array([0,-1,1.])/np.sqrt(2)
|
||||
u = np.array([1,0,0.]); v = np.array([0,1,1.])/np.sqrt(2)
|
||||
R = np.column_stack([u,v,n])
|
||||
fn = unit.face_normals; fc = unit.triangles_center; fa = unit.area_faces
|
||||
sel = (fn@n) > 0.9
|
||||
face_c = (fc[sel]*fa[sel,None]).sum(0)/fa[sel].sum()
|
||||
|
||||
def text_mesh(s):
|
||||
tp = TextPath((0,0), s, size=TEXT_H, prop=FontProperties(family='DejaVu Sans'))
|
||||
rings = [ShPoly(p) for p in tp.to_polygons() if len(p)>=3]
|
||||
rings.sort(key=lambda r:r.area, reverse=True)
|
||||
used=[False]*len(rings); parts=[]
|
||||
for i,o in enumerate(rings):
|
||||
if used[i]: continue
|
||||
holes=[]
|
||||
for j in range(i+1,len(rings)):
|
||||
if not used[j] and o.contains(rings[j]): holes.append(rings[j].exterior.coords); used[j]=True
|
||||
parts.append(ShPoly(o.exterior.coords,holes)); used[i]=True
|
||||
poly = unary_union(parts)
|
||||
geoms = list(poly.geoms) if poly.geom_type=='MultiPolygon' else [poly]
|
||||
m = trimesh.util.concatenate([trimesh.creation.extrude_polygon(g,height=TEXT_DEPTH+TEXT_OVERSHOOT) for g in geoms])
|
||||
c = m.bounds.mean(axis=0); m.apply_translation([-c[0],-c[1],0]); return m
|
||||
|
||||
for t_start, t_end in RANGES:
|
||||
temps = list(range(t_start, t_end-1, -5))
|
||||
parts=[]
|
||||
for i,T in enumerate(temps):
|
||||
c = unit.copy(); c.apply_translation([0, i*PITCH, 0])
|
||||
t = text_mesh(str(T)); M=np.eye(4); M[:3,:3]=R; t.apply_transform(M)
|
||||
# place the text spanning from TEXT_DEPTH inside the face to TEXT_OVERSHOOT outside,
|
||||
# then CUT it out of the provino (engrave) — no raised material, no Y-overhang.
|
||||
t.apply_translation(face_c - n*TEXT_DEPTH + np.array([0,i*PITCH,0]))
|
||||
c = trimesh.boolean.difference([c, t], engine='manifold')
|
||||
parts.append(c)
|
||||
asset = trimesh.util.concatenate(parts)
|
||||
out = os.path.join(HERE, f"belt_temp_tower_{t_start}_{t_end}.stl")
|
||||
asset.export(out)
|
||||
dims = np.round(asset.bounds[1]-asset.bounds[0],1)
|
||||
wt = all(p.is_watertight for p in parts)
|
||||
print(f" {t_start}->{t_end}: {len(temps)} zones bbox={dims} watertight={wt} -> {os.path.basename(out)}")
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 579 KiB After Width: | Height: | Size: 171 KiB |
@@ -1,13 +1,9 @@
|
||||
{
|
||||
"name": "Custom Printer",
|
||||
"version": "02.04.00.04",
|
||||
"version": "02.04.00.03",
|
||||
"force_update": "0",
|
||||
"description": "My configurations",
|
||||
"machine_model_list": [
|
||||
{
|
||||
"name": "Generic Belt Printer",
|
||||
"sub_path": "machine/MyBeltPrinter.json"
|
||||
},
|
||||
{
|
||||
"name": "Generic Klipper Printer",
|
||||
"sub_path": "machine/MyKlipper.json"
|
||||
@@ -66,14 +62,6 @@
|
||||
"name": "0.16mm Optimal @MyKlipper",
|
||||
"sub_path": "process/0.16mm Optimal @MyKlipper.json"
|
||||
},
|
||||
{
|
||||
"name": "0.12mm Fine @MyBeltPrinter",
|
||||
"sub_path": "process/0.12mm Fine @MyBeltPrinter.json"
|
||||
},
|
||||
{
|
||||
"name": "0.20mm Standard @MyBeltPrinter",
|
||||
"sub_path": "process/0.20mm Standard @MyBeltPrinter.json"
|
||||
},
|
||||
{
|
||||
"name": "0.20mm Standard @MyKlipper",
|
||||
"sub_path": "process/0.20mm Standard @MyKlipper.json"
|
||||
@@ -274,38 +262,18 @@
|
||||
"name": "MyKlipper 0.8 nozzle",
|
||||
"sub_path": "machine/MyKlipper 0.8 nozzle.json"
|
||||
},
|
||||
{
|
||||
"name": "fdm_belt_common",
|
||||
"sub_path": "machine/fdm_belt_common.json"
|
||||
},
|
||||
{
|
||||
"name": "fdm_toolchanger_common",
|
||||
"sub_path": "machine/fdm_toolchanger_common.json"
|
||||
},
|
||||
{
|
||||
"name": "MyRRF 0.4 nozzle",
|
||||
"sub_path": "machine/MyRRF 0.4 nozzle.json"
|
||||
},
|
||||
{
|
||||
"name": "MyBeltPrinter 0.2 nozzle",
|
||||
"sub_path": "machine/MyBeltPrinter 0.2 nozzle.json"
|
||||
},
|
||||
{
|
||||
"name": "MyBeltPrinter 0.4 nozzle",
|
||||
"sub_path": "machine/MyBeltPrinter 0.4 nozzle.json"
|
||||
},
|
||||
{
|
||||
"name": "MyBeltPrinter 0.6 nozzle",
|
||||
"sub_path": "machine/MyBeltPrinter 0.6 nozzle.json"
|
||||
},
|
||||
{
|
||||
"name": "MyBeltPrinter 0.8 nozzle",
|
||||
"sub_path": "machine/MyBeltPrinter 0.8 nozzle.json"
|
||||
},
|
||||
{
|
||||
"name": "MyRepetier 0.4 nozzle",
|
||||
"sub_path": "machine/MyRepetier 0.4 nozzle.json"
|
||||
},
|
||||
{
|
||||
"name": "MyRRF 0.4 nozzle",
|
||||
"sub_path": "machine/MyRRF 0.4 nozzle.json"
|
||||
},
|
||||
{
|
||||
"name": "MyToolChanger 0.2 nozzle",
|
||||
"sub_path": "machine/MyToolChanger 0.2 nozzle.json"
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 30 KiB |
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"type": "machine",
|
||||
"name": "MyBeltPrinter 0.2 nozzle",
|
||||
"inherits": "fdm_belt_common",
|
||||
"from": "system",
|
||||
"setting_id": "3w1uyJdmm14QhDnH",
|
||||
"instantiation": "true",
|
||||
"printer_model": "Generic Belt Printer",
|
||||
"default_print_profile": "0.12mm Fine @MyBeltPrinter",
|
||||
"nozzle_diameter": [
|
||||
"0.2"
|
||||
],
|
||||
"max_layer_height": [
|
||||
"0.16"
|
||||
],
|
||||
"min_layer_height": [
|
||||
"0.04"
|
||||
],
|
||||
"printer_variant": "0.2",
|
||||
"printable_area": [
|
||||
"0x0",
|
||||
"350x0",
|
||||
"350x350",
|
||||
"0x350"
|
||||
],
|
||||
"printable_height": "300"
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"type": "machine",
|
||||
"name": "MyBeltPrinter 0.4 nozzle",
|
||||
"inherits": "fdm_belt_common",
|
||||
"from": "system",
|
||||
"setting_id": "6nRHUtvJOUffocbu",
|
||||
"instantiation": "true",
|
||||
"printer_model": "Generic Belt Printer",
|
||||
"nozzle_diameter": [
|
||||
"0.4"
|
||||
],
|
||||
"printer_variant": "0.4",
|
||||
"printable_area": [
|
||||
"0x0",
|
||||
"350x0",
|
||||
"350x350",
|
||||
"0x350"
|
||||
],
|
||||
"printable_height": "300"
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"type": "machine",
|
||||
"name": "MyBeltPrinter 0.6 nozzle",
|
||||
"inherits": "fdm_belt_common",
|
||||
"from": "system",
|
||||
"setting_id": "K0m9HbUNwKT4UCJV",
|
||||
"instantiation": "true",
|
||||
"printer_model": "Generic Belt Printer",
|
||||
"nozzle_diameter": [
|
||||
"0.6"
|
||||
],
|
||||
"max_layer_height": [
|
||||
"0.4"
|
||||
],
|
||||
"min_layer_height": [
|
||||
"0.12"
|
||||
],
|
||||
"printer_variant": "0.6",
|
||||
"printable_area": [
|
||||
"0x0",
|
||||
"350x0",
|
||||
"350x350",
|
||||
"0x350"
|
||||
],
|
||||
"printable_height": "300"
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
{
|
||||
"type": "machine",
|
||||
"name": "MyBeltPrinter 0.8 nozzle",
|
||||
"inherits": "fdm_belt_common",
|
||||
"from": "system",
|
||||
"setting_id": "rHAweDz4eNwttPNA",
|
||||
"instantiation": "true",
|
||||
"printer_model": "Generic Belt Printer",
|
||||
"nozzle_diameter": [
|
||||
"0.8"
|
||||
],
|
||||
"max_layer_height": [
|
||||
"0.6"
|
||||
],
|
||||
"min_layer_height": [
|
||||
"0.2"
|
||||
],
|
||||
"printer_variant": "0.8",
|
||||
"printable_area": [
|
||||
"0x0",
|
||||
"350x0",
|
||||
"350x350",
|
||||
"0x350"
|
||||
],
|
||||
"printable_height": "300"
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"type": "machine_model",
|
||||
"name": "Generic Belt Printer",
|
||||
"model_id": "my_belt_01",
|
||||
"nozzle_diameter": "0.4;0.2;0.6;0.8",
|
||||
"machine_tech": "FFF",
|
||||
"family": "MyPrinter",
|
||||
"bed_model": "Custom_350_bed.stl",
|
||||
"bed_texture": "orcaslicer_bed_texture.svg",
|
||||
"hotend_model": "",
|
||||
"default_materials": "Generic PLA @System;Generic PLA-CF @System;Generic PETG @System;Generic TPU @System;Generic PC @System;Generic PVA @System;Generic PA @System;Generic PA-CF @System"
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
{
|
||||
"type": "machine",
|
||||
"name": "fdm_belt_common",
|
||||
"inherits": "fdm_klipper_common",
|
||||
"from": "system",
|
||||
"instantiation": "false",
|
||||
"gcode_flavor": "klipper",
|
||||
"single_extruder_multi_material": "0",
|
||||
"default_filament_profile": [
|
||||
"Generic PLA @System"
|
||||
],
|
||||
"default_print_profile": "0.20mm Standard @MyBeltPrinter",
|
||||
"max_layer_height": [
|
||||
"0.32"
|
||||
],
|
||||
"min_layer_height": [
|
||||
"0.08"
|
||||
],
|
||||
"deretraction_speed": [
|
||||
"30"
|
||||
],
|
||||
"extruder_colour": [
|
||||
"#FCE94F"
|
||||
],
|
||||
"extruder_offset": [
|
||||
"0x0"
|
||||
],
|
||||
"long_retractions_when_cut": [
|
||||
"0"
|
||||
],
|
||||
"nozzle_diameter": [
|
||||
"0.4"
|
||||
],
|
||||
"retract_before_wipe": [
|
||||
"70%"
|
||||
],
|
||||
"retract_length_toolchange": [
|
||||
"2"
|
||||
],
|
||||
"retract_lift_above": [
|
||||
"0"
|
||||
],
|
||||
"retract_lift_below": [
|
||||
"0"
|
||||
],
|
||||
"retract_lift_enforce": [
|
||||
"All Surfaces"
|
||||
],
|
||||
"retract_restart_extra": [
|
||||
"0"
|
||||
],
|
||||
"retract_restart_extra_toolchange": [
|
||||
"0"
|
||||
],
|
||||
"retract_when_changing_layer": [
|
||||
"1"
|
||||
],
|
||||
"retraction_distances_when_cut": [
|
||||
"18"
|
||||
],
|
||||
"retraction_length": [
|
||||
"0.8"
|
||||
],
|
||||
"retraction_minimum_travel": [
|
||||
"1"
|
||||
],
|
||||
"retraction_speed": [
|
||||
"30"
|
||||
],
|
||||
"travel_slope": [
|
||||
"3"
|
||||
],
|
||||
"wipe": [
|
||||
"1"
|
||||
],
|
||||
"wipe_distance": [
|
||||
"1"
|
||||
],
|
||||
"z_hop": [
|
||||
"0.4"
|
||||
],
|
||||
"z_hop_types": [
|
||||
"Normal Lift"
|
||||
],
|
||||
"gcode_remap_x": "rev_x",
|
||||
"gcode_remap_y": "pos_z",
|
||||
"gcode_remap_z": "pos_y",
|
||||
"printer_extruder_id": [
|
||||
"1"
|
||||
],
|
||||
"belt_printer": "1",
|
||||
"belt_slice_rotation": "x",
|
||||
"belt_slice_rotation_angle": "45",
|
||||
"belt_slice_rotation_global": "1",
|
||||
"build_plate_tilt_x": "45",
|
||||
"purge_in_prime_tower": "0",
|
||||
"scan_first_layer": "0",
|
||||
"auxiliary_fan": "0"
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"type": "process",
|
||||
"name": "0.12mm Fine @MyBeltPrinter",
|
||||
"inherits": "fdm_process_klipper_common",
|
||||
"from": "system",
|
||||
"setting_id": "EugqqdLJ423bgEwN",
|
||||
"instantiation": "true",
|
||||
"layer_height": "0.12",
|
||||
"initial_layer_print_height": "0.12",
|
||||
"bottom_shell_layers": "5",
|
||||
"top_shell_layers": "6",
|
||||
"support_top_z_distance": "0.08",
|
||||
"support_bottom_z_distance": "0.08",
|
||||
"skirt_loops": "0",
|
||||
"skirt_distance": "0",
|
||||
"compatible_printers": [
|
||||
"MyBeltPrinter 0.2 nozzle",
|
||||
"MyBeltPrinter 0.4 nozzle"
|
||||
]
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"type": "process",
|
||||
"name": "0.20mm Standard @MyBeltPrinter",
|
||||
"inherits": "fdm_process_klipper_common",
|
||||
"from": "system",
|
||||
"setting_id": "YzCDAgH3uLOM53pF",
|
||||
"instantiation": "true",
|
||||
"layer_height": "0.2",
|
||||
"initial_layer_print_height": "0.2",
|
||||
"skirt_loops": "0",
|
||||
"skirt_distance": "0",
|
||||
"compatible_printers": [
|
||||
"MyBeltPrinter 0.4 nozzle",
|
||||
"MyBeltPrinter 0.6 nozzle",
|
||||
"MyBeltPrinter 0.8 nozzle"
|
||||
]
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
{
|
||||
"name": "IdeaFormer",
|
||||
"version": "02.00.00.03",
|
||||
"force_update": "0",
|
||||
"description": "IdeaFormer belt printer configurations",
|
||||
"machine_model_list": [
|
||||
{
|
||||
"name": "IdeaFormer IR3 V2",
|
||||
"sub_path": "machine/IdeaFormer IR3 V2.json"
|
||||
}
|
||||
],
|
||||
"process_list": [
|
||||
{
|
||||
"name": "fdm_process_common",
|
||||
"sub_path": "process/fdm_process_common.json"
|
||||
},
|
||||
{
|
||||
"name": "0.20mm Standard @IdeaFormer IR3 V2",
|
||||
"sub_path": "process/0.20mm Standard @IdeaFormer IR3 V2.json"
|
||||
}
|
||||
],
|
||||
"filament_list": [
|
||||
{
|
||||
"name": "Generic PLA @IdeaFormer IR3 V2",
|
||||
"sub_path": "filament/Generic PLA @IdeaFormer IR3 V2.json"
|
||||
},
|
||||
{
|
||||
"name": "eSUN PLA @IdeaFormer IR3 V2",
|
||||
"sub_path": "filament/eSUN PLA @IdeaFormer IR3 V2.json"
|
||||
},
|
||||
{
|
||||
"name": "Generic PETG @IdeaFormer IR3 V2",
|
||||
"sub_path": "filament/Generic PETG @IdeaFormer IR3 V2.json"
|
||||
}
|
||||
],
|
||||
"machine_list": [
|
||||
{
|
||||
"name": "fdm_machine_common",
|
||||
"sub_path": "machine/fdm_machine_common.json"
|
||||
},
|
||||
{
|
||||
"name": "fdm_klipper_common",
|
||||
"sub_path": "machine/fdm_klipper_common.json"
|
||||
},
|
||||
{
|
||||
"name": "fdm_belt_common",
|
||||
"sub_path": "machine/fdm_belt_common.json"
|
||||
},
|
||||
{
|
||||
"name": "IdeaFormer IR3 V2 0.4 nozzle",
|
||||
"sub_path": "machine/IdeaFormer IR3 V2 0.4 nozzle.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 183 KiB |
@@ -1,113 +0,0 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Generic PETG @IdeaFormer IR3 V2",
|
||||
"inherits": "Generic PETG @System",
|
||||
"from": "system",
|
||||
"setting_id": "n4zaXcUUzTqAxq5f",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"IdeaFormer IR3 V2 0.4 nozzle"
|
||||
],
|
||||
"filament_type": [
|
||||
"PETG"
|
||||
],
|
||||
"filament_vendor": [
|
||||
"Generic"
|
||||
],
|
||||
"filament_settings_id": [
|
||||
"Generic PETG @IdeaFormer IR3 V2"
|
||||
],
|
||||
"filament_diameter": [
|
||||
"1.75"
|
||||
],
|
||||
"filament_density": [
|
||||
"1.27"
|
||||
],
|
||||
"filament_flow_ratio": [
|
||||
"0.95"
|
||||
],
|
||||
"filament_cost": [
|
||||
"25"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"10"
|
||||
],
|
||||
"nozzle_temperature": [
|
||||
"240"
|
||||
],
|
||||
"nozzle_temperature_initial_layer": [
|
||||
"245"
|
||||
],
|
||||
"nozzle_temperature_range_low": [
|
||||
"220"
|
||||
],
|
||||
"nozzle_temperature_range_high": [
|
||||
"260"
|
||||
],
|
||||
"temperature_vitrification": [
|
||||
"70"
|
||||
],
|
||||
"hot_plate_temp": [
|
||||
"80"
|
||||
],
|
||||
"hot_plate_temp_initial_layer": [
|
||||
"80"
|
||||
],
|
||||
"cool_plate_temp": [
|
||||
"80"
|
||||
],
|
||||
"cool_plate_temp_initial_layer": [
|
||||
"80"
|
||||
],
|
||||
"textured_plate_temp": [
|
||||
"80"
|
||||
],
|
||||
"textured_plate_temp_initial_layer": [
|
||||
"80"
|
||||
],
|
||||
"fan_min_speed": [
|
||||
"40"
|
||||
],
|
||||
"fan_max_speed": [
|
||||
"60"
|
||||
],
|
||||
"overhang_fan_threshold": [
|
||||
"25%"
|
||||
],
|
||||
"overhang_fan_speed": [
|
||||
"80"
|
||||
],
|
||||
"close_fan_the_first_x_layers": [
|
||||
"3"
|
||||
],
|
||||
"full_fan_speed_layer": [
|
||||
"8"
|
||||
],
|
||||
"slow_down_min_speed": [
|
||||
"20"
|
||||
],
|
||||
"slow_down_layer_time": [
|
||||
"4"
|
||||
],
|
||||
"fan_cooling_layer_time": [
|
||||
"100"
|
||||
],
|
||||
"reduce_fan_stop_start_freq": [
|
||||
"1"
|
||||
],
|
||||
"filament_retraction_length": [
|
||||
"2"
|
||||
],
|
||||
"filament_retraction_speed": [
|
||||
"40"
|
||||
],
|
||||
"filament_deretraction_speed": [
|
||||
"40"
|
||||
],
|
||||
"filament_z_hop": [
|
||||
"0.4"
|
||||
],
|
||||
"filament_start_gcode": [
|
||||
"; Generic PETG @IdeaFormer IR3 V2 — belt PETG, bed 80C"
|
||||
]
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Generic PLA @IdeaFormer IR3 V2",
|
||||
"inherits": "Generic PLA @System",
|
||||
"from": "system",
|
||||
"setting_id": "1xjycsEAFh6KQIhp",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"IdeaFormer IR3 V2 0.4 nozzle"
|
||||
],
|
||||
"filament_type": [
|
||||
"PLA"
|
||||
],
|
||||
"filament_vendor": [
|
||||
"Generic"
|
||||
],
|
||||
"filament_settings_id": [
|
||||
"Generic PLA @IdeaFormer IR3 V2"
|
||||
],
|
||||
"filament_diameter": [
|
||||
"1.75"
|
||||
],
|
||||
"filament_density": [
|
||||
"1.24"
|
||||
],
|
||||
"filament_flow_ratio": [
|
||||
"0.98"
|
||||
],
|
||||
"filament_cost": [
|
||||
"20"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"12"
|
||||
],
|
||||
"nozzle_temperature": [
|
||||
"215"
|
||||
],
|
||||
"nozzle_temperature_initial_layer": [
|
||||
"220"
|
||||
],
|
||||
"nozzle_temperature_range_low": [
|
||||
"190"
|
||||
],
|
||||
"nozzle_temperature_range_high": [
|
||||
"240"
|
||||
],
|
||||
"temperature_vitrification": [
|
||||
"45"
|
||||
],
|
||||
"hot_plate_temp": [
|
||||
"75"
|
||||
],
|
||||
"hot_plate_temp_initial_layer": [
|
||||
"75"
|
||||
],
|
||||
"cool_plate_temp": [
|
||||
"75"
|
||||
],
|
||||
"cool_plate_temp_initial_layer": [
|
||||
"75"
|
||||
],
|
||||
"textured_plate_temp": [
|
||||
"75"
|
||||
],
|
||||
"textured_plate_temp_initial_layer": [
|
||||
"75"
|
||||
],
|
||||
"fan_min_speed": [
|
||||
"100"
|
||||
],
|
||||
"fan_max_speed": [
|
||||
"100"
|
||||
],
|
||||
"overhang_fan_threshold": [
|
||||
"50%"
|
||||
],
|
||||
"overhang_fan_speed": [
|
||||
"100"
|
||||
],
|
||||
"close_fan_the_first_x_layers": [
|
||||
"3"
|
||||
],
|
||||
"full_fan_speed_layer": [
|
||||
"8"
|
||||
],
|
||||
"slow_down_min_speed": [
|
||||
"20"
|
||||
],
|
||||
"slow_down_layer_time": [
|
||||
"4"
|
||||
],
|
||||
"fan_cooling_layer_time": [
|
||||
"100"
|
||||
],
|
||||
"reduce_fan_stop_start_freq": [
|
||||
"1"
|
||||
],
|
||||
"filament_retraction_length": [
|
||||
"1.5"
|
||||
],
|
||||
"filament_retraction_speed": [
|
||||
"35"
|
||||
],
|
||||
"filament_deretraction_speed": [
|
||||
"30"
|
||||
],
|
||||
"filament_z_hop": [
|
||||
"0.4"
|
||||
],
|
||||
"filament_start_gcode": [
|
||||
"; Generic PLA @IdeaFormer IR3 V2 — belt PLA, bed 75C"
|
||||
]
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "eSUN PLA @IdeaFormer IR3 V2",
|
||||
"inherits": "Generic PLA @IdeaFormer IR3 V2",
|
||||
"from": "system",
|
||||
"setting_id": "XqkviBmFHEglXueX",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"IdeaFormer IR3 V2 0.4 nozzle"
|
||||
],
|
||||
"filament_type": [
|
||||
"PLA"
|
||||
],
|
||||
"filament_vendor": [
|
||||
"eSUN"
|
||||
],
|
||||
"filament_settings_id": [
|
||||
"eSUN PLA @IdeaFormer IR3 V2"
|
||||
],
|
||||
"nozzle_temperature_initial_layer": [
|
||||
"200"
|
||||
],
|
||||
"nozzle_temperature": [
|
||||
"200"
|
||||
],
|
||||
"enable_pressure_advance": [
|
||||
"1"
|
||||
],
|
||||
"pressure_advance": [
|
||||
"0.12"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"20"
|
||||
]
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
{
|
||||
"type": "machine",
|
||||
"name": "IdeaFormer IR3 V2 0.4 nozzle",
|
||||
"inherits": "fdm_belt_common",
|
||||
"from": "system",
|
||||
"setting_id": "MDQZgwRgg72lmjtu",
|
||||
"instantiation": "true",
|
||||
"printer_model": "IdeaFormer IR3 V2",
|
||||
"printer_variant": "0.4",
|
||||
"nozzle_diameter": [
|
||||
"0.4"
|
||||
],
|
||||
"printable_area": [
|
||||
"0x0",
|
||||
"250x0",
|
||||
"250x2000",
|
||||
"0x2000"
|
||||
],
|
||||
"printable_height": "250",
|
||||
"belt_printer_infinite_y": "1",
|
||||
"thumbnails": [
|
||||
"48x48/PNG",
|
||||
"300x300/PNG"
|
||||
],
|
||||
"default_filament_profile": [
|
||||
"Generic PLA @IdeaFormer IR3 V2"
|
||||
],
|
||||
"default_print_profile": "0.20mm Standard @IdeaFormer IR3 V2",
|
||||
"use_relative_e_distances": "1",
|
||||
"machine_max_acceleration_e": [
|
||||
"5000"
|
||||
],
|
||||
"machine_max_acceleration_extruding": [
|
||||
"5000"
|
||||
],
|
||||
"machine_max_acceleration_retracting": [
|
||||
"1000"
|
||||
],
|
||||
"machine_max_acceleration_travel": [
|
||||
"9000"
|
||||
],
|
||||
"machine_max_acceleration_x": [
|
||||
"5000"
|
||||
],
|
||||
"machine_max_acceleration_y": [
|
||||
"5000"
|
||||
],
|
||||
"machine_max_acceleration_z": [
|
||||
"100"
|
||||
],
|
||||
"machine_max_jerk_e": [
|
||||
"2.5"
|
||||
],
|
||||
"machine_max_jerk_x": [
|
||||
"10"
|
||||
],
|
||||
"machine_max_jerk_y": [
|
||||
"10"
|
||||
],
|
||||
"machine_max_jerk_z": [
|
||||
"0.4"
|
||||
],
|
||||
"machine_max_speed_e": [
|
||||
"60"
|
||||
],
|
||||
"machine_max_speed_x": [
|
||||
"500"
|
||||
],
|
||||
"machine_max_speed_y": [
|
||||
"500"
|
||||
],
|
||||
"machine_max_speed_z": [
|
||||
"20"
|
||||
],
|
||||
"retraction_length": [
|
||||
"2"
|
||||
],
|
||||
"retraction_speed": [
|
||||
"40"
|
||||
],
|
||||
"deretraction_speed": [
|
||||
"40"
|
||||
],
|
||||
"z_hop": [
|
||||
"0.4"
|
||||
],
|
||||
"retract_lift_below": [
|
||||
"300"
|
||||
],
|
||||
"machine_start_gcode": "; === IdeaFormer IR3 V2 Belt Printer Start ===\n; Axes: X=lateral, Y=gantry height (probe), Z=belt\nG90 ; absolute positioning\nM82 ; absolute extruder\nG21 ; millimeters\nG28 ; home all axes\nG1 Y20 F500 ; lift nozzle 20mm from belt\n; Bed + hotend temps come from the active filament profile. Belt PLA requires 75 C bed — use Generic/eSun PLA @IdeaFormer IR3 V2 filament presets to get it automatically.\nM140 S[hot_plate_temp_initial_layer] ; set bed temp\nM104 S[nozzle_temperature_initial_layer] ; hotend temp\nM109 S[nozzle_temperature_initial_layer] ; wait hotend\nM190 S[hot_plate_temp_initial_layer] ; wait bed\n; --- Purge blob ---\nG92 E0 ; zero extruder\nG1 Y.1 ; nozzle 0.1mm above belt\nG1 E15 F1000 ; purge 15mm blob\nG1 Z20 E25 F800 ; belt advance 20mm + extrude\nG1 E23 ; retract 2mm\nG28 Y ; re-probe belt surface\nG1 E25 ; de-retract\n; --- Prime lines (full 250mm bed width) ---\nFMS_on ; filament motion sensor\nG1 X250 E50 F2000 ; prime line 1\nG92 Z0 ; reset belt origin\nG1 Z.4 ; belt advance 0.4mm\nG1 X0 E75 ; prime line 2\nG1 F1000 ; default feedrate\nG92 E0 Z0 ; zero extruder + belt = print origin\n",
|
||||
"machine_end_gcode": "; === IdeaFormer IR3 V2 Belt Printer End ===\nM400 ; wait for moves to finish\nM104 S0 ; heater off\nM140 S0 ; bed off\nG92 E0 ; zero extruder\nG1 E-5 F300 ; retract 5mm\nG4 P5000 ; wait for ooze\nG91 ; relative mode - keep every end move relative on a belt\nG1 Y20 F1000 ; raise gantry 20mm for clearance over the part\nG1 Z676 F3000 ; advance belt one full machine-depth to eject the part and clean the belt\nG90 ; back to absolute\nG28 X ; home X only - NEVER 'G28' all: that homes Z/belt and reverses the whole print back into the gantry\nFMS_off ; filament motion sensor off\nBED_MESH_CLEAR\nM84 ; disable motors\n",
|
||||
"machine_pause_gcode": "PAUSE",
|
||||
"layer_change_gcode": "G92 E0 ; belt: reset extruder at layer change (relative E)"
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"type": "machine_model",
|
||||
"name": "IdeaFormer IR3 V2",
|
||||
"model_id": "IdeaFormer_IR3_V2",
|
||||
"nozzle_diameter": "0.4",
|
||||
"machine_tech": "FFF",
|
||||
"family": "IdeaFormer",
|
||||
"bed_model": "",
|
||||
"bed_texture": "",
|
||||
"hotend_model": "",
|
||||
"default_materials": "Generic PLA @IdeaFormer IR3 V2;Generic PETG @IdeaFormer IR3 V2"
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
{
|
||||
"type": "machine",
|
||||
"name": "fdm_belt_common",
|
||||
"inherits": "fdm_klipper_common",
|
||||
"from": "system",
|
||||
"instantiation": "false",
|
||||
"gcode_flavor": "klipper",
|
||||
"single_extruder_multi_material": "0",
|
||||
"default_filament_profile": [
|
||||
"Generic PLA @System"
|
||||
],
|
||||
"default_print_profile": "0.20mm Standard @IdeaFormer IR3 V2",
|
||||
"max_layer_height": [
|
||||
"0.32"
|
||||
],
|
||||
"min_layer_height": [
|
||||
"0.08"
|
||||
],
|
||||
"deretraction_speed": [
|
||||
"30"
|
||||
],
|
||||
"extruder_colour": [
|
||||
"#FCE94F"
|
||||
],
|
||||
"extruder_offset": [
|
||||
"0x0"
|
||||
],
|
||||
"long_retractions_when_cut": [
|
||||
"0"
|
||||
],
|
||||
"nozzle_diameter": [
|
||||
"0.4"
|
||||
],
|
||||
"retract_before_wipe": [
|
||||
"70%"
|
||||
],
|
||||
"retract_length_toolchange": [
|
||||
"2"
|
||||
],
|
||||
"retract_lift_above": [
|
||||
"0"
|
||||
],
|
||||
"retract_lift_below": [
|
||||
"0"
|
||||
],
|
||||
"retract_lift_enforce": [
|
||||
"All Surfaces"
|
||||
],
|
||||
"retract_restart_extra": [
|
||||
"0"
|
||||
],
|
||||
"retract_restart_extra_toolchange": [
|
||||
"0"
|
||||
],
|
||||
"retract_when_changing_layer": [
|
||||
"1"
|
||||
],
|
||||
"retraction_distances_when_cut": [
|
||||
"18"
|
||||
],
|
||||
"retraction_length": [
|
||||
"0.8"
|
||||
],
|
||||
"retraction_minimum_travel": [
|
||||
"1"
|
||||
],
|
||||
"retraction_speed": [
|
||||
"30"
|
||||
],
|
||||
"travel_slope": [
|
||||
"3"
|
||||
],
|
||||
"wipe": [
|
||||
"1"
|
||||
],
|
||||
"wipe_distance": [
|
||||
"1"
|
||||
],
|
||||
"z_hop": [
|
||||
"0.4"
|
||||
],
|
||||
"z_hop_types": [
|
||||
"Normal Lift"
|
||||
],
|
||||
"gcode_remap_x": "rev_x",
|
||||
"gcode_remap_y": "pos_z",
|
||||
"gcode_remap_z": "pos_y",
|
||||
"printer_extruder_id": [
|
||||
"1"
|
||||
],
|
||||
"belt_printer": "1",
|
||||
"belt_slice_rotation": "x",
|
||||
"belt_slice_rotation_angle": "45",
|
||||
"belt_slice_rotation_global": "1",
|
||||
"build_plate_tilt_x": "45",
|
||||
"purge_in_prime_tower": "0",
|
||||
"scan_first_layer": "0",
|
||||
"auxiliary_fan": "0"
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
{
|
||||
"type": "machine",
|
||||
"name": "fdm_klipper_common",
|
||||
"inherits": "fdm_machine_common",
|
||||
"from": "system",
|
||||
"instantiation": "false",
|
||||
"gcode_flavor": "klipper",
|
||||
"machine_max_acceleration_e": [
|
||||
"5000",
|
||||
"5000"
|
||||
],
|
||||
"machine_max_acceleration_extruding": [
|
||||
"20000",
|
||||
"20000"
|
||||
],
|
||||
"machine_max_acceleration_retracting": [
|
||||
"5000",
|
||||
"5000"
|
||||
],
|
||||
"machine_max_acceleration_travel": [
|
||||
"20000",
|
||||
"20000"
|
||||
],
|
||||
"machine_max_acceleration_x": [
|
||||
"20000",
|
||||
"20000"
|
||||
],
|
||||
"machine_max_acceleration_y": [
|
||||
"20000",
|
||||
"20000"
|
||||
],
|
||||
"machine_max_acceleration_z": [
|
||||
"500",
|
||||
"200"
|
||||
],
|
||||
"machine_max_speed_e": [
|
||||
"25",
|
||||
"25"
|
||||
],
|
||||
"machine_max_speed_x": [
|
||||
"500",
|
||||
"200"
|
||||
],
|
||||
"machine_max_speed_y": [
|
||||
"500",
|
||||
"200"
|
||||
],
|
||||
"machine_max_speed_z": [
|
||||
"12",
|
||||
"12"
|
||||
],
|
||||
"machine_max_jerk_e": [
|
||||
"2.5",
|
||||
"2.5"
|
||||
],
|
||||
"machine_max_jerk_x": [
|
||||
"9",
|
||||
"9"
|
||||
],
|
||||
"machine_max_jerk_y": [
|
||||
"9",
|
||||
"9"
|
||||
],
|
||||
"machine_max_jerk_z": [
|
||||
"0.2",
|
||||
"0.4"
|
||||
],
|
||||
"machine_min_extruding_rate": [
|
||||
"0",
|
||||
"0"
|
||||
],
|
||||
"machine_min_travel_rate": [
|
||||
"0",
|
||||
"0"
|
||||
],
|
||||
"max_layer_height": [
|
||||
"0.32"
|
||||
],
|
||||
"min_layer_height": [
|
||||
"0.08"
|
||||
],
|
||||
"printable_height": "250",
|
||||
"extruder_clearance_radius": "65",
|
||||
"extruder_clearance_height_to_rod": "36",
|
||||
"extruder_clearance_height_to_lid": "140",
|
||||
"printer_settings_id": "",
|
||||
"printer_technology": "FFF",
|
||||
"printer_variant": "0.4",
|
||||
"retraction_minimum_travel": [
|
||||
"1"
|
||||
],
|
||||
"retract_before_wipe": [
|
||||
"70%"
|
||||
],
|
||||
"retract_when_changing_layer": [
|
||||
"1"
|
||||
],
|
||||
"retraction_length": [
|
||||
"0.8"
|
||||
],
|
||||
"retract_length_toolchange": [
|
||||
"2"
|
||||
],
|
||||
"z_hop": [
|
||||
"0.4"
|
||||
],
|
||||
"retract_restart_extra": [
|
||||
"0"
|
||||
],
|
||||
"retract_restart_extra_toolchange": [
|
||||
"0"
|
||||
],
|
||||
"retraction_speed": [
|
||||
"30"
|
||||
],
|
||||
"deretraction_speed": [
|
||||
"30"
|
||||
],
|
||||
"z_hop_types": "Normal Lift",
|
||||
"silent_mode": "0",
|
||||
"single_extruder_multi_material": "1",
|
||||
"change_filament_gcode": "",
|
||||
"wipe": [
|
||||
"1"
|
||||
],
|
||||
"default_filament_profile": [
|
||||
"Generic PLA @System"
|
||||
],
|
||||
"default_print_profile": "0.20mm Standard @MyKlipper",
|
||||
"bed_exclude_area": [
|
||||
"0x0"
|
||||
],
|
||||
"machine_start_gcode": "M190 S[bed_temperature_initial_layer_single]\nM109 S[nozzle_temperature_initial_layer]\nPRINT_START EXTRUDER=[nozzle_temperature_initial_layer] BED=[bed_temperature_initial_layer_single]\n",
|
||||
"machine_end_gcode": "PRINT_END",
|
||||
"layer_change_gcode": ";AFTER_LAYER_CHANGE\n;[layer_z]",
|
||||
"before_layer_change_gcode": ";BEFORE_LAYER_CHANGE\n;[layer_z]\nG92 E0\n",
|
||||
"machine_pause_gcode": "PAUSE",
|
||||
"scan_first_layer": "0",
|
||||
"nozzle_type": "undefine",
|
||||
"auxiliary_fan": "0"
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
{
|
||||
"type": "machine",
|
||||
"name": "fdm_machine_common",
|
||||
"from": "system",
|
||||
"instantiation": "false",
|
||||
"printer_technology": "FFF",
|
||||
"deretraction_speed": [
|
||||
"40"
|
||||
],
|
||||
"extruder_colour": [
|
||||
"#FCE94F"
|
||||
],
|
||||
"extruder_offset": [
|
||||
"0x0"
|
||||
],
|
||||
"gcode_flavor": "marlin",
|
||||
"silent_mode": "0",
|
||||
"machine_max_acceleration_e": [
|
||||
"5000"
|
||||
],
|
||||
"machine_max_acceleration_extruding": [
|
||||
"10000"
|
||||
],
|
||||
"machine_max_acceleration_retracting": [
|
||||
"1000"
|
||||
],
|
||||
"machine_max_acceleration_x": [
|
||||
"10000"
|
||||
],
|
||||
"machine_max_acceleration_y": [
|
||||
"10000"
|
||||
],
|
||||
"machine_max_acceleration_z": [
|
||||
"500"
|
||||
],
|
||||
"machine_max_speed_e": [
|
||||
"60"
|
||||
],
|
||||
"machine_max_speed_x": [
|
||||
"500"
|
||||
],
|
||||
"machine_max_speed_y": [
|
||||
"500"
|
||||
],
|
||||
"machine_max_speed_z": [
|
||||
"10"
|
||||
],
|
||||
"machine_max_jerk_e": [
|
||||
"5"
|
||||
],
|
||||
"machine_max_jerk_x": [
|
||||
"8"
|
||||
],
|
||||
"machine_max_jerk_y": [
|
||||
"8"
|
||||
],
|
||||
"machine_max_jerk_z": [
|
||||
"0.4"
|
||||
],
|
||||
"machine_min_extruding_rate": [
|
||||
"0"
|
||||
],
|
||||
"machine_min_travel_rate": [
|
||||
"0"
|
||||
],
|
||||
"max_layer_height": [
|
||||
"0.32"
|
||||
],
|
||||
"min_layer_height": [
|
||||
"0.08"
|
||||
],
|
||||
"printable_height": "250",
|
||||
"extruder_clearance_radius": "65",
|
||||
"extruder_clearance_height_to_rod": "36",
|
||||
"extruder_clearance_height_to_lid": "140",
|
||||
"nozzle_diameter": [
|
||||
"0.4"
|
||||
],
|
||||
"printer_settings_id": "",
|
||||
"printer_variant": "0.4",
|
||||
"retraction_minimum_travel": [
|
||||
"2"
|
||||
],
|
||||
"retract_before_wipe": [
|
||||
"70%"
|
||||
],
|
||||
"retract_when_changing_layer": [
|
||||
"1"
|
||||
],
|
||||
"retraction_length": [
|
||||
"1"
|
||||
],
|
||||
"retract_length_toolchange": [
|
||||
"1"
|
||||
],
|
||||
"z_hop": [
|
||||
"0"
|
||||
],
|
||||
"retract_restart_extra": [
|
||||
"0"
|
||||
],
|
||||
"retract_restart_extra_toolchange": [
|
||||
"0"
|
||||
],
|
||||
"retraction_speed": [
|
||||
"60"
|
||||
],
|
||||
"single_extruder_multi_material": "1",
|
||||
"change_filament_gcode": "",
|
||||
"wipe": [
|
||||
"1"
|
||||
],
|
||||
"default_print_profile": "",
|
||||
"machine_start_gcode": "G0 Z20 F9000\nG92 E0; G1 E-10 F1200\nG28\nM970 Q1 A10 B10 C130 K0\nM970 Q1 A10 B131 C250 K1\nM974 Q1 S1 P0\nM970 Q0 A10 B10 C130 H20 K0\nM970 Q0 A10 B131 C250 K1\nM974 Q0 S1 P0\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\nG29 ;Home\nG90;\nG92 E0 ;Reset Extruder \nG1 Z2.0 F3000 ;Move Z Axis up \nG1 X10.1 Y20 Z0.28 F5000.0 ;Move to start position\nM109 S205;\nG1 X10.1 Y200.0 Z0.28 F1500.0 E15 ;Draw the first line\nG1 X10.4 Y200.0 Z0.28 F5000.0 ;Move to side a little\nG1 X10.4 Y20 Z0.28 F1500.0 E30 ;Draw the second line\nG92 E0 ;Reset Extruder \nG1 X110 Y110 Z2.0 F3000 ;Move Z Axis up",
|
||||
"machine_end_gcode": "M400 ; wait for buffer to clear\nG92 E0 ; zero the extruder\nG1 E-4.0 F3600; retract \nG91\nG1 Z3;\nM104 S0 ; turn off hotend\nM140 S0 ; turn off bed\nM106 S0 ; turn off fan\nG90 \nG0 X110 Y200 F3600 \nprint_end",
|
||||
"layer_change_gcode": ";AFTER_LAYER_CHANGE\n;[layer_z]",
|
||||
"before_layer_change_gcode": ";BEFORE_LAYER_CHANGE\n;[layer_z]\nG92 E0\n",
|
||||
"machine_pause_gcode": "M601"
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"type": "process",
|
||||
"name": "0.20mm Standard @IdeaFormer IR3 V2",
|
||||
"inherits": "fdm_process_common",
|
||||
"from": "system",
|
||||
"setting_id": "91atcIwv5728phqX",
|
||||
"instantiation": "true",
|
||||
"layer_height": "0.2",
|
||||
"initial_layer_print_height": "0.2",
|
||||
"initial_layer_line_width": "0.42",
|
||||
"wall_loops": "2",
|
||||
"reduce_infill_retraction": "1",
|
||||
"detect_overhang_wall": "1",
|
||||
"skirt_loops": "0",
|
||||
"skirt_distance": "0",
|
||||
"sparse_infill_pattern": "grid",
|
||||
"sparse_infill_speed": "200",
|
||||
"support_base_pattern": "rectilinear",
|
||||
"support_interface_pattern": "rectilinear",
|
||||
"compatible_printers": [
|
||||
"IdeaFormer IR3 V2 0.4 nozzle"
|
||||
]
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
{
|
||||
"type": "process",
|
||||
"name": "fdm_process_common",
|
||||
"from": "system",
|
||||
"instantiation": "false",
|
||||
"adaptive_layer_height": "0",
|
||||
"reduce_crossing_wall": "0",
|
||||
"max_travel_detour_distance": "0",
|
||||
"bottom_surface_pattern": "monotonic",
|
||||
"bottom_shell_thickness": "0",
|
||||
"bridge_speed": "50",
|
||||
"brim_width": "5",
|
||||
"brim_object_gap": "0.1",
|
||||
"compatible_printers": [],
|
||||
"compatible_printers_condition": "",
|
||||
"print_sequence": "by layer",
|
||||
"default_acceleration": "1000",
|
||||
"initial_layer_acceleration": "500",
|
||||
"top_surface_acceleration": "1000",
|
||||
"travel_acceleration": "1000",
|
||||
"inner_wall_acceleration": "1000",
|
||||
"outer_wall_acceleration": "700",
|
||||
"bridge_no_support": "0",
|
||||
"draft_shield": "disabled",
|
||||
"elefant_foot_compensation": "0",
|
||||
"enable_arc_fitting": "0",
|
||||
"wall_infill_order": "inner wall/outer wall/infill",
|
||||
"infill_direction": "45",
|
||||
"sparse_infill_density": "15%",
|
||||
"sparse_infill_pattern": "crosshatch",
|
||||
"initial_layer_print_height": "0.2",
|
||||
"infill_combination": "0",
|
||||
"infill_wall_overlap": "25%",
|
||||
"interface_shells": "0",
|
||||
"ironing_flow": "10%",
|
||||
"ironing_spacing": "0.15",
|
||||
"ironing_speed": "30",
|
||||
"ironing_type": "no ironing",
|
||||
"reduce_infill_retraction": "1",
|
||||
"filename_format": "{input_filename_base}_{layer_height}mm_{filament_type[initial_tool]}_{printer_model}_{print_time}.gcode",
|
||||
"detect_overhang_wall": "1",
|
||||
"slowdown_for_curled_perimeters": "1",
|
||||
"overhang_1_4_speed": "0",
|
||||
"overhang_2_4_speed": "50",
|
||||
"overhang_3_4_speed": "30",
|
||||
"overhang_4_4_speed": "10",
|
||||
"line_width": "110%",
|
||||
"inner_wall_line_width": "110%",
|
||||
"outer_wall_line_width": "100%",
|
||||
"top_surface_line_width": "93.75%",
|
||||
"sparse_infill_line_width": "110%",
|
||||
"initial_layer_line_width": "120%",
|
||||
"internal_solid_infill_line_width": "120%",
|
||||
"support_line_width": "96%",
|
||||
"wall_loops": "3",
|
||||
"print_settings_id": "",
|
||||
"raft_layers": "0",
|
||||
"seam_position": "aligned",
|
||||
"skirt_distance": "2",
|
||||
"skirt_height": "3",
|
||||
"min_skirt_length": "4",
|
||||
"skirt_loops": "0",
|
||||
"minimum_sparse_infill_area": "15",
|
||||
"spiral_mode": "0",
|
||||
"standby_temperature_delta": "-5",
|
||||
"enable_support": "0",
|
||||
"resolution": "0.012",
|
||||
"support_type": "normal(auto)",
|
||||
"support_on_build_plate_only": "0",
|
||||
"support_top_z_distance": "0.2",
|
||||
"support_bottom_z_distance": "0.2",
|
||||
"support_filament": "0",
|
||||
"support_interface_loop_pattern": "0",
|
||||
"support_interface_filament": "0",
|
||||
"support_interface_top_layers": "2",
|
||||
"support_interface_bottom_layers": "2",
|
||||
"support_interface_spacing": "0.5",
|
||||
"support_interface_speed": "80",
|
||||
"support_base_pattern": "default",
|
||||
"support_base_pattern_spacing": "2.5",
|
||||
"support_speed": "150",
|
||||
"support_threshold_angle": "30",
|
||||
"support_object_xy_distance": "0.35",
|
||||
"tree_support_branch_angle": "30",
|
||||
"tree_support_wall_count": "0",
|
||||
"tree_support_with_infill": "0",
|
||||
"detect_thin_wall": "0",
|
||||
"top_surface_pattern": "monotonicline",
|
||||
"top_shell_thickness": "0.8",
|
||||
"enable_prime_tower": "1",
|
||||
"wipe_tower_no_sparse_layers": "0",
|
||||
"prime_tower_width": "60",
|
||||
"xy_hole_compensation": "0",
|
||||
"xy_contour_compensation": "0",
|
||||
"layer_height": "0.2",
|
||||
"bottom_shell_layers": "3",
|
||||
"top_shell_layers": "4",
|
||||
"bridge_flow": "1",
|
||||
"initial_layer_speed": "45",
|
||||
"initial_layer_infill_speed": "45",
|
||||
"outer_wall_speed": "45",
|
||||
"inner_wall_speed": "80",
|
||||
"sparse_infill_speed": "150",
|
||||
"internal_solid_infill_speed": "150",
|
||||
"top_surface_speed": "50",
|
||||
"gap_infill_speed": "30",
|
||||
"travel_speed": "200"
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
{
|
||||
"name": "Printcepts",
|
||||
"version": "01.00.00.01",
|
||||
"force_update": "0",
|
||||
"description": "Printcepts belt printer configurations",
|
||||
"machine_model_list": [
|
||||
{
|
||||
"name": "BabyBelt Pro",
|
||||
"sub_path": "machine/BabyBelt Pro.json"
|
||||
}
|
||||
],
|
||||
"process_list": [
|
||||
{
|
||||
"name": "fdm_process_common",
|
||||
"sub_path": "process/fdm_process_common.json"
|
||||
},
|
||||
{
|
||||
"name": "0.20mm Standard @BabyBelt Pro",
|
||||
"sub_path": "process/0.20mm Standard @BabyBelt Pro.json"
|
||||
}
|
||||
],
|
||||
"filament_list": [
|
||||
{
|
||||
"name": "Generic PLA @BabyBelt Pro",
|
||||
"sub_path": "filament/Generic PLA @BabyBelt Pro.json"
|
||||
},
|
||||
{
|
||||
"name": "eSUN PLA @BabyBelt Pro",
|
||||
"sub_path": "filament/eSUN PLA @BabyBelt Pro.json"
|
||||
},
|
||||
{
|
||||
"name": "Generic PETG @BabyBelt Pro",
|
||||
"sub_path": "filament/Generic PETG @BabyBelt Pro.json"
|
||||
}
|
||||
],
|
||||
"machine_list": [
|
||||
{
|
||||
"name": "fdm_machine_common",
|
||||
"sub_path": "machine/fdm_machine_common.json"
|
||||
},
|
||||
{
|
||||
"name": "fdm_klipper_common",
|
||||
"sub_path": "machine/fdm_klipper_common.json"
|
||||
},
|
||||
{
|
||||
"name": "fdm_belt_common",
|
||||
"sub_path": "machine/fdm_belt_common.json"
|
||||
},
|
||||
{
|
||||
"name": "BabyBelt Pro 0.4 nozzle",
|
||||
"sub_path": "machine/BabyBelt Pro 0.4 nozzle.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="95.0mm" height="500.0mm" viewBox="0 0 95.0 500.0" preserveAspectRatio="xMidYMid meet">
|
||||
<!-- Printcepts BabyBelt Pro bed texture: 95 x 500 mm belt plate. -->
|
||||
<!-- Transparent plate; green (#195F30) BabyBelt Pro logo centered along X, near the bottom edge. -->
|
||||
<rect x="0" y="0" width="95.0" height="500.0" fill="none"/>
|
||||
<g transform="translate(14.2500,436.3488) scale(0.067538)">
|
||||
<g transform="translate(-11.000000,692.938562) scale(0.100000,-0.100000)"
|
||||
fill="#195F30" stroke="none">
|
||||
<path d="M1963 5604 l-1423 -1324 0 -2050 0 -2050 443 0 c244 0 741 3 1105 7
|
||||
l662 6 0 746 c-1 575 -4 768 -14 841 -47 324 -179 486 -473 581 -40 12 -73 26
|
||||
-73 30 0 4 32 17 72 29 212 64 333 166 378 320 35 121 38 191 32 868 l-5 662
|
||||
-629 0 c-395 0 -628 4 -628 10 0 5 635 601 1410 1325 776 724 1410 1318 1410
|
||||
1321 0 2 -190 4 -422 3 l-423 0 -1422 -1325z m-334 -2029 c143 -16 174 -96
|
||||
173 -446 -2 -411 -24 -458 -224 -462 l-93 -2 -3 450 c-1 248 0 456 3 463 3 9
|
||||
18 12 47 8 24 -3 67 -8 97 -11z m-4 -1556 c160 -29 173 -62 182 -469 9 -455
|
||||
-14 -593 -108 -641 -39 -19 -193 -44 -210 -33 -10 6 -13 1147 -3 1157 6 6 45
|
||||
2 139 -14z"/>
|
||||
<path d="M3464 5979 c-142 -132 -263 -245 -268 -250 -6 -5 69 -9 190 -9 l199
|
||||
1 268 249 267 250 -198 0 -198 0 -260 -241z"/>
|
||||
<path d="M3650 5649 c-135 -126 -254 -238 -265 -249 -19 -20 -18 -20 177 -20
|
||||
l197 0 228 211 c125 116 246 229 268 250 l40 39 -200 -1 -200 0 -245 -230z"/>
|
||||
<path d="M2537 5089 c-101 -24 -204 -105 -251 -197 -96 -190 -19 -420 172
|
||||
-514 l67 -33 2670 0 2670 0 57 27 c74 34 146 107 184 183 43 88 43 230 0 322
|
||||
-35 76 -113 153 -193 191 l-58 27 -2640 2 c-1513 0 -2656 -3 -2678 -8z m5063
|
||||
-77 c-57 -37 -118 -111 -140 -168 -31 -82 -25 -206 12 -279 26 -49 93 -121
|
||||
133 -143 15 -8 -640 -11 -2410 -11 l-2430 0 30 21 c200 146 201 425 1 569
|
||||
l-39 29 2434 -1 c2263 0 2432 -1 2409 -17z m-4890 -43 c270 -122 185 -526
|
||||
-109 -522 -257 2 -370 324 -170 485 74 60 194 76 279 37z m5201 -12 c94 -55
|
||||
140 -135 140 -242 0 -285 -393 -374 -517 -117 -26 54 -30 162 -9 219 28 74 97
|
||||
139 173 164 53 17 166 4 213 -24z"/>
|
||||
<path d="M2917 3973 c-4 -174 -7 -550 -7 -835 l0 -518 326 0 326 0 -7 150 -7
|
||||
150 110 0 110 0 11 -32 c5 -18 26 -86 46 -150 l36 -118 325 0 c179 0 323 4
|
||||
320 9 -3 4 -155 374 -337 822 -182 448 -333 820 -336 827 -4 9 -105 12 -457
|
||||
12 l-453 0 -6 -317z m773 -745 c0 -5 -47 -8 -104 -8 l-103 0 -7 92 c-3 50 -6
|
||||
202 -5 337 l1 246 109 -330 c60 -181 109 -333 109 -337z"/>
|
||||
<path d="M4680 3455 l0 -835 448 0 c693 1 885 16 985 80 99 63 126 132 134
|
||||
340 12 327 -44 405 -342 476 -28 7 -27 8 25 19 199 42 255 95 267 248 11 146
|
||||
-32 285 -110 353 -145 126 -329 153 -1049 154 l-358 0 0 -835z m846 520 c36
|
||||
-23 44 -54 44 -162 0 -152 -29 -183 -170 -183 l-40 0 0 186 0 187 71 -6 c39
|
||||
-3 82 -13 95 -22z m4 -625 c33 -18 40 -52 40 -208 0 -200 -9 -214 -143 -228
|
||||
l-67 -7 0 233 0 233 74 -6 c41 -3 84 -10 96 -17z"/>
|
||||
<path d="M6150 4286 c0 -3 131 -242 290 -531 l290 -526 0 -304 0 -305 385 0
|
||||
385 0 0 299 0 299 305 533 305 534 -377 3 c-207 1 -381 -2 -385 -6 -16 -16
|
||||
-110 -258 -172 -444 l-62 -187 -18 77 c-18 75 -139 450 -171 525 l-15 37 -380
|
||||
0 c-209 0 -380 -2 -380 -4z"/>
|
||||
<path d="M2910 1355 l0 -1185 830 0 830 0 0 240 0 240 -350 0 -350 0 0 255 0
|
||||
255 300 0 300 0 0 230 0 230 -300 0 -300 0 0 220 0 220 320 0 320 0 0 240 0
|
||||
240 -800 0 -800 0 0 -1185z"/>
|
||||
<path d="M4680 1355 l0 -1185 775 0 775 0 0 240 0 240 -295 0 -295 0 0 945 0
|
||||
945 -480 0 -480 0 0 -1185z"/>
|
||||
<path d="M5800 2300 l0 -240 280 0 280 0 0 -945 0 -945 480 0 480 0 0 945 0
|
||||
945 285 0 285 0 0 240 0 240 -1045 0 -1045 0 0 -240z"/>
|
||||
<path d="M8032 1358 l-2 -1188 1008 1 c621 1 971 5 912 10 -309 27 -631 139
|
||||
-885 306 -593 391 -984 1122 -1025 1918 -4 77 -8 -394 -8 -1047z"/>
|
||||
<path d="M7441 1934 c-43 -36 -59 -70 -70 -148 -18 -124 16 -252 76 -291 32
|
||||
-21 226 -33 328 -20 114 14 161 97 153 269 -5 96 -24 151 -68 191 -20 18 -39
|
||||
20 -205 23 l-182 3 -32 -27z m389 -199 c7 -8 10 -22 6 -30 -4 -13 -34 -15
|
||||
-186 -15 -189 0 -202 3 -186 45 8 22 348 22 366 0z"/>
|
||||
<path d="M7450 1267 c-14 -6 -35 -32 -47 -57 -21 -41 -23 -58 -23 -222 l0
|
||||
-178 270 0 270 0 0 105 0 105 -121 0 -120 0 3 28 3 27 118 3 117 3 0 99 0 100
|
||||
-113 0 c-121 0 -138 -7 -162 -65 -8 -19 -9 -19 -12 2 -10 55 -116 84 -183 50z
|
||||
m134 -203 c15 -38 8 -44 -54 -44 -62 0 -69 6 -54 44 9 23 99 23 108 0z"/>
|
||||
<path d="M466 1193 l-29 -43 -163 0 -164 0 0 -235 0 -235 165 0 165 0 27 -42
|
||||
28 -42 3 163 c1 89 1 233 0 319 l-3 157 -29 -42z"/>
|
||||
<path d="M7443 620 c-48 -20 -58 -60 -61 -262 l-4 -188 271 0 271 0 0 110 0
|
||||
110 -110 0 -110 0 0 70 c0 76 -21 145 -51 160 -22 12 -176 12 -206 0z m161
|
||||
-186 c15 -39 8 -44 -64 -44 -72 0 -79 5 -64 44 9 23 119 23 128 0z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 4.5 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 55 KiB |
@@ -1,113 +0,0 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Generic PETG @BabyBelt Pro",
|
||||
"inherits": "Generic PETG @System",
|
||||
"from": "system",
|
||||
"setting_id": "gCzHpDNgVwQR6tgk",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"BabyBelt Pro 0.4 nozzle"
|
||||
],
|
||||
"filament_type": [
|
||||
"PETG"
|
||||
],
|
||||
"filament_vendor": [
|
||||
"Generic"
|
||||
],
|
||||
"filament_settings_id": [
|
||||
"Generic PETG @BabyBelt Pro"
|
||||
],
|
||||
"filament_diameter": [
|
||||
"1.75"
|
||||
],
|
||||
"filament_density": [
|
||||
"1.27"
|
||||
],
|
||||
"filament_flow_ratio": [
|
||||
"0.95"
|
||||
],
|
||||
"filament_cost": [
|
||||
"25"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"10"
|
||||
],
|
||||
"nozzle_temperature": [
|
||||
"240"
|
||||
],
|
||||
"nozzle_temperature_initial_layer": [
|
||||
"245"
|
||||
],
|
||||
"nozzle_temperature_range_low": [
|
||||
"220"
|
||||
],
|
||||
"nozzle_temperature_range_high": [
|
||||
"260"
|
||||
],
|
||||
"temperature_vitrification": [
|
||||
"70"
|
||||
],
|
||||
"hot_plate_temp": [
|
||||
"80"
|
||||
],
|
||||
"hot_plate_temp_initial_layer": [
|
||||
"80"
|
||||
],
|
||||
"cool_plate_temp": [
|
||||
"80"
|
||||
],
|
||||
"cool_plate_temp_initial_layer": [
|
||||
"80"
|
||||
],
|
||||
"textured_plate_temp": [
|
||||
"80"
|
||||
],
|
||||
"textured_plate_temp_initial_layer": [
|
||||
"80"
|
||||
],
|
||||
"fan_min_speed": [
|
||||
"40"
|
||||
],
|
||||
"fan_max_speed": [
|
||||
"60"
|
||||
],
|
||||
"overhang_fan_threshold": [
|
||||
"25%"
|
||||
],
|
||||
"overhang_fan_speed": [
|
||||
"80"
|
||||
],
|
||||
"close_fan_the_first_x_layers": [
|
||||
"3"
|
||||
],
|
||||
"full_fan_speed_layer": [
|
||||
"8"
|
||||
],
|
||||
"slow_down_min_speed": [
|
||||
"20"
|
||||
],
|
||||
"slow_down_layer_time": [
|
||||
"4"
|
||||
],
|
||||
"fan_cooling_layer_time": [
|
||||
"100"
|
||||
],
|
||||
"reduce_fan_stop_start_freq": [
|
||||
"1"
|
||||
],
|
||||
"filament_retraction_length": [
|
||||
"2"
|
||||
],
|
||||
"filament_retraction_speed": [
|
||||
"40"
|
||||
],
|
||||
"filament_deretraction_speed": [
|
||||
"40"
|
||||
],
|
||||
"filament_z_hop": [
|
||||
"0.4"
|
||||
],
|
||||
"filament_start_gcode": [
|
||||
"; Generic PETG @BabyBelt Pro — belt PETG, bed 80C"
|
||||
]
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "Generic PLA @BabyBelt Pro",
|
||||
"inherits": "Generic PLA @System",
|
||||
"from": "system",
|
||||
"setting_id": "24PpcnhVx9v5f4fD",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"BabyBelt Pro 0.4 nozzle"
|
||||
],
|
||||
"filament_type": [
|
||||
"PLA"
|
||||
],
|
||||
"filament_vendor": [
|
||||
"Generic"
|
||||
],
|
||||
"filament_settings_id": [
|
||||
"Generic PLA @BabyBelt Pro"
|
||||
],
|
||||
"filament_diameter": [
|
||||
"1.75"
|
||||
],
|
||||
"filament_density": [
|
||||
"1.24"
|
||||
],
|
||||
"filament_flow_ratio": [
|
||||
"0.98"
|
||||
],
|
||||
"filament_cost": [
|
||||
"20"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"12"
|
||||
],
|
||||
"nozzle_temperature": [
|
||||
"215"
|
||||
],
|
||||
"nozzle_temperature_initial_layer": [
|
||||
"220"
|
||||
],
|
||||
"nozzle_temperature_range_low": [
|
||||
"190"
|
||||
],
|
||||
"nozzle_temperature_range_high": [
|
||||
"240"
|
||||
],
|
||||
"temperature_vitrification": [
|
||||
"45"
|
||||
],
|
||||
"hot_plate_temp": [
|
||||
"75"
|
||||
],
|
||||
"hot_plate_temp_initial_layer": [
|
||||
"75"
|
||||
],
|
||||
"cool_plate_temp": [
|
||||
"75"
|
||||
],
|
||||
"cool_plate_temp_initial_layer": [
|
||||
"75"
|
||||
],
|
||||
"textured_plate_temp": [
|
||||
"75"
|
||||
],
|
||||
"textured_plate_temp_initial_layer": [
|
||||
"75"
|
||||
],
|
||||
"fan_min_speed": [
|
||||
"100"
|
||||
],
|
||||
"fan_max_speed": [
|
||||
"100"
|
||||
],
|
||||
"overhang_fan_threshold": [
|
||||
"50%"
|
||||
],
|
||||
"overhang_fan_speed": [
|
||||
"100"
|
||||
],
|
||||
"close_fan_the_first_x_layers": [
|
||||
"3"
|
||||
],
|
||||
"full_fan_speed_layer": [
|
||||
"8"
|
||||
],
|
||||
"slow_down_min_speed": [
|
||||
"20"
|
||||
],
|
||||
"slow_down_layer_time": [
|
||||
"4"
|
||||
],
|
||||
"fan_cooling_layer_time": [
|
||||
"100"
|
||||
],
|
||||
"reduce_fan_stop_start_freq": [
|
||||
"1"
|
||||
],
|
||||
"filament_retraction_length": [
|
||||
"1.5"
|
||||
],
|
||||
"filament_retraction_speed": [
|
||||
"35"
|
||||
],
|
||||
"filament_deretraction_speed": [
|
||||
"30"
|
||||
],
|
||||
"filament_z_hop": [
|
||||
"0.4"
|
||||
],
|
||||
"filament_start_gcode": [
|
||||
"; Generic PLA @BabyBelt Pro — belt PLA, bed 75C"
|
||||
]
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
{
|
||||
"type": "filament",
|
||||
"name": "eSUN PLA @BabyBelt Pro",
|
||||
"inherits": "Generic PLA @BabyBelt Pro",
|
||||
"from": "system",
|
||||
"setting_id": "EH3X7oE0DU5tSpjW",
|
||||
"instantiation": "true",
|
||||
"compatible_printers": [
|
||||
"BabyBelt Pro 0.4 nozzle"
|
||||
],
|
||||
"filament_type": [
|
||||
"PLA"
|
||||
],
|
||||
"filament_vendor": [
|
||||
"eSUN"
|
||||
],
|
||||
"filament_settings_id": [
|
||||
"eSUN PLA @BabyBelt Pro"
|
||||
],
|
||||
"nozzle_temperature_initial_layer": [
|
||||
"200"
|
||||
],
|
||||
"nozzle_temperature": [
|
||||
"200"
|
||||
],
|
||||
"enable_pressure_advance": [
|
||||
"1"
|
||||
],
|
||||
"pressure_advance": [
|
||||
"0.12"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"20"
|
||||
]
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
{
|
||||
"type": "machine",
|
||||
"name": "BabyBelt Pro 0.4 nozzle",
|
||||
"inherits": "fdm_belt_common",
|
||||
"from": "system",
|
||||
"setting_id": "34OWINlJpJgA9DwQ",
|
||||
"instantiation": "true",
|
||||
"printer_model": "BabyBelt Pro",
|
||||
"printer_variant": "0.4",
|
||||
"nozzle_diameter": [
|
||||
"0.4"
|
||||
],
|
||||
"default_filament_profile": [
|
||||
"Generic PLA @BabyBelt Pro"
|
||||
],
|
||||
"default_print_profile": "0.20mm Standard @BabyBelt Pro",
|
||||
"printable_area": [
|
||||
"0x0",
|
||||
"95x0",
|
||||
"95x500",
|
||||
"0x500"
|
||||
],
|
||||
"printable_height": "100",
|
||||
"best_object_pos": "0.5,0.05",
|
||||
"nozzle_type": [
|
||||
"hardened_steel"
|
||||
],
|
||||
"printer_extruder_id": [
|
||||
"1"
|
||||
],
|
||||
"printer_extruder_variant": [
|
||||
"Direct Drive Standard"
|
||||
],
|
||||
"thumbnails": [
|
||||
"48x48/PNG",
|
||||
"300x300/PNG"
|
||||
],
|
||||
"machine_max_acceleration_e": [
|
||||
"500",
|
||||
"5000"
|
||||
],
|
||||
"machine_max_acceleration_extruding": [
|
||||
"500",
|
||||
"20000"
|
||||
],
|
||||
"machine_max_acceleration_retracting": [
|
||||
"500",
|
||||
"5000"
|
||||
],
|
||||
"machine_max_acceleration_x": [
|
||||
"500",
|
||||
"20000"
|
||||
],
|
||||
"machine_max_acceleration_y": [
|
||||
"500",
|
||||
"20000"
|
||||
],
|
||||
"machine_max_junction_deviation": [
|
||||
"0.01"
|
||||
],
|
||||
"machine_max_speed_x": [
|
||||
"50",
|
||||
"200"
|
||||
],
|
||||
"machine_max_speed_y": [
|
||||
"50",
|
||||
"200"
|
||||
],
|
||||
"machine_max_speed_z": [
|
||||
"5",
|
||||
"12"
|
||||
],
|
||||
"retraction_length": [
|
||||
"1.5"
|
||||
],
|
||||
"retraction_speed": [
|
||||
"20"
|
||||
],
|
||||
"deretraction_speed": [
|
||||
"25"
|
||||
],
|
||||
"retract_lift_enforce": [
|
||||
"Top and Bottom"
|
||||
],
|
||||
"support_chamber_temp_control": "0",
|
||||
"machine_start_gcode": ";Start GCode\nPRINT_START ANGLE=[belt_slice_rotation_angle] EXTRUDER=[nozzle_temperature_initial_layer] BED=[hot_plate_temp_initial_layer] MATERIAL=[filament_type]\n"
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
{
|
||||
"type": "machine_model",
|
||||
"name": "BabyBelt Pro",
|
||||
"model_id": "Printcepts_BabyBelt_Pro",
|
||||
"nozzle_diameter": "0.4",
|
||||
"machine_tech": "FFF",
|
||||
"family": "Printcepts",
|
||||
"bed_model": "",
|
||||
"bed_texture": "BabyBelt Pro_bed_texture.svg",
|
||||
"hotend_model": "",
|
||||
"default_materials": "Generic PLA @BabyBelt Pro;Generic PETG @BabyBelt Pro"
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
{
|
||||
"type": "machine",
|
||||
"name": "fdm_belt_common",
|
||||
"inherits": "fdm_klipper_common",
|
||||
"from": "system",
|
||||
"instantiation": "false",
|
||||
"gcode_flavor": "klipper",
|
||||
"single_extruder_multi_material": "0",
|
||||
"default_filament_profile": [
|
||||
"Generic PLA @System"
|
||||
],
|
||||
"default_print_profile": "0.20mm Standard @BabyBelt Pro",
|
||||
"max_layer_height": [
|
||||
"0.32"
|
||||
],
|
||||
"min_layer_height": [
|
||||
"0.08"
|
||||
],
|
||||
"deretraction_speed": [
|
||||
"30"
|
||||
],
|
||||
"extruder_colour": [
|
||||
"#FCE94F"
|
||||
],
|
||||
"extruder_offset": [
|
||||
"0x0"
|
||||
],
|
||||
"long_retractions_when_cut": [
|
||||
"0"
|
||||
],
|
||||
"nozzle_diameter": [
|
||||
"0.4"
|
||||
],
|
||||
"retract_before_wipe": [
|
||||
"70%"
|
||||
],
|
||||
"retract_length_toolchange": [
|
||||
"2"
|
||||
],
|
||||
"retract_lift_above": [
|
||||
"0"
|
||||
],
|
||||
"retract_lift_below": [
|
||||
"0"
|
||||
],
|
||||
"retract_lift_enforce": [
|
||||
"All Surfaces"
|
||||
],
|
||||
"retract_restart_extra": [
|
||||
"0"
|
||||
],
|
||||
"retract_restart_extra_toolchange": [
|
||||
"0"
|
||||
],
|
||||
"retract_when_changing_layer": [
|
||||
"1"
|
||||
],
|
||||
"retraction_distances_when_cut": [
|
||||
"18"
|
||||
],
|
||||
"retraction_length": [
|
||||
"0.8"
|
||||
],
|
||||
"retraction_minimum_travel": [
|
||||
"1"
|
||||
],
|
||||
"retraction_speed": [
|
||||
"30"
|
||||
],
|
||||
"travel_slope": [
|
||||
"3"
|
||||
],
|
||||
"wipe": [
|
||||
"1"
|
||||
],
|
||||
"wipe_distance": [
|
||||
"1"
|
||||
],
|
||||
"z_hop": [
|
||||
"0.4"
|
||||
],
|
||||
"z_hop_types": [
|
||||
"Normal Lift"
|
||||
],
|
||||
"gcode_remap_x": "rev_x",
|
||||
"gcode_remap_y": "pos_z",
|
||||
"gcode_remap_z": "pos_y",
|
||||
"printer_extruder_id": [
|
||||
"1"
|
||||
],
|
||||
"belt_printer": "1",
|
||||
"belt_slice_rotation": "x",
|
||||
"belt_slice_rotation_angle": "45",
|
||||
"belt_slice_rotation_global": "1",
|
||||
"build_plate_tilt_x": "45",
|
||||
"purge_in_prime_tower": "0",
|
||||
"scan_first_layer": "0",
|
||||
"auxiliary_fan": "0"
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
{
|
||||
"type": "machine",
|
||||
"name": "fdm_klipper_common",
|
||||
"inherits": "fdm_machine_common",
|
||||
"from": "system",
|
||||
"instantiation": "false",
|
||||
"gcode_flavor": "klipper",
|
||||
"machine_max_acceleration_e": [
|
||||
"5000",
|
||||
"5000"
|
||||
],
|
||||
"machine_max_acceleration_extruding": [
|
||||
"20000",
|
||||
"20000"
|
||||
],
|
||||
"machine_max_acceleration_retracting": [
|
||||
"5000",
|
||||
"5000"
|
||||
],
|
||||
"machine_max_acceleration_travel": [
|
||||
"20000",
|
||||
"20000"
|
||||
],
|
||||
"machine_max_acceleration_x": [
|
||||
"20000",
|
||||
"20000"
|
||||
],
|
||||
"machine_max_acceleration_y": [
|
||||
"20000",
|
||||
"20000"
|
||||
],
|
||||
"machine_max_acceleration_z": [
|
||||
"500",
|
||||
"200"
|
||||
],
|
||||
"machine_max_speed_e": [
|
||||
"25",
|
||||
"25"
|
||||
],
|
||||
"machine_max_speed_x": [
|
||||
"500",
|
||||
"200"
|
||||
],
|
||||
"machine_max_speed_y": [
|
||||
"500",
|
||||
"200"
|
||||
],
|
||||
"machine_max_speed_z": [
|
||||
"12",
|
||||
"12"
|
||||
],
|
||||
"machine_max_jerk_e": [
|
||||
"2.5",
|
||||
"2.5"
|
||||
],
|
||||
"machine_max_jerk_x": [
|
||||
"9",
|
||||
"9"
|
||||
],
|
||||
"machine_max_jerk_y": [
|
||||
"9",
|
||||
"9"
|
||||
],
|
||||
"machine_max_jerk_z": [
|
||||
"0.2",
|
||||
"0.4"
|
||||
],
|
||||
"machine_min_extruding_rate": [
|
||||
"0",
|
||||
"0"
|
||||
],
|
||||
"machine_min_travel_rate": [
|
||||
"0",
|
||||
"0"
|
||||
],
|
||||
"max_layer_height": [
|
||||
"0.32"
|
||||
],
|
||||
"min_layer_height": [
|
||||
"0.08"
|
||||
],
|
||||
"printable_height": "250",
|
||||
"extruder_clearance_radius": "65",
|
||||
"extruder_clearance_height_to_rod": "36",
|
||||
"extruder_clearance_height_to_lid": "140",
|
||||
"printer_settings_id": "",
|
||||
"printer_technology": "FFF",
|
||||
"printer_variant": "0.4",
|
||||
"retraction_minimum_travel": [
|
||||
"1"
|
||||
],
|
||||
"retract_before_wipe": [
|
||||
"70%"
|
||||
],
|
||||
"retract_when_changing_layer": [
|
||||
"1"
|
||||
],
|
||||
"retraction_length": [
|
||||
"0.8"
|
||||
],
|
||||
"retract_length_toolchange": [
|
||||
"2"
|
||||
],
|
||||
"z_hop": [
|
||||
"0.4"
|
||||
],
|
||||
"retract_restart_extra": [
|
||||
"0"
|
||||
],
|
||||
"retract_restart_extra_toolchange": [
|
||||
"0"
|
||||
],
|
||||
"retraction_speed": [
|
||||
"30"
|
||||
],
|
||||
"deretraction_speed": [
|
||||
"30"
|
||||
],
|
||||
"z_hop_types": "Normal Lift",
|
||||
"silent_mode": "0",
|
||||
"single_extruder_multi_material": "1",
|
||||
"change_filament_gcode": "",
|
||||
"wipe": [
|
||||
"1"
|
||||
],
|
||||
"default_filament_profile": [
|
||||
"Generic PLA @System"
|
||||
],
|
||||
"default_print_profile": "0.20mm Standard @MyKlipper",
|
||||
"bed_exclude_area": [
|
||||
"0x0"
|
||||
],
|
||||
"machine_start_gcode": "M190 S[bed_temperature_initial_layer_single]\nM109 S[nozzle_temperature_initial_layer]\nPRINT_START EXTRUDER=[nozzle_temperature_initial_layer] BED=[bed_temperature_initial_layer_single]\n",
|
||||
"machine_end_gcode": "PRINT_END",
|
||||
"layer_change_gcode": ";AFTER_LAYER_CHANGE\n;[layer_z]",
|
||||
"before_layer_change_gcode": ";BEFORE_LAYER_CHANGE\n;[layer_z]\nG92 E0\n",
|
||||
"machine_pause_gcode": "PAUSE",
|
||||
"scan_first_layer": "0",
|
||||
"nozzle_type": "undefine",
|
||||
"auxiliary_fan": "0"
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
{
|
||||
"type": "machine",
|
||||
"name": "fdm_machine_common",
|
||||
"from": "system",
|
||||
"instantiation": "false",
|
||||
"printer_technology": "FFF",
|
||||
"deretraction_speed": [
|
||||
"40"
|
||||
],
|
||||
"extruder_colour": [
|
||||
"#FCE94F"
|
||||
],
|
||||
"extruder_offset": [
|
||||
"0x0"
|
||||
],
|
||||
"gcode_flavor": "marlin",
|
||||
"silent_mode": "0",
|
||||
"machine_max_acceleration_e": [
|
||||
"5000"
|
||||
],
|
||||
"machine_max_acceleration_extruding": [
|
||||
"10000"
|
||||
],
|
||||
"machine_max_acceleration_retracting": [
|
||||
"1000"
|
||||
],
|
||||
"machine_max_acceleration_x": [
|
||||
"10000"
|
||||
],
|
||||
"machine_max_acceleration_y": [
|
||||
"10000"
|
||||
],
|
||||
"machine_max_acceleration_z": [
|
||||
"500"
|
||||
],
|
||||
"machine_max_speed_e": [
|
||||
"60"
|
||||
],
|
||||
"machine_max_speed_x": [
|
||||
"500"
|
||||
],
|
||||
"machine_max_speed_y": [
|
||||
"500"
|
||||
],
|
||||
"machine_max_speed_z": [
|
||||
"10"
|
||||
],
|
||||
"machine_max_jerk_e": [
|
||||
"5"
|
||||
],
|
||||
"machine_max_jerk_x": [
|
||||
"8"
|
||||
],
|
||||
"machine_max_jerk_y": [
|
||||
"8"
|
||||
],
|
||||
"machine_max_jerk_z": [
|
||||
"0.4"
|
||||
],
|
||||
"machine_min_extruding_rate": [
|
||||
"0"
|
||||
],
|
||||
"machine_min_travel_rate": [
|
||||
"0"
|
||||
],
|
||||
"max_layer_height": [
|
||||
"0.32"
|
||||
],
|
||||
"min_layer_height": [
|
||||
"0.08"
|
||||
],
|
||||
"printable_height": "250",
|
||||
"extruder_clearance_radius": "65",
|
||||
"extruder_clearance_height_to_rod": "36",
|
||||
"extruder_clearance_height_to_lid": "140",
|
||||
"nozzle_diameter": [
|
||||
"0.4"
|
||||
],
|
||||
"printer_settings_id": "",
|
||||
"printer_variant": "0.4",
|
||||
"retraction_minimum_travel": [
|
||||
"2"
|
||||
],
|
||||
"retract_before_wipe": [
|
||||
"70%"
|
||||
],
|
||||
"retract_when_changing_layer": [
|
||||
"1"
|
||||
],
|
||||
"retraction_length": [
|
||||
"1"
|
||||
],
|
||||
"retract_length_toolchange": [
|
||||
"1"
|
||||
],
|
||||
"z_hop": [
|
||||
"0"
|
||||
],
|
||||
"retract_restart_extra": [
|
||||
"0"
|
||||
],
|
||||
"retract_restart_extra_toolchange": [
|
||||
"0"
|
||||
],
|
||||
"retraction_speed": [
|
||||
"60"
|
||||
],
|
||||
"single_extruder_multi_material": "1",
|
||||
"change_filament_gcode": "",
|
||||
"wipe": [
|
||||
"1"
|
||||
],
|
||||
"default_print_profile": "",
|
||||
"machine_start_gcode": "G0 Z20 F9000\nG92 E0; G1 E-10 F1200\nG28\nM970 Q1 A10 B10 C130 K0\nM970 Q1 A10 B131 C250 K1\nM974 Q1 S1 P0\nM970 Q0 A10 B10 C130 H20 K0\nM970 Q0 A10 B131 C250 K1\nM974 Q0 S1 P0\nM220 S100 ;Reset Feedrate\nM221 S100 ;Reset Flowrate\nG29 ;Home\nG90;\nG92 E0 ;Reset Extruder \nG1 Z2.0 F3000 ;Move Z Axis up \nG1 X10.1 Y20 Z0.28 F5000.0 ;Move to start position\nM109 S205;\nG1 X10.1 Y200.0 Z0.28 F1500.0 E15 ;Draw the first line\nG1 X10.4 Y200.0 Z0.28 F5000.0 ;Move to side a little\nG1 X10.4 Y20 Z0.28 F1500.0 E30 ;Draw the second line\nG92 E0 ;Reset Extruder \nG1 X110 Y110 Z2.0 F3000 ;Move Z Axis up",
|
||||
"machine_end_gcode": "M400 ; wait for buffer to clear\nG92 E0 ; zero the extruder\nG1 E-4.0 F3600; retract \nG91\nG1 Z3;\nM104 S0 ; turn off hotend\nM140 S0 ; turn off bed\nM106 S0 ; turn off fan\nG90 \nG0 X110 Y200 F3600 \nprint_end",
|
||||
"layer_change_gcode": ";AFTER_LAYER_CHANGE\n;[layer_z]",
|
||||
"before_layer_change_gcode": ";BEFORE_LAYER_CHANGE\n;[layer_z]\nG92 E0\n",
|
||||
"machine_pause_gcode": "M601"
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
{
|
||||
"type": "process",
|
||||
"name": "0.20mm Standard @BabyBelt Pro",
|
||||
"inherits": "fdm_process_common",
|
||||
"from": "system",
|
||||
"setting_id": "JGfGtqX6CWjCt437",
|
||||
"instantiation": "true",
|
||||
"layer_height": "0.2",
|
||||
"initial_layer_print_height": "0.2",
|
||||
"initial_layer_line_width": "0.42",
|
||||
"wall_loops": "2",
|
||||
"reduce_infill_retraction": "1",
|
||||
"detect_overhang_wall": "1",
|
||||
"skirt_loops": "0",
|
||||
"skirt_distance": "0",
|
||||
"sparse_infill_pattern": "grid",
|
||||
"sparse_infill_speed": "200",
|
||||
"support_base_pattern": "rectilinear",
|
||||
"support_interface_pattern": "rectilinear",
|
||||
"compatible_printers": [
|
||||
"BabyBelt Pro 0.4 nozzle"
|
||||
]
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
{
|
||||
"type": "process",
|
||||
"name": "fdm_process_common",
|
||||
"from": "system",
|
||||
"instantiation": "false",
|
||||
"adaptive_layer_height": "0",
|
||||
"reduce_crossing_wall": "0",
|
||||
"max_travel_detour_distance": "0",
|
||||
"bottom_surface_pattern": "monotonic",
|
||||
"bottom_shell_thickness": "0",
|
||||
"bridge_speed": "50",
|
||||
"brim_width": "5",
|
||||
"brim_object_gap": "0.1",
|
||||
"compatible_printers": [],
|
||||
"compatible_printers_condition": "",
|
||||
"print_sequence": "by layer",
|
||||
"default_acceleration": "1000",
|
||||
"initial_layer_acceleration": "500",
|
||||
"top_surface_acceleration": "1000",
|
||||
"travel_acceleration": "1000",
|
||||
"inner_wall_acceleration": "1000",
|
||||
"outer_wall_acceleration": "700",
|
||||
"bridge_no_support": "0",
|
||||
"draft_shield": "disabled",
|
||||
"elefant_foot_compensation": "0",
|
||||
"enable_arc_fitting": "0",
|
||||
"wall_infill_order": "inner wall/outer wall/infill",
|
||||
"infill_direction": "45",
|
||||
"sparse_infill_density": "15%",
|
||||
"sparse_infill_pattern": "crosshatch",
|
||||
"initial_layer_print_height": "0.2",
|
||||
"infill_combination": "0",
|
||||
"infill_wall_overlap": "25%",
|
||||
"interface_shells": "0",
|
||||
"ironing_flow": "10%",
|
||||
"ironing_spacing": "0.15",
|
||||
"ironing_speed": "30",
|
||||
"ironing_type": "no ironing",
|
||||
"reduce_infill_retraction": "1",
|
||||
"filename_format": "{input_filename_base}_{layer_height}mm_{filament_type[initial_tool]}_{printer_model}_{print_time}.gcode",
|
||||
"detect_overhang_wall": "1",
|
||||
"slowdown_for_curled_perimeters": "1",
|
||||
"overhang_1_4_speed": "0",
|
||||
"overhang_2_4_speed": "50",
|
||||
"overhang_3_4_speed": "30",
|
||||
"overhang_4_4_speed": "10",
|
||||
"line_width": "110%",
|
||||
"inner_wall_line_width": "110%",
|
||||
"outer_wall_line_width": "100%",
|
||||
"top_surface_line_width": "93.75%",
|
||||
"sparse_infill_line_width": "110%",
|
||||
"initial_layer_line_width": "120%",
|
||||
"internal_solid_infill_line_width": "120%",
|
||||
"support_line_width": "96%",
|
||||
"wall_loops": "3",
|
||||
"print_settings_id": "",
|
||||
"raft_layers": "0",
|
||||
"seam_position": "aligned",
|
||||
"skirt_distance": "2",
|
||||
"skirt_height": "3",
|
||||
"min_skirt_length": "4",
|
||||
"skirt_loops": "0",
|
||||
"minimum_sparse_infill_area": "15",
|
||||
"spiral_mode": "0",
|
||||
"standby_temperature_delta": "-5",
|
||||
"enable_support": "0",
|
||||
"resolution": "0.012",
|
||||
"support_type": "normal(auto)",
|
||||
"support_on_build_plate_only": "0",
|
||||
"support_top_z_distance": "0.2",
|
||||
"support_bottom_z_distance": "0.2",
|
||||
"support_filament": "0",
|
||||
"support_interface_loop_pattern": "0",
|
||||
"support_interface_filament": "0",
|
||||
"support_interface_top_layers": "2",
|
||||
"support_interface_bottom_layers": "2",
|
||||
"support_interface_spacing": "0.5",
|
||||
"support_interface_speed": "80",
|
||||
"support_base_pattern": "default",
|
||||
"support_base_pattern_spacing": "2.5",
|
||||
"support_speed": "150",
|
||||
"support_threshold_angle": "30",
|
||||
"support_object_xy_distance": "0.35",
|
||||
"tree_support_branch_angle": "30",
|
||||
"tree_support_wall_count": "0",
|
||||
"tree_support_with_infill": "0",
|
||||
"detect_thin_wall": "0",
|
||||
"top_surface_pattern": "monotonicline",
|
||||
"top_shell_thickness": "0.8",
|
||||
"enable_prime_tower": "1",
|
||||
"wipe_tower_no_sparse_layers": "0",
|
||||
"prime_tower_width": "60",
|
||||
"xy_hole_compensation": "0",
|
||||
"xy_contour_compensation": "0",
|
||||
"layer_height": "0.2",
|
||||
"bottom_shell_layers": "3",
|
||||
"top_shell_layers": "4",
|
||||
"bridge_flow": "1",
|
||||
"initial_layer_speed": "45",
|
||||
"initial_layer_infill_speed": "45",
|
||||
"outer_wall_speed": "45",
|
||||
"inner_wall_speed": "80",
|
||||
"sparse_infill_speed": "150",
|
||||
"internal_solid_infill_speed": "150",
|
||||
"top_surface_speed": "50",
|
||||
"gap_infill_speed": "30",
|
||||
"travel_speed": "200"
|
||||
}
|
||||
@@ -26,7 +26,6 @@ struct SlopeDetection
|
||||
bool actived;
|
||||
float normal_z;
|
||||
mat3 volume_world_normal_matrix;
|
||||
vec3 up_direction;
|
||||
};
|
||||
|
||||
uniform vec4 uniform_color;
|
||||
|
||||
@@ -23,7 +23,6 @@ struct SlopeDetection
|
||||
bool actived;
|
||||
float normal_z;
|
||||
mat3 volume_world_normal_matrix;
|
||||
vec3 up_direction;
|
||||
};
|
||||
|
||||
uniform mat4 view_model_matrix;
|
||||
@@ -74,8 +73,8 @@ void main()
|
||||
// Point in homogenous coordinates.
|
||||
world_pos = volume_world_matrix * vec4(v_position, 1.0);
|
||||
|
||||
// dot product of world normal with up direction, used for slope shading
|
||||
world_normal_z = slope.actived ? dot(normalize(slope.volume_world_normal_matrix * v_normal), slope.up_direction) : 0.0;
|
||||
// z component of normal vector in world coordinate used for slope shading
|
||||
world_normal_z = slope.actived ? (normalize(slope.volume_world_normal_matrix * v_normal)).z : 0.0;
|
||||
|
||||
gl_Position = projection_matrix * position;
|
||||
if (is_outline) {
|
||||
|
||||
@@ -37,7 +37,6 @@ struct SlopeDetection
|
||||
bool actived;
|
||||
float normal_z;
|
||||
mat3 volume_world_normal_matrix;
|
||||
vec3 up_direction;
|
||||
};
|
||||
uniform SlopeDetection slope;
|
||||
|
||||
@@ -86,7 +85,7 @@ void main()
|
||||
color = LightBlue;
|
||||
alpha = 1.0;
|
||||
}
|
||||
else if( dot(transformed_normal, slope.up_direction) < slope.normal_z - EPSILON)
|
||||
else if( transformed_normal.z < slope.normal_z - EPSILON)
|
||||
{
|
||||
color = color * 0.5 + LightRed * 0.5;
|
||||
alpha = 1.0;
|
||||
|
||||
@@ -24,7 +24,6 @@ struct SlopeDetection
|
||||
bool actived;
|
||||
float normal_z;
|
||||
mat3 volume_world_normal_matrix;
|
||||
vec3 up_direction;
|
||||
};
|
||||
uniform SlopeDetection slope;
|
||||
void main()
|
||||
|
||||
@@ -29,7 +29,6 @@ struct SlopeDetection
|
||||
bool actived;
|
||||
float normal_z;
|
||||
mat3 volume_world_normal_matrix;
|
||||
vec3 up_direction;
|
||||
};
|
||||
|
||||
uniform vec4 uniform_color;
|
||||
|
||||
@@ -23,7 +23,6 @@ struct SlopeDetection
|
||||
bool actived;
|
||||
float normal_z;
|
||||
mat3 volume_world_normal_matrix;
|
||||
vec3 up_direction;
|
||||
};
|
||||
|
||||
uniform mat4 view_model_matrix;
|
||||
@@ -74,8 +73,8 @@ void main()
|
||||
// Point in homogenous coordinates.
|
||||
world_pos = volume_world_matrix * vec4(v_position, 1.0);
|
||||
|
||||
// dot product of world normal with up direction, used for slope shading
|
||||
world_normal_z = slope.actived ? dot(normalize(slope.volume_world_normal_matrix * v_normal), slope.up_direction) : 0.0;
|
||||
// z component of normal vector in world coordinate used for slope shading
|
||||
world_normal_z = slope.actived ? (normalize(slope.volume_world_normal_matrix * v_normal)).z : 0.0;
|
||||
|
||||
gl_Position = projection_matrix * position;
|
||||
if (is_outline) {
|
||||
|
||||
@@ -37,7 +37,6 @@ struct SlopeDetection
|
||||
bool actived;
|
||||
float normal_z;
|
||||
mat3 volume_world_normal_matrix;
|
||||
vec3 up_direction;
|
||||
};
|
||||
uniform SlopeDetection slope;
|
||||
|
||||
@@ -88,7 +87,7 @@ void main()
|
||||
color = LightBlue;
|
||||
alpha = 1.0;
|
||||
}
|
||||
else if( dot(transformed_normal, slope.up_direction) < slope.normal_z - EPSILON)
|
||||
else if( transformed_normal.z < slope.normal_z - EPSILON)
|
||||
{
|
||||
color = color * 0.5 + LightRed * 0.5;
|
||||
alpha = 1.0;
|
||||
|
||||
@@ -24,7 +24,6 @@ struct SlopeDetection
|
||||
bool actived;
|
||||
float normal_z;
|
||||
mat3 volume_world_normal_matrix;
|
||||
vec3 up_direction;
|
||||
};
|
||||
uniform SlopeDetection slope;
|
||||
void main()
|
||||
|
||||
@@ -276,12 +276,6 @@ modules:
|
||||
sha256: 27b72ba2d5ff3d0a9814ad40d4cb88f8dc89a35491c0866d952473f8f9416b77
|
||||
dest: external-packages/Draco
|
||||
|
||||
# Assimp 5.4.3
|
||||
- type: file
|
||||
url: https://github.com/assimp/assimp/archive/refs/tags/v5.4.3.tar.gz
|
||||
sha256: 66dfbaee288f2bc43172440a55d0235dfc7bf885dda6435c038e8000e79582cb
|
||||
dest: external-packages/Assimp
|
||||
|
||||
# OpenSSL 1.1.1w (GNOME SDK has 3.x; OrcaSlicer requires 1.1.x)
|
||||
- type: file
|
||||
url: https://github.com/openssl/openssl/archive/OpenSSL_1_1_1w.tar.gz
|
||||
@@ -318,12 +312,6 @@ modules:
|
||||
sha256: 0ba163956f2d468b19a91b96c5aba66ee9610843ea41dda628ea44cdafde7db7
|
||||
dest: external-packages/wxInspector
|
||||
|
||||
# FFmpeg n7.0.3
|
||||
- type: file
|
||||
url: https://github.com/FFmpeg/FFmpeg/archive/refs/tags/n7.0.3.tar.gz
|
||||
sha256: deedcabe339165214a3637df4c86a507aef0d793cf8774ff68735f4737e8ddbc
|
||||
dest: external-packages/FFMPEG
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# Fallback archives for deps normally provided by the GNOME SDK.
|
||||
# These are only used if find_package() fails to locate them.
|
||||
|
||||
@@ -25,9 +25,6 @@ export REQUIRED_DEV_PACKAGES=(
|
||||
wayland-protocols
|
||||
webkit2gtk-4.1
|
||||
wget
|
||||
pkgconf
|
||||
yasm
|
||||
nasm
|
||||
)
|
||||
|
||||
if [[ -n "$UPDATE_LIB" ]]
|
||||
|
||||
@@ -25,9 +25,6 @@ export REQUIRED_DEV_PACKAGES=(
|
||||
wayland-protocols
|
||||
webkit2gtk
|
||||
wget
|
||||
pkgconf
|
||||
yasm
|
||||
nasm
|
||||
)
|
||||
|
||||
if [[ -n "$UPDATE_LIB" ]]
|
||||
|
||||
@@ -20,7 +20,6 @@ export REQUIRED_BUNDLES=(
|
||||
perl-basic
|
||||
texinfo
|
||||
wget
|
||||
nasm
|
||||
)
|
||||
|
||||
if [[ -n "$UPDATE_LIB" ]]
|
||||
|
||||
@@ -27,9 +27,6 @@ REQUIRED_DEV_PACKAGES=(
|
||||
ninja-build
|
||||
texinfo
|
||||
wget
|
||||
pkgconf
|
||||
yasm
|
||||
nasm
|
||||
)
|
||||
|
||||
if [[ -n "$UPDATE_LIB" ]]
|
||||
|
||||
@@ -31,9 +31,6 @@ REQUIRED_DEV_PACKAGES=(
|
||||
webkit2gtk4.1-devel
|
||||
wget
|
||||
libcurl-devel
|
||||
pkgconf
|
||||
yasm
|
||||
nasm
|
||||
)
|
||||
|
||||
if [[ -n "$UPDATE_LIB" ]]
|
||||
|
||||
@@ -32,9 +32,6 @@ REQUIRED_DEV_PACKAGES=(
|
||||
sys-devel/m4
|
||||
virtual/libudev
|
||||
x11-libs/gtk+:3
|
||||
dev-util/pkgconf
|
||||
dev-lang/yasm
|
||||
dev-lang/nasm
|
||||
)
|
||||
|
||||
if [[ -n "$UPDATE_LIB" ]]
|
||||
|
||||
@@ -30,9 +30,6 @@ REQUIRED_DEV_PACKAGES=(
|
||||
webkit2gtk4-devel
|
||||
wget
|
||||
libcurl-devel
|
||||
pkgconf
|
||||
yasm
|
||||
nasm
|
||||
)
|
||||
|
||||
if [[ -n "$UPDATE_LIB" ]]
|
||||
|
||||
@@ -75,7 +75,7 @@ if (SLIC3R_GUI)
|
||||
list(FILTER wxWidgets_LIBRARIES EXCLUDE REGEX expat)
|
||||
list(APPEND wxWidgets_LIBRARIES ${EXPAT_LIBRARIES})
|
||||
endif ()
|
||||
|
||||
|
||||
# This is an issue in the new wxWidgets cmake build, doesn't deal with librt
|
||||
find_library(LIBRT rt)
|
||||
if(LIBRT)
|
||||
@@ -90,12 +90,6 @@ if (SLIC3R_GUI)
|
||||
# list(REMOVE_ITEM wxWidgets_LIBRARIES oleacc)
|
||||
|
||||
find_package(wxInspector REQUIRED)
|
||||
# wxInspector 1.0.0 installs its headers but accidentally declares the
|
||||
# INSTALL_INTERFACE include directory PRIVATE, so its imported target does
|
||||
# not expose them to consumers. Restore the package prefix include path until
|
||||
# the upstream export is fixed.
|
||||
get_filename_component(WXINSPECTOR_PREFIX "${wxInspector_DIR}/../../.." ABSOLUTE)
|
||||
target_include_directories(wxInspector::wxInspector INTERFACE "${WXINSPECTOR_PREFIX}/include")
|
||||
list(APPEND wxWidgets_LIBRARIES "wxInspector::wxInspector")
|
||||
|
||||
message(STATUS "wx libs: ${wxWidgets_LIBRARIES}")
|
||||
@@ -181,7 +175,7 @@ endif ()
|
||||
# Add the Slic3r GUI library, libcurl, OpenGL and GLU libraries.
|
||||
if (SLIC3R_GUI)
|
||||
# target_link_libraries(OrcaSlicer ws2_32 uxtheme setupapi libslic3r_gui ${wxWidgets_LIBRARIES})
|
||||
target_link_libraries(OrcaSlicer libslic3r_gui wxInspector::wxInspector)
|
||||
target_link_libraries(OrcaSlicer libslic3r_gui)
|
||||
if (MSVC)
|
||||
# Generate debug symbols even in release mode.
|
||||
target_link_options(OrcaSlicer PUBLIC "$<$<CONFIG:RELEASE>:/DEBUG>")
|
||||
@@ -300,16 +294,6 @@ if (WIN32)
|
||||
endif()
|
||||
|
||||
else ()
|
||||
if (NOT APPLE)
|
||||
set(output_sos_Release "")
|
||||
set(output_sos_Debug "")
|
||||
add_custom_target(OrcaSlicerSosCopy ALL DEPENDS OrcaSlicer)
|
||||
if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
|
||||
orcaslicer_copy_sos(OrcaSlicerSosCopy "Debug" "d" output_sos_Debug)
|
||||
else()
|
||||
orcaslicer_copy_sos(OrcaSlicerSosCopy "Release" "" output_sos_Release)
|
||||
endif()
|
||||
endif()
|
||||
if (APPLE AND NOT CMAKE_MACOSX_BUNDLE)
|
||||
# On OSX, the name of the binary matches the name of the Application.
|
||||
add_custom_command(TARGET OrcaSlicer POST_BUILD
|
||||
@@ -388,9 +372,5 @@ if (WIN32)
|
||||
install(FILES ${output_dlls_${build_type}} DESTINATION ".")
|
||||
install(DIRECTORY "${CMAKE_PREFIX_PATH}/libpython/" DESTINATION "python")
|
||||
else ()
|
||||
if (APPLE)
|
||||
else()
|
||||
install(FILES ${output_sos_${build_type}} DESTINATION "${CMAKE_INSTALL_PREFIX}")
|
||||
endif()
|
||||
install(TARGETS OrcaSlicer RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
endif ()
|
||||
|
||||
@@ -3685,10 +3685,6 @@ int CLI::run(int argc, char **argv)
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("%1%, set disable_wipe_tower_after_mapping back to false due to wrapping detect")%__LINE__;
|
||||
}
|
||||
|
||||
// Belt printers never get the classic wipe tower (see Print::has_wipe_tower()), so reserve no space for it.
|
||||
const ConfigOptionBool* belt_printer_opt = m_print_config.option<ConfigOptionBool>("belt_printer");
|
||||
const bool is_belt_printer = belt_printer_opt && belt_printer_opt->value;
|
||||
|
||||
auto timelapse_type_opt = m_print_config.option("timelapse_type");
|
||||
bool is_smooth_timelapse = false;
|
||||
if (enable_timelapse && timelapse_type_opt && (timelapse_type_opt->getInt() == TimelapseType::tlSmooth))
|
||||
@@ -3912,11 +3908,11 @@ int CLI::run(int argc, char **argv)
|
||||
}
|
||||
};
|
||||
|
||||
auto check_plate_wipe_tower = [get_print_sequence, is_smooth_timelapse, is_belt_printer, new_extruder_count](Slic3r::GUI::PartPlate* plate, int plate_index, DynamicPrintConfig& print_config, plate_obj_size_info_t &plate_obj_size_info) {
|
||||
auto check_plate_wipe_tower = [get_print_sequence, is_smooth_timelapse, new_extruder_count](Slic3r::GUI::PartPlate* plate, int plate_index, DynamicPrintConfig& print_config, plate_obj_size_info_t &plate_obj_size_info) {
|
||||
plate_obj_size_info.obj_bbox= plate->get_objects_bounding_box();
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("plate %1%, object bbox: min {%2%, %3%, %4%} - max {%5%, %6%, %7%}")
|
||||
%(plate_index+1) %plate_obj_size_info.obj_bbox.min.x() % plate_obj_size_info.obj_bbox.min.y() % plate_obj_size_info.obj_bbox.min.z() %plate_obj_size_info.obj_bbox.max.x() % plate_obj_size_info.obj_bbox.max.y() % plate_obj_size_info.obj_bbox.max.z();
|
||||
if (is_belt_printer || !print_config.has("wipe_tower_x")) {
|
||||
if (!print_config.has("wipe_tower_x")) {
|
||||
plate_obj_size_info.has_wipe_tower = false;
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("can not found wipe_tower_x in config, set to no wipe tower");
|
||||
return;
|
||||
@@ -4726,7 +4722,7 @@ int CLI::run(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_belt_printer && (!arrange_cfg.is_seq_print && (assemble_plate.filaments_count > 1)||(enable_wrapping_detect && !current_wrapping_exclude_area.empty())))
|
||||
if (!arrange_cfg.is_seq_print && (assemble_plate.filaments_count > 1)||(enable_wrapping_detect && !current_wrapping_exclude_area.empty()))
|
||||
{
|
||||
//prepare the wipe tower
|
||||
int plate_count = partplate_list.get_plate_count();
|
||||
@@ -4871,7 +4867,7 @@ int CLI::run(int argc, char **argv)
|
||||
bool is_seq_print = false;
|
||||
get_print_sequence(cur_plate, m_print_config, is_seq_print);
|
||||
|
||||
if (!is_belt_printer && !is_seq_print && (assemble_plate.filaments_count > 1) && !has_wipe_tower_position)
|
||||
if (!is_seq_print && (assemble_plate.filaments_count > 1) && !has_wipe_tower_position)
|
||||
{
|
||||
//prepare the wipe tower
|
||||
auto printer_structure_opt = m_print_config.option<ConfigOptionEnum<PrinterStructure>>("printer_structure");
|
||||
@@ -5020,7 +5016,7 @@ int CLI::run(int argc, char **argv)
|
||||
//add the virtual object into unselect list if has
|
||||
partplate_list.preprocess_exclude_areas(unselected, enable_wrapping_detect);
|
||||
|
||||
if (!is_belt_printer && used_filament_set.size() > 0)
|
||||
if (used_filament_set.size() > 0)
|
||||
{
|
||||
//prepare the wipe tower
|
||||
int plate_count = partplate_list.get_plate_count();
|
||||
@@ -5123,7 +5119,7 @@ int CLI::run(int argc, char **argv)
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": found single object mode");
|
||||
}
|
||||
|
||||
if (!is_belt_printer && m_print_config.has("wipe_tower_x") && (is_smooth_timelapse || !arrange_cfg.is_seq_print || (selected.size() <= 1))) {
|
||||
if (m_print_config.has("wipe_tower_x") && (is_smooth_timelapse || !arrange_cfg.is_seq_print || (selected.size() <= 1))) {
|
||||
float x;
|
||||
float y;
|
||||
if (duplicate_count > 0) {
|
||||
|
||||
@@ -83,10 +83,6 @@ copy_shared_object_to_dir() {
|
||||
src_real="$(readlink -f "$src")"
|
||||
dst_name="$(basename "$src_real")"
|
||||
mkdir -p "$dst_dir"
|
||||
if [ "$src_real" = "$dst_dir/$dst_name" ]; then
|
||||
# Already bundled; the dependency resolved from the bundle directory.
|
||||
return 0
|
||||
fi
|
||||
cp -fL "$src_real" "$dst_dir/$dst_name"
|
||||
|
||||
if [ -L "$src" ]; then
|
||||
@@ -100,23 +96,12 @@ copy_shared_object_to_dir() {
|
||||
}
|
||||
|
||||
bundle_dependency_closure() {
|
||||
local dst_dir
|
||||
dst_dir="$(cd -- "$1" && pwd)"
|
||||
local dst_dir="$1"
|
||||
shift
|
||||
|
||||
local -a queue=("$@")
|
||||
local target dep dep_real dep_key copied_path
|
||||
local target dep dep_real copied_path
|
||||
declare -A seen=()
|
||||
# Dependencies are resolved with ldd, which only searches the default
|
||||
# loader path. Deps-built shared libraries (e.g. the FFmpeg stack) are not
|
||||
# installed there and carry no RUNPATH of their own, so once copied into
|
||||
# the bundle ldd can no longer resolve one sibling from another
|
||||
# (libavcodec -> libavutil) and reports it as missing. Extend the loader
|
||||
# path with the bundle directory plus the source directories of files
|
||||
# already bundled, so every library that was resolved once keeps resolving
|
||||
# for its own dependencies. The audit script does the same
|
||||
# (scripts/check_appimage_libs.sh).
|
||||
local -a search_dirs=("$dst_dir")
|
||||
|
||||
while [ ${#queue[@]} -gt 0 ]; do
|
||||
target="${queue[0]}"
|
||||
@@ -137,24 +122,17 @@ bundle_dependency_closure() {
|
||||
continue
|
||||
fi
|
||||
|
||||
# Key dedup on the bundled file rather than the source path: once
|
||||
# ldd resolves a library from the bundle directory (via the
|
||||
# LD_LIBRARY_PATH above) its path is a dst_dir path, which differs
|
||||
# from the source path the first resolution returned. Keying on
|
||||
# the source path would re-copy the file onto itself.
|
||||
dep_key="$dst_dir/$(basename "$dep_real")"
|
||||
if [ -n "${seen[$dep_key]}" ]; then
|
||||
if [ -n "${seen[$dep_real]}" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
seen[$dep_key]=1
|
||||
seen[$dep_real]=1
|
||||
copy_shared_object_to_dir "$dep" "$dst_dir"
|
||||
search_dirs+=("$(dirname "$dep_real")")
|
||||
copied_path="$dst_dir/$(basename "$dep_real")"
|
||||
if [ -e "$copied_path" ]; then
|
||||
queue+=("$copied_path")
|
||||
fi
|
||||
done < <(LD_LIBRARY_PATH="$(IFS=:; printf '%s' "${search_dirs[*]}")${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" appimage_list_direct_dependencies "$target")
|
||||
done < <(appimage_list_direct_dependencies "$target")
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
@@ -229,7 +229,7 @@ public:
|
||||
m_bbox(bbox.min - Point(SCALED_EPSILON, SCALED_EPSILON), bbox.max + Point(SCALED_EPSILON, SCALED_EPSILON)) {}
|
||||
size_t idx() const { return m_idx; }
|
||||
const BoundingBox& bbox() const { return m_bbox; }
|
||||
Point centroid() const { return (m_bbox.min() + m_bbox.max()) / 2; }
|
||||
Point centroid() const { return (m_bbox.min() + m_bbox.max() / 2); }
|
||||
private:
|
||||
size_t m_idx;
|
||||
BoundingBox m_bbox;
|
||||
|
||||
@@ -57,24 +57,24 @@ void ArcFitter::do_arc_fitting(const Points& points, std::vector<PathFittingData
|
||||
//BBS: can be fit as arc, then save arc data temperarily
|
||||
last_arc = target_arc;
|
||||
if (back_index == points.size() - 1) {
|
||||
result.emplace_back(PathFittingData{ front_index,
|
||||
result.emplace_back(std::move(PathFittingData{ front_index,
|
||||
back_index,
|
||||
last_arc.direction == ArcDirection::Arc_Dir_CCW ? EMovePathType::Arc_move_ccw : EMovePathType::Arc_move_cw,
|
||||
last_arc });
|
||||
last_arc }));
|
||||
front_index = back_index;
|
||||
}
|
||||
} else {
|
||||
if (back_index - front_index > 2) {
|
||||
//BBS: althought current point_stack can't be fit as arc,
|
||||
//but previous must can be fit if removing the top in stack, so save last arc
|
||||
result.emplace_back(PathFittingData{ front_index,
|
||||
result.emplace_back(std::move(PathFittingData{ front_index,
|
||||
back_index - 1,
|
||||
last_arc.direction == ArcDirection::Arc_Dir_CCW ? EMovePathType::Arc_move_ccw : EMovePathType::Arc_move_cw,
|
||||
last_arc });
|
||||
last_arc }));
|
||||
} else {
|
||||
//BBS: save the first segment as line move when 3 point-line can't be fit as arc move
|
||||
if (result.empty() || result.back().path_type != EMovePathType::Linear_move)
|
||||
result.emplace_back(PathFittingData{front_index, front_index + 1, EMovePathType::Linear_move, ArcSegment()});
|
||||
result.emplace_back(std::move(PathFittingData{front_index, front_index + 1, EMovePathType::Linear_move, ArcSegment()}));
|
||||
else if(result.back().path_type == EMovePathType::Linear_move)
|
||||
result.back().end_point_index = front_index + 1;
|
||||
}
|
||||
@@ -87,7 +87,7 @@ void ArcFitter::do_arc_fitting(const Points& points, std::vector<PathFittingData
|
||||
//BBS: handle the remain data
|
||||
if (front_index != back_index) {
|
||||
if (result.empty() || result.back().path_type != EMovePathType::Linear_move)
|
||||
result.emplace_back(PathFittingData{front_index, back_index, EMovePathType::Linear_move, ArcSegment()});
|
||||
result.emplace_back(std::move(PathFittingData{front_index, back_index, EMovePathType::Linear_move, ArcSegment()}));
|
||||
else if (result.back().path_type == EMovePathType::Linear_move)
|
||||
result.back().end_point_index = back_index;
|
||||
}
|
||||
|
||||
@@ -1,516 +0,0 @@
|
||||
#include "BeltBrim.hpp"
|
||||
|
||||
#include "ClipperUtils.hpp"
|
||||
#include "Flow.hpp"
|
||||
#include "Layer.hpp"
|
||||
#include "Polygon.hpp"
|
||||
#include "Print.hpp"
|
||||
#include "ShortestPath.hpp"
|
||||
#include "Support/BeltFloorContext.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// ---------------------------------------------------------------- scaling
|
||||
|
||||
static inline Point scale_u_point(const Point &p, int from_axis, double factor)
|
||||
{
|
||||
// llround, not a cast: casting truncates toward zero, so a round trip would
|
||||
// walk every vertex toward the origin by up to one unit per pass.
|
||||
return from_axis == 0 ?
|
||||
Point(coord_t(std::llround(double(p.x()) * factor)), p.y()) :
|
||||
Point(p.x(), coord_t(std::llround(double(p.y()) * factor)));
|
||||
}
|
||||
|
||||
static inline void scale_u_polygon(Polygon &poly, int from_axis, double factor)
|
||||
{
|
||||
for (Point &p : poly.points)
|
||||
p = scale_u_point(p, from_axis, factor);
|
||||
}
|
||||
|
||||
ExPolygons belt_scale_u(const ExPolygons &src, const BeltBrimFrame &frame, double factor)
|
||||
{
|
||||
ExPolygons out = src;
|
||||
for (ExPolygon &ex : out) {
|
||||
scale_u_polygon(ex.contour, frame.from_axis, factor);
|
||||
for (Polygon &hole : ex.holes)
|
||||
scale_u_polygon(hole, frame.from_axis, factor);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
Polylines belt_scale_u(const Polylines &src, const BeltBrimFrame &frame, double factor)
|
||||
{
|
||||
Polylines out = src;
|
||||
for (Polyline &pl : out)
|
||||
for (Point &p : pl.points)
|
||||
p = scale_u_point(p, frame.from_axis, factor);
|
||||
return out;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- sweep
|
||||
|
||||
ExPolygons sweep_ex(const ExPolygons &src, const Point &t)
|
||||
{
|
||||
if (src.empty())
|
||||
return {};
|
||||
if (t == Point(0, 0))
|
||||
return src;
|
||||
|
||||
// One parallelogram per boundary edge. Together with P and P + t these
|
||||
// cover the Minkowski sum exactly: for any q = p + s*t with p in P and
|
||||
// s in [0, 1], let s* be the smallest lambda >= 0 with q - lambda*t in P.
|
||||
// Either s* == 0 (so q is in P) or q - s* * t lies on some boundary edge e,
|
||||
// putting q in that edge's parallelogram. Hole edges must be included, or
|
||||
// holes narrower than t along t would wrongly survive the sweep.
|
||||
Polygons quads;
|
||||
for (const ExPolygon &ex : src)
|
||||
for (size_t c = 0; c < ex.num_contours(); ++ c)
|
||||
for (const Line &e : ex.contour_or_hole(c).lines()) {
|
||||
if (e.a == e.b)
|
||||
continue;
|
||||
Polygon q;
|
||||
q.points = { e.a, e.b, e.b + t, e.a + t };
|
||||
// The non-zero fill rule counts a clockwise ring as -1, which
|
||||
// would punch a hole instead of adding material. Edges parallel
|
||||
// to t give a zero-area quad; Clipper discards those harmlessly.
|
||||
if (q.is_clockwise())
|
||||
q.reverse();
|
||||
quads.emplace_back(std::move(q));
|
||||
}
|
||||
|
||||
ExPolygons shifted = src;
|
||||
for (ExPolygon &ex : shifted)
|
||||
ex.translate(t);
|
||||
|
||||
// union_ex(ExPolygons, Polygons) uses pftNonZero, which is the fill rule the
|
||||
// argument above relies on.
|
||||
return union_ex(union_ex(src, shifted), quads);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- brim region
|
||||
|
||||
ExPolygons belt_brim_region(const ExPolygons &footprint_flat,
|
||||
bool has_outer,
|
||||
bool has_inner,
|
||||
coord_t brim_width,
|
||||
coord_t object_gap,
|
||||
coord_t leading,
|
||||
coord_t lateral,
|
||||
const BeltBrimFrame &frame)
|
||||
{
|
||||
if (footprint_flat.empty() || (! has_outer && ! has_inner))
|
||||
return {};
|
||||
|
||||
ExPolygons out;
|
||||
|
||||
if (has_outer) {
|
||||
// Offset the outer ring from the contours only, so a hole cannot punch
|
||||
// through it. Same reasoning as the plate brim in Brim.cpp.
|
||||
Polygons contours;
|
||||
contours.reserve(footprint_flat.size());
|
||||
for (const ExPolygon &ex : footprint_flat)
|
||||
contours.emplace_back(ex.contour);
|
||||
|
||||
// Inner and outer boundary offset from the same polygon, to avoid
|
||||
// round-off mismatch between them.
|
||||
ExPolygons inner = offset_ex(contours, float(object_gap), jtRound, SCALED_RESOLUTION);
|
||||
|
||||
// Close the interior before offsetting outwards. A belt contact patch is often a
|
||||
// narrow, broken-up strip, and the offset rings of two islands less than
|
||||
// 2 x brim_width apart merge and fill the space between them - space that lies
|
||||
// UNDER the part, which is not what "outer brim" means. Closing also swallows
|
||||
// holes in the patch for the same reason. Concavity-filling only, so an apron or
|
||||
// any other outward protrusion is untouched.
|
||||
ExPolygons envelope = brim_width > 0 ? closing_ex(inner, float(brim_width)) : inner;
|
||||
|
||||
ExPolygons base = envelope;
|
||||
if (leading > 0) {
|
||||
// Sweep downhill from the gapped keep-out, so the apron is contiguous with
|
||||
// the ring instead of starting inside the gap.
|
||||
const Point t = frame.from_axis == 0 ?
|
||||
Point(frame.downhill_sign() * leading, 0) :
|
||||
Point(0, frame.downhill_sign() * leading);
|
||||
base = union_ex(base, sweep_ex(envelope, t));
|
||||
}
|
||||
if (lateral > 0) {
|
||||
// Across the belt, both ways. Swept from `base` so the apron is widened
|
||||
// too, and in the flattened frame the cross-belt axis is unscaled, so this
|
||||
// distance is already a true on-belt distance.
|
||||
const Point t = frame.from_axis == 0 ? Point(0, lateral) : Point(lateral, 0);
|
||||
ExPolygons widened = union_ex(sweep_ex(base, t), sweep_ex(base, Point(-t.x(), -t.y())));
|
||||
base = union_ex(base, to_polygons(widened));
|
||||
}
|
||||
ExPolygons outer = offset_ex(base, float(brim_width), jtRound, SCALED_RESOLUTION);
|
||||
expolygons_append(out, diff_ex(outer, envelope));
|
||||
}
|
||||
|
||||
if (has_inner) {
|
||||
// Holes reversed so a negative offset grows inward, mirroring Brim.cpp.
|
||||
// No apron here: an apron growing into a hole interior is never useful.
|
||||
Polygons holes;
|
||||
for (const ExPolygon &ex : footprint_flat)
|
||||
polygons_append(holes, ex.holes);
|
||||
polygons_reverse(holes);
|
||||
if (! holes.empty()) {
|
||||
ExPolygons hole_inner = offset_ex(holes, - float(brim_width + object_gap));
|
||||
ExPolygons hole_outer = offset_ex(holes, - float(object_gap));
|
||||
expolygons_append(out, intersection_ex(diff_ex(hole_outer, hole_inner), holes));
|
||||
}
|
||||
}
|
||||
|
||||
return union_ex(out);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- line lattice
|
||||
|
||||
std::vector<coord_t> belt_brim_line_positions(coord_t u_lo,
|
||||
coord_t u_hi,
|
||||
coord_t pitch_u,
|
||||
coord_t u_anchor)
|
||||
{
|
||||
std::vector<coord_t> out;
|
||||
if (pitch_u <= 0 || u_hi <= u_lo)
|
||||
return out;
|
||||
|
||||
// Walk the lattice from just below u_lo. Integer arithmetic throughout, so
|
||||
// the half-open interval needs no epsilon: a point landing exactly on u_hi
|
||||
// belongs to the next band.
|
||||
int64_t k = int64_t(std::floor(double(u_lo - u_anchor) / double(pitch_u))) - 1;
|
||||
while (u_anchor + coord_t(k) * pitch_u < u_lo)
|
||||
++ k;
|
||||
for (;; ++ k) {
|
||||
const coord_t u = u_anchor + coord_t(k) * pitch_u;
|
||||
if (u >= u_hi)
|
||||
break;
|
||||
out.emplace_back(u);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- pipeline
|
||||
|
||||
// A band of the belt surface as an explicit box, clamped to `bounds` along the
|
||||
// shear axis. Deliberately not BeltFloorContext::surface_polygon(): those
|
||||
// half-planes span +-1000 mm, which is wasteful to clip against and dangerous to
|
||||
// feed through the flattening scale.
|
||||
static Polygon band_box(const BoundingBox &bounds, int from_axis, coordf_t u_lo, coordf_t u_hi)
|
||||
{
|
||||
coord_t lo = scale_(u_lo);
|
||||
coord_t hi = scale_(u_hi);
|
||||
const coord_t bmin = from_axis == 0 ? bounds.min.x() : bounds.min.y();
|
||||
const coord_t bmax = from_axis == 0 ? bounds.max.x() : bounds.max.y();
|
||||
lo = std::max(lo, bmin);
|
||||
hi = std::min(hi, bmax);
|
||||
Polygon poly;
|
||||
if (hi <= lo)
|
||||
return poly;
|
||||
if (from_axis == 0)
|
||||
poly.points = { Point(lo, bounds.min.y()), Point(hi, bounds.min.y()),
|
||||
Point(hi, bounds.max.y()), Point(lo, bounds.max.y()) };
|
||||
else
|
||||
poly.points = { Point(bounds.min.x(), lo), Point(bounds.max.x(), lo),
|
||||
Point(bounds.max.x(), hi), Point(bounds.min.x(), hi) };
|
||||
return poly;
|
||||
}
|
||||
|
||||
// Everything the per-band line generator needs, gathered once per object.
|
||||
struct BeltBrimContext
|
||||
{
|
||||
BeltFloorContext ctx;
|
||||
BeltBrimFrame frame;
|
||||
ExPolygons region; // brim region, object-local slicing XY
|
||||
BoundingBox region_bbox;
|
||||
Flow brim_flow;
|
||||
coord_t pitch_u = 0;
|
||||
coord_t u_anchor = 0;
|
||||
double in_plane_pitch = 0.; // mm
|
||||
};
|
||||
|
||||
// Emit the cross-belt brim lines that belong to the band [print_z - height, print_z].
|
||||
static void belt_brim_band_paths(const BeltBrimContext &bc,
|
||||
coordf_t print_z,
|
||||
coordf_t height,
|
||||
const Polygons &obstacles,
|
||||
ExtrusionEntityCollection &out,
|
||||
ExPolygons &areas_out)
|
||||
{
|
||||
coordf_t u_lo = bc.ctx.cutoff_u(print_z - height);
|
||||
coordf_t u_hi = bc.ctx.cutoff_u(print_z);
|
||||
if (u_lo > u_hi)
|
||||
std::swap(u_lo, u_hi);
|
||||
|
||||
// How wide this band is measured ON the belt, versus one nominal bead.
|
||||
const double band_in_plane = (u_hi - u_lo) * bc.frame.u_stretch();
|
||||
|
||||
// Fraction of the layer height at which a line sits above the belt. Toward the
|
||||
// downhill edge, so the sheet is reasonably thick while the nozzle stays clear of
|
||||
// the belt itself.
|
||||
static constexpr double BAND_CLEARANCE_FRACTION = 0.75;
|
||||
|
||||
std::vector<coord_t> us;
|
||||
double uniform_clearance = 0.; // 0 => derive per line from its own position
|
||||
double line_pitch = bc.in_plane_pitch;
|
||||
if (band_in_plane <= bc.in_plane_pitch + EPSILON) {
|
||||
// Steep belt, which is the normal case: the band is narrower than one bead, so
|
||||
// exactly one line fits. Place it at a FIXED fraction of the band rather than
|
||||
// on a nominal-spacing lattice. On a lattice each line lands at an arbitrary
|
||||
// point in its band, the clearance sweeps [0, height] from band to band, and the
|
||||
// bead width therefore varies by 2x - visible as ragged, uneven brim lines.
|
||||
// Anchoring to the band makes the clearance identical everywhere, so every bead
|
||||
// is the same width.
|
||||
//
|
||||
// The spacing is then whatever the bands give (height / sin(tilt) on the belt)
|
||||
// rather than the nominal bead spacing, so the flow below is matched to THAT
|
||||
// pitch. Matched flow at the real pitch is what keeps the sheet uniform and
|
||||
// gap-free; using nominal flow at band spacing would over-feed it.
|
||||
us.push_back(scale_(bc.ctx.cutoff_u(print_z - BAND_CLEARANCE_FRACTION * height)));
|
||||
uniform_clearance = BAND_CLEARANCE_FRACTION * height;
|
||||
line_pitch = band_in_plane;
|
||||
} else {
|
||||
// Shallow belt: the band is wider than a bead, so it takes several lines and they
|
||||
// have to sit on the nominal lattice. Their clearances then differ, and so do
|
||||
// their widths - unavoidable here, but shallow belts are the rare case.
|
||||
us = belt_brim_line_positions(scale_(u_lo), scale_(u_hi), bc.pitch_u, bc.u_anchor);
|
||||
}
|
||||
if (us.empty())
|
||||
return;
|
||||
|
||||
const Polygons region_polys = to_polygons(bc.region);
|
||||
|
||||
// One lattice line at a time: the clearance - and therefore the extrusion
|
||||
// volume - is a property of the line's u, so the pieces of different lines
|
||||
// must not be pooled before the flow is resolved.
|
||||
// Overshoot the region so the clip, not the line's ends, decides the extent.
|
||||
const coord_t margin = coord_t(SCALED_EPSILON) + 1;
|
||||
for (const coord_t u : us) {
|
||||
Polyline line;
|
||||
if (bc.frame.from_axis == 0)
|
||||
line.points = { Point(u, coord_t(bc.region_bbox.min.y() - margin)),
|
||||
Point(u, coord_t(bc.region_bbox.max.y() + margin)) };
|
||||
else
|
||||
line.points = { Point(coord_t(bc.region_bbox.min.x() - margin), u),
|
||||
Point(coord_t(bc.region_bbox.max.x() + margin), u) };
|
||||
|
||||
Polylines pieces = intersection_pl(Polylines{ line }, region_polys);
|
||||
if (! obstacles.empty())
|
||||
pieces = diff_pl(pieces, obstacles);
|
||||
if (pieces.empty())
|
||||
continue;
|
||||
|
||||
// Nozzle-to-belt clearance for this line. Constant along the line, because the
|
||||
// belt height depends only on the shear-axis coordinate. Band-anchored lines
|
||||
// share one clearance by construction; lattice lines (shallow belts) each get
|
||||
// their own, clamped so neither end of a band yields an unprintable bead.
|
||||
double clearance = uniform_clearance;
|
||||
if (clearance <= 0.) {
|
||||
const Point probe = bc.frame.from_axis == 0 ? Point(u, 0) : Point(0, u);
|
||||
clearance = print_z - bc.ctx.floor_print_z(probe);
|
||||
clearance = std::min(std::max(clearance, 0.5 * height), height);
|
||||
}
|
||||
|
||||
// with_cross_section, not with_height: it reaches the prescribed volume while
|
||||
// KEEPING the extrusion spacing, so the bead is sized to fill exactly one
|
||||
// pitch x clearance cell of the sheet.
|
||||
const Flow f = bc.brim_flow.with_cross_section(float(line_pitch * clearance));
|
||||
|
||||
// Footprint of these beads, for the first-layer convex hull and bbox.
|
||||
for (const Polygon &p : offset(pieces, 0.5f * float(f.scaled_width())))
|
||||
areas_out.emplace_back(ExPolygon(p));
|
||||
|
||||
extrusion_entities_append_paths(out.entities, chain_polylines(std::move(pieces)),
|
||||
erBrim, f.mm3_per_mm(), f.width(), float(clearance));
|
||||
}
|
||||
}
|
||||
|
||||
// Union of everything extruded at `print_z` that the brim must keep clear of, expressed
|
||||
// in `self`'s local slicing frame. Includes `self` itself: its slice at this Z can
|
||||
// overhang outside the belt footprint and land in the brim ring, which the flattened
|
||||
// brim_object_gap - a belt-plane separation - does not cover.
|
||||
//
|
||||
// THREADING: this runs inside posSupportMaterial, which Print::process() executes for all
|
||||
// objects in a tbb::parallel_for (Print.cpp). Object slices are finished by then and safe
|
||||
// to read across objects, but SUPPORT layers are not: another object's thread may be
|
||||
// inside clear_support_layers() - which deletes the SupportLayer pointers - right now, so
|
||||
// touching a foreign object's support_layers() here is a use-after-free. Only this
|
||||
// object's own supports are consulted; they are complete, because make_belt_brim() runs at
|
||||
// the tail of this object's own generate_support_material(). The cost is that the brim
|
||||
// does not dodge a *different* object's support at the same Z, which needs the objects to
|
||||
// overlap in the belt direction in the first place.
|
||||
// `region_bbox` bounds the brim; anything outside it cannot clip a brim line, so whole
|
||||
// objects are skipped without materialising their polygons. On a typical plate the
|
||||
// objects do not overlap and every foreign object drops out here, which matters because
|
||||
// this runs once per band - hundreds of times per object.
|
||||
static Polygons belt_brim_obstacles(const Print &print, const PrintObject &self,
|
||||
const BoundingBox ®ion_bbox, coordf_t print_z, coordf_t tol)
|
||||
{
|
||||
const Point shift_self = self.instances().empty() ? Point(0, 0)
|
||||
: self.instances().front().shift_without_plate_offset();
|
||||
Polygons out;
|
||||
for (const PrintObject *o : print.objects()) {
|
||||
const bool is_self = (o == &self);
|
||||
for (const PrintInstance &inst : o->instances()) {
|
||||
const Point delta = inst.shift_without_plate_offset() - shift_self;
|
||||
if (const Layer *l = o->get_layer_at_printz(print_z, tol)) {
|
||||
BoundingBox lb = get_extents(l->lslices);
|
||||
lb.translate(delta.x(), delta.y());
|
||||
if (lb.overlap(region_bbox)) {
|
||||
Polygons ps = to_polygons(l->lslices);
|
||||
for (Polygon &p : ps)
|
||||
p.translate(delta);
|
||||
polygons_append(out, std::move(ps));
|
||||
}
|
||||
}
|
||||
if (! is_self)
|
||||
continue;
|
||||
if (const SupportLayer *sl = o->get_support_layer_at_printz(print_z, tol)) {
|
||||
Polygons ps = sl->support_fills.polygons_covered_by_spacing();
|
||||
for (Polygon &p : ps)
|
||||
p.translate(delta);
|
||||
polygons_append(out, std::move(ps));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (out.size() < 2)
|
||||
return out; // union_() of 0 or 1 polygons is pure overhead
|
||||
return union_(out);
|
||||
}
|
||||
|
||||
void make_belt_brim(PrintObject &object)
|
||||
{
|
||||
object.clear_belt_brim();
|
||||
if (! object.has_belt_brim())
|
||||
return;
|
||||
|
||||
const Print &print = *object.print();
|
||||
BeltBrimContext bc;
|
||||
if (! bc.ctx.init(object.slicing_parameters(), print.config()))
|
||||
return;
|
||||
bc.frame = BeltBrimFrame{ bc.ctx.shear_factor(), bc.ctx.from_axis() };
|
||||
|
||||
const size_t nlayers = object.layers().size();
|
||||
if (nlayers == 0)
|
||||
return;
|
||||
|
||||
// 1. Belt footprint: the union of each layer's slice clipped to that layer's
|
||||
// own contact band. This is the object's bottom face, which on a belt is
|
||||
// spread over every layer instead of sitting in layer 0.
|
||||
ExPolygons footprint_acc;
|
||||
for (size_t i = 0; i < nlayers; ++ i) {
|
||||
const Layer &layer = *object.layers()[i];
|
||||
if (layer.lslices.empty())
|
||||
continue;
|
||||
// print_z - height, not the previous layer's print_z: variable layer
|
||||
// heights make the latter wrong.
|
||||
coordf_t u_lo = bc.ctx.cutoff_u(layer.print_z - layer.height);
|
||||
coordf_t u_hi = bc.ctx.cutoff_u(layer.print_z);
|
||||
if (u_lo > u_hi)
|
||||
std::swap(u_lo, u_hi);
|
||||
BoundingBox bb = get_extents(layer.lslices);
|
||||
bb.offset(scale_(1.));
|
||||
const Polygon band = band_box(bb, bc.frame.from_axis, u_lo, u_hi);
|
||||
if (band.empty())
|
||||
continue;
|
||||
expolygons_append(footprint_acc, intersection_ex(layer.lslices, Polygons{ band }));
|
||||
}
|
||||
const ExPolygons footprint = union_ex(footprint_acc);
|
||||
if (footprint.empty())
|
||||
return;
|
||||
|
||||
// 2. Brim region, offset in the flattened (true on-belt) metric.
|
||||
const PrintObjectConfig &cfg = object.config();
|
||||
bc.brim_flow = print.brim_flow();
|
||||
const double flow_w = bc.brim_flow.scaled_spacing() * SCALING_FACTOR;
|
||||
// Quantize to an even number of lines, as the plate brim does.
|
||||
const coord_t width = scale_(std::floor(cfg.brim_width.value / flow_w / 2) * flow_w * 2);
|
||||
const coord_t leading = scale_(cfg.leading_brim_length.value);
|
||||
const coord_t lateral = scale_(cfg.extra_brim_width.value);
|
||||
const coord_t gap = scale_(cfg.brim_object_gap.value);
|
||||
|
||||
// Belt printers collapse Auto / Mouse ear / Painted to outer-only: the auto width
|
||||
// heuristic and flat ear discs have no meaning on a tilted plane. Leading-edge-only
|
||||
// is an outer brim too; it is narrowed down to the first contact below.
|
||||
const BrimType bt = cfg.brim_type.value;
|
||||
const bool has_outer = bt == btOuterOnly || bt == btOuterAndInner
|
||||
|| bt == btAutoBrim || bt == btEar || bt == btPainted
|
||||
|| bt == btLeadingEdgeOnly;
|
||||
const bool has_inner = bt == btInnerOnly || bt == btOuterAndInner;
|
||||
|
||||
bc.region = belt_unflatten(
|
||||
belt_brim_region(belt_flatten(footprint, bc.frame), has_outer, has_inner,
|
||||
width, gap, leading, lateral, bc.frame),
|
||||
bc.frame);
|
||||
|
||||
if (bt == btLeadingEdgeOnly && ! bc.region.empty()) {
|
||||
// Keep only what lies at or downhill of the object's FIRST contact with the
|
||||
// belt, so the part is supported as it lands and nothing is printed alongside
|
||||
// it afterwards. The cut is the uphill edge of the first layer's contact band:
|
||||
// everything past it belongs to later contacts.
|
||||
const coordf_t u_cut = bc.ctx.cutoff_u(object.layers().front()->print_z);
|
||||
BoundingBox keep_bb = get_extents(bc.region);
|
||||
keep_bb.offset(scale_(1.));
|
||||
const bool low_side = bc.frame.shear > 0.; // downhill is -u
|
||||
const Polygon keep = band_box(keep_bb, bc.frame.from_axis,
|
||||
low_side ? unscale<double>(bc.frame.from_axis == 0 ? keep_bb.min.x() : keep_bb.min.y()) : u_cut,
|
||||
low_side ? u_cut : unscale<double>(bc.frame.from_axis == 0 ? keep_bb.max.x() : keep_bb.max.y()));
|
||||
bc.region = keep.empty() ? ExPolygons{} : intersection_ex(bc.region, Polygons{ keep });
|
||||
}
|
||||
|
||||
if (bc.region.empty())
|
||||
return;
|
||||
bc.region_bbox = get_extents(bc.region);
|
||||
|
||||
// 3. Line lattice. Fixed pitch in the flattened metric, anchored at the
|
||||
// footprint's leading-most edge so lines stay collinear across
|
||||
// disconnected islands and across the apron prologue.
|
||||
bc.pitch_u = std::max<coord_t>(1, coord_t(bc.brim_flow.scaled_spacing() * bc.frame.cos_tilt()));
|
||||
bc.in_plane_pitch = unscale<double>(bc.pitch_u) * bc.frame.u_stretch();
|
||||
{
|
||||
const BoundingBox fbb = get_extents(footprint);
|
||||
const bool low_side = bc.frame.shear > 0.;
|
||||
bc.u_anchor = bc.frame.from_axis == 0 ? (low_side ? fbb.min.x() : fbb.max.x())
|
||||
: (low_side ? fbb.min.y() : fbb.max.y());
|
||||
}
|
||||
|
||||
// 4. Bands coincident with an object layer.
|
||||
std::vector<ExtrusionEntityCollection> by_layer(nlayers);
|
||||
std::vector<ExPolygons> areas_by_layer(nlayers);
|
||||
for (size_t i = 0; i < nlayers; ++ i) {
|
||||
const Layer &layer = *object.layers()[i];
|
||||
const Polygons obstacles = belt_brim_obstacles(print, object, bc.region_bbox, layer.print_z, 0.5 * layer.height);
|
||||
belt_brim_band_paths(bc, layer.print_z, layer.height, obstacles, by_layer[i], areas_by_layer[i]);
|
||||
}
|
||||
|
||||
// 5. Apron prologue: the part of the region downhill of the object's first
|
||||
// layer, which has no object layer to ride on.
|
||||
std::vector<BeltBrimBand> prologue;
|
||||
{
|
||||
const Layer &first = *object.layers().front();
|
||||
const coordf_t h = first.height;
|
||||
const bool low_side = bc.frame.shear > 0.;
|
||||
const coord_t u_lead_s = bc.frame.from_axis == 0
|
||||
? (low_side ? bc.region_bbox.min.x() : bc.region_bbox.max.x())
|
||||
: (low_side ? bc.region_bbox.min.y() : bc.region_bbox.max.y());
|
||||
const coordf_t u_lead = unscale<double>(u_lead_s);
|
||||
// print_z at which the belt surface crosses the region's leading edge.
|
||||
const coordf_t z_lead = bc.ctx.shear_factor() * u_lead
|
||||
+ bc.ctx.floor_offset() + bc.ctx.z_shift();
|
||||
if (h > EPSILON)
|
||||
for (coordf_t z = first.print_z - h; z > z_lead - h; z -= h) {
|
||||
const Polygons obstacles = belt_brim_obstacles(print, object, bc.region_bbox, z, 0.5 * h);
|
||||
BeltBrimBand band;
|
||||
band.print_z = z;
|
||||
band.height = h;
|
||||
belt_brim_band_paths(bc, z, h, obstacles, band.fills, band.areas);
|
||||
if (! band.fills.empty())
|
||||
prologue.emplace_back(std::move(band));
|
||||
}
|
||||
// Lowest Z first, so collect_layers_to_print sees them in print order.
|
||||
std::reverse(prologue.begin(), prologue.end());
|
||||
}
|
||||
|
||||
object.set_belt_brim(std::move(by_layer), std::move(areas_by_layer), std::move(prologue));
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -1,169 +0,0 @@
|
||||
#ifndef slic3r_BeltBrim_hpp_
|
||||
#define slic3r_BeltBrim_hpp_
|
||||
|
||||
#include "ExPolygon.hpp"
|
||||
#include "ExtrusionEntityCollection.hpp"
|
||||
#include "Point.hpp"
|
||||
#include "Polyline.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
// Belt-printer brim geometry.
|
||||
//
|
||||
// A belt printer slices in a ROTATED frame, so the belt surface is not the
|
||||
// Z=0 bed plane but a tilted plane in slicing space:
|
||||
//
|
||||
// z_slicing(u) = shear * u + floor_offset + z_shift, u = X or Y
|
||||
//
|
||||
// where `shear == tan(tilt)` (SlicingParameters::belt_floor_shear_factor) and
|
||||
// the axis is selected by SlicingParameters::belt_floor_from_axis. See
|
||||
// Support/BeltFloorContext.hpp for the canonical accessors.
|
||||
//
|
||||
// Consequences that drive everything in this file:
|
||||
//
|
||||
// * A horizontal slicing layer touches the belt only along a narrow strip at
|
||||
// its leading edge, `layer_height / shear` wide (~0.2 mm at 45 degrees).
|
||||
// The object's belt footprint - its bottom face - is therefore spread over
|
||||
// every layer, not contained in layer 0.
|
||||
// * Distances measured in slicing XY are NOT on-belt distances: moving `du`
|
||||
// along the shear axis travels `du / cos(tilt)` across the belt. So brim
|
||||
// offsets have to be taken in a "flattened" space where the shear axis is
|
||||
// stretched by `1 / cos(tilt)`, then mapped back.
|
||||
// * Brim ahead of the part (downhill) lies at slicing Z BELOW the object's
|
||||
// first layer, because the object's layer 0 is precisely its leading
|
||||
// contact with the belt.
|
||||
//
|
||||
// Everything here is pure geometry on ExPolygons/Polylines so it can be unit
|
||||
// tested without a Print. Keep user-visible strings out of this file: it is
|
||||
// not listed in localization/i18n/list.txt.
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// Tilt window within which the BELT plane, not the bed plane, is the adhesion
|
||||
// surface. Below ~1 degree a belt is a flat bed as far as adhesion goes, and the
|
||||
// contact band would be layer_height/sin(tilt) - tens of millimetres - so the
|
||||
// ordinary plate brim is both correct and cheaper. Above ~85 degrees the whole
|
||||
// brim compresses into a sliver and is not worth generating.
|
||||
inline constexpr double BELT_BRIM_MIN_TILT_DEG = 1.;
|
||||
inline constexpr double BELT_BRIM_MAX_TILT_DEG = 85.;
|
||||
|
||||
// Description of the tilted belt plane, reduced to what the brim geometry needs.
|
||||
struct BeltBrimFrame
|
||||
{
|
||||
// tan(tilt). Sign selects which way is downhill.
|
||||
double shear = 0.;
|
||||
// 0 = X, 1 = Y. Matches BeltFloorContext::from_axis().
|
||||
int from_axis = 1;
|
||||
|
||||
// 1 / cos(tilt). Stretch factor that turns a projected distance along
|
||||
// `from_axis` into the true distance travelled across the belt.
|
||||
double u_stretch() const { return std::sqrt(1. + shear * shear); }
|
||||
// cos(tilt). The inverse mapping.
|
||||
double cos_tilt() const { return 1. / this->u_stretch(); }
|
||||
// Downhill is where the belt surface is lower, i.e. printed earlier, i.e.
|
||||
// the leading edge of the part. For shear > 0 that is -u.
|
||||
int downhill_sign() const { return shear > 0. ? -1 : +1; }
|
||||
};
|
||||
|
||||
// Scale only the `from_axis` component by `factor`, rounding to nearest.
|
||||
//
|
||||
// Deliberately not MultiPoint::scale(fx, fy) / ExPolygon::scale(fx, fy): those
|
||||
// truncate toward zero, which is asymmetric about the origin and loses up to a
|
||||
// full coordinate unit per vertex on every round trip.
|
||||
ExPolygons belt_scale_u(const ExPolygons &src, const BeltBrimFrame &frame, double factor);
|
||||
Polylines belt_scale_u(const Polylines &src, const BeltBrimFrame &frame, double factor);
|
||||
|
||||
// Into / out of the space where Euclidean offsets equal true on-belt distances.
|
||||
inline ExPolygons belt_flatten(const ExPolygons &src, const BeltBrimFrame &frame)
|
||||
{ return belt_scale_u(src, frame, frame.u_stretch()); }
|
||||
inline ExPolygons belt_unflatten(const ExPolygons &src, const BeltBrimFrame &frame)
|
||||
{ return belt_scale_u(src, frame, frame.cos_tilt()); }
|
||||
|
||||
// Minkowski sum of `src` with the segment [0, t]: the region swept by sliding
|
||||
// `src` along t. Used to grow the brim downhill for "extra brim width".
|
||||
//
|
||||
// Implemented as union_(P, P + t, {parallelogram per boundary edge}) over ALL
|
||||
// contours including holes, with every parallelogram forced counter-clockwise
|
||||
// so the non-zero fill rule closes holes narrower than t along the sweep
|
||||
// direction. A hole survives exactly when it is wider than |t| measured along
|
||||
// t - not when it is wider in its narrowest Euclidean direction.
|
||||
ExPolygons sweep_ex(const ExPolygons &src, const Point &t);
|
||||
|
||||
// Brim region for one already-flattened belt footprint. All lengths are scaled
|
||||
// and measured in the flattened (true on-belt) metric.
|
||||
//
|
||||
// `has_outer` / `has_inner` are the resolved BrimType: belt printers collapse
|
||||
// Auto / Mouse ear / Painted to outer-only, so the caller does that mapping and
|
||||
// this function never needs PrintConfig.
|
||||
//
|
||||
// Two directional extras are applied to the footprint before the outer offset, so
|
||||
// each one buys reach in one direction only:
|
||||
//
|
||||
// `leading` (leading_brim_length) sweeps the footprint DOWNHILL along the belt,
|
||||
// so every leading-facing edge gains an apron ahead of it.
|
||||
// `lateral` (extra_brim_width) sweeps it BOTH WAYS across the belt, widening
|
||||
// the brim sideways without pushing it further ahead or behind.
|
||||
//
|
||||
// Neither is applied to the inner (hole) ring.
|
||||
ExPolygons belt_brim_region(const ExPolygons &footprint_flat,
|
||||
bool has_outer,
|
||||
bool has_inner,
|
||||
coord_t brim_width,
|
||||
coord_t object_gap,
|
||||
coord_t leading,
|
||||
coord_t lateral,
|
||||
const BeltBrimFrame &frame);
|
||||
|
||||
// Brim line positions for one layer band.
|
||||
//
|
||||
// Lines sit on a fixed lattice `u_anchor + k * pitch_u` so the on-belt spacing
|
||||
// between neighbouring brim lines is constant regardless of how the lattice
|
||||
// falls across layer bands. Snapping to band centres instead would quantise
|
||||
// the spacing to whole bands and under-deposit by ~35% at 45 degrees.
|
||||
//
|
||||
// The band is half-open, [u_lo, u_hi), so every lattice point belongs to
|
||||
// exactly one band: none duplicated at a boundary, none dropped. A band
|
||||
// narrower than the pitch simply yields nothing; a band much wider (shallow
|
||||
// tilt) yields several lines.
|
||||
std::vector<coord_t> belt_brim_line_positions(coord_t u_lo,
|
||||
coord_t u_hi,
|
||||
coord_t pitch_u,
|
||||
coord_t u_anchor);
|
||||
|
||||
// ---------------------------------------------------------------- pipeline
|
||||
|
||||
// One brim-only layer printed BEFORE the object's first layer, carrying the
|
||||
// apron that has to be stuck to the belt ahead of the part.
|
||||
//
|
||||
// Deliberately not a Layer subclass. A synthetic Layer would inherit id()
|
||||
// semantics that leak into initial-layer temperature selection, the spiral vase
|
||||
// probe, gradual interpolation, avoid-crossing-perimeters and cooling, all of
|
||||
// which key off Layer::id() == 0 or off a layer's regions. A plain record
|
||||
// carries only what the emitter needs.
|
||||
//
|
||||
// `height` is the LAYER height, used for the Z move and ordering metadata only.
|
||||
// Each extrusion path inside `fills` carries its own height, equal to that
|
||||
// line's nozzle-to-belt clearance, which varies across the band.
|
||||
struct BeltBrimBand
|
||||
{
|
||||
coordf_t print_z = 0.;
|
||||
coordf_t height = 0.;
|
||||
// erBrim paths in the object's local slicing frame, untranslated.
|
||||
ExtrusionEntityCollection fills;
|
||||
// Footprint of those paths, for the first-layer convex hull / bbox.
|
||||
ExPolygons areas;
|
||||
};
|
||||
|
||||
class PrintObject;
|
||||
|
||||
// Generate the belt brim for one object: fills its per-object-layer bands and
|
||||
// its apron prologue. No-op unless PrintObject::has_belt_brim().
|
||||
//
|
||||
// Runs inside posSupportMaterial rather than the brim step, because the prologue
|
||||
// print_z values must exist before ToolOrdering is built at psWipeTower.
|
||||
void make_belt_brim(PrintObject &object);
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif // slic3r_BeltBrim_hpp_
|
||||
@@ -1,70 +0,0 @@
|
||||
#include "BeltGCode.hpp"
|
||||
#include "BeltGCodeWriter.hpp"
|
||||
#include "BeltTransform.hpp"
|
||||
#include "Print.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
void BeltGCode::init_belt_writer(Print &print)
|
||||
{
|
||||
auto belt_writer = std::make_unique<BeltGCodeWriter>();
|
||||
// Axis remap and build volume max are set by base GCode after init_belt_writer returns.
|
||||
belt_writer->set_belt_back_transform(print.config());
|
||||
belt_writer->set_machine_frame_transform(print.config());
|
||||
belt_writer->set_xy_offset(m_gcode_offset.x(), m_gcode_offset.y());
|
||||
m_writer = std::move(belt_writer);
|
||||
}
|
||||
|
||||
void BeltGCode::write_belt_header(GCodeOutputStream &file, const Print &print)
|
||||
{
|
||||
const auto &full_cfg = print.full_print_config();
|
||||
// Slicing rotation: the belt tilt (axis + angle) and the single source of truth
|
||||
// for the physical tilt the G-code viewer uses to enable belt view.
|
||||
file.write_format("; belt_slice_rotation = %s\n", full_cfg.opt_serialize("belt_slice_rotation").c_str());
|
||||
file.write_format("; belt_slice_rotation_angle = %.1f\n", print.config().belt_slice_rotation_angle.value);
|
||||
file.write_format("; belt_slice_rotation_global = %d\n", print.config().belt_slice_rotation_global.value ? 1 : 0);
|
||||
// Pre-slice remap configs
|
||||
file.write_format("; preslice_remap_x = %s\n", full_cfg.opt_serialize("preslice_remap_x").c_str());
|
||||
file.write_format("; preslice_remap_y = %s\n", full_cfg.opt_serialize("preslice_remap_y").c_str());
|
||||
file.write_format("; preslice_remap_z = %s\n", full_cfg.opt_serialize("preslice_remap_z").c_str());
|
||||
file.write_format("; preslice_remap_global = %d\n", print.config().preslice_remap_global.value ? 1 : 0);
|
||||
file.write_format("; belt_preslice_global = %d\n", print.config().belt_preslice_global.value ? 1 : 0);
|
||||
// Machine-frame transform: shear (tan) + scale (1/cos) derived from the belt
|
||||
// tilt angle (or belt_frame_tilt_angle when decoupled).
|
||||
file.write_format("; belt_frame_tilt_decouple = %d\n", print.config().belt_frame_tilt_decouple.value ? 1 : 0);
|
||||
file.write_format("; belt_frame_tilt_angle = %.1f\n", print.config().belt_frame_tilt_angle.value);
|
||||
}
|
||||
|
||||
void BeltGCode::on_set_origin(const PrintObject * /*obj*/, const Point & /*inst_shift*/)
|
||||
{
|
||||
// Global pre-slice mode: adjust origin using computed correction.
|
||||
// Transform the origin through the belt pipeline so that
|
||||
// back_transform(T * origin) = origin (correct machine position).
|
||||
//
|
||||
// Flags that trigger this path:
|
||||
// belt_preslice_global — full pipeline (rotation * remap) is global
|
||||
// preslice_remap_global — only the pre-slice remap is global
|
||||
// belt_slice_rotation_global — slicing rotation treated as global (matches
|
||||
// the per-instance Z-offset added in PrintObjectSlice.cpp)
|
||||
// The XY origin adjustment uses the FULL forward transform, because the
|
||||
// back_transform applied during G-code emission is always the inverse of
|
||||
// the full pipeline.
|
||||
bool use_global = m_config.belt_preslice_global.value
|
||||
|| (m_config.preslice_remap_global.value
|
||||
&& BeltTransformPipeline::has_preslice_remap(m_config))
|
||||
|| (m_config.belt_slice_rotation_global.value
|
||||
&& m_config.belt_slice_rotation.value != BeltRotationAxis::None
|
||||
&& std::abs(m_config.belt_slice_rotation_angle.value) > EPSILON);
|
||||
if (!use_global)
|
||||
return;
|
||||
|
||||
// Adjust origin: transform through belt forward pipeline so that
|
||||
// the back-transform correctly recovers model-space positions.
|
||||
Transform3d T = BeltTransformPipeline::build_forward_transform(m_config);
|
||||
Vec2d cur_origin = this->origin();
|
||||
Vec3d origin3d(cur_origin.x(), cur_origin.y(), 0.);
|
||||
Vec3d adjusted = T.linear() * origin3d;
|
||||
this->set_origin(Vec2d(adjusted.x(), adjusted.y()));
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -1,23 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "GCode.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// Belt-printer-specific GCode export.
|
||||
//
|
||||
// Inherits from GCode and overrides virtual hooks to:
|
||||
// - Create a BeltGCodeWriter instead of a plain GCodeWriter
|
||||
// - Write belt configuration to the G-code header
|
||||
// - Adjust the origin for global pre-slice transforms when switching instances
|
||||
// - Disable arc fitting (G2/G3 not supported on belt printers)
|
||||
class BeltGCode : public GCode
|
||||
{
|
||||
protected:
|
||||
void init_belt_writer(Print &print) override;
|
||||
void write_belt_header(GCodeOutputStream &file, const Print &print) override;
|
||||
void on_set_origin(const PrintObject *obj, const Point &inst_shift) override;
|
||||
bool should_disable_arc_fitting() const override { return true; }
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -1,278 +0,0 @@
|
||||
#include "BeltGCodeWriter.hpp"
|
||||
#include "FirstLayerPlane.hpp"
|
||||
#include "Geometry.hpp"
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace {
|
||||
|
||||
// Decide whether a particular destination point gets first-layer treatment.
|
||||
// When the plane evaluator is active, distance from the plane wins; otherwise
|
||||
// fall back to the layer-coarse m_is_first_layer flag set by the caller.
|
||||
inline bool belt_point_on_first_layer(
|
||||
const FirstLayerPlane *plane,
|
||||
double first_layer_thickness_mm,
|
||||
bool layer_first_flag,
|
||||
const Vec3d &point_slicing_mm)
|
||||
{
|
||||
if (plane && plane->is_active())
|
||||
return plane->is_first_layer(point_slicing_mm, first_layer_thickness_mm);
|
||||
return layer_first_flag;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// ---- Belt configuration ---------------------------------------------------
|
||||
|
||||
void BeltGCodeWriter::set_belt_back_transform(const PrintConfig &config)
|
||||
{
|
||||
m_belt_back_transform.init_from_config(config);
|
||||
}
|
||||
|
||||
void BeltGCodeWriter::set_machine_frame_transform(const PrintConfig &config)
|
||||
{
|
||||
m_machine_frame_transform.init_from_config(config);
|
||||
}
|
||||
|
||||
Vec3d BeltGCodeWriter::to_machine_coords(const Vec3d &pos) const
|
||||
{
|
||||
// Step 1+2: To Cartesian (back_transform + axis_remap).
|
||||
// In world-coordinates mode (PA line / PA pattern calibration) the input
|
||||
// already describes a point relative to the belt surface, so the
|
||||
// slicer->world back-transform is skipped and only the machine kinematics
|
||||
// (axis remap + frame shear/scale) are applied.
|
||||
Vec3d after_back = m_world_coordinates ? pos : m_belt_back_transform.apply(pos);
|
||||
Vec3d result = apply_axis_remap(after_back);
|
||||
Vec3d after_remap = result;
|
||||
// Step 3: Machine-frame transform (belt frame tilt) applied LAST so it acts
|
||||
// as a global linear transform on the placed coords.
|
||||
Vec3d final = m_machine_frame_transform.apply(result);
|
||||
|
||||
// [BELT-DEBUG] One-shot log per layer transition (i.e. when the input Z
|
||||
// crosses an integer mm boundary) to keep the log volume manageable while
|
||||
// still capturing one sample per ~5 layers. Shows the full pipeline so
|
||||
// Case A vs Case B can be compared step-by-step.
|
||||
static thread_local int s_last_logged_z = std::numeric_limits<int>::min();
|
||||
int z_bucket = static_cast<int>(std::floor(pos.z() * 5.0)); // every 0.2mm
|
||||
if (z_bucket != s_last_logged_z) {
|
||||
s_last_logged_z = z_bucket;
|
||||
BOOST_LOG_TRIVIAL(trace) << "[BELT-DEBUG] to_machine_coords"
|
||||
<< " slicer_in=(" << pos.x() << "," << pos.y() << "," << pos.z() << ")"
|
||||
<< " after_back=(" << after_back.x() << "," << after_back.y() << "," << after_back.z() << ")"
|
||||
<< " after_remap=(" << after_remap.x() << "," << after_remap.y() << "," << after_remap.z() << ")"
|
||||
<< " final=(" << final.x() << "," << final.y() << "," << final.z() << ")"
|
||||
<< " mft_active=" << m_machine_frame_transform.is_active()
|
||||
<< " back_active=" << m_belt_back_transform.is_active();
|
||||
}
|
||||
return final;
|
||||
}
|
||||
|
||||
// ---- Overridden movement methods ------------------------------------------
|
||||
|
||||
std::string BeltGCodeWriter::travel_to_xy(const Vec2d &point, const std::string &comment)
|
||||
{
|
||||
m_pos(0) = point(0);
|
||||
m_pos(1) = point(1);
|
||||
|
||||
this->set_current_position_clear(true);
|
||||
Vec2d point_on_plate = { point(0) - m_x_offset, point(1) - m_y_offset };
|
||||
|
||||
// Belt printer: transform to machine coordinates (XY travel also needs Z due to YZ rotation)
|
||||
Vec3d machine = to_machine_coords(Vec3d(point_on_plate.x(), point_on_plate.y(), m_pos.z()));
|
||||
|
||||
GCodeG1Formatter w;
|
||||
w.emit_xyz(machine);
|
||||
const bool first_layer_for_point = belt_point_on_first_layer(
|
||||
m_first_layer_plane, m_first_layer_thickness_mm, m_is_first_layer,
|
||||
Vec3d(point_on_plate.x(), point_on_plate.y(), m_pos.z()));
|
||||
auto speed = first_layer_for_point
|
||||
? this->config.get_abs_value_at("initial_layer_travel_speed", m_cached_extruder_idx)
|
||||
: this->config.travel_speed.get_at(m_cached_extruder_idx);
|
||||
w.emit_f(speed * 60.0);
|
||||
w.emit_comment(GCodeWriter::full_gcode_comment, comment);
|
||||
return w.string();
|
||||
}
|
||||
|
||||
std::string BeltGCodeWriter::lazy_lift(LiftType lift_type, bool spiral_vase)
|
||||
{
|
||||
// Belt printer: force NormalLift since SpiralLift and SlopeLift compute
|
||||
// slope angles that don't account for the YZ coordinate rotation.
|
||||
return GCodeWriter::lazy_lift(LiftType::NormalLift, spiral_vase);
|
||||
}
|
||||
|
||||
std::string BeltGCodeWriter::eager_lift(const LiftType type)
|
||||
{
|
||||
// Belt printer: force NormalLift (SpiralLift/SlopeLift don't account for YZ rotation).
|
||||
return GCodeWriter::eager_lift(LiftType::NormalLift);
|
||||
}
|
||||
|
||||
std::string BeltGCodeWriter::_travel_to_z(double z, const std::string &comment)
|
||||
{
|
||||
m_pos(2) = z;
|
||||
|
||||
double speed = this->config.travel_speed_z.get_at(m_cached_extruder_idx);
|
||||
if (speed == 0.) {
|
||||
const bool first_layer_for_point = belt_point_on_first_layer(
|
||||
m_first_layer_plane, m_first_layer_thickness_mm, m_is_first_layer,
|
||||
Vec3d(m_pos.x() - m_x_offset, m_pos.y() - m_y_offset, z));
|
||||
speed = first_layer_for_point ? this->config.get_abs_value_at("initial_layer_travel_speed", m_cached_extruder_idx)
|
||||
: this->config.travel_speed.get_at(m_cached_extruder_idx);
|
||||
}
|
||||
|
||||
// Belt printer: a Z-only move in slicing frame needs to emit both Y and Z in machine coords.
|
||||
Vec3d machine = to_machine_coords(Vec3d(m_pos.x() - m_x_offset, m_pos.y() - m_y_offset, z));
|
||||
|
||||
GCodeG1Formatter w;
|
||||
w.emit_xyz(machine);
|
||||
w.emit_f(speed * 60.0);
|
||||
w.emit_comment(GCodeWriter::full_gcode_comment, comment);
|
||||
return w.string();
|
||||
}
|
||||
|
||||
std::string BeltGCodeWriter::extrude_to_xy(const Vec2d &point, double dE, const std::string &comment, bool force_no_extrusion)
|
||||
{
|
||||
m_pos(0) = point(0);
|
||||
m_pos(1) = point(1);
|
||||
if (std::abs(dE) <= std::numeric_limits<double>::epsilon())
|
||||
force_no_extrusion = true;
|
||||
|
||||
if (!force_no_extrusion)
|
||||
filament()->extrude(dE);
|
||||
|
||||
Vec2d point_on_plate = { point(0) - m_x_offset, point(1) - m_y_offset };
|
||||
|
||||
// Belt printer: transform and emit XYZ (Y and Z are coupled)
|
||||
Vec3d machine = to_machine_coords(Vec3d(point_on_plate.x(), point_on_plate.y(), m_pos.z()));
|
||||
|
||||
GCodeG1Formatter w;
|
||||
w.emit_xyz(machine);
|
||||
if (!force_no_extrusion)
|
||||
w.emit_e(filament()->E());
|
||||
w.emit_comment(GCodeWriter::full_gcode_comment, comment);
|
||||
return w.string();
|
||||
}
|
||||
|
||||
std::string BeltGCodeWriter::extrude_to_xyz(const Vec3d &point, double dE, const std::string &comment, bool force_no_extrusion)
|
||||
{
|
||||
m_pos = point;
|
||||
m_lifted = 0;
|
||||
if (!force_no_extrusion)
|
||||
filament()->extrude(dE);
|
||||
|
||||
Vec3d point_on_plate = { point(0) - m_x_offset, point(1) - m_y_offset, point(2) };
|
||||
point_on_plate = to_machine_coords(point_on_plate);
|
||||
|
||||
GCodeG1Formatter w;
|
||||
w.emit_xyz(point_on_plate);
|
||||
if (!force_no_extrusion)
|
||||
w.emit_e(filament()->E());
|
||||
w.emit_comment(GCodeWriter::full_gcode_comment, comment);
|
||||
return w.string();
|
||||
}
|
||||
|
||||
std::string BeltGCodeWriter::travel_to_xyz(const Vec3d &point, const std::string &comment, bool force_z)
|
||||
{
|
||||
// Belt-specific override of travel_to_xyz.
|
||||
// Key differences from base:
|
||||
// 1. All coordinates go through to_machine_coords()
|
||||
// 2. Always emit full XYZ (can't split XY and Z due to coupling)
|
||||
// 3. Lift type forced to NormalLift (handled by lazy_lift/eager_lift overrides)
|
||||
|
||||
Vec3d dest_point = point;
|
||||
const bool first_layer_for_point = belt_point_on_first_layer(
|
||||
m_first_layer_plane, m_first_layer_thickness_mm, m_is_first_layer,
|
||||
Vec3d(point.x() - m_x_offset, point.y() - m_y_offset, point.z()));
|
||||
auto travel_speed =
|
||||
first_layer_for_point ? this->config.get_abs_value_at("initial_layer_travel_speed", m_cached_extruder_idx)
|
||||
: this->config.travel_speed.get_at(m_cached_extruder_idx);
|
||||
|
||||
// Handle pending z_hop
|
||||
if (std::abs(m_to_lift) > EPSILON) {
|
||||
assert(std::abs(m_lifted) < EPSILON);
|
||||
if ((!this->is_current_position_clear() || m_pos != dest_point) &&
|
||||
m_to_lift + m_pos(2) > point(2)) {
|
||||
m_lifted = m_to_lift + m_pos(2) - point(2);
|
||||
dest_point(2) = m_to_lift + m_pos(2);
|
||||
}
|
||||
m_to_lift = 0.;
|
||||
|
||||
std::string slop_move;
|
||||
Vec3d source = { m_pos(0) - m_x_offset, m_pos(1) - m_y_offset, m_pos(2) };
|
||||
Vec3d target = { dest_point(0) - m_x_offset, dest_point(1) - m_y_offset, dest_point(2) };
|
||||
Vec3d delta = target - source;
|
||||
Vec2d delta_no_z = { delta(0), delta(1) };
|
||||
|
||||
if (delta(2) > 0 && delta_no_z.norm() != 0.0f) {
|
||||
// Belt: SpiralLift and SlopeLift are disabled (lazy_lift forces NormalLift),
|
||||
// but handle NormalLift and fallthrough.
|
||||
if (m_to_lift_type == LiftType::SlopeLift &&
|
||||
this->is_current_position_clear() &&
|
||||
atan2(delta(2), delta_no_z.norm()) < this->filament()->travel_slope()) {
|
||||
Vec2d temp = delta_no_z.normalized() * delta(2) / tan(this->filament()->travel_slope());
|
||||
Vec3d slope_top_point = Vec3d(temp(0), temp(1), delta(2)) + source;
|
||||
slope_top_point = to_machine_coords(slope_top_point);
|
||||
GCodeG1Formatter w0;
|
||||
w0.emit_xyz(slope_top_point);
|
||||
w0.emit_f(travel_speed * 60.0);
|
||||
w0.emit_comment(GCodeWriter::full_gcode_comment, comment);
|
||||
slop_move = w0.string();
|
||||
}
|
||||
else if (m_to_lift_type == LiftType::NormalLift && this->is_current_position_clear()) {
|
||||
// Only lift-in-place when the current position is known. On a normal
|
||||
// printer _travel_to_z emits a Z-only move, but in belt mode Z is coupled
|
||||
// to Y/X, so _travel_to_z re-emits the current m_pos through the belt
|
||||
// shear. At print start (and after custom gcode) m_pos.xy is still the
|
||||
// uninitialised origin (0,0), which shears into a bogus machine point
|
||||
// (e.g. X=bed_max, Y=layer_z) far up the gantry. Skipping the separate
|
||||
// lift here is safe: there is nothing to lift over yet, and the
|
||||
// xy_z_move below travels straight to the destination with full XYZ,
|
||||
// establishing the correct position. This mirrors the SlopeLift branch
|
||||
// above, which already guards on is_current_position_clear().
|
||||
slop_move = _travel_to_z(target.z(), "normal lift Z");
|
||||
}
|
||||
}
|
||||
|
||||
std::string xy_z_move;
|
||||
{
|
||||
Vec3d emit_target = to_machine_coords(target);
|
||||
GCodeG1Formatter w0;
|
||||
// Belt mode: always emit full XYZ since Y and Z are coupled
|
||||
w0.emit_xyz(emit_target);
|
||||
w0.emit_f(travel_speed * 60.0);
|
||||
w0.emit_comment(GCodeWriter::full_gcode_comment, comment);
|
||||
xy_z_move = w0.string();
|
||||
}
|
||||
m_pos = dest_point;
|
||||
this->set_current_position_clear(true);
|
||||
return slop_move + xy_z_move;
|
||||
}
|
||||
else if (!force_z && !this->will_move_z(point(2))) {
|
||||
double nominal_z = m_pos(2) - m_lifted;
|
||||
m_lifted -= (point(2) - nominal_z);
|
||||
if (std::abs(m_lifted) < EPSILON)
|
||||
m_lifted = 0.;
|
||||
this->set_current_position_clear(true);
|
||||
return this->travel_to_xy(to_2d(point));
|
||||
}
|
||||
else {
|
||||
m_lifted = 0;
|
||||
}
|
||||
|
||||
Vec3d point_on_plate = { dest_point(0) - m_x_offset, dest_point(1) - m_y_offset, dest_point(2) };
|
||||
point_on_plate = to_machine_coords(point_on_plate);
|
||||
|
||||
// Belt mode: always emit full XYZ
|
||||
GCodeG1Formatter w;
|
||||
w.emit_xyz(point_on_plate);
|
||||
// Use the first-layer-aware travel_speed computed at the top of this function,
|
||||
// not the raw config travel_speed, so initial-layer travels are correctly slowed.
|
||||
w.emit_f(travel_speed * 60.0);
|
||||
w.emit_comment(GCodeWriter::full_gcode_comment, comment);
|
||||
|
||||
m_pos = dest_point;
|
||||
this->set_current_position_clear(true);
|
||||
return w.string();
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -1,64 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "GCodeWriter.hpp"
|
||||
#include "GCode/BeltBackTransform.hpp"
|
||||
#include "GCode/MachineFrameTransform.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class FirstLayerPlane;
|
||||
|
||||
// Belt-printer-specific GCode writer.
|
||||
//
|
||||
// Inherits from GCodeWriter and overrides movement methods to apply
|
||||
// coordinate transformation (back-transform, axis remap, machine-frame
|
||||
// transform) and emit coupled XYZ moves (Y and Z are coupled due to belt tilt).
|
||||
class BeltGCodeWriter : public GCodeWriter
|
||||
{
|
||||
public:
|
||||
BeltGCodeWriter() : GCodeWriter() {}
|
||||
|
||||
// Belt configuration (axis remap is inherited from GCodeWriter)
|
||||
void set_belt_back_transform(const PrintConfig &config);
|
||||
void set_machine_frame_transform(const PrintConfig &config);
|
||||
Vec3d to_machine_coords(const Vec3d &pos) const;
|
||||
|
||||
// World-coordinates mode: incoming coordinates are treated as points
|
||||
// relative to the physical belt surface (X across, Y along the belt,
|
||||
// Z height above it) instead of slicing-frame coordinates — the
|
||||
// slicer->world back-transform is skipped. Used by the PA line / PA
|
||||
// pattern calibration generators, whose logical bed coordinates describe
|
||||
// first-layer drawings on the build surface.
|
||||
void set_world_coordinates(bool enable) { m_world_coordinates = enable; }
|
||||
|
||||
// First-layer plane: when set to a non-null active evaluator, travel
|
||||
// speed selection consults the plane per-move and uses
|
||||
// initial_layer_travel_speed for points within first_layer_height_mm
|
||||
// of the plane (regardless of slicing layer index).
|
||||
void set_first_layer_plane(const FirstLayerPlane *plane,
|
||||
double first_layer_height_mm) {
|
||||
m_first_layer_plane = plane;
|
||||
m_first_layer_thickness_mm = first_layer_height_mm;
|
||||
}
|
||||
|
||||
// Overridden movement methods
|
||||
std::string travel_to_xy(const Vec2d &point, const std::string &comment = std::string()) override;
|
||||
std::string travel_to_xyz(const Vec3d &point, const std::string &comment = std::string(), bool force_z = false) override;
|
||||
std::string extrude_to_xy(const Vec2d &point, double dE, const std::string &comment = std::string(), bool force_no_extrusion = false) override;
|
||||
std::string extrude_to_xyz(const Vec3d &point, double dE, const std::string &comment = std::string(), bool force_no_extrusion = false) override;
|
||||
std::string lazy_lift(LiftType lift_type = LiftType::NormalLift, bool spiral_vase = false) override;
|
||||
std::string eager_lift(const LiftType type) override;
|
||||
|
||||
protected:
|
||||
std::string _travel_to_z(double z, const std::string &comment) override;
|
||||
|
||||
private:
|
||||
BeltBackTransform m_belt_back_transform;
|
||||
MachineFrameTransform m_machine_frame_transform;
|
||||
bool m_world_coordinates = false;
|
||||
// Borrowed pointer; lifetime owned by GCode. null = inactive.
|
||||
const FirstLayerPlane *m_first_layer_plane = nullptr;
|
||||
double m_first_layer_thickness_mm = 0.;
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -1,346 +0,0 @@
|
||||
// ORCA-Belt: backend of the belt purge tower (the belt replacement for the
|
||||
// classic wipe/prime tower).
|
||||
//
|
||||
// Kept in its own translation unit so the belt-purge logic stays out of the way
|
||||
// of unrelated upstream changes to Print.cpp / PrintObjectSlice.cpp and carries
|
||||
// no regression risk for normal printers: none of these methods do anything
|
||||
// unless the print is a belt printer with the belt purge tower enabled.
|
||||
//
|
||||
// Print::has_belt_purge_tower() - is the belt purge tower active?
|
||||
// Print::_align_belt_purge_layers() - snap the prism's layer grid onto the
|
||||
// printed objects' grid
|
||||
// Print::_plan_belt_purge() - route filament-change purging into the
|
||||
// prism (flush-into-objects), no wipe tower
|
||||
// PrintObject::belt_shift_layer_grid() - shift a sliced layer grid
|
||||
// PrintObject::belt_truncate_layers_above() - cancel the prism past the last swap
|
||||
//
|
||||
// (Declarations live in Print.hpp alongside the rest of the Print interface.)
|
||||
|
||||
#include "Print.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
#include "Exception.hpp"
|
||||
#include "GCode/ToolOrdering.hpp"
|
||||
#include "Layer.hpp"
|
||||
#include "ExtrusionEntity.hpp"
|
||||
#include "ExtrusionEntityCollection.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "format.hpp"
|
||||
#include "LocalesUtils.hpp"
|
||||
#include "libslic3r.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// Belt purge prism: purging after filament changes is routed into a sliced
|
||||
// prism object via the flush-into-objects machinery instead of a wipe tower.
|
||||
bool Print::has_belt_purge_tower() const
|
||||
{
|
||||
// Its own purge-tower "type", gated by the belt-only enable_belt_purge_tower
|
||||
// option (not the classic enable_prime_tower).
|
||||
if (!(m_config.belt_printer.value
|
||||
&& m_config.enable_belt_purge_tower.value
|
||||
&& !m_config.spiral_mode.value
|
||||
&& m_config.filament_diameter.values.size() > 1))
|
||||
return false;
|
||||
|
||||
return std::any_of(m_objects.begin(), m_objects.end(), [](const PrintObject *object) {
|
||||
return object->config().belt_purge_tower_object.value;
|
||||
});
|
||||
}
|
||||
|
||||
// Belt mode: align ALL objects on the plate (the printed objects AND the purge
|
||||
// prism) onto one common layer grid, so the prism can absorb every toolchange.
|
||||
//
|
||||
// After belt slicing each object's layer print_z carries a per-object global z
|
||||
// offset (mesh-vertex-scan belt_z_shift + instance-Y-dependent terms), so
|
||||
// objects at different belt-Y positions get layer grids with DIFFERENT residues
|
||||
// (mod layer height). Purge marking looks absorbers up with
|
||||
// get_layer_at_printz(lt.print_z, EPSILON), so a toolchange on object B only
|
||||
// absorbs into the prism if the prism has a layer at B's print_z. Snapping only
|
||||
// the prism to one object therefore worked for a single (assembled) multi-color
|
||||
// object but failed with multiple separate objects — the prism could follow only
|
||||
// one grid, and toolchanges on the others went unabsorbed ("multiple layer
|
||||
// grids" warning).
|
||||
//
|
||||
// Fix: pick one reference grid (the tallest object) and shift every object onto
|
||||
// it. Each shift is at most half a layer height — a sub-100µm move along the
|
||||
// belt, the very same mechanism the per-object global_z_offset already uses, and
|
||||
// it keeps each object internally consistent (belt_shift_layer_grid moves the
|
||||
// object's layers, its support layers, and its belt floor together). Equal layer
|
||||
// height across objects is enforced by Print::validate(), so once residues match
|
||||
// every object steps on the same lattice {ref_offset + k*h} and every toolchange
|
||||
// layer coincides with a prism layer.
|
||||
void Print::_align_belt_purge_layers()
|
||||
{
|
||||
PrintObject *prism = nullptr;
|
||||
for (PrintObject *po : m_objects)
|
||||
if (po->config().belt_purge_tower_object.value && !po->layers().empty()) {
|
||||
prism = po;
|
||||
break;
|
||||
}
|
||||
if (prism == nullptr || prism->layers().empty())
|
||||
return;
|
||||
|
||||
const double h = prism->config().layer_height.value;
|
||||
if (h <= EPSILON)
|
||||
return;
|
||||
|
||||
// Grid residue of an object's layer grid: identical for all of an object's
|
||||
// layers above the first since they step by h.
|
||||
auto grid_offset = [h](const PrintObject *po) -> double {
|
||||
if (po->layers().empty())
|
||||
return 0.;
|
||||
const double z = po->layers().front()->print_z;
|
||||
return z - std::floor(z / h) * h; // in [0, h)
|
||||
};
|
||||
|
||||
// Reference grid: the tallest non-prism object (proxy for the object with
|
||||
// the most toolchange layers — minimizes how far the rest must move).
|
||||
const PrintObject *ref = nullptr;
|
||||
double ref_top = -std::numeric_limits<double>::max();
|
||||
for (const PrintObject *po : m_objects) {
|
||||
if (po->config().belt_purge_tower_object.value || po->layers().empty())
|
||||
continue;
|
||||
const double top = po->layers().back()->print_z;
|
||||
if (top > ref_top) {
|
||||
ref_top = top;
|
||||
ref = po;
|
||||
}
|
||||
}
|
||||
if (ref == nullptr)
|
||||
return;
|
||||
|
||||
const double ref_offset = grid_offset(ref);
|
||||
|
||||
// Snap every object (printed objects AND the prism) onto the reference grid.
|
||||
for (PrintObject *po : m_objects) {
|
||||
if (po->layers().empty())
|
||||
continue;
|
||||
double delta = ref_offset - grid_offset(po);
|
||||
if (delta > 0.5 * h)
|
||||
delta -= h;
|
||||
else if (delta <= -0.5 * h)
|
||||
delta += h;
|
||||
po->belt_shift_layer_grid(delta); // no-op for the reference object (delta ~ 0)
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << "[BELT-DEBUG] purge grid align: snapped " << m_objects.size()
|
||||
<< " objects onto ref grid offset=" << ref_offset
|
||||
<< " (ref=" << ref->model_object()->name << ")";
|
||||
}
|
||||
|
||||
// Belt mode replacement for _make_wipe_tower(): plan filament-change purging
|
||||
// into the belt purge prism (and any other flush_into_* object) using the
|
||||
// flush-into-objects machinery, without generating classic wipe tower G-code.
|
||||
// The toolchange itself is emitted by GCode::set_extruder() via the
|
||||
// change_filament_gcode macro; the overrides marked here make the new
|
||||
// filament's first extrusions land in the purge prism.
|
||||
void Print::_plan_belt_purge()
|
||||
{
|
||||
m_wipe_tower_data.clear();
|
||||
|
||||
// psWipeTower may be invalidated without posSlice (for example after a
|
||||
// filament-map or tool-ordering change). Restore a prism shortened by the
|
||||
// previous plan so a newly higher toolchange can use its original layers.
|
||||
for (PrintObject *po : m_objects)
|
||||
if (po->config().belt_purge_tower_object.value)
|
||||
po->belt_restore_truncated_layers();
|
||||
|
||||
// Must run before ToolOrdering is built: LayerTools merge per-object layer
|
||||
// print_z values, and the prism only absorbs purge where its (snapped)
|
||||
// layers coincide with the toolchange layers.
|
||||
this->_align_belt_purge_layers();
|
||||
|
||||
const unsigned int number_of_extruders = (unsigned int) m_config.filament_colour.values.size();
|
||||
|
||||
// No initial priming extrusions: there is no tower to prime on.
|
||||
m_wipe_tower_data.tool_ordering = ToolOrdering(*this, (unsigned int) -1, false);
|
||||
m_wipe_tower_data.tool_ordering.sort_and_build_data(*this, (unsigned int) -1, false);
|
||||
|
||||
if (m_wipe_tower_data.tool_ordering.empty() || m_wipe_tower_data.tool_ordering.last_extruder() == unsigned(-1))
|
||||
throw Slic3r::SlicingError("The print is empty. The model is not printable with current print settings.");
|
||||
|
||||
if (!m_wipe_tower_data.tool_ordering.has_wipe_tower())
|
||||
// No toolchanges anywhere, nothing to purge.
|
||||
return;
|
||||
|
||||
this->throw_if_canceled();
|
||||
|
||||
// Flush volumes per filament pair, mirroring the generic wipe tower path:
|
||||
// full flush matrix for single extruder multi material with purging enabled,
|
||||
// plain prime volume otherwise.
|
||||
std::vector<float> flush_matrix(cast<float>(
|
||||
get_flush_volumes_matrix(m_config.flush_volumes_matrix.values, 0, m_config.nozzle_diameter.values.size())));
|
||||
std::vector<std::vector<float>> wipe_volumes;
|
||||
for (unsigned int i = 0; i < number_of_extruders; ++i)
|
||||
wipe_volumes.push_back(std::vector<float>(flush_matrix.begin() + i * number_of_extruders,
|
||||
flush_matrix.begin() + (i + 1) * number_of_extruders));
|
||||
const bool use_flush_matrix = m_config.purge_in_prime_tower && m_config.single_extruder_multi_material;
|
||||
const float flush_multiplier = (float) m_config.flush_multiplier.get_at(0);
|
||||
|
||||
// Cancel the purge prism early: pre-scan the tool ordering for the highest
|
||||
// print_z that actually has a toolchange, then drop the prism's layers above
|
||||
// it so the tower stops at the last color swap (saves filament/time). This
|
||||
// MUST happen before the marking loop below: ensure_perimeters_infills_order
|
||||
// force-overrides the prism's extrusions on every layer (it is a dedicated
|
||||
// flush object), so truncating afterwards would leave dangling overrides
|
||||
// pointing into deleted layers.
|
||||
{
|
||||
double last_tc_z = -1.;
|
||||
unsigned int cur_ext = m_wipe_tower_data.tool_ordering.first_extruder();
|
||||
for (const auto < : m_wipe_tower_data.tool_ordering.layer_tools())
|
||||
for (const unsigned int e : lt.extruders)
|
||||
if (e != cur_ext) { last_tc_z = lt.print_z; cur_ext = e; }
|
||||
if (last_tc_z >= 0.)
|
||||
for (PrintObject *po : m_objects)
|
||||
if (po->config().belt_purge_tower_object.value && !po->layers().empty()) {
|
||||
po->belt_truncate_layers_above(last_tc_z);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Diagnostic: the prism only absorbs purge at toolchange layers whose
|
||||
// print_z coincides with one of its own layers. Compare the prism's layer
|
||||
// print_z range to the toolchange print_z range and count how many
|
||||
// toolchange layers actually land on a prism layer. This distinguishes a
|
||||
// range/grid-alignment failure (no coverage) from a capacity shortfall
|
||||
// (covered but not enough cross-section).
|
||||
PrintObject *prism_po = nullptr;
|
||||
for (PrintObject *po : m_objects)
|
||||
if (po->config().belt_purge_tower_object.value && !po->layers().empty()) { prism_po = po; break; }
|
||||
const PrintObject *diag_prism = prism_po;
|
||||
if (diag_prism != nullptr)
|
||||
BOOST_LOG_TRIVIAL(warning) << "[BELT-DEBUG] purge prism layer range print_z=["
|
||||
<< diag_prism->layers().front()->print_z << ", " << diag_prism->layers().back()->print_z
|
||||
<< "] nlayers=" << diag_prism->layers().size();
|
||||
int tc_layers = 0, tc_layers_covered = 0;
|
||||
|
||||
float total_leftover = 0.f;
|
||||
float worst_layer_leftover = 0.f;
|
||||
double worst_layer_z = 0.;
|
||||
|
||||
unsigned int current_extruder_id = m_wipe_tower_data.tool_ordering.first_extruder();
|
||||
for (auto &layer_tools : m_wipe_tower_data.tool_ordering.layer_tools()) {
|
||||
float layer_leftover = 0.f;
|
||||
bool layer_has_tc = false;
|
||||
for (const unsigned int extruder_id : layer_tools.extruders) {
|
||||
if (extruder_id == current_extruder_id)
|
||||
continue;
|
||||
if (!layer_has_tc) {
|
||||
layer_has_tc = true;
|
||||
++tc_layers;
|
||||
if (diag_prism != nullptr && diag_prism->get_layer_at_printz(layer_tools.print_z, EPSILON) != nullptr)
|
||||
++tc_layers_covered;
|
||||
}
|
||||
float volume_to_wipe = use_flush_matrix ?
|
||||
wipe_volumes[current_extruder_id][extruder_id] * flush_multiplier :
|
||||
(float) m_config.prime_volume;
|
||||
float leftover = layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, current_extruder_id, extruder_id,
|
||||
volume_to_wipe);
|
||||
BOOST_LOG_TRIVIAL(trace) << "[BELT-DEBUG] purge toolchange print_z=" << layer_tools.print_z
|
||||
<< " filament " << current_extruder_id << "->" << extruder_id
|
||||
<< " requested=" << volume_to_wipe
|
||||
<< " absorbed=" << volume_to_wipe - leftover
|
||||
<< " leftover=" << leftover;
|
||||
layer_leftover += leftover;
|
||||
current_extruder_id = extruder_id;
|
||||
}
|
||||
|
||||
// Do not destructively remove unclaimed fill entities here. psWipeTower
|
||||
// can rerun without regenerating infill, and a later tool ordering may
|
||||
// need entities that were unclaimed by the previous plan.
|
||||
layer_tools.wiping_extrusions().ensure_perimeters_infills_order(*this);
|
||||
if (layer_leftover > 0.f) {
|
||||
total_leftover += layer_leftover;
|
||||
if (layer_leftover > worst_layer_leftover) {
|
||||
worst_layer_leftover = layer_leftover;
|
||||
worst_layer_z = layer_tools.print_z;
|
||||
}
|
||||
}
|
||||
this->throw_if_canceled();
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(warning) << "[BELT-DEBUG] purge coverage: " << tc_layers_covered << "/" << tc_layers
|
||||
<< " toolchange layers land on a prism layer"
|
||||
<< (tc_layers > 0 && tc_layers_covered == 0 ? " (RANGE/GRID MISALIGNMENT — prism absorbs nothing)" :
|
||||
tc_layers_covered < tc_layers ? " (partial coverage)" : " (full coverage)");
|
||||
|
||||
if (total_leftover > 1.f) {
|
||||
this->active_step_add_warning(
|
||||
PrintStateBase::WarningLevel::CRITICAL,
|
||||
Slic3r::format(_u8L("The belt purge tower cannot absorb the full purge volume: %1% mm³ in total could not "
|
||||
"be purged (worst layer: %2% mm³ at height %3%). The print may show color bleeding. "
|
||||
"Increase the belt purge tower width, or reduce flushing volumes."),
|
||||
int(std::ceil(total_leftover)), int(std::ceil(worst_layer_leftover)),
|
||||
Slic3r::float_to_string_decimal_point(worst_layer_z, 2)));
|
||||
BOOST_LOG_TRIVIAL(warning) << "[BELT-DEBUG] purge planning leftover total=" << total_leftover
|
||||
<< " worst_layer=" << worst_layer_leftover << " at print_z=" << worst_layer_z;
|
||||
}
|
||||
}
|
||||
|
||||
// Belt mode: shift the sliced layer grid by delta. Mirrors the global_z_offset
|
||||
// application in slice() — layer print_z and belt_floor_z_shift move together
|
||||
// so belt floor clipping stays consistent with the shifted grid. Used by
|
||||
// Print::_align_belt_purge_layers() to snap the purge prism onto the printed
|
||||
// objects' layer grid; |delta| <= half a layer height, i.e. a sub-layer shift
|
||||
// of the prism along the belt.
|
||||
void PrintObject::belt_shift_layer_grid(double delta)
|
||||
{
|
||||
if (std::abs(delta) < EPSILON)
|
||||
return;
|
||||
for (Layer *layer : m_layers)
|
||||
layer->print_z += delta;
|
||||
for (SupportLayer *layer : m_support_layers)
|
||||
layer->print_z += delta;
|
||||
m_slicing_params.belt_floor_z_shift += delta;
|
||||
BOOST_LOG_TRIVIAL(trace) << "[BELT-DEBUG] belt_shift_layer_grid"
|
||||
<< " obj=" << this->model_object()->name
|
||||
<< " delta=" << delta
|
||||
<< " first_layer.print_z=" << (m_layers.empty() ? 0. : m_layers.front()->print_z);
|
||||
}
|
||||
|
||||
// Belt mode: drop layers strictly above z (used to cancel the purge prism early
|
||||
// once there are no more toolchanges above z, so the tower stops at the last
|
||||
// color swap instead of wasting filament up the rest of the belt). Each layer's
|
||||
// cross-section is already sliced, so removing upper layers does not affect the
|
||||
// last toolchange's coverage. Deletes the Layer objects and clears the new top
|
||||
// layer's upper-layer link. Returns the number of layers removed.
|
||||
size_t PrintObject::belt_truncate_layers_above(coordf_t z)
|
||||
{
|
||||
// A repeated plan always starts from the restored full layer set.
|
||||
assert(m_belt_truncated_layers.empty());
|
||||
size_t keep = m_layers.size();
|
||||
while (keep > 0 && m_layers[keep - 1]->print_z > z + EPSILON)
|
||||
--keep;
|
||||
if (keep >= m_layers.size())
|
||||
return 0;
|
||||
const size_t removed = m_layers.size() - keep;
|
||||
m_belt_truncated_layers.assign(m_layers.begin() + keep, m_layers.end());
|
||||
m_layers.resize(keep);
|
||||
if (!m_layers.empty())
|
||||
m_layers.back()->upper_layer = nullptr;
|
||||
BOOST_LOG_TRIVIAL(debug) << "[BELT-DEBUG] truncate purge prism above print_z=" << z
|
||||
<< " kept=" << keep << " removed=" << removed
|
||||
<< " new_top=" << (m_layers.empty() ? 0. : m_layers.back()->print_z);
|
||||
return removed;
|
||||
}
|
||||
|
||||
void PrintObject::belt_restore_truncated_layers()
|
||||
{
|
||||
if (m_belt_truncated_layers.empty())
|
||||
return;
|
||||
|
||||
m_layers.insert(m_layers.end(), m_belt_truncated_layers.begin(), m_belt_truncated_layers.end());
|
||||
m_belt_truncated_layers.clear();
|
||||
for (size_t i = 0; i < m_layers.size(); ++i) {
|
||||
m_layers[i]->lower_layer = i == 0 ? nullptr : m_layers[i - 1];
|
||||
m_layers[i]->upper_layer = i + 1 < m_layers.size() ? m_layers[i + 1] : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -1,143 +0,0 @@
|
||||
#include "BeltSliceStrategy.hpp"
|
||||
#include "Model.hpp"
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#ifdef SLIC3R_BELT_DIAGNOSTIC_LOG
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#endif
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
void BeltSliceStrategy::apply_preslice_transforms(Transform3d &trafo,
|
||||
const PrintConfig &config,
|
||||
const ModelVolumePtrs &model_volumes,
|
||||
double *out_belt_min_z)
|
||||
{
|
||||
// 1. Standalone pre-slice axis remap (works without belt mode).
|
||||
const bool has_remap = BeltTransformPipeline::has_preslice_remap(config);
|
||||
if (has_remap)
|
||||
trafo = BeltTransformPipeline::build_preslice_remap(config) * trafo;
|
||||
|
||||
// 2. Belt rotation — the sole mesh-side belt transform (matching
|
||||
// BeltTransformPipeline::build_forward_transform). Only active in
|
||||
// belt-printer mode.
|
||||
bool has_rotation = false;
|
||||
if (config.belt_printer.value) {
|
||||
const Matrix3d rot = BeltTransformPipeline::build_rotation_matrix(config, &has_rotation);
|
||||
if (has_rotation) {
|
||||
Transform3d belt_xform = Transform3d::Identity();
|
||||
belt_xform.linear() = rot;
|
||||
trafo = belt_xform * trafo;
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_remap && !has_rotation)
|
||||
return;
|
||||
|
||||
// 3. Z-shift — detect if the mesh clips below the build plate after the
|
||||
// transforms and lift it. Each mesh vertex must be brought into object space
|
||||
// via mv->get_matrix() before applying the full trafo (which is in object
|
||||
// space). Missing this on assemblies (where per-volume get_matrix() positions
|
||||
// each volume within the object) would compute min_z against mesh-local vertex
|
||||
// coordinates rather than object-space coordinates, so volumes translated along
|
||||
// the slicer's Z axis would be silently excluded from the bound check.
|
||||
#ifdef SLIC3R_BELT_DIAGNOSTIC_LOG
|
||||
// Capture the incoming trafo for diagnostic logging.
|
||||
// This is the slicer-frame transform AFTER remap + rotation but BEFORE z_shift.
|
||||
const Transform3d trafo_pre_shift = trafo;
|
||||
auto log_mat = [](const Matrix3d &m) {
|
||||
std::ostringstream ss;
|
||||
ss << std::fixed << std::setprecision(4);
|
||||
ss << "[[" << m(0,0) << "," << m(0,1) << "," << m(0,2) << "],"
|
||||
<< "[" << m(1,0) << "," << m(1,1) << "," << m(1,2) << "],"
|
||||
<< "[" << m(2,0) << "," << m(2,1) << "," << m(2,2) << "]]";
|
||||
return ss.str();
|
||||
};
|
||||
auto log_vec3 = [](const Vec3d &v) {
|
||||
std::ostringstream ss;
|
||||
ss << std::fixed << std::setprecision(4);
|
||||
ss << "(" << v.x() << "," << v.y() << "," << v.z() << ")";
|
||||
return ss.str();
|
||||
};
|
||||
BOOST_LOG_TRIVIAL(trace) << "[BELT-DEBUG] apply_preslice_transforms enter"
|
||||
<< " has_rotation=" << has_rotation
|
||||
<< " has_remap=" << has_remap
|
||||
<< " trafo.linear=" << log_mat(trafo_pre_shift.linear())
|
||||
<< " trafo.translation=" << log_vec3(trafo_pre_shift.translation())
|
||||
<< " volumes=" << model_volumes.size();
|
||||
#endif
|
||||
|
||||
double min_z = std::numeric_limits<double>::max();
|
||||
#ifdef SLIC3R_BELT_DIAGNOSTIC_LOG
|
||||
int vol_idx = 0;
|
||||
#endif
|
||||
for (const ModelVolume *mv : model_volumes) {
|
||||
#ifdef SLIC3R_BELT_DIAGNOSTIC_LOG
|
||||
if (!mv->is_model_part()) { ++vol_idx; continue; }
|
||||
#else
|
||||
if (!mv->is_model_part()) continue;
|
||||
#endif
|
||||
Transform3d vol_trafo = trafo * mv->get_matrix();
|
||||
const auto &its = mv->mesh().its;
|
||||
#ifdef SLIC3R_BELT_DIAGNOSTIC_LOG
|
||||
// Per-volume bbox in mesh-frame and post-trafo slicer-frame.
|
||||
Vec3d mesh_min(std::numeric_limits<double>::max(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max());
|
||||
Vec3d mesh_max(std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest());
|
||||
Vec3d slicer_min(std::numeric_limits<double>::max(), std::numeric_limits<double>::max(), std::numeric_limits<double>::max());
|
||||
Vec3d slicer_max(std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest(), std::numeric_limits<double>::lowest());
|
||||
double vol_min_z = std::numeric_limits<double>::max();
|
||||
#endif
|
||||
for (const stl_vertex &v : its.vertices) {
|
||||
Vec3d vm = v.cast<double>();
|
||||
Vec3d pt = vol_trafo * vm;
|
||||
min_z = std::min(min_z, pt.z());
|
||||
#ifdef SLIC3R_BELT_DIAGNOSTIC_LOG
|
||||
mesh_min = mesh_min.cwiseMin(vm);
|
||||
mesh_max = mesh_max.cwiseMax(vm);
|
||||
slicer_min = slicer_min.cwiseMin(pt);
|
||||
slicer_max = slicer_max.cwiseMax(pt);
|
||||
vol_min_z = std::min(vol_min_z, pt.z());
|
||||
#endif
|
||||
}
|
||||
#ifdef SLIC3R_BELT_DIAGNOSTIC_LOG
|
||||
BOOST_LOG_TRIVIAL(trace) << "[BELT-DEBUG] vol[" << vol_idx
|
||||
<< "] id=" << mv->id().id << " name='" << mv->name << "'"
|
||||
<< " mesh_bbox_min=" << log_vec3(mesh_min) << " mesh_bbox_max=" << log_vec3(mesh_max)
|
||||
<< " get_matrix.translation=" << log_vec3(mv->get_matrix().translation())
|
||||
<< " slicer_bbox_min=" << log_vec3(slicer_min) << " slicer_bbox_max=" << log_vec3(slicer_max)
|
||||
<< " vol_min_z=" << vol_min_z;
|
||||
++vol_idx;
|
||||
#endif
|
||||
}
|
||||
const double z_shift_val = (min_z < 0. && min_z != std::numeric_limits<double>::max()) ? -min_z : 0.;
|
||||
#ifdef SLIC3R_BELT_DIAGNOSTIC_LOG
|
||||
BOOST_LOG_TRIVIAL(trace) << "[BELT-DEBUG] combined min_z=" << min_z
|
||||
<< " z_shift_val=" << z_shift_val;
|
||||
#endif
|
||||
if (z_shift_val > 0.) {
|
||||
Transform3d z_shift = Transform3d::Identity();
|
||||
z_shift.matrix()(2, 3) = z_shift_val;
|
||||
trafo = z_shift * trafo;
|
||||
}
|
||||
// out_belt_min_z is only meaningful in belt mode; the standalone-remap path
|
||||
// never reported it.
|
||||
if (out_belt_min_z && config.belt_printer.value) {
|
||||
const double new_val = (min_z != std::numeric_limits<double>::max()) ? min_z : 0.;
|
||||
#ifdef SLIC3R_BELT_DIAGNOSTIC_LOG
|
||||
BOOST_LOG_TRIVIAL(trace) << "[BELT-DEBUG] write m_belt_min_z tid=" << std::this_thread::get_id()
|
||||
<< " target=" << out_belt_min_z << " old=" << *out_belt_min_z << " new=" << new_val;
|
||||
#endif
|
||||
*out_belt_min_z = new_val;
|
||||
}
|
||||
#ifdef SLIC3R_BELT_DIAGNOSTIC_LOG
|
||||
BOOST_LOG_TRIVIAL(trace) << "[BELT-DEBUG] apply_preslice_transforms exit"
|
||||
<< " final_trafo.linear=" << log_mat(trafo.linear())
|
||||
<< " final_trafo.translation=" << log_vec3(trafo.translation());
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -1,36 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "libslic3r.h"
|
||||
#include "Point.hpp"
|
||||
#include "BeltTransform.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
#include "Model.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// Belt printer / pre-slice transform strategy.
|
||||
//
|
||||
// Composes, in order, the pre-slice mesh transforms applied before slicing:
|
||||
// 1. Pre-slice axis remap (standalone — works without belt mode)
|
||||
// 2. Belt rotation (the sole mesh-side belt transform; shear & scale are a
|
||||
// g-code-side stage, see MachineFrameTransform)
|
||||
// 3. Per-object Z-shift that lifts the mesh above the build plate
|
||||
//
|
||||
// Isolates this belt/remap-specific logic from the generic slicing pipeline in
|
||||
// PrintObjectSlice.cpp.
|
||||
class BeltSliceStrategy
|
||||
{
|
||||
public:
|
||||
// Apply the pre-slice remap + belt rotation + Z-shift to `trafo` in place.
|
||||
// No-op when neither a remap nor a belt rotation is configured.
|
||||
//
|
||||
// out_belt_min_z (if non-null) receives the minimum mesh Z after the
|
||||
// transforms, but only in belt-printer mode — the standalone-remap path
|
||||
// never reported it.
|
||||
static void apply_preslice_transforms(Transform3d &trafo,
|
||||
const PrintConfig &config,
|
||||
const ModelVolumePtrs &model_volumes,
|
||||
double *out_belt_min_z = nullptr);
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -1,223 +0,0 @@
|
||||
#include "BeltTransform.hpp"
|
||||
#include "Model.hpp"
|
||||
|
||||
#include <limits>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// ---- Matrix builders ------------------------------------------------------
|
||||
|
||||
Transform3d BeltTransformPipeline::build_preslice_remap(const PrintConfig &config)
|
||||
{
|
||||
Transform3d pre_remap = Transform3d::Identity();
|
||||
if (!has_preslice_remap(config))
|
||||
return pre_remap;
|
||||
|
||||
int pre_rx = int(config.preslice_remap_x.value);
|
||||
int pre_ry = int(config.preslice_remap_y.value);
|
||||
int pre_rz = int(config.preslice_remap_z.value);
|
||||
|
||||
// Each remap value selects a source axis and sign.
|
||||
auto remap_column = [](int r) -> Vec3d {
|
||||
int axis = r % 3;
|
||||
Vec3d col = Vec3d::Zero();
|
||||
if (r < 3) col[axis] = 1.0; // +axis
|
||||
else if (r < 6) col[axis] = -1.0; // -axis
|
||||
else col[axis] = -1.0; // Rev: max - pos = -(pos - max)
|
||||
return col;
|
||||
};
|
||||
|
||||
Matrix3d remap_lin;
|
||||
remap_lin.col(0) = remap_column(pre_rx);
|
||||
remap_lin.col(1) = remap_column(pre_ry);
|
||||
remap_lin.col(2) = remap_column(pre_rz);
|
||||
pre_remap.linear() = remap_lin;
|
||||
|
||||
// Translation for Rev modes (needs build volume extents).
|
||||
if (pre_rx >= 6 || pre_ry >= 6 || pre_rz >= 6) {
|
||||
BoundingBoxf bbox_bed(config.printable_area.values);
|
||||
Vec3d vol_max(bbox_bed.max.x(), bbox_bed.max.y(),
|
||||
config.printable_height.value);
|
||||
Vec3d remap_trans = Vec3d::Zero();
|
||||
auto add_rev = [&](int r, int out) {
|
||||
if (r >= 6) remap_trans[out] = vol_max[r % 3];
|
||||
};
|
||||
add_rev(pre_rx, 0);
|
||||
add_rev(pre_ry, 1);
|
||||
add_rev(pre_rz, 2);
|
||||
pre_remap.translation() = remap_trans;
|
||||
}
|
||||
|
||||
return pre_remap;
|
||||
}
|
||||
|
||||
Matrix3d BeltTransformPipeline::build_rotation_matrix(const PrintConfig &config, bool *has_rot_out)
|
||||
{
|
||||
BeltRotationAxis axis = config.belt_slice_rotation.value;
|
||||
double angle_deg = config.belt_slice_rotation_angle.value;
|
||||
bool active = axis != BeltRotationAxis::None && std::abs(angle_deg) > EPSILON;
|
||||
if (has_rot_out) *has_rot_out = active;
|
||||
if (!active)
|
||||
return Matrix3d::Identity();
|
||||
double angle_rad = Geometry::deg2rad(angle_deg);
|
||||
Vec3d unit_axis;
|
||||
switch (axis) {
|
||||
case BeltRotationAxis::X: unit_axis = Vec3d::UnitX(); break;
|
||||
case BeltRotationAxis::Y: unit_axis = Vec3d::UnitY(); break;
|
||||
case BeltRotationAxis::Z: unit_axis = Vec3d::UnitZ(); break;
|
||||
default: return Matrix3d::Identity();
|
||||
}
|
||||
return Eigen::AngleAxisd(angle_rad, unit_axis).toRotationMatrix();
|
||||
}
|
||||
|
||||
Transform3d BeltTransformPipeline::build_forward_transform(const PrintConfig &config)
|
||||
{
|
||||
// Mesh-side belt transform: rotation applied after the pre-slice axis remap.
|
||||
// (Shear & scale are a g-code-side stage, not part of the mesh transform.)
|
||||
Transform3d pre_remap = build_preslice_remap(config);
|
||||
Matrix3d rot = build_rotation_matrix(config);
|
||||
|
||||
Transform3d combined = Transform3d::Identity();
|
||||
combined.linear() = rot;
|
||||
combined = combined * pre_remap;
|
||||
return combined;
|
||||
}
|
||||
|
||||
// ---- Bounding box remap ---------------------------------------------------
|
||||
|
||||
BoundingBoxf3 BeltTransformPipeline::remap_bbox(const BoundingBoxf3 &bb, const PrintConfig &config)
|
||||
{
|
||||
int pre_rx = int(config.preslice_remap_x.value);
|
||||
int pre_ry = int(config.preslice_remap_y.value);
|
||||
int pre_rz = int(config.preslice_remap_z.value);
|
||||
|
||||
if (pre_rx == int(RemapAxis::PosX) &&
|
||||
pre_ry == int(RemapAxis::PosY) &&
|
||||
pre_rz == int(RemapAxis::PosZ))
|
||||
return bb; // Identity remap.
|
||||
|
||||
auto remap_coord = [](int r, const Vec3d &v) -> double {
|
||||
int axis = r % 3;
|
||||
if (r < 3) return v[axis];
|
||||
return -v[axis];
|
||||
};
|
||||
|
||||
Vec3d mn = bb.min.cast<double>(), mx = bb.max.cast<double>();
|
||||
BoundingBoxf3 rbb;
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
Vec3d c((i & 1) ? mx.x() : mn.x(),
|
||||
(i & 2) ? mx.y() : mn.y(),
|
||||
(i & 4) ? mx.z() : mn.z());
|
||||
Vec3d rc(remap_coord(pre_rx, c), remap_coord(pre_ry, c), remap_coord(pre_rz, c));
|
||||
if (i == 0) rbb = BoundingBoxf3(rc, rc);
|
||||
else rbb.merge(rc);
|
||||
}
|
||||
return rbb;
|
||||
}
|
||||
|
||||
BoundingBoxf3 BeltTransformPipeline::remap_bbox(const ModelObject &model_object, const PrintConfig &config)
|
||||
{
|
||||
return remap_bbox(model_object.raw_bounding_box(), config);
|
||||
}
|
||||
|
||||
// ---- Belt floor parameters ------------------------------------------------
|
||||
|
||||
// Shared implementation for both PrintConfig and DynamicPrintConfig.
|
||||
// Template avoids duplicating the math for the two config types.
|
||||
namespace {
|
||||
|
||||
template<typename Config>
|
||||
BeltTransformPipeline::BeltHeightResult compute_belt_height_and_floor_impl(
|
||||
const Config &config, const BoundingBoxf3 &bb, double original_height)
|
||||
{
|
||||
BeltTransformPipeline::BeltHeightResult result;
|
||||
result.object_height = original_height;
|
||||
|
||||
// Extract the mesh rotation from config (the sole mesh-side belt transform).
|
||||
BeltRotationAxis rot_axis;
|
||||
double rot_angle;
|
||||
|
||||
if constexpr (std::is_same_v<Config, PrintConfig>) {
|
||||
rot_axis = config.belt_slice_rotation.value;
|
||||
rot_angle = config.belt_slice_rotation_angle.value;
|
||||
} else {
|
||||
// DynamicPrintConfig path
|
||||
auto get_float = [&](const char *key) {
|
||||
auto *opt = config.template option<ConfigOptionFloat>(key);
|
||||
return opt ? opt->value : 0.0;
|
||||
};
|
||||
auto get_rot_axis = [&](const char *key) {
|
||||
auto *opt = config.template option<ConfigOptionEnum<BeltRotationAxis>>(key);
|
||||
return opt ? opt->value : BeltRotationAxis::None;
|
||||
};
|
||||
rot_axis = get_rot_axis("belt_slice_rotation");
|
||||
rot_angle = get_float("belt_slice_rotation_angle");
|
||||
}
|
||||
|
||||
bool has_rotation = rot_axis != BeltRotationAxis::None && std::abs(rot_angle) > EPSILON;
|
||||
if (!has_rotation)
|
||||
return result;
|
||||
|
||||
// Rotation path: sweep the 8 bbox corners through R to get the rotated height,
|
||||
// then derive the belt floor (the image of machine-Z = 0 under R).
|
||||
double angle_rad = Geometry::deg2rad(rot_angle);
|
||||
Vec3d unit_axis;
|
||||
switch (rot_axis) {
|
||||
case BeltRotationAxis::X: unit_axis = Vec3d::UnitX(); break;
|
||||
case BeltRotationAxis::Y: unit_axis = Vec3d::UnitY(); break;
|
||||
case BeltRotationAxis::Z: unit_axis = Vec3d::UnitZ(); break;
|
||||
default: unit_axis = Vec3d::UnitX(); break;
|
||||
}
|
||||
Matrix3d R = Eigen::AngleAxisd(angle_rad, unit_axis).toRotationMatrix();
|
||||
double min_rz = std::numeric_limits<double>::max();
|
||||
double max_rz = std::numeric_limits<double>::lowest();
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
Vec3d c((i & 1) ? bb.max.x() : bb.min.x(),
|
||||
(i & 2) ? bb.max.y() : bb.min.y(),
|
||||
(i & 4) ? bb.max.z() : bb.min.z());
|
||||
double z = (R * c).z();
|
||||
min_rz = std::min(min_rz, z);
|
||||
max_rz = std::max(max_rz, z);
|
||||
}
|
||||
result.object_height = max_rz - min_rz;
|
||||
|
||||
// Belt floor in slicer-frame is the image of z_machine = 0 under R.
|
||||
// R(+α, X): point (·, y, 0) → (·, cos α · y, sin α · y) ⇒ z = tan(α) · y_s
|
||||
// R(+α, Y): point (x, ·, 0) → (cos α · x, ·, -sin α · x) ⇒ z = -tan(α) · x_s
|
||||
// R(+α, Z): point (·, ·, 0) → (·, ·, 0); no tilt → no floor
|
||||
double sin_a = std::sin(angle_rad), cos_a = std::cos(angle_rad);
|
||||
switch (rot_axis) {
|
||||
case BeltRotationAxis::X:
|
||||
result.floor_params.shear_factor = (std::abs(cos_a) > EPSILON) ? sin_a / cos_a : 0.;
|
||||
result.floor_params.from_axis = 1; // Y
|
||||
break;
|
||||
case BeltRotationAxis::Y:
|
||||
result.floor_params.shear_factor = (std::abs(cos_a) > EPSILON) ? -sin_a / cos_a : 0.;
|
||||
result.floor_params.from_axis = 0; // X
|
||||
break;
|
||||
case BeltRotationAxis::Z:
|
||||
default:
|
||||
result.floor_params.shear_factor = 0.0;
|
||||
result.floor_params.from_axis = 1;
|
||||
break;
|
||||
}
|
||||
result.floor_params.z_shift = bb.min.z() + ((min_rz < 0.) ? -min_rz : 0.);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
BeltTransformPipeline::BeltHeightResult BeltTransformPipeline::compute_belt_height_and_floor(
|
||||
const PrintConfig &config, const BoundingBoxf3 &remapped_bbox, double original_height)
|
||||
{
|
||||
return compute_belt_height_and_floor_impl(config, remapped_bbox, original_height);
|
||||
}
|
||||
|
||||
BeltTransformPipeline::BeltHeightResult BeltTransformPipeline::compute_belt_height_and_floor(
|
||||
const DynamicPrintConfig &config, const BoundingBoxf3 &remapped_bbox, double original_height)
|
||||
{
|
||||
return compute_belt_height_and_floor_impl(config, remapped_bbox, original_height);
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -1,152 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "libslic3r.h"
|
||||
#include "Point.hpp"
|
||||
#include "BoundingBox.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
#include "Geometry.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class ModelObject;
|
||||
|
||||
// Shared belt-printer transform math.
|
||||
//
|
||||
// The pre-slice pipeline applied in PrintObjectSlice.cpp is:
|
||||
// trafo_out = z_shift * rotation * pre_remap * trafo_in
|
||||
//
|
||||
// Rotation is the sole mesh-side belt transform; shear & scale are applied
|
||||
// to the g-code instead (see MachineFrameTransform). This class provides the
|
||||
// building blocks so every call site uses the same implementation. z_shift is
|
||||
// object-dependent (computed from mesh vertex bounds) and is NOT included in
|
||||
// build_forward_transform(). The machine-frame shear/scale is derived directly
|
||||
// from the tilt angle in MachineFrameTransform and no longer lives here.
|
||||
//
|
||||
// Design note: this mesh-rotation approach replaced an earlier pre-shear
|
||||
// method (now removed). While that initial pre-shear method was instrumental
|
||||
// in getting belt printer slicing off the ground in the first place, its place is
|
||||
// in the past. A big thank you goes to the Unlayered3D team, who recommended
|
||||
// switching to a pre-slice rotation stage instead. Doing so keeps the slicing
|
||||
// operation isometric — no distortion of the sliced geometry — while the
|
||||
// non-orthogonal machine-axis compensation is confined to a g-code-side shear/scale
|
||||
// derived from the same tilt angle.
|
||||
//
|
||||
// This fixed a number of issues, including several issues noticed by hotcubcar
|
||||
// regarding adaptive infills not working, gyroid becoming anisotropic, and more
|
||||
// that were all mostly resolved as a result of the switch.
|
||||
//
|
||||
// This also means that the pre-slice rotation transform methodology can be used
|
||||
// more cleanly on non-belt printers.
|
||||
// - HarrierPigeon (Joseph Robertson)
|
||||
|
||||
class BeltTransformPipeline
|
||||
{
|
||||
public:
|
||||
// ---- Identity checks --------------------------------------------------
|
||||
|
||||
static bool has_preslice_remap(const PrintConfig &config)
|
||||
{
|
||||
return int(config.preslice_remap_x.value) != int(RemapAxis::PosX) ||
|
||||
int(config.preslice_remap_y.value) != int(RemapAxis::PosY) ||
|
||||
int(config.preslice_remap_z.value) != int(RemapAxis::PosZ);
|
||||
}
|
||||
|
||||
// Overload accepting DynamicPrintConfig (used in static slicing_parameters).
|
||||
static bool has_preslice_remap(const DynamicPrintConfig &config)
|
||||
{
|
||||
auto get_int = [&](const char *key) -> int {
|
||||
auto *opt = config.option<ConfigOptionEnum<RemapAxis>>(key);
|
||||
return opt ? int(opt->value) : 0;
|
||||
};
|
||||
return get_int("preslice_remap_x") != int(RemapAxis::PosX) ||
|
||||
get_int("preslice_remap_y") != int(RemapAxis::PosY) ||
|
||||
get_int("preslice_remap_z") != int(RemapAxis::PosZ);
|
||||
}
|
||||
|
||||
static bool has_rotation(const PrintConfig &config)
|
||||
{
|
||||
return config.belt_slice_rotation.value != BeltRotationAxis::None &&
|
||||
std::abs(config.belt_slice_rotation_angle.value) > EPSILON;
|
||||
}
|
||||
|
||||
// Physical belt tilt derived from the slicing rotation — the single source of
|
||||
// truth for bed rendering, support gravity tilt and the bed-exclusion
|
||||
// projection. Returns the tilt magnitude in degrees split onto the X and Y
|
||||
// build-plate tilt axes according to the rotation axis:
|
||||
// rotation about X → tilt_x = angle (gantry tilts in the YZ plane)
|
||||
// rotation about Y → tilt_y = angle (gantry tilts in the XZ plane)
|
||||
// rotation about Z / None → no tilt (in-plane spin doesn't tilt the belt)
|
||||
// The magnitude uses abs(angle) so a negative rotation still reports a positive
|
||||
// physical tilt.
|
||||
struct PhysicalTilt { double tilt_x_deg = 0.; double tilt_y_deg = 0.; };
|
||||
|
||||
static PhysicalTilt physical_tilt(BeltRotationAxis axis, double angle_deg)
|
||||
{
|
||||
PhysicalTilt t;
|
||||
double mag = std::abs(angle_deg);
|
||||
switch (axis) {
|
||||
case BeltRotationAxis::X: t.tilt_x_deg = mag; break;
|
||||
case BeltRotationAxis::Y: t.tilt_y_deg = mag; break;
|
||||
default: break; // Z / None: no physical tilt
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
static PhysicalTilt physical_tilt(const PrintConfig &config)
|
||||
{
|
||||
return physical_tilt(config.belt_slice_rotation.value,
|
||||
config.belt_slice_rotation_angle.value);
|
||||
}
|
||||
|
||||
// ---- Matrix builders --------------------------------------------------
|
||||
|
||||
// Build the pre-slice axis remap transform (includes Rev-mode translation).
|
||||
static Transform3d build_preslice_remap(const PrintConfig &config);
|
||||
|
||||
// Build the 3x3 rotation matrix from belt_slice_rotation* config.
|
||||
// Returns Identity if rotation axis is None or angle is ~0.
|
||||
// Also sets has_rot_out if non-null.
|
||||
static Matrix3d build_rotation_matrix(const PrintConfig &config, bool *has_rot_out = nullptr);
|
||||
|
||||
// Combined forward transform (rotation * pre_remap) — the mesh-side belt
|
||||
// transform that BeltSliceStrategy applies and BeltBackTransform inverts.
|
||||
// Does NOT include the per-object Z-shift.
|
||||
static Transform3d build_forward_transform(const PrintConfig &config);
|
||||
|
||||
// ---- Bounding box remap -----------------------------------------------
|
||||
|
||||
// Remap a bounding box through the pre-slice axis remap.
|
||||
// Returns the original bbox if remap is identity.
|
||||
static BoundingBoxf3 remap_bbox(const BoundingBoxf3 &bb, const PrintConfig &config);
|
||||
static BoundingBoxf3 remap_bbox(const ModelObject &model_object, const PrintConfig &config);
|
||||
|
||||
// ---- Belt floor parameters --------------------------------------------
|
||||
|
||||
struct BeltFloorParams {
|
||||
double shear_factor = 0.0;
|
||||
int from_axis = 1;
|
||||
double z_shift = 0.0;
|
||||
};
|
||||
|
||||
// Result of computing belt height + floor params.
|
||||
struct BeltHeightResult {
|
||||
double object_height; // Effective object height after shear/scale
|
||||
BeltFloorParams floor_params;
|
||||
};
|
||||
|
||||
// Compute effective object height and belt floor parameters from config
|
||||
// and pre-remapped bounding box. original_height is the input height
|
||||
// (bb.size().z() or model_object.max_z()).
|
||||
static BeltHeightResult compute_belt_height_and_floor(
|
||||
const PrintConfig &config, const BoundingBoxf3 &remapped_bbox,
|
||||
double original_height);
|
||||
|
||||
// Overload for DynamicPrintConfig (used by static slicing_parameters).
|
||||
static BeltHeightResult compute_belt_height_and_floor(
|
||||
const DynamicPrintConfig &config, const BoundingBoxf3 &remapped_bbox,
|
||||
double original_height);
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -449,9 +449,7 @@ static ExPolygons outer_inner_brim_area(const Print& print,
|
||||
const bool use_brim_ears = object->config().brim_type == btPainted;
|
||||
const bool use_inner_brim_ears = (use_auto_brim_ears || use_brim_ears) && !object->config().brim_ears_outer_only.value;
|
||||
const bool has_inner_brim = brim_type == btInnerOnly || brim_type == btOuterAndInner || use_inner_brim_ears;
|
||||
// btLeadingEdgeOnly is a belt-printer mode; on a flat bed there is no leading
|
||||
// edge, so it degrades to an ordinary outer brim rather than silently to none.
|
||||
const bool has_outer_brim = brim_type == btOuterOnly || brim_type == btOuterAndInner || brim_type == btAutoBrim || brim_type == btLeadingEdgeOnly || use_auto_brim_ears || use_brim_ears;
|
||||
const bool has_outer_brim = brim_type == btOuterOnly || brim_type == btOuterAndInner || brim_type == btAutoBrim || use_auto_brim_ears || use_brim_ears;
|
||||
coord_t ear_detection_length = scale_(object->config().brim_ears_detection_length.value);
|
||||
coordf_t brim_ears_max_angle = object->config().brim_ears_max_angle.value;
|
||||
//ORCA: Select brim base slices from EFC-compensated outline when enabled.
|
||||
@@ -866,17 +864,6 @@ void make_brim(const Print& print, PrintTryCancel try_cancel, Polygons& islands_
|
||||
std::vector<unsigned int>& printExtruders,
|
||||
std::map<ObjectInstanceID, ExPolygons>* objectBrimAreasByInstanceOut)
|
||||
{
|
||||
// Belt printers never use the flat plate brim.
|
||||
//
|
||||
// With a tilted belt the brim has to be laid onto the belt plane over many layers,
|
||||
// which BeltBrim.cpp does during posSupportMaterial. With an untilted belt this
|
||||
// could in principle fall through and produce an ordinary brim, but it would never
|
||||
// reach the G-code: the plate brim is emitted out of skirt_brim_groups(), which
|
||||
// _make_skirt() builds, and that returns early for every belt printer. Running the
|
||||
// generator anyway would just burn time on geometry nobody prints.
|
||||
if (print.config().belt_printer.value)
|
||||
return;
|
||||
|
||||
std::map<ObjectInstanceID, ExPolygons> brimAreaMap;
|
||||
Flow flow = print.brim_flow();
|
||||
ExPolygons islands_area_ex = outer_inner_brim_area(print,
|
||||
|
||||
@@ -176,31 +176,6 @@ BuildVolume::BuildVolume(const std::vector<Vec2d> &printable_area, const double
|
||||
BOOST_LOG_TRIVIAL(debug) << "BuildVolume printable_area clasified as: " << this->type_name();
|
||||
}
|
||||
|
||||
void BuildVolume::set_belt_printer(bool enabled, double angle_deg, bool infinite_y)
|
||||
{
|
||||
m_is_belt_printer = enabled;
|
||||
m_belt_angle = angle_deg;
|
||||
m_belt_infinite_y = infinite_y;
|
||||
|
||||
// Restart from the unmodified bbox each call. Without this, toggling
|
||||
// belt mode off (or switching infinite_y true→false) would leave the
|
||||
// extents inflated and break collision / object_state checks.
|
||||
BoundingBoxf bboxf = get_extents(m_bed_shape);
|
||||
m_bboxf = BoundingBoxf3{ to_3d(bboxf.min, 0.), to_3d(bboxf.max, m_max_print_height) };
|
||||
|
||||
if (enabled) {
|
||||
if (infinite_y) {
|
||||
// Extend the Y bound to a very large value for infinite belt.
|
||||
m_bboxf.max.y() = 100000.;
|
||||
}
|
||||
// Belt printer: the Z extent already equals printable_height (set above), which
|
||||
// is the usable vertical clearance above the belt. The gantry's axis range is
|
||||
// sized to reach height/cos(tilt), so no diagonal scaling is applied here — this
|
||||
// keeps the live "outside build volume" highlight in agreement with Print::validate().
|
||||
(void) angle_deg;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Tests intersections of projected triangles, not just their vertices against a bounding box.
|
||||
// This test also correctly evaluates collision of a non-convex object with the bounding box.
|
||||
@@ -409,11 +384,6 @@ BuildVolume::ObjectState BuildVolume::object_state(const indexed_triangle_set& i
|
||||
build_volume.max.z() = std::numeric_limits<double>::max();
|
||||
if (ignore_bottom)
|
||||
build_volume.min.z() = -std::numeric_limits<double>::max();
|
||||
// Belt printer: extend Y bounds for infinite Y.
|
||||
if (m_is_belt_printer && m_belt_infinite_y) {
|
||||
build_volume.min.y() = -std::numeric_limits<double>::max();
|
||||
build_volume.max.y() = std::numeric_limits<double>::max();
|
||||
}
|
||||
BoundingBox3Base<Vec3f> build_volumef(build_volume.min.cast<float>(), build_volume.max.cast<float>());
|
||||
// The following test correctly interprets intersection of a non-convex object with a rectangular build volume.
|
||||
//return rectangle_test(its, trafo, to_2d(build_volume.min), to_2d(build_volume.max), build_volume.max.z());
|
||||
|
||||
@@ -57,10 +57,6 @@ public:
|
||||
// Initialize from PrintConfig::printable_area and PrintConfig::printable_height
|
||||
BuildVolume(const std::vector<Vec2d> &printable_area, const double printable_height, const std::vector<std::vector<Vec2d>> &extruder_areas, const std::vector<double>& extruder_printable_heights);
|
||||
|
||||
// Belt printer configuration.
|
||||
void set_belt_printer(bool enabled, double angle_deg, bool infinite_y);
|
||||
bool is_belt_printer() const { return m_is_belt_printer; }
|
||||
|
||||
// Source data, unscaled coordinates.
|
||||
const std::vector<Vec2d>& printable_area() const { return m_bed_shape; }
|
||||
double printable_height() const { return m_max_print_height; }
|
||||
@@ -84,7 +80,7 @@ public:
|
||||
indexed_triangle_set bounding_mesh(bool scale=true) const;
|
||||
|
||||
// Center of the print bed, unscaled.
|
||||
Vec2d bed_center() const { return get_extents(m_bed_shape).center(); }
|
||||
Vec2d bed_center() const { return to_2d(m_bboxf.center()); }
|
||||
// Convex hull of polygon(), scaled.
|
||||
const Polygon& convex_hull() const { return m_convex_hull; }
|
||||
// Smallest enclosing circle of polygon(), scaled.
|
||||
@@ -143,10 +139,6 @@ private:
|
||||
// Source definition of the print volume height (PrintConfig::printable_height)
|
||||
double m_max_print_height { 0.f };
|
||||
std::vector<double> m_extruder_printable_height;
|
||||
// Belt printer state.
|
||||
bool m_is_belt_printer { false };
|
||||
double m_belt_angle { 0. };
|
||||
bool m_belt_infinite_y { false };
|
||||
|
||||
// Derived values.
|
||||
BuildVolume_Type m_type { BuildVolume_Type::Invalid };
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user