Compare commits

..
Author SHA1 Message Date
Hanif Koh 40f703df0c Keep the Added Test Includes Below the NOMINMAX Guard
test_marchingsquares.cpp and test_texture_displacement.cpp had includes inside #ifndef NOMINMAX, which the tests inherit as defined on Windows from libslic3r, so those were skipped there. .clang-tidy also ignores the MSVC STL and UCRT internals, Boost.Multiprecision's fwd.hpp and CPython's Windows include directory, as in #16068.
2026-10-02 21:42:47 +08:00
Hanif Koh 7fa7469355 Add the GUI Includes the First Pass Missed
Covers headers that only became editable once they compiled on their own, and wx symbols whose suggested header changed as the clang-tidy ignore list grew after the src/slic3r/GUI pass.
2026-10-02 18:08:04 +08:00
Hanif Koh 7e996ecf66 Keep Windows and nanosvg Setup Ahead of the Added Includes
OrcaSlicer.cpp and several tests set _WIN32_WINNT, WIN32_LEAN_AND_MEAN or NOMINMAX before including Windows.h, and the profile validator defines NANOSVG_IMPLEMENTATION before any libslic3r header. The added includes had landed above those blocks, which broke the Windows build.
2026-10-02 16:52:28 +08:00
Hanif Koh 28dc67e382 Make the GUI and Test Headers Compile on Their Own
Each now includes, or forward-declares, what it uses instead of relying on what its includers happened to include first. Headers that only compile on one platform, or that nothing built includes, are left alone.
2026-10-02 16:34:35 +08:00
Hanif Koh 59deff0694 Add Missing Includes Across the Remaining Sources and Tests
Covers src/slic3r/Utils, src/slic3r/plugin, src/slic3r/Config, src/libvgcode, src/dev-utils, src/OrcaSlicer.cpp and tests/, the directories left after src/slic3r/GUI and src/libslic3r. Generated with clang-tidy misc-include-cleaner. libvgcode's own headers are included by relative path as in the rest of that library, and Catch2 and pybind11 with angle brackets as elsewhere in the repo.
2026-10-02 15:41:30 +08:00
Hanif Koh 31aba32335 Ignore minilzo's Config Headers in clang-tidy
lzoconf.h and lzodefs.h are internal to minilzo.h, which is what the code includes.
2026-10-02 15:41:30 +08:00
Hanif Koh 68099b6c1f Ignore Clipper, libpng, mcut and Boost.Polygon Internals in clang-tidy
Each only works through a wrapper or umbrella header: libslic3r/clipper.hpp or clipper_z.hpp configure Clipper before including it, png.h pulls in libpng's config headers, and Boost.Polygon's headers only compile through polygon.hpp or voronoi.hpp.
2026-10-02 15:13:25 +08:00
711 changed files with 11279 additions and 14856 deletions
+18 -3
View File
@@ -7,9 +7,15 @@ CheckOptions:
misc-include-cleaner.UnusedIncludes: false
# Headers that declare a symbol but are not the one to include: per-platform
# implementations of wxWidgets and Boost.Thread (a Linux run would suggest the
# GTK or pthread one), library internals and forward declarations, CPython's
# headers behind Python.h, curl's behind curl.h, oneTBB's behind tbb/, and
# GTK or pthread one), library internals and forward declarations (MSVC's STL
# __msvc_* and the Windows UCRT's corecrt_* included), CPython's headers behind
# Python.h (python3.x/ on Linux and macOS, libpython/include/ on Windows),
# curl's behind curl.h, oneTBB's behind tbb/, and
# admesh's stl.h, which the include path also exposes without its directory.
# Clipper's own clipper.hpp is only included through libslic3r/clipper.hpp or
# clipper_z.hpp, which configure it first, and Boost.Polygon's headers only
# work through boost/polygon/polygon.hpp or voronoi.hpp. minilzo's config
# headers are internal to minilzo.h.
# FFmpeg's C headers are left alone because they are only included inside
# extern "C", which an inserted include would miss. OS-specific headers (GLib,
# GTK, D-Bus, POSIX, the Windows SDK) are only used inside platform #if blocks,
@@ -23,7 +29,11 @@ CheckOptions:
.*[/\\]impl[/\\].*;
.*_fwd\.hpp;
python3\.[0-9]+[/\\].*;
libpython[/\\]include[/\\].*;
bits[/\\].*;
corecrt_.*\.h;
__msvc_.*\.hpp;
boost[/\\]multiprecision[/\\]fwd\.hpp;
imconfig\.h;
expat_external\.h;
admesh[/\\]stl\.h;
@@ -44,4 +54,9 @@ CheckOptions:
fcntl\.h;
termios\.h;
[/\\](um|shared)[/\\].*;
sal\.h
sal\.h;
clipper[/\\]clipper\.hpp;
png(lib)?conf\.h;
mcut[/\\]platform\.h;
boost[/\\]polygon[/\\].*;
lzo(conf|defs)\.h
+1 -2
View File
@@ -206,8 +206,7 @@ known class with a pitfall entry and a fixing commit.
- **macOS:** capture-lost is never sent (a leaked capture freezes all clicks); transient popups hover-
dismiss across a gap — anchor flush and re-verify the cursor; native modals (file/dir dialogs, native
message boxes) and generic progress dialogs re-activate the main window, so re-raise a secondary window
afterwards with a deferred, liveness-guarded `Raise()` — but never `Raise()` a `wxPopupWindow`, which makes
it the key window; a live menu accelerator consumes the key before
afterwards with a deferred, liveness-guarded `Raise()`; a live menu accelerator consumes the key before
any wx key event; Control+click arrives as a right-click.
- **Windows:** `IsDark()` and `wxSYS_COLOUR_*` follow the system app mode, not Orca's theme — use
`dark_mode()`; menu bitmaps follow `check_dark_mode()`; windows are not double-buffered by default in
@@ -57,9 +57,8 @@ Contents: [Rules](#rules) · [1. Choosing the window kind](#1-choosing-the-windo
17. Don't shrink a dropdown below two rows to fit the screen. (§7)
18. Content that needs typing focus or hosts a `wxWebView` uses a frameless `wxDialog` that hides on
deactivation, not a transient popup. (§10)
19. Display-only overlays (HUDs, toasts) use a plain `wxPopupWindow`: `Show()` never gives it focus and it
never auto-dismisses. Never `Raise()` any `wxPopupWindow`: `Raise()` is for top-level windows only, and on
macOS it makes the popup the key window. (§4, §5, §10)
19. Display-only overlays (HUDs, toasts) use a plain `wxPopupWindow`: it never takes focus and never
auto-dismisses. (§4, §10)
20. On MSW, don't `SetFocus()` another window on hover while `wxCurrentPopupWindow` is non-null. (§8)
21. Menu items use `wxID_ANY` and read `item->GetId()`. `wxNewId()` is deprecated. (§13)
22. Set a menu item's bitmap before `Append`. Don't expect icons on check or radio items. Never call
@@ -357,13 +356,6 @@ compensates for (§6).
with `ShowWithoutActivating` → `setHidesOnDeactivate:YES` + `orderFront` (`src/osx/carbon/popupwin.cpp:56-75`,
`nonownedwnd.mm:938-945`). When the app deactivates, Cocoa hides the panel and shows it again on reactivation.
wx never calls `OnDismiss`, and `IsShown()` stays true. This applies to plain `wxPopupWindow` overlays too.
- `Raise()` activates the popup. It is `makeKeyAndOrderFront` (`src/osx/nonownedwnd_osx.cpp:289-295`,
`nonownedwnd.mm:897-899`), and `wxNSPanel` answers `canBecomeKeyWindow` with YES (`nonownedwnd.mm:271`), so
the popup becomes the key window. Keys go to it, and the frame loses key status: `windowDidResignKey` →
`HandleActivated(0, false)` → `wxEVT_ACTIVATE(false)` on the frame (`nonownedwnd.mm:567-576`,
`nonownedwnd_osx.cpp:303-310`). Hiding the key popup gives key back to the frame, which then gets
`wxEVT_ACTIVATE(true)` (observed; AppKit behaviour, not in the wx tree). A popup at `NSPopUpMenuWindowLevel`
is already above its frame, so `Raise()` buys nothing.
- Capture: `Show(true)` makes `m_child` capture the mouse ("Assume that the mouse is outside the popup to begin
with", `popupcmn.cpp:421-426`). `OnIdle` releases the capture while the cursor is inside and re-captures it
outside, but only when the mouse position has changed since the last idle pass. `s_posLast` is a
@@ -747,26 +739,8 @@ created with `wxBORDER_NONE | wxFRAME_NO_TASKBAR | wxFRAME_FLOAT_ON_PARENT | wxF
- **Rule:** For overlays that must never take keyboard focus (above a GL surface), use a plain
`wxPopupWindow(top, wxBORDER_NONE)`, not a `wxFrame`.
**Why:** A frame took the X input focus and swallowed every shortcut until the user clicked the canvas. A popup
window does not take focus when shown. On macOS these overlays hide while the app is inactive (§5).
window cannot take focus. On macOS these overlays hide while the app is inactive (§5).
Cite: `CAD/DesignCanvas.cpp` (`m_hud`, `m_status_hud`).
- **Rule:** Never `Raise()` a `wxPopupWindow`. To bring an overlay up, `Show()` it if it is hidden, then
`Move()` it.
**Why:** `Raise()` is documented for top-level windows only (`interface/wx/window.h:3028-3029`), and a popup
derives from `wxNonOwnedWindow`, not `wxTopLevelWindow` (`include/wx/popupwin.h:33`). On macOS it makes the
popup the key window (§5): the popup takes the keys meant for the window below it, and the frame receives
`wxEVT_ACTIVATE(false)`. A frame activate handler that hides the overlay on deactivation and re-places it on
activation then loops: each `Raise()` deactivates the frame, the hide reactivates it, and the re-place raises
again, recursing until the main thread's stack overflows. A popup's `Show()` is `ShowWithoutActivating`
and is safe.
```cpp
// Wrong
if (!overlay->IsShown()) overlay->Show();
overlay->Move(pos);
overlay->Raise();
// Right
if (!overlay->IsShown()) overlay->Show();
overlay->Move(pos);
```
## 11. wxComboCtrl / wxComboPopup
@@ -428,8 +428,7 @@ this function does *not* show it", top-level windows only (`interface/wx/window.
since 3.3 (`docs/changes.txt:144-146`). **[source]** MSW = `::SetForegroundWindow`, subject to the
foreground lock — Windows may only flash the taskbar button (`src/msw/toplevel.cpp:650-655`); GTK =
`gtk_window_present` only if shown (`src/gtk/toplevel.cpp:1301-1310`; during a deferred X11 first show it
already counts as shown); macOS = `makeKeyAndOrderFront` only if shown (`src/osx/nonownedwnd_osx.cpp:289-295`, `src/osx/cocoa/nonownedwnd.mm:897-899`),
which also makes a `wxPopupWindow` the key window — never `Raise()` a popup (`references/popups-menus.md` §5, §10).
already counts as shown); macOS = `makeKeyAndOrderFront` only if shown (`src/osx/nonownedwnd_osx.cpp:289-295`, `src/osx/cocoa/nonownedwnd.mm:896-899`).
**Enable.** `Enable(false)` on a parent disables children logically: `IsEnabled()` reflects ancestors,
`IsThisEnabled()` the window's own flag (`interface/wx/window.h:3060-3070, 3116-3138`). **[source]** On MSW/macOS wx
+17
View File
@@ -49,11 +49,28 @@ jobs:
key: ${{ inputs.cache-key }}
- uses: lukka/get-cmake@latest
# The windows-11-arm runner needs CMake <= 3.31 (handled in the next step).
if: ${{ !(runner.os == 'Windows' && inputs.arch == 'arm64') }}
with:
cmakeVersion: "~4.3.0" # use most recent 4.3.x version
useLocalCache: true # <--= Use the local cache (default is 'false').
useCloudCache: true
- name: Install CMake 3.31.x (Windows ARM64)
# windows-11-arm ships CMake 4.x, which removed pre-3.5 policy
# compatibility AND has incomplete ASM_ARMASM linker modules
# (breaks Boost.Context on ARM64). Pin to the last 3.x release.
if: runner.os == 'Windows' && inputs.arch == 'arm64'
shell: pwsh
run: |
$ver = "3.31.6"
$url = "https://github.com/Kitware/CMake/releases/download/v$ver/cmake-$ver-windows-arm64.zip"
Invoke-WebRequest -Uri $url -OutFile "$env:RUNNER_TEMP\cmake.zip"
Expand-Archive -Path "$env:RUNNER_TEMP\cmake.zip" -DestinationPath "$env:RUNNER_TEMP\cmake" -Force
$cmakeBin = "$env:RUNNER_TEMP\cmake\cmake-$ver-windows-arm64\bin"
if (-not (Test-Path "$cmakeBin\cmake.exe")) { throw "cmake.exe not found at $cmakeBin" }
Add-Content -Path $env:GITHUB_PATH -Value $cmakeBin
- name: setup dev on Windows
if: runner.os == 'Windows'
uses: microsoft/setup-msbuild@v3
+17 -1
View File
@@ -54,11 +54,28 @@ jobs:
fail-on-cache-miss: true
- uses: lukka/get-cmake@latest
# The windows-11-arm runner needs CMake <= 3.31 (handled in the next step).
if: ${{ !(runner.os == 'Windows' && inputs.arch == 'arm64') }}
with:
cmakeVersion: "~4.3.0" # use most recent 4.3.x version
useLocalCache: true # <--= Use the local cache (default is 'false').
useCloudCache: true
- name: Install CMake 3.31.x (Windows ARM64)
# windows-11-arm ships CMake 4.x, which removed pre-3.5 policy
# compatibility AND has incomplete ASM_ARMASM linker modules
# (breaks Boost.Context on ARM64). Pin to the last 3.x release.
if: runner.os == 'Windows' && inputs.arch == 'arm64'
shell: pwsh
run: |
$ver = "3.31.6"
$url = "https://github.com/Kitware/CMake/releases/download/v$ver/cmake-$ver-windows-arm64.zip"
Invoke-WebRequest -Uri $url -OutFile "$env:RUNNER_TEMP\cmake.zip"
Expand-Archive -Path "$env:RUNNER_TEMP\cmake.zip" -DestinationPath "$env:RUNNER_TEMP\cmake" -Force
$cmakeBin = "$env:RUNNER_TEMP\cmake\cmake-$ver-windows-arm64\bin"
if (-not (Test-Path "$cmakeBin\cmake.exe")) { throw "cmake.exe not found at $cmakeBin" }
Add-Content -Path $env:GITHUB_PATH -Value $cmakeBin
# Compiler cache. Pushes save it, so main keeps it warm; pull requests
# restore it and discard what they compiled. Objects are keyed on the
# preprocessed source, the compiler and the flags, so a leg only ever
@@ -259,7 +276,6 @@ jobs:
# Thanks to RaySajuuk, it's working now
- name: Sign app and notary
if: github.repository == 'OrcaSlicer/OrcaSlicer' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/belt-printer' || startsWith(github.ref, 'refs/heads/release/')) && runner.os == 'macOS' && inputs.macos-combine-only
timeout-minutes: 30
working-directory: ${{ github.workspace }}
env:
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
+8
View File
@@ -44,6 +44,14 @@ jobs:
uses: actions/download-artifact@v8
with:
name: ${{ inputs.artifact }}
# run_unit_tests.sh installs the plugin tests' numpy with the uv the build stages
# beside them; the Windows arm64 build bundles none, so put one on PATH there.
- name: Install uv
if: runner.os == 'Windows' && runner.arch == 'ARM64'
uses: astral-sh/setup-uv@v10.2.0
with:
version: "0.11.21" # ORCA_UV_VERSION in CMakeLists.txt
enable-cache: false
- uses: lukka/get-cmake@latest
with:
cmakeVersion: "~4.3.0" # use most recent 4.3.x version
+2 -16
View File
@@ -135,7 +135,6 @@ set(ORCA_UV_SHA256_aarch64-apple-darwin "1f921d491ba5ffeea774eb04d6681ecee3
set(ORCA_UV_SHA256_x86_64-apple-darwin "f3c8e5708a84b920c18b691214d54d2b0da6b984789caae95d47c95120cb7765")
set(ORCA_UV_SHA256_aarch64-unknown-linux-gnu "88e800834007cc5efd4675f166eb2a51e7e3ad19876d85fa8805a6fb5c922397")
set(ORCA_UV_SHA256_x86_64-unknown-linux-gnu "8c88519b0ef0af9801fcdee419bbb12116bd9e6b18e162ae093c932d8b264050")
set(ORCA_UV_SHA256_aarch64-pc-windows-msvc "74e443f8004022dde57a1bd0d10c097830f9ea8feb4ec927db52cd5d805c2f48")
set(ORCA_UV_SHA256_x86_64-pc-windows-msvc "ace861f360c6de2babedc1607d0f454b6b09a820dbc8182dc15af927e4df9589")
# Version-scoped cache dir so a version bump invalidates the cached binary.
@@ -174,10 +173,7 @@ if(NOT ORCA_BUNDLED_UV_EXECUTABLE)
set(ORCA_UV_ARCH "x86_64-unknown-linux-gnu")
endif()
elseif(_orca_uv_proc MATCHES "aarch64|arm64|ARM64")
if(WIN32)
set(ORCA_UV_ARCH "aarch64-pc-windows-msvc")
set(ORCA_UV_EXT "zip")
elseif(APPLE)
if(APPLE)
set(ORCA_UV_ARCH "aarch64-apple-darwin")
else()
set(ORCA_UV_ARCH "aarch64-unknown-linux-gnu")
@@ -823,9 +819,7 @@ if(SLIC3R_STATIC)
set(TBB_STATIC 1)
endif()
set(TBB_DEBUG 1)
if ("${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo" OR MSVC)
set(CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO RelWithDebInfo Release "")
endif()
set(CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO RelWithDebInfo Release "")
find_package(TBB REQUIRED)
# include_directories(SYSTEM ${TBB_INCLUDE_DIRS})
# add_definitions(${TBB_DEFINITIONS})
@@ -1307,14 +1301,6 @@ if (WIN32)
endif()
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE)
include(InstallRequiredSystemLibraries)
# A missing MSVC runtime is an error because an installer without it cannot start on a clean machine.
set(_orca_runtime_names ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS})
list(TRANSFORM _orca_runtime_names REPLACE "^.*/" "")
if (MSVC AND (NOT "msvcp140.dll" IN_LIST _orca_runtime_names OR NOT "vcruntime140.dll" IN_LIST _orca_runtime_names))
set(_orca_runtime_error "CMake ${CMAKE_VERSION} did not find msvcp140.dll and vcruntime140.dll for MSVC ${MSVC_VERSION}. Update CMake to a release that supports this Visual Studio.")
message(WARNING "${_orca_runtime_error}")
install(CODE "message(FATAL_ERROR \"${_orca_runtime_error}\")")
endif ()
install (PROGRAMS ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS} DESTINATION ".")
elseif (SLIC3R_FHS)
# CMAKE_INSTALL_FULL_DATAROOTDIR: read-only architecture-independent data root (share)
+5 -1
View File
@@ -282,7 +282,11 @@ if "%install_deps%" == "ON" (
call :note_failed "Visual Studio" !errorlevel!
)
call :print_and_run winget install !winget_args! --id=Kitware.CMake
REM CMake 4 dropped pre-3.5 policy support and ships incomplete ASM_ARMASM
REM linker modules, which breaks Boost.Context on ARM64. CI pins the same way.
set "cmake_version_flag="
if /I "%arch%" == "ARM64" set "cmake_version_flag=--version 3.31.8"
call :print_and_run winget install !winget_args! --id=Kitware.CMake !cmake_version_flag!
call :note_failed CMake !errorlevel!
call :print_and_run winget install !winget_args! --id=StrawberryPerl.StrawberryPerl
call :note_failed Perl !errorlevel!
-10
View File
@@ -29,15 +29,6 @@ if(WIN32)
# driven through nmake from a Ninja configure step.
set(_conf_cmd ${CMAKE_COMMAND} -E env ${_openssl_msvc_env} perl Configure )
set(_cross_comp_prefix_line "")
if("${DEPS_ARCH}" STREQUAL "arm64")
# OpenSSL's VC configs pass /Gs0, which puts a __chkstk probe in every
# function. MSVC 14.51 and 14.52 (VS 2026) for ARM64 emit that call
# before the prologue saves LR, so the function returns into itself;
# in tls_parse_all_extensions that breaks every TLS handshake. 14.44
# (VS 2022) is unaffected. Restore cl's default threshold: Configure
# appends /Gs4096 after /Gs0, and the later option wins.
set(_openssl_extra_cflags /Gs4096)
endif()
set(_make_cmd ${CMAKE_COMMAND} -E env ${_openssl_msvc_env} nmake)
set(_install_cmd ${CMAKE_COMMAND} -E env ${_openssl_msvc_env} nmake install_sw )
else()
@@ -80,7 +71,6 @@ ExternalProject_Add(dep_OpenSSL
# prefix stays single-layout.
"--libdir=lib"
${_cross_comp_prefix_line}
${_openssl_extra_cflags}
no-shared
no-asm
no-ssl3-method
+1
View File
@@ -15,6 +15,7 @@ add_subdirectory(stb_dxt) # Header-only STB DXT compression library
# Static libraries
add_subdirectory(Shiny)
add_subdirectory(admesh)
add_subdirectory(clipper)
add_subdirectory(clipper2)
add_subdirectory(expat)
add_subdirectory(glu-libtess)
+20
View File
@@ -0,0 +1,20 @@
cmake_minimum_required(VERSION 3.13)
project(clipper)
add_library(clipper STATIC
# We are using ClipperLib compiled as part of the libslic3r project using Slic3r::Point as its base type.
# clipper.cpp
# clipper.hpp
clipper_z.cpp
clipper_z.hpp
)
target_include_directories(clipper SYSTEM
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}
)
target_link_libraries(clipper
PUBLIC Eigen3::Eigen
PRIVATE TBB::tbb TBB::tbbmalloc
)
File diff suppressed because it is too large Load Diff
+606
View File
@@ -0,0 +1,606 @@
/*******************************************************************************
* *
* Author : Angus Johnson *
* Version : 6.4.2 *
* Date : 27 February 2017 *
* Website : http://www.angusj.com *
* Copyright : Angus Johnson 2010-2017 *
* *
* License: *
* Use, modification & distribution is subject to Boost Software License Ver 1. *
* http://www.boost.org/LICENSE_1_0.txt *
* *
* Attributions: *
* The code in this library is an extension of Bala Vatti's clipping algorithm: *
* "A generic solution to polygon clipping" *
* Communications of the ACM, Vol 35, Issue 7 (July 1992) pp 56-63. *
* http://portal.acm.org/citation.cfm?id=129906 *
* *
* Computer graphics and geometric modeling: implementation and algorithms *
* By Max K. Agoston *
* Springer; 1 edition (January 4, 2005) *
* http://books.google.com/books?q=vatti+clipping+agoston *
* *
* See also: *
* "Polygon Offsetting by Computing Winding Numbers" *
* Paper no. DETC2005-85513 pp. 565-575 *
* ASME 2005 International Design Engineering Technical Conferences *
* and Computers and Information in Engineering Conference (IDETC/CIE2005) *
* September 24-28, 2005 , Long Beach, California, USA *
* http://www.me.berkeley.edu/~mcmains/pubs/DAC05OffsetPolygon.pdf *
* *
*******************************************************************************/
#ifndef clipper_hpp
#define clipper_hpp
#include <inttypes.h>
#include <functional>
#include <Eigen/Geometry>
#include <oneapi/tbb/scalable_allocator.h>
#define CLIPPER_VERSION "6.2.6"
//CLIPPERLIB_USE_XYZ: adds a Z member to IntPoint. Adds a minor cost to perfomance.
//#define CLIPPERLIB_USE_XYZ
//use_lines: Enables line clipping. Adds a very minor cost to performance.
#define use_lines
//use_deprecated: Enables temporary support for the obsolete functions
//#define use_deprecated
#include <array>
#include <vector>
#include <deque>
#include <stdexcept>
#include <cstring>
#include <cstdlib>
#include <ostream>
#include <functional>
#include <queue>
#ifdef CLIPPERLIB_NAMESPACE_PREFIX
namespace CLIPPERLIB_NAMESPACE_PREFIX {
#endif // CLIPPERLIB_NAMESPACE_PREFIX
#ifdef CLIPPERLIB_USE_XYZ
namespace ClipperLib_Z {
#else
namespace ClipperLib {
#endif
enum ClipType { ctIntersection, ctUnion, ctDifference, ctXor };
enum PolyType { ptSubject, ptClip };
//By far the most widely used winding rules for polygon filling are
//EvenOdd & NonZero (GDI, GDI+, XLib, OpenGL, Cairo, AGG, Quartz, SVG, Gr32)
//Others rules include Positive, Negative and ABS_GTR_EQ_TWO (only in OpenGL)
//see http://glprogramming.com/red/chapter11.html
enum PolyFillType { pftEvenOdd, pftNonZero, pftPositive, pftNegative };
// If defined, Clipper will work with 32bit signed int coordinates to reduce memory
// consumption and to speed up exact orientation predicate calculation.
// In that case, coordinates and their differences (vectors of the coordinates) have to fit int32_t.
// #define CLIPPERLIB_INT32
// Point coordinate type
#ifdef CLIPPERLIB_INT32
// Coordinates and their differences (vectors of the coordinates) have to fit int32_t.
using cInt = int32_t;
using CrossProductType = int64_t;
#else
using cInt = int64_t;
using CrossProductType = double;
// Maximum cInt value to allow a cross product calculation using 32bit expressions.
static constexpr cInt const loRange = 0x3FFFFFFF; // 0x3FFFFFFF = 1 073 741 823
// Maximum allowed cInt value.
static constexpr cInt const hiRange = 0x3FFFFFFFFFFFFFFFLL;
#endif // CLIPPERLIB_INT32
#ifdef CLIPPERLIB_INTPOINT_TYPE
using IntPoint = CLIPPERLIB_INTPOINT_TYPE;
#else // CLIPPERLIB_INTPOINT_TYPE
using IntPoint = Eigen::Matrix<cInt,
#ifdef CLIPPERLIB_USE_XYZ
3
#else // CLIPPERLIB_USE_XYZ
2
#endif // CLIPPERLIB_USE_XYZ
, 1, Eigen::DontAlign>;
#endif // CLIPPERLIB_INTPOINT_TYPE
using DoublePoint = Eigen::Matrix<double, 2, 1, Eigen::DontAlign>;
//------------------------------------------------------------------------------
template<typename BaseType>
using Allocator = tbb::scalable_allocator<BaseType>;
//using Allocator = std::allocator<BaseType>;
using Path = std::vector<IntPoint, Allocator<IntPoint>>;
using Paths = std::vector<Path, Allocator<Path>>;
inline Path& operator <<(Path& poly, const IntPoint& p) {poly.push_back(p); return poly;}
inline Paths& operator <<(Paths& polys, const Path& p) {polys.push_back(p); return polys;}
std::ostream& operator <<(std::ostream &s, const IntPoint &p);
std::ostream& operator <<(std::ostream &s, const Path &p);
std::ostream& operator <<(std::ostream &s, const Paths &p);
//------------------------------------------------------------------------------
#ifdef CLIPPERLIB_USE_XYZ
typedef std::function<void(const IntPoint& e1bot, const IntPoint& e1top, const IntPoint& e2bot, const IntPoint& e2top, IntPoint& pt)> ZFillCallback;
#endif
enum InitOptions {ioReverseSolution = 1, ioStrictlySimple = 2, ioPreserveCollinear = 4};
enum JoinType {jtSquare, jtRound, jtMiter};
enum EndType {etClosedPolygon, etClosedLine, etOpenButt, etOpenSquare, etOpenRound};
class PolyNode;
typedef std::vector<PolyNode*, Allocator<PolyNode*>> PolyNodes;
class PolyNode
{
public:
PolyNode() : Parent(0), Index(0), m_IsOpen(false) {}
virtual ~PolyNode(){};
Path Contour;
PolyNodes Childs;
PolyNode* Parent;
// Traversal of the polygon tree in a depth first fashion.
PolyNode* GetNext() const { return Childs.empty() ? GetNextSiblingUp() : Childs.front(); }
bool IsHole() const;
bool IsOpen() const { return m_IsOpen; }
int ChildCount() const { return (int)Childs.size(); }
private:
unsigned Index; //node index in Parent.Childs
bool m_IsOpen;
JoinType m_jointype;
EndType m_endtype;
PolyNode* GetNextSiblingUp() const { return Parent ? ((Index == Parent->Childs.size() - 1) ? Parent->GetNextSiblingUp() : Parent->Childs[Index + 1]) : nullptr; }
void AddChild(PolyNode& child);
friend class Clipper; //to access Index
friend class ClipperOffset;
friend class PolyTree; //to implement the PolyTree::move operator
};
class PolyTree: public PolyNode
{
public:
PolyTree() {}
PolyTree(PolyTree &&src) { *this = std::move(src); }
virtual ~PolyTree(){Clear();};
PolyTree& operator=(PolyTree &&src) {
AllNodes = std::move(src.AllNodes);
Contour = std::move(src.Contour);
Childs = std::move(src.Childs);
Parent = nullptr;
Index = src.Index;
m_IsOpen = src.m_IsOpen;
m_jointype = src.m_jointype;
m_endtype = src.m_endtype;
for (size_t i = 0; i < Childs.size(); ++ i)
Childs[i]->Parent = this;
return *this;
}
PolyNode* GetFirst() const { return Childs.empty() ? nullptr : Childs.front(); }
void Clear() { AllNodes.clear(); Childs.clear(); }
int Total() const;
void RemoveOutermostPolygon();
private:
PolyTree(const PolyTree &src) = delete;
PolyTree& operator=(const PolyTree &src) = delete;
std::vector<PolyNode, Allocator<PolyNode>> AllNodes;
friend class Clipper; //to access AllNodes
};
double Area(const Path &poly);
inline bool Orientation(const Path &poly) { return Area(poly) >= 0; }
int PointInPolygon(const IntPoint &pt, const Path &path);
// Union with "strictly simple" fix enabled.
Paths SimplifyPolygon(const Path &in_poly, PolyFillType fillType = pftNonZero, bool strictly_simple = true);
void CleanPolygon(const Path& in_poly, Path& out_poly, double distance = 1.415);
void CleanPolygon(Path& poly, double distance = 1.415);
void CleanPolygons(const Paths& in_polys, Paths& out_polys, double distance = 1.415);
void CleanPolygons(Paths& polys, double distance = 1.415);
void MinkowskiSum(const Path& pattern, const Path& path, Paths& solution, bool pathIsClosed);
void MinkowskiSum(const Path& pattern, const Paths& paths, Paths& solution, bool pathIsClosed);
void MinkowskiDiff(const Path& poly1, const Path& poly2, Paths& solution);
void PolyTreeToPaths(const PolyTree& polytree, Paths& paths);
void PolyTreeToPaths(PolyTree&& polytree, Paths& paths);
void ClosedPathsFromPolyTree(const PolyTree& polytree, Paths& paths);
void OpenPathsFromPolyTree(PolyTree& polytree, Paths& paths);
void ReversePath(Path& p);
void ReversePaths(Paths& p);
struct IntRect { cInt left; cInt top; cInt right; cInt bottom; };
//enums that are used internally ...
enum EdgeSide { esLeft = 1, esRight = 2};
// namespace Internal {
//forward declarations (for stuff used internally) ...
struct TEdge {
// Bottom point of this edge (with minimum Y).
IntPoint Bot;
// Current position.
IntPoint Curr;
// Top point of this edge (with maximum Y).
IntPoint Top;
// Slope (dx/dy). For horiontal edges, the slope is set to HORIZONTAL (-1.0E+40).
double Dx;
PolyType PolyTyp;
EdgeSide Side;
// Winding number delta. 1 or -1 depending on winding direction, 0 for open paths and flat closed paths.
int WindDelta;
int WindCnt;
int WindCnt2; //winding count of the opposite polytype
int OutIdx;
// Next edge in the input path.
TEdge *Next;
// Previous edge in the input path.
TEdge *Prev;
// Next edge in the Local Minima List chain.
TEdge *NextInLML;
TEdge *NextInAEL;
TEdge *PrevInAEL;
TEdge *NextInSEL;
TEdge *PrevInSEL;
};
struct IntersectNode {
IntersectNode(TEdge *Edge1, TEdge *Edge2, IntPoint Pt) :
Edge1(Edge1), Edge2(Edge2), Pt(Pt) {}
TEdge *Edge1;
TEdge *Edge2;
IntPoint Pt;
};
struct LocalMinimum {
cInt Y;
TEdge *LeftBound;
TEdge *RightBound;
};
// Point of an output polygon.
// 36B on 64bit system without CLIPPERLIB_USE_XYZ.
struct OutPt {
// 4B
int Idx;
// 16B without CLIPPERLIB_USE_XYZ / 24B with CLIPPERLIB_USE_XYZ
IntPoint Pt;
// 4B on 32bit system, 8B on 64bit system
OutPt *Next;
// 4B on 32bit system, 8B on 64bit system
OutPt *Prev;
};
using OutPts = std::vector<OutPt, Allocator<OutPt>>;
// Output polygon.
struct OutRec {
int Idx;
bool IsHole;
bool IsOpen;
//The 'FirstLeft' field points to another OutRec that contains or is the
//'parent' of OutRec. It is 'first left' because the ActiveEdgeList (AEL) is
//parsed left from the current edge (owning OutRec) until the owner OutRec
//is found. This field simplifies sorting the polygons into a tree structure
//which reflects the parent/child relationships of all polygons.
//This field should be renamed Parent, and will be later.
OutRec* FirstLeft;
// Used only by void Clipper::BuildResult2(PolyTree& polytree)
PolyNode* PolyNd;
// Linked list of output points, dynamically allocated.
OutPt* Pts;
OutPt* BottomPt;
};
struct Join {
Join(OutPt *OutPt1, OutPt *OutPt2, IntPoint OffPt) :
OutPt1(OutPt1), OutPt2(OutPt2), OffPt(OffPt) {}
OutPt *OutPt1;
OutPt *OutPt2;
IntPoint OffPt;
};
// }; // namespace Internal
//------------------------------------------------------------------------------
//ClipperBase is the ancestor to the Clipper class. It should not be
//instantiated directly. This class simply abstracts the conversion of sets of
//polygon coordinates into edge objects that are stored in a LocalMinima list.
class ClipperBase
{
public:
ClipperBase() :
#ifndef CLIPPERLIB_INT32
m_UseFullRange(false),
#endif // CLIPPERLIB_INT32
m_HasOpenPaths(false) {}
~ClipperBase() { Clear(); }
bool AddPath(const Path &pg, PolyType PolyTyp, bool Closed);
template<typename PathsProvider>
bool AddPaths(PathsProvider &&paths_provider, PolyType PolyTyp, bool Closed)
{
size_t num_paths = paths_provider.size();
if (num_paths == 0)
return false;
if (num_paths == 1)
return AddPath(*paths_provider.begin(), PolyTyp, Closed);
std::vector<int, Allocator<int>> num_edges(num_paths, 0);
int num_edges_total = 0;
size_t i = 0;
for (const Path &pg : paths_provider) {
// Remove duplicate end point from a closed input path.
// Remove duplicate points from the end of the input path.
int highI = (int)pg.size() -1;
if (Closed)
while (highI > 0 && (pg[highI] == pg[0]))
--highI;
while (highI > 0 && (pg[highI] == pg[highI -1]))
--highI;
if ((Closed && highI < 2) || (!Closed && highI < 1))
highI = -1;
num_edges[i ++] = highI + 1;
num_edges_total += highI + 1;
}
if (num_edges_total == 0)
return false;
// Allocate a new edge array.
std::vector<TEdge, Allocator<TEdge>> edges(num_edges_total);
// Fill in the edge array.
bool result = false;
TEdge *p_edge = edges.data();
i = 0;
for (const Path &pg : paths_provider) {
if (num_edges[i] && !pg.empty()) {
bool res = AddPathInternal(pg, num_edges[i] - 1, PolyTyp, Closed, p_edge);
if (res) {
p_edge += num_edges[i];
result = true;
}
}
++ i;
}
if (result)
// At least some edges were generated. Remember the edge array.
m_edges.emplace_back(std::move(edges));
return result;
}
void Clear();
IntRect GetBounds();
// By default, when three or more vertices are collinear in input polygons (subject or clip), the Clipper object removes the 'inner' vertices before clipping.
// When enabled the PreserveCollinear property prevents this default behavior to allow these inner vertices to appear in the solution.
bool PreserveCollinear() const {return m_PreserveCollinear;};
void PreserveCollinear(bool value) {m_PreserveCollinear = value;};
protected:
bool AddPathInternal(const Path &pg, int highI, PolyType PolyTyp, bool Closed, TEdge* edges);
TEdge* AddBoundsToLML(TEdge *e, bool IsClosed);
void Reset();
TEdge* ProcessBound(TEdge* E, bool IsClockwise);
TEdge* DescendToMin(TEdge *&E);
void AscendToMax(TEdge *&E, bool Appending, bool IsClosed);
// Local minima (Y, left edge, right edge) sorted by ascending Y.
std::vector<LocalMinimum, Allocator<LocalMinimum>> m_MinimaList;
#ifdef CLIPPERLIB_INT32
static constexpr const bool m_UseFullRange = false;
#else // CLIPPERLIB_INT32
// True if the input polygons have abs values higher than loRange, but lower than hiRange.
// False if the input polygons have abs values lower or equal to loRange.
bool m_UseFullRange;
#endif // CLIPPERLIB_INT32
// A vector of edges per each input path.
using Edges = std::vector<TEdge, Allocator<TEdge>>;
std::vector<Edges, Allocator<Edges>> m_edges;
// Don't remove intermediate vertices of a collinear sequence of points.
bool m_PreserveCollinear;
// Is any of the paths inserted by AddPath() or AddPaths() open?
bool m_HasOpenPaths;
};
//------------------------------------------------------------------------------
class Clipper : public ClipperBase
{
public:
Clipper(int initOptions = 0);
~Clipper() { Clear(); }
void Clear() { ClipperBase::Clear(); DisposeAllOutRecs(); }
bool Execute(ClipType clipType,
Paths &solution,
PolyFillType fillType = pftEvenOdd)
{ return Execute(clipType, solution, fillType, fillType); }
bool Execute(ClipType clipType,
Paths &solution,
PolyFillType subjFillType,
PolyFillType clipFillType);
bool Execute(ClipType clipType,
PolyTree &polytree,
PolyFillType fillType = pftEvenOdd)
{ return Execute(clipType, polytree, fillType, fillType); }
bool Execute(ClipType clipType,
PolyTree &polytree,
PolyFillType subjFillType,
PolyFillType clipFillType);
bool ReverseSolution() const { return m_ReverseOutput; };
void ReverseSolution(bool value) {m_ReverseOutput = value;};
bool StrictlySimple() const {return m_StrictSimple;};
void StrictlySimple(bool value) {m_StrictSimple = value;};
//set the callback function for z value filling on intersections (otherwise Z is 0)
#ifdef CLIPPERLIB_USE_XYZ
void ZFillFunction(ZFillCallback zFillFunc) { m_ZFill = zFillFunc; }
#endif
protected:
void Reset();
virtual bool ExecuteInternal();
private:
// Output polygons.
std::deque<OutRec, Allocator<OutRec>> m_PolyOuts;
// Output points, allocated by a continuous sets of m_OutPtsChunkSize.
static constexpr const size_t m_OutPtsChunkSize = 32;
std::deque<std::array<OutPt, m_OutPtsChunkSize>, Allocator<std::array<OutPt, m_OutPtsChunkSize>>> m_OutPts;
// List of free output points, to be used before taking a point from m_OutPts or allocating a new chunk.
OutPt *m_OutPtsFree;
size_t m_OutPtsChunkLast;
std::vector<Join, Allocator<Join>> m_Joins;
std::vector<Join, Allocator<Join>> m_GhostJoins;
std::vector<IntersectNode, Allocator<IntersectNode>> m_IntersectList;
ClipType m_ClipType;
// A priority queue (a binary heap) of Y coordinates.
using cInts = std::vector<cInt, Allocator<cInt>>;
std::priority_queue<cInt, cInts> m_Scanbeam;
// Maxima are collected by ProcessEdgesAtTopOfScanbeam(), consumed by ProcessHorizontal().
cInts m_Maxima;
TEdge *m_ActiveEdges;
TEdge *m_SortedEdges;
PolyFillType m_ClipFillType;
PolyFillType m_SubjFillType;
bool m_ReverseOutput;
// Does the result go to a PolyTree or Paths?
bool m_UsingPolyTree;
bool m_StrictSimple;
#ifdef CLIPPERLIB_USE_XYZ
ZFillCallback m_ZFill; //custom callback
#endif
void SetWindingCount(TEdge& edge) const;
bool IsEvenOddFillType(const TEdge& edge) const
{ return (edge.PolyTyp == ptSubject) ? m_SubjFillType == pftEvenOdd : m_ClipFillType == pftEvenOdd; }
bool IsEvenOddAltFillType(const TEdge& edge) const
{ return (edge.PolyTyp == ptSubject) ? m_ClipFillType == pftEvenOdd : m_SubjFillType == pftEvenOdd; }
void InsertLocalMinimaIntoAEL(const cInt botY);
void InsertEdgeIntoAEL(TEdge *edge, TEdge* startEdge);
void AddEdgeToSEL(TEdge *edge);
void CopyAELToSEL();
void DeleteFromSEL(TEdge *e);
void DeleteFromAEL(TEdge *e);
void UpdateEdgeIntoAEL(TEdge *&e);
void SwapPositionsInSEL(TEdge *edge1, TEdge *edge2);
bool IsContributing(const TEdge& edge) const;
bool IsTopHorz(const cInt XPos);
void SwapPositionsInAEL(TEdge *edge1, TEdge *edge2);
void DoMaxima(TEdge *e);
void ProcessHorizontals();
void ProcessHorizontal(TEdge *horzEdge);
void AddLocalMaxPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
OutPt* AddLocalMinPoly(TEdge *e1, TEdge *e2, const IntPoint &pt);
OutRec* GetOutRec(int idx);
void AppendPolygon(TEdge *e1, TEdge *e2);
void IntersectEdges(TEdge *e1, TEdge *e2, IntPoint &pt);
OutRec* CreateOutRec();
OutPt* AddOutPt(TEdge *e, const IntPoint &pt);
OutPt* GetLastOutPt(TEdge *e);
OutPt* AllocateOutPt();
OutPt* DupOutPt(OutPt* outPt, bool InsertAfter);
// Add the point to a list of free points.
void DisposeOutPt(OutPt *pt) { pt->Next = m_OutPtsFree; m_OutPtsFree = pt; }
void DisposeOutPts(OutPt*& pp) { if (pp != nullptr) { pp->Prev->Next = m_OutPtsFree; m_OutPtsFree = pp; } }
void DisposeAllOutRecs();
bool ProcessIntersections(const cInt topY);
void BuildIntersectList(const cInt topY);
void ProcessEdgesAtTopOfScanbeam(const cInt topY);
void BuildResult(Paths& polys);
void BuildResult2(PolyTree& polytree);
void SetHoleState(TEdge *e, OutRec *outrec);
bool FixupIntersectionOrder();
void FixupOutPolygon(OutRec &outrec);
void FixupOutPolyline(OutRec &outrec);
bool FindOwnerFromSplitRecs(OutRec &outRec, OutRec *&currOrfl);
void FixHoleLinkage(OutRec &outrec);
bool JoinPoints(Join *j, OutRec* outRec1, OutRec* outRec2);
bool JoinHorz(OutPt* op1, OutPt* op1b, OutPt* op2, OutPt* op2b, const IntPoint &Pt, bool DiscardLeft);
void JoinCommonEdges();
void DoSimplePolygons();
void FixupFirstLefts1(OutRec* OldOutRec, OutRec* NewOutRec);
void FixupFirstLefts2(OutRec* InnerOutRec, OutRec* OuterOutRec);
void FixupFirstLefts3(OutRec* OldOutRec, OutRec* NewOutRec);
#ifdef CLIPPERLIB_USE_XYZ
void SetZ(IntPoint& pt, TEdge& e1, TEdge& e2);
#endif
};
//------------------------------------------------------------------------------
class ClipperOffset
{
public:
ClipperOffset(double miterLimit = 2.0, double roundPrecision = 0.25, double shortestEdgeLength = 0.) :
MiterLimit(miterLimit), ArcTolerance(roundPrecision), ShortestEdgeLength(shortestEdgeLength), m_lowest(-1, 0) {}
~ClipperOffset() { Clear(); }
void AddPath(const Path& path, JoinType joinType, EndType endType);
template<typename PathsProvider>
void AddPaths(PathsProvider &&paths, JoinType joinType, EndType endType) {
for (const Path &path : paths)
AddPath(path, joinType, endType);
}
void Execute(Paths& solution, double delta);
void Execute(PolyTree& solution, double delta);
void Clear();
double MiterLimit;
double ArcTolerance;
double ShortestEdgeLength;
private:
Paths m_destPolys;
Path m_srcPoly;
Path m_destPoly;
std::vector<DoublePoint, Allocator<DoublePoint>> m_normals;
double m_delta, m_sinA, m_sin, m_cos;
double m_miterLim, m_StepsPerRad;
// x: index of the lowest contour in m_polyNodes
// y: index of the lowest point in the lowest contour
IntPoint m_lowest;
PolyNode m_polyNodes;
void FixOrientations();
void DoOffset(double delta);
void OffsetPoint(int j, int& k, JoinType jointype);
void DoSquare(int j, int k);
void DoMiter(int j, int k, double r);
void DoRound(int j, int k);
};
//------------------------------------------------------------------------------
class clipperException : public std::exception
{
public:
clipperException(const char* description): m_descr(description) {}
virtual ~clipperException() throw() {}
virtual const char* what() const throw() {return m_descr.c_str();}
private:
std::string m_descr;
};
//------------------------------------------------------------------------------
// Union with "strictly simple" fix enabled.
template<typename PathsProvider>
inline Paths SimplifyPolygons(PathsProvider &&in_polys, PolyFillType fillType = pftNonZero, bool strictly_simple = true) {
Clipper c;
c.StrictlySimple(strictly_simple);
c.AddPaths(std::forward<PathsProvider>(in_polys), ptSubject, true);
Paths out;
c.Execute(ctUnion, out, fillType, fillType);
return out;
}
} //ClipperLib namespace
#ifdef CLIPPERLIB_NAMESPACE_PREFIX
} // namespace CLIPPERLIB_NAMESPACE_PREFIX
#endif // CLIPPERLIB_NAMESPACE_PREFIX
#endif //clipper_hpp
+7
View File
@@ -0,0 +1,7 @@
// Hackish wrapper around the ClipperLib library to compile the Clipper library with the Z support.
// Enable the Z coordinate support.
#define CLIPPERLIB_USE_XYZ
// and let it compile
#include "clipper.cpp"
+18
View File
@@ -0,0 +1,18 @@
// Hackish wrapper around the ClipperLib library to compile the Clipper library with the Z support.
#ifndef clipper_z_hpp
#ifdef clipper_hpp
#error "You should include clipper_z.hpp before clipper.hpp"
#endif
#define clipper_z_hpp
// Enable the Z coordinate support.
#define CLIPPERLIB_USE_XYZ
#include "clipper.hpp"
#undef clipper_hpp
#undef CLIPPERLIB_USE_XYZ
#endif // clipper_z_hpp
+1 -6
View File
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.10)
project(Clipper2 VERSION 2.0.1 LANGUAGES C CXX)
project(Clipper2 VERSION 1.5.2 LANGUAGES C CXX)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_CXX_STANDARD 17)
@@ -19,7 +19,6 @@ set(CLIPPER2_INC
Clipper2Lib/include/clipper2/clipper.minkowski.h
Clipper2Lib/include/clipper2/clipper.offset.h
Clipper2Lib/include/clipper2/clipper.rectclip.h
Clipper2Lib/include/clipper2/clipper.triangulation.h
Clipper2Lib/include/clipper2/clipper2_z.hpp
)
@@ -27,7 +26,6 @@ set(CLIPPER2_SRC
Clipper2Lib/src/clipper.engine.cpp
Clipper2Lib/src/clipper.offset.cpp
Clipper2Lib/src/clipper.rectclip.cpp
Clipper2Lib/src/clipper.triangulation.cpp
Clipper2Lib/src/clipper2_z.cpp
)
@@ -38,9 +36,6 @@ target_include_directories(Clipper2
PUBLIC Clipper2Lib/include
)
# Engine nodes are allocated through tbbmalloc (see clipper.engine.cpp).
target_link_libraries(Clipper2 PRIVATE TBB::tbbmalloc)
if (WIN32)
if (MSVC AND NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
target_compile_options(Clipper2 PRIVATE /W4 /WX)
@@ -1,8 +1,8 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 12 October 2025 *
* Date : 12 May 2024 *
* Website : https://www.angusj.com *
* Copyright : Angus Johnson 2010-2025 *
* Copyright : Angus Johnson 2010-2024 *
* Purpose : Core Clipper Library structures and functions *
* License : https://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
@@ -251,20 +251,6 @@ namespace Clipper2Lib {
template <typename T>
using Paths = std::vector<Path<T>>;
template <typename T, typename T2=T>
Path<T>& operator<<(Path<T>& poly, const Point<T2>& p)
{
poly.emplace_back(p);
return poly;
}
template <typename T>
Paths<T>& operator<<(Paths<T>& polys, const Path<T>& p)
{
polys.emplace_back(p);
return polys;
}
using Path64 = Path<int64_t>;
using PathD = Path<double>;
using Paths64 = std::vector< Path64>;
@@ -699,31 +685,32 @@ namespace Clipper2Lib {
inline int TriSign(int64_t x) // returns 0, 1 or -1
{
return (x > 0) - (x < 0);
return (x > 0) - (x < 0);
}
struct UInt128Struct
struct MultiplyUInt64Result
{
const uint64_t lo = 0;
const uint64_t hi = 0;
const uint64_t result = 0;
const uint64_t carry = 0;
bool operator==(const UInt128Struct& other) const
bool operator==(const MultiplyUInt64Result& other) const
{
return lo == other.lo && hi == other.hi;
return result == other.result && carry == other.carry;
};
};
inline UInt128Struct MultiplyUInt64(uint64_t a, uint64_t b) // #834, #835
inline MultiplyUInt64Result Multiply(uint64_t a, uint64_t b) // #834, #835
{
// note to self - lamba expressions follow
const auto lo = [](uint64_t x) { return x & 0xFFFFFFFF; };
const auto hi = [](uint64_t x) { return x >> 32; };
const uint64_t x1 = lo(a) * lo(b);
const uint64_t x2 = hi(a) * lo(b) + hi(x1);
const uint64_t x3 = lo(a) * hi(b) + lo(x2);
return { uint64_t(lo(x3) << 32 | lo(x1)), uint64_t(hi(a) * hi(b) + hi(x2) + hi(x3)) };
const uint64_t result = lo(x3) << 32 | lo(x1);
const uint64_t carry = hi(a) * hi(b) + hi(x2) + hi(x3);
return { result, carry };
}
// returns true if (and only if) a * b == c * d
@@ -740,50 +727,14 @@ namespace Clipper2Lib {
const auto abs_c = static_cast<uint64_t>(std::abs(c));
const auto abs_d = static_cast<uint64_t>(std::abs(d));
const auto ab = MultiplyUInt64(abs_a, abs_b);
const auto cd = MultiplyUInt64(abs_c, abs_d);
const auto abs_ab = Multiply(abs_a, abs_b);
const auto abs_cd = Multiply(abs_c, abs_d);
// nb: it's important to differentiate 0 values here from other values
const auto sign_ab = TriSign(a) * TriSign(b);
const auto sign_cd = TriSign(c) * TriSign(d);
return ab == cd && sign_ab == sign_cd;
#endif
}
template <typename T>
inline int CrossProductSign(const Point<T>& pt1, const Point<T>& pt2, const Point<T>& pt3)
{
const auto a = pt2.x - pt1.x;
const auto b = pt3.y - pt2.y;
const auto c = pt2.y - pt1.y;
const auto d = pt3.x - pt2.x;
#if (defined(__clang__) || defined(__GNUC__)) && UINTPTR_MAX >= UINT64_MAX
const auto ab = static_cast<__int128_t>(a) * static_cast<__int128_t>(b);
const auto cd = static_cast<__int128_t>(c) * static_cast<__int128_t>(d);
if (ab > cd) return 1;
else if (ab < cd) return -1;
else return 0;
#else
const auto ab = MultiplyUInt64(std::abs(a), std::abs(b));
const auto cd = MultiplyUInt64(std::abs(c), std::abs(d));
const auto sign_ab = TriSign(a) * TriSign(b);
const auto sign_cd = TriSign(c) * TriSign(d);
if (sign_ab == sign_cd)
{
int result;
if (ab.hi == cd.hi)
{
if (ab.lo == cd.lo) return 0;
result = (ab.lo > cd.lo) ? 1 : -1;
}
else result = (ab.hi > cd.hi) ? 1 : -1;
return (sign_ab > 0) ? result : -result;
}
return (sign_ab > sign_cd) ? 1 : -1;
return abs_ab == abs_cd && sign_ab == sign_cd;
#endif
}
@@ -887,10 +838,6 @@ namespace Clipper2Lib {
return Area<T>(poly) >= 0;
}
// GetLineIntersectPt - a 'true' result is non-parallel. The 'ip' will also
// be constrained to seg1. However, it's possible that 'ip' won't be inside
// seg2, even when 'ip' hasn't been constrained (ie 'ip' is inside seg1).
#if CLIPPER2_HI_PRECISION
// caution: this will compromise performance
// https://github.com/AngusJohnson/Clipper2/issues/317#issuecomment-1314023253
@@ -898,7 +845,7 @@ namespace Clipper2Lib {
#define CC_MIN(x,y) ((x)>(y)?(y):(x))
#define CC_MAX(x,y) ((x)<(y)?(y):(x))
template<typename T>
inline bool GetLineIntersectPt(const Point<T>& ln1a, const Point<T>& ln1b,
inline bool GetSegmentIntersectPt(const Point<T>& ln1a, const Point<T>& ln1b,
const Point<T>& ln2a, const Point<T>& ln2b, Point<T>& ip)
{
double ln1dy = static_cast<double>(ln1b.y - ln1a.y);
@@ -944,14 +891,11 @@ namespace Clipper2Lib {
ip.x = originx + static_cast<T>(hitx);
ip.y = originy + static_cast<T>(hity);
}
#ifdef USINGZ
ip.z = 0;
#endif
return true;
}
#else
template<typename T>
inline bool GetLineIntersectPt(const Point<T>& ln1a, const Point<T>& ln1b,
inline bool GetSegmentIntersectPt(const Point<T>& ln1a, const Point<T>& ln1b,
const Point<T>& ln2a, const Point<T>& ln2b, Point<T>& ip)
{
// https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection
@@ -969,10 +913,7 @@ namespace Clipper2Lib {
{
ip.x = static_cast<T>(ln1a.x + t * dx1);
ip.y = static_cast<T>(ln1a.y + t * dy1);
#ifdef USINGZ
ip.z = 0;
#endif
}
}
return true;
}
#endif
@@ -999,53 +940,30 @@ namespace Clipper2Lib {
}
template<typename T>
inline int GetSign(const T& val)
{
if (!val) return 0;
inline int GetSign(const T& val)
{
if (!val) return 0;
return (val > 0) ? 1 : -1;
}
inline bool SegmentsIntersect(const Point64& seg1a, const Point64& seg1b,
const Point64& seg2a, const Point64& seg2b, bool inclusive = false)
{
double dy1 = static_cast<double>(seg1b.y - seg1a.y);
double dx1 = static_cast<double>(seg1b.x - seg1a.x);
double dy2 = static_cast<double>(seg2b.y - seg2a.y);
double dx2 = static_cast<double>(seg2b.x - seg2a.x);
double cp = dy1 * dx2 - dy2 * dx1;
if (cp == 0) return false; // ie parallel segments
if (inclusive)
{
//result **includes** segments that touch at an end point
double t = ((seg1a.x - seg2a.x) * dy2 - (seg1a.y - seg2a.y) * dx2);
if (t == 0) return true;
if (t > 0)
{
if (cp < 0 || t > cp) return false;
}
else if (cp > 0 || t < cp) return false; // false when t more neg. than cp
t = ((seg1a.x - seg2a.x) * dy1 - (seg1a.y - seg2a.y) * dx1);
if (t == 0) return true;
if (t > 0) return (cp > 0 && t <= cp);
else return (cp < 0 && t >= cp); // true when t less neg. than cp
double res1 = CrossProduct(seg1a, seg2a, seg2b);
double res2 = CrossProduct(seg1b, seg2a, seg2b);
if (res1 * res2 > 0) return false;
double res3 = CrossProduct(seg2a, seg1a, seg1b);
double res4 = CrossProduct(seg2b, seg1a, seg1b);
if (res3 * res4 > 0) return false;
return (res1 || res2 || res3 || res4); // ensures not collinear
}
else
{
//result **excludes** segments that touch at an end point
double t = ((seg1a.x - seg2a.x) * dy2 - (seg1a.y - seg2a.y) * dx2);
if (t == 0) return false;
if (t > 0)
{
if (cp < 0 || t >= cp) return false;
}
else if (cp > 0 || t <= cp ) return false; // false when t more neg. than cp
t = ((seg1a.x - seg2a.x) * dy1 - (seg1a.y - seg2a.y) * dx1);
if (t == 0) return false;
if (t > 0) return (cp > 0 && t < cp);
else return (cp < 0 && t > cp); // true when t less neg. than cp
else {
return (GetSign(CrossProduct(seg1a, seg2a, seg2b)) *
GetSign(CrossProduct(seg1b, seg2a, seg2b)) < 0) &&
(GetSign(CrossProduct(seg2a, seg1a, seg1b)) *
GetSign(CrossProduct(seg2b, seg1a, seg1b)) < 0);
}
}
@@ -1133,7 +1051,7 @@ namespace Clipper2Lib {
val = 1 - val; // toggle val
else
{
int d = CrossProductSign(*prev, *curr, pt);
double d = CrossProduct(*prev, *curr, pt);
if (d == 0) return PointInPolygonResult::IsOn;
if ((d < 0) == is_above) val = 1 - val;
}
@@ -1147,7 +1065,7 @@ namespace Clipper2Lib {
if (curr == cend) curr = cbegin;
if (curr == cbegin) prev = cend - 1;
else prev = curr - 1;
int d = CrossProductSign(*prev, *curr, pt);
double d = CrossProduct(*prev, *curr, pt);
if (d == 0) return PointInPolygonResult::IsOn;
if ((d < 0) == is_above) val = 1 - val;
}
@@ -15,13 +15,6 @@
#include <functional>
#include <memory>
// Orca: engine nodes are allocated through tbbmalloc, see clipper.engine.cpp.
#define CLIPPER2_NODE_ALLOCATOR \
static void* operator new(size_t size); \
static void operator delete(void* ptr) noexcept; \
static void* operator new[](size_t size); \
static void operator delete[](void* ptr) noexcept;
#ifdef USINGZ
namespace Clipper2Lib_Z {
#else
@@ -57,7 +50,6 @@ namespace Clipper2Lib {
}
struct Vertex {
CLIPPER2_NODE_ALLOCATOR
Point64 pt;
Vertex* next = nullptr;
Vertex* prev = nullptr;
@@ -65,7 +57,6 @@ namespace Clipper2Lib {
};
struct OutPt {
CLIPPER2_NODE_ALLOCATOR
Point64 pt;
OutPt* next = nullptr;
OutPt* prev = nullptr;
@@ -90,7 +81,6 @@ namespace Clipper2Lib {
//OutRec: contains a path in the clipping solution. Edges in the AEL will
//have OutRec pointers assigned when they form part of the clipping solution.
struct OutRec {
CLIPPER2_NODE_ALLOCATOR
size_t idx = 0;
OutRec* owner = nullptr;
Active* front_edge = nullptr;
@@ -116,7 +106,6 @@ namespace Clipper2Lib {
///////////////////////////////////////////////////////////////////
struct Active {
CLIPPER2_NODE_ALLOCATOR
Point64 bot;
Point64 top;
int64_t curr_x = 0; //current (updated at every new scanline)
@@ -144,7 +133,6 @@ namespace Clipper2Lib {
};
struct LocalMinima {
CLIPPER2_NODE_ALLOCATOR
Vertex* vertex;
PathType polytype;
bool is_open;
@@ -315,7 +303,6 @@ namespace Clipper2Lib {
protected:
PolyPath* parent_;
public:
CLIPPER2_NODE_ALLOCATOR
PolyPath(PolyPath* parent = nullptr): parent_(parent){}
virtual ~PolyPath() {};
//https://en.cppreference.com/w/cpp/language/rule_of_three
@@ -343,16 +330,15 @@ namespace Clipper2Lib {
//Even levels except level 0
return lvl && !(lvl & 1);
}
// Area() of the namespace this header is compiled into (Clipper2Lib or Clipper2Lib_Z).
template<typename T>
static double Clipper2LibArea(const Path<T> &poly)
{
template<typename T>
static double Clipper2LibArea(const Path<T> &poly)
{
#ifdef USINGZ
return Clipper2Lib_Z::Area<T>(poly);
return Clipper2Lib_Z::Area<T>(poly);
#else
return Clipper2Lib::Area<T>(poly);
return Clipper2Lib::Area<T>(poly);
#endif
}
}
};
typedef typename std::vector<std::unique_ptr<PolyPath64>> PolyPath64List;
@@ -402,8 +388,7 @@ namespace Clipper2Lib {
double Area() const
{
return std::accumulate(childs_.cbegin(), childs_.cend(),
Clipper2LibArea<int64_t>(polygon_),
return std::accumulate(childs_.cbegin(), childs_.cend(), Clipper2LibArea<int64_t>(polygon_),
[](double a, const auto& child) {return a + child->Area(); });
}
@@ -477,8 +462,7 @@ namespace Clipper2Lib {
double Area() const
{
return std::accumulate(childs_.begin(), childs_.end(),
Clipper2LibArea<double>(polygon_),
return std::accumulate(childs_.begin(), childs_.end(), Clipper2LibArea<double>(polygon_),
[](double a, const auto& child) {return a + child->Area(); });
}
};
@@ -19,17 +19,17 @@
The path structures used extensively in other parts of this library are all
based on std::vector classes. Since C++ classes can't be accessed by other
languages, these paths are exported here as very simple array structures
(either of int64_t or double) that can be parsed by just about any
languages, these paths are exported here as very simple array structures
(either of int64_t or double) that can be parsed by just about any
programming language.
These 2D paths are defined by series of x and y coordinates together with an
optional user-defined 'z' value (see Z-values below). Hence, a vertex refers
to a single x and y coordinate (+/- a user-defined value). Data structures
have names with suffixes that indicate the array type (either int64_t or
double). For example, the data structure CPath64 contains an array of int64_t
values, whereas the data structure CPathD contains an array of double.
Where documentation omits the type suffix (eg CPath), it is referring to an
to a single x and y coordinate (+/- a user-defined value). Data structures
have names with suffixes that indicate the array type (either int64_t or
double). For example, the data structure CPath64 contains an array of int64_t
values, whereas the data structure CPathD contains an array of double.
Where documentation omits the type suffix (eg CPath), it is referring to an
array whose data type could be either int64_t or double.
For conciseness, the following letters are used in the diagrams below:
@@ -39,10 +39,10 @@ A: Number of elements in an array
CPath64 and CPathD:
These are arrays of either int64_t or double values. Apart from
the first two elements, these arrays are a series of vertices
that together define a path. The very first element contains the
number of vertices (N) in the path, while second element should
These are arrays of either int64_t or double values. Apart from
the first two elements, these arrays are a series of vertices
that together define a path. The very first element contains the
number of vertices (N) in the path, while second element should
contain a 0 value.
_______________________________________________________________
| counters | vertex1 | vertex2 | ... | vertexN |
@@ -52,9 +52,9 @@ _______________________________________________________________
CPaths64 and CPathsD:
These are also arrays of either int64_t or double values that
contain any number of consecutive CPath structures. However,
contain any number of consecutive CPath structures. However,
preceding the first path is a pair of values. The first value
contains the length of the entire array structure (A), and the
contains the length of the entire array structure (A), and the
second contains the number (ie count) of contained paths (C).
Memory allocation for CPaths64 = A * sizeof(int64_t)
Memory allocation for CPathsD = A * sizeof(double)
@@ -65,12 +65,12 @@ __________________________________________
CPolytree64 and CPolytreeD:
The entire polytree structure is an array of int64_t or double. The
first element in the array indicates the array's total length (A).
The second element indicates the number (C) of CPolyPath structures
The entire polytree structure is an array of int64_t or double. The
first element in the array indicates the array's total length (A).
The second element indicates the number (C) of CPolyPath structures
that are the TOP LEVEL CPolyPath in the polytree, and these top
level CPolyPath immediately follow these first two array elements.
These top level CPolyPath structures may, in turn, contain nested
level CPolyPath immediately follow these first two array elements.
These top level CPolyPath structures may, in turn, contain nested
CPolyPath children, and these collectively make a tree structure.
_________________________________________________________
| counters | CPolyPath1 | CPolyPath2 | ... | CPolyPathC |
@@ -116,10 +116,13 @@ the four vertices that define the two segments that are intersecting.
#include "clipper2/clipper.engine.h"
#include "clipper2/clipper.offset.h"
#include "clipper2/clipper.rectclip.h"
#include "clipper2/clipper.triangulation.h"
#include <cstdlib>
#ifdef USINGZ
namespace Clipper2Lib_Z {
#else
namespace Clipper2Lib {
#endif
typedef int64_t* CPath64;
typedef int64_t* CPaths64;
@@ -251,9 +254,9 @@ ZCallback64 dllCallback64 = nullptr;
ZCallbackD dllCallbackD = nullptr;
constexpr int EXPORT_VERTEX_DIMENSIONALITY = 3;
#else
#else
constexpr int EXPORT_VERTEX_DIMENSIONALITY = 2;
#endif
#endif
template <typename T>
static void GetPathCountAndCPathsArrayLen(const Paths<T>& paths,
@@ -393,7 +396,7 @@ static Path<T> ConvertCPathToPathT(T* path)
#ifdef USINGZ
z_type z = Reinterpret<z_type>(*v++);
result.emplace_back(x, y, z);
#else
#else
result.emplace_back(x, y);
#endif
}
@@ -411,7 +414,7 @@ static Paths<T> ConvertCPathsToPathsT(T* paths)
for (size_t i = 0; i < cnt; ++i)
{
size_t cnt2 = static_cast<size_t>(*v);
v += 2;
v += 2;
Path<T> path;
path.reserve(cnt2);
for (size_t j = 0; j < cnt2; ++j)
@@ -444,7 +447,7 @@ static Path64 ConvertCPathDToPath64WithScale(const CPathD path, double scale)
#ifdef USINGZ
z_type z = Reinterpret<z_type>(*v++);
result.emplace_back(x, y, z);
#else
#else
result.emplace_back(x, y);
#endif
}
@@ -489,7 +492,7 @@ static void CreateCPolyPath64(const PolyPath64* pp, int64_t*& v)
{
*v++ = pt.x;
*v++ = pt.y;
#ifdef USINGZ
#ifdef USINGZ
* v++ = Reinterpret<int64_t>(pt.z); // raw memory copy
#endif
}
@@ -505,7 +508,7 @@ static void CreateCPolyPathD(const PolyPathD* pp, double*& v)
{
*v++ = pt.x;
*v++ = pt.y;
#ifdef USINGZ
#ifdef USINGZ
* v++ = Reinterpret<double>(pt.z); // raw memory copy
#endif
}
@@ -813,24 +816,6 @@ EXTERN_DLL_EXPORT CPaths64 MinkowskiDiff64(const CPath64& cpattern, const CPath6
return CreateCPathsFromPathsT(solution);
}
EXTERN_DLL_EXPORT CPaths64 Triangulate64(const CPaths64 paths, bool use_delaunay)
{
Paths64 pp = ConvertCPathsToPathsT(paths);
Paths64 sol;
if (Triangulate(pp, sol, use_delaunay) != TriangulateResult::success) return nullptr;
return CreateCPathsFromPathsT(sol);
}
EXTERN_DLL_EXPORT CPathsD TriangulateD(const CPathsD paths, int decimal_precison, bool use_delaunay)
{
if (decimal_precison < -8 || decimal_precison > 8) return nullptr;
const double scale = std::pow(10, decimal_precison);
Paths64 pp = ConvertCPathsDToPaths64(paths, scale);
Paths64 sol;
if (Triangulate(pp, sol, use_delaunay) != TriangulateResult::success) return nullptr;
return CreateCPathsDFromPaths64(sol, 1 / scale);
}
#ifdef USINGZ
typedef void (*DLLZCallback64)(const Point64& e1bot, const Point64& e1top, const Point64& e2bot, const Point64& e2top, Point64& pt);
typedef void (*DLLZCallbackD)(const PointD& e1bot, const PointD& e1top, const PointD& e2bot, const PointD& e2top, PointD& pt);
@@ -1,8 +1,8 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 5 March 2025 *
* Date : 27 April 2024 *
* Website : https://www.angusj.com *
* Copyright : Angus Johnson 2010-2025 *
* Copyright : Angus Johnson 2010-2024 *
* Purpose : This module provides a simple interface to the Clipper Library *
* License : https://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
@@ -13,15 +13,14 @@
#include "clipper2/clipper.core.h"
#include "clipper2/clipper.engine.h"
#include "clipper2/clipper.offset.h"
#include "clipper2/clipper.rectclip.h"
#include "clipper2/clipper.minkowski.h"
#include "clipper2/clipper.triangulation.h"
#include "clipper2/clipper.rectclip.h"
#include <type_traits>
#ifdef USINGZ
namespace Clipper2Lib_Z {
namespace Clipper2Lib_Z {
#else
namespace Clipper2Lib {
namespace Clipper2Lib {
#endif
inline Paths64 BooleanOp(ClipType cliptype, FillRule fillrule,
@@ -155,14 +154,14 @@ namespace Clipper2Lib {
if (!delta) return paths;
if (error_code) return PathsD();
const double scale = std::pow(10, precision);
ClipperOffset clip_offset(miter_limit, arc_tolerance * scale);
ClipperOffset clip_offset(miter_limit, arc_tolerance);
clip_offset.AddPaths(ScalePaths<int64_t,double>(paths, scale, error_code), jt, et);
if (error_code) return PathsD();
Paths64 solution;
clip_offset.Execute(delta * scale, solution);
return ScalePaths<double, int64_t>(solution, 1 / scale, error_code);
}
template <typename T>
inline Path<T> TranslatePath(const Path<T>& path, T dx, T dy)
{
@@ -356,29 +355,6 @@ namespace Clipper2Lib {
#endif
}
inline size_t GetNext(size_t current, size_t high,
const std::vector<bool>& flags)
{
++current;
while (current <= high && flags[current]) ++current;
if (current <= high) return current;
current = 0;
while (flags[current]) ++current;
return current;
}
inline size_t GetPrior(size_t current, size_t high,
const std::vector<bool>& flags)
{
if (current == 0) current = high;
else --current;
while (current > 0 && flags[current]) --current;
if (!flags[current]) return current;
current = high;
while (flags[current]) --current;
return current;
}
} // end details namespace
inline std::ostream& operator<< (std::ostream& os, const PolyTree64& pp)
@@ -639,6 +615,29 @@ namespace Clipper2Lib {
return result;
}
inline size_t GetNext(size_t current, size_t high,
const std::vector<bool>& flags)
{
++current;
while (current <= high && flags[current]) ++current;
if (current <= high) return current;
current = 0;
while (flags[current]) ++current;
return current;
}
inline size_t GetPrior(size_t current, size_t high,
const std::vector<bool>& flags)
{
if (current == 0) current = high;
else --current;
while (current > 0 && flags[current]) --current;
if (!flags[current]) return current;
current = high;
while (flags[current]) --current;
return current;
}
template <typename T>
inline Path<T> SimplifyPath(const Path<T> &path,
double epsilon, bool isClosedPath = true)
@@ -670,13 +669,13 @@ namespace Clipper2Lib {
start = curr;
do
{
curr = details::GetNext(curr, high, flags);
curr = GetNext(curr, high, flags);
} while (curr != start && distSqr[curr] > epsSqr);
if (curr == start) break;
}
prior = details::GetPrior(curr, high, flags);
next = details::GetNext(curr, high, flags);
prior = GetPrior(curr, high, flags);
next = GetNext(curr, high, flags);
if (next == prior) break;
// flag for removal the smaller of adjacent 'distances'
@@ -685,14 +684,14 @@ namespace Clipper2Lib {
prior2 = prior;
prior = curr;
curr = next;
next = details::GetNext(next, high, flags);
next = GetNext(next, high, flags);
}
else
prior2 = details::GetPrior(prior, high, flags);
prior2 = GetPrior(prior, high, flags);
flags[curr] = true;
curr = next;
next = details::GetNext(next, high, flags);
next = GetNext(next, high, flags);
if (isClosedPath || ((curr != high) && (curr != 0)))
distSqr[curr] = PerpendicDistFromLineSqrd(path[curr], path[prior], path[next]);
@@ -717,35 +716,6 @@ namespace Clipper2Lib {
return result;
}
template <typename T>
inline bool Path2ContainsPath1(const Path<T>& path1, const Path<T>& path2)
{
// precondition: paths must not intersect, except for
// transient (and presumed 'micro') path intersections
PointInPolygonResult pip = PointInPolygonResult::IsOn;
for (const Point<T>& pt : path1)
{
switch (PointInPolygon(pt, path2))
{
case PointInPolygonResult::IsOutside:
if (pip == PointInPolygonResult::IsOutside) return false;
pip = PointInPolygonResult::IsOutside;
break;
case PointInPolygonResult::IsInside:
if (pip == PointInPolygonResult::IsInside) return true;
pip = PointInPolygonResult::IsInside;
break;
default:
break;
}
}
if (pip != PointInPolygonResult::IsInside) return false;
// result is likely true but check midpoint
Point<T> mp1 = GetBounds(path1).MidPoint();
return PointInPolygon(mp1, path2) == PointInPolygonResult::IsInside;
}
template <typename T>
inline void RDP(const Path<T> path, std::size_t begin,
std::size_t end, double epsSqrd, std::vector<bool>& flags)
@@ -39,7 +39,7 @@ private:
class Group {
public:
Paths64 paths_in;
std::optional<size_t> lowest_path_idx{};
std::optional<size_t> lowest_path_idx{};
bool is_reversed = false;
JoinType join_type;
EndType end_type;
@@ -100,7 +100,7 @@ public:
void AddPath(const Path64& path, JoinType jt_, EndType et_);
void AddPaths(const Paths64& paths, JoinType jt_, EndType et_);
void Clear() { groups_.clear(); norms.clear(); };
void Execute(double delta, Paths64& sols_64);
void Execute(double delta, PolyTree64& polytree);
void Execute(DeltaCallback64 delta_cb, Paths64& paths);
@@ -114,7 +114,7 @@ public:
bool PreserveCollinear() const { return preserve_collinear_; }
void PreserveCollinear(bool preserve_collinear){preserve_collinear_ = preserve_collinear;}
bool ReverseSolution() const { return reverse_solution_; }
void ReverseSolution(bool reverse_solution) {reverse_solution_ = reverse_solution;}
@@ -1,30 +0,0 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 6 December 2025 *
* Release : BETA RELEASE *
* Website : https://www.angusj.com *
* Copyright : Angus Johnson 2010-2025 *
* Purpose : Delaunay Triangulation *
* License : https://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
#ifndef CLIPPER_TRIANGULATION_H
#define CLIPPER_TRIANGULATION_H
#include <stack>
#include "clipper2/clipper.core.h"
#ifdef USINGZ
namespace Clipper2Lib_Z {
#else
namespace Clipper2Lib {
#endif
enum class TriangulateResult { success, fail, no_polygons, paths_intersect };
// Triangulate - this function will not accept intesecting paths
TriangulateResult Triangulate(const Paths64& pp, Paths64& solution, bool useDelaunay = true);
TriangulateResult Triangulate(const PathsD& pp, int decPlaces, PathsD& solution, bool useDelaunay = true);
} // Clipper2Lib namespace
#endif // CLIPPER_TRIANGULATION_H
@@ -1,6 +1,6 @@
#ifndef CLIPPER_VERSION_H
#define CLIPPER_VERSION_H
constexpr auto CLIPPER2_VERSION = "2.0.1";
constexpr auto CLIPPER2_VERSION = "1.5.2";
#endif // CLIPPER_VERSION_H
@@ -1,8 +1,8 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 5 November 2025 *
* Date : 17 September 2024 *
* Website : https://www.angusj.com *
* Copyright : Angus Johnson 2010-2025 *
* Copyright : Angus Johnson 2010-2024 *
* Purpose : This is the main polygon clipping module *
* License : https://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
@@ -10,8 +10,6 @@
#include "clipper2/clipper.engine.h"
#include "clipper2/clipper.h"
#include <stdexcept>
#include <new>
#include <oneapi/tbb/scalable_allocator.h>
// https://github.com/AngusJohnson/Clipper2/discussions/334
// #discussioncomment-4248602
@@ -29,30 +27,10 @@ namespace Clipper2Lib_Z {
namespace Clipper2Lib {
#endif
// Orca: tbbmalloc scales far better than the default heap when all slicing threads clip at once.
static void* NodeAlloc(size_t size)
{
if (void* p = scalable_malloc(size)) return p;
throw std::bad_alloc();
}
#define CLIPPER2_DEFINE_NODE_ALLOCATOR(T) \
void* T::operator new(size_t size) { return NodeAlloc(size); } \
void T::operator delete(void* ptr) noexcept { scalable_free(ptr); } \
void* T::operator new[](size_t size) { return NodeAlloc(size); } \
void T::operator delete[](void* ptr) noexcept { scalable_free(ptr); }
CLIPPER2_DEFINE_NODE_ALLOCATOR(Vertex)
CLIPPER2_DEFINE_NODE_ALLOCATOR(OutPt)
CLIPPER2_DEFINE_NODE_ALLOCATOR(OutRec)
CLIPPER2_DEFINE_NODE_ALLOCATOR(Active)
CLIPPER2_DEFINE_NODE_ALLOCATOR(LocalMinima)
CLIPPER2_DEFINE_NODE_ALLOCATOR(PolyPath)
#undef CLIPPER2_DEFINE_NODE_ALLOCATOR
static const Rect64 invalid_rect = Rect64(false);
// Every closed path (ie polygon) is made up of a series of vertices forming edge
// 'bounds' that alternate between ascending bounds (containing edges going up
// Every closed path (ie polygon) is made up of a series of vertices forming edge
// 'bounds' that alternate between ascending bounds (containing edges going up
// relative to the Y-axis) and descending bounds. 'Local Minima' refers to
// vertices where ascending and descending bounds join at the bottom, and
// 'Local Maxima' are where ascending and descending bounds join at the top.
@@ -504,7 +482,8 @@ namespace Clipper2Lib {
inline void SetOwner(OutRec* outrec, OutRec* new_owner)
{
//precondition1: new_owner is never null
new_owner->owner = GetRealOutRec(new_owner->owner);
while (new_owner->owner && !new_owner->owner->pts)
new_owner->owner = new_owner->owner->owner;
OutRec* tmp = new_owner;
while (tmp && tmp != outrec) tmp = tmp->owner;
if (tmp) new_owner->owner = outrec->owner;
@@ -557,9 +536,9 @@ namespace Clipper2Lib {
val = 1 - val; // toggle val
else
{
int i = CrossProductSign(op2->prev->pt, op2->pt, pt);
if (i == 0) return PointInPolygonResult::IsOn;
if ((i < 0) == is_above) val = 1 - val;
double d = CrossProduct(op2->prev->pt, op2->pt, pt);
if (d == 0) return PointInPolygonResult::IsOn;
if ((d < 0) == is_above) val = 1 - val;
}
is_above = !is_above;
op2 = op2->next;
@@ -567,9 +546,9 @@ namespace Clipper2Lib {
if (is_above != starting_above)
{
int i = CrossProductSign(op2->prev->pt, op2->pt, pt);
if (i == 0) return PointInPolygonResult::IsOn;
if ((i < 0) == is_above) val = 1 - val;
double d = CrossProduct(op2->prev->pt, op2->pt, pt);
if (d == 0) return PointInPolygonResult::IsOn;
if ((d < 0) == is_above) val = 1 - val;
}
if (val == 0) return PointInPolygonResult::IsOutside;
@@ -599,31 +578,30 @@ namespace Clipper2Lib {
return result;
}
inline bool Path2ContainsPath1(OutPt* op1, OutPt* op2)
inline bool Path1InsidePath2(OutPt* op1, OutPt* op2)
{
// this function accommodates rounding errors that
// can cause path micro intersections
PointInPolygonResult pip = PointInPolygonResult::IsOn;
// we need to make some accommodation for rounding errors
// so we won't jump if the first vertex is found outside
PointInPolygonResult result;
int outside_cnt = 0;
OutPt* op = op1;
do {
switch (PointInOpPolygon(op->pt, op2))
{
case PointInPolygonResult::IsOutside:
if (pip == PointInPolygonResult::IsOutside) return false;
pip = PointInPolygonResult::IsOutside;
break;
case PointInPolygonResult::IsInside:
if (pip == PointInPolygonResult::IsInside) return true;
pip = PointInPolygonResult::IsInside;
break;
default: break;
}
do
{
result = PointInOpPolygon(op->pt, op2);
if (result == PointInPolygonResult::IsOutside) ++outside_cnt;
else if (result == PointInPolygonResult::IsInside) --outside_cnt;
op = op->next;
} while (op != op1);
// result unclear, so try again using cleaned paths
return Path2ContainsPath1(GetCleanPath(op1), GetCleanPath(op2)); // (#973)
} while (op != op1 && std::abs(outside_cnt) < 2);
if (std::abs(outside_cnt) > 1) return (outside_cnt < 0);
// since path1's location is still equivocal, check its midpoint
Point64 mp = GetBounds(GetCleanPath(op1)).MidPoint();
Path64 path2 = GetCleanPath(op2);
return PointInPolygon(mp, path2) != PointInPolygonResult::IsOutside;
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void AddLocMin(LocalMinimaList& list,
Vertex& vert, PathType polytype, bool is_open)
{
@@ -1148,19 +1126,21 @@ namespace Clipper2Lib {
return newcomer.curr_x > resident.curr_x;
//get the turning direction a1.top, a2.bot, a2.top
int i = CrossProductSign(resident.top, newcomer.bot, newcomer.top);
if (i != 0) return i < 0;
double d = CrossProduct(resident.top, newcomer.bot, newcomer.top);
if (d != 0) return d < 0;
//edges must be collinear to get here
//for starting open paths, place them according to
//the direction they're about to turn
if (!IsMaxima(resident) && (resident.top.y > newcomer.top.y))
{
return (CrossProductSign(newcomer.bot, resident.top, NextVertex(resident)->pt) <= 0);
return CrossProduct(newcomer.bot,
resident.top, NextVertex(resident)->pt) <= 0;
}
else if (!IsMaxima(newcomer) && (newcomer.top.y > resident.top.y))
{
return (CrossProductSign(newcomer.bot, newcomer.top, NextVertex(newcomer)->pt) >= 0);
return CrossProduct(newcomer.bot,
newcomer.top, NextVertex(newcomer)->pt) >= 0;
}
int64_t y = newcomer.bot.y;
@@ -1175,7 +1155,7 @@ namespace Clipper2Lib {
resident.bot, resident.top)) return true;
else
//compare turning direction of the alternate bound
return (CrossProductSign(PrevPrevVertex(resident)->pt,
return (CrossProduct(PrevPrevVertex(resident)->pt,
newcomer.bot, PrevPrevVertex(newcomer)->pt) > 0) == newcomerIsLeft;
}
@@ -1585,7 +1565,7 @@ namespace Clipper2Lib {
FixSelfIntersects(outrec);
}
void ClipperBase::DoSplitOp (OutRec* outrec, OutPt* splitOp)
void ClipperBase::DoSplitOp(OutRec* outrec, OutPt* splitOp)
{
// splitOp.prev -> splitOp &&
// splitOp.next -> splitOp.next.next are intersecting
@@ -1594,7 +1574,7 @@ namespace Clipper2Lib {
outrec->pts = prevOp;
Point64 ip;
GetLineIntersectPt(prevOp->pt, splitOp->pt,
GetSegmentIntersectPt(prevOp->pt, splitOp->pt,
splitOp->next->pt, nextNextOp->pt, ip);
#ifdef USINGZ
@@ -1650,7 +1630,7 @@ namespace Clipper2Lib {
if (using_polytree_)
{
if (Path2ContainsPath1(prevOp, newOp))
if (Path1InsidePath2(prevOp, newOp))
{
newOr->splits = new OutRecList();
newOr->splits->emplace_back(outrec);
@@ -1672,32 +1652,19 @@ namespace Clipper2Lib {
void ClipperBase::FixSelfIntersects(OutRec* outrec)
{
OutPt* op2 = outrec->pts;
if (op2->prev == op2->next->next)
return; // because triangles can't self-intersect
for (; ; )
{
// triangles can't self-intersect
if (op2->prev == op2->next->next) break;
if (SegmentsIntersect(op2->prev->pt,
op2->pt, op2->next->pt, op2->next->next->pt))
{
if (SegmentsIntersect(op2->prev->pt,
op2->pt, op2->next->next->pt, op2->next->next->next->pt))
{
// adjacent intersections (ie a micro self-intersections)
op2 = DuplicateOp(op2, false);
op2->pt = op2->next->next->next->pt;
op2 = op2->next;
}
else
{
if (op2 == outrec->pts || op2->next == outrec->pts)
outrec->pts = outrec->pts->prev;
DoSplitOp(outrec, op2);
if (!outrec->pts) break;
op2 = outrec->pts;
if (op2->prev == op2->next->next)
break; // again, because triangles can't self-intersect
continue;
}
if (op2 == outrec->pts || op2->next == outrec->pts)
outrec->pts = outrec->pts->prev;
DoSplitOp(outrec, op2);
if (!outrec->pts) break;
op2 = outrec->pts;
continue;
}
else
op2 = op2->next;
@@ -1838,14 +1805,14 @@ namespace Clipper2Lib {
switch (fillrule_)
{
case FillRule::Positive:
if (edge_c->wind_cnt != 1) return;
case FillRule::Positive:
if (edge_c->wind_cnt != 1) return;
break;
case FillRule::Negative:
if (edge_c->wind_cnt != -1) return;
case FillRule::Negative:
if (edge_c->wind_cnt != -1) return;
break;
default:
if (std::abs(edge_c->wind_cnt) != 1) return;
default:
if (std::abs(edge_c->wind_cnt) != 1) return;
}
#ifdef USINGZ
@@ -1966,7 +1933,7 @@ namespace Clipper2Lib {
const bool e1_windcnt_in_01 = old_e1_windcnt == 0 || old_e1_windcnt == 1;
const bool e2_windcnt_in_01 = old_e2_windcnt == 0 || old_e2_windcnt == 1;
if ((!IsHotEdge(e1) && !e1_windcnt_in_01) ||
if ((!IsHotEdge(e1) && !e1_windcnt_in_01) ||
(!IsHotEdge(e2) && !e2_windcnt_in_01))
return;
@@ -2145,9 +2112,10 @@ namespace Clipper2Lib {
e->prev_in_sel = e->prev_in_ael;
e->next_in_sel = e->next_in_ael;
e->jump = e->next_in_sel;
// it is safe to ignore 'joined' edges here because
// if necessary they will be split in IntersectEdges()
e->curr_x = TopX(*e, top_y);
if (e->join_with == JoinWith::Left)
e->curr_x = e->prev_in_ael->curr_x; // also avoids complications
else
e->curr_x = TopX(*e, top_y);
e = e->next_in_ael;
}
}
@@ -2294,14 +2262,15 @@ namespace Clipper2Lib {
void MoveSplits(OutRec* fromOr, OutRec* toOr)
{
if (!fromOr->splits) return;
if (!toOr->splits) toOr->splits = new OutRecList();
OutRecList::iterator orIter = fromOr->splits->begin();
for (; orIter != fromOr->splits->end(); ++orIter)
if (toOr != *orIter) // #987
toOr->splits->emplace_back(*orIter);
toOr->splits->emplace_back(*orIter);
fromOr->splits->clear();
}
void ClipperBase::ProcessHorzJoins()
{
for (const HorzJoin& j : horz_join_list_)
@@ -2330,8 +2299,8 @@ namespace Clipper2Lib {
}
if (using_polytree_) //#498, #520, #584, D#576, #618
{
if (Path2ContainsPath1(or1->pts, or2->pts))
{
if (Path1InsidePath2(or1->pts, or2->pts))
{
//swap or1's & or2's pts
OutPt* tmp = or1->pts;
@@ -2342,7 +2311,7 @@ namespace Clipper2Lib {
//or2 is now inside or1
or2->owner = or1;
}
else if (Path2ContainsPath1(or2->pts, or1->pts))
else if (Path1InsidePath2(or2->pts, or1->pts))
{
or2->owner = or1;
}
@@ -2355,14 +2324,13 @@ namespace Clipper2Lib {
else
or2->owner = or1;
}
else // joining, not splitting
else
{
or2->pts = nullptr;
if (using_polytree_)
{
SetOwner(or2, or1);
if (or2->splits)
MoveSplits(or2, or1); //#618
MoveSplits(or2, or1); //#618
}
else
or2->owner = or1;
@@ -2382,7 +2350,7 @@ namespace Clipper2Lib {
void ClipperBase::AddNewIntersectNode(Active& e1, Active& e2, int64_t top_y)
{
Point64 ip;
if (!GetLineIntersectPt(e1.bot, e1.top, e2.bot, e2.top, ip))
if (!GetSegmentIntersectPt(e1.bot, e1.top, e2.bot, e2.top, ip))
ip = Point64(e1.curr_x, top_y); //parallel edges
//rounding errors can occasionally place the calculated intersection
@@ -2966,28 +2934,22 @@ namespace Clipper2Lib {
bool ClipperBase::CheckSplitOwner(OutRec* outrec, OutRecList* splits)
{
// nb: use indexing (not an iterator) in case 'splits' is modified inside this loop (#1029)
for (size_t idx = 0; idx < splits->size(); ++idx)
for (auto split : *splits)
{
OutRec* split = (*splits)[idx];
if (!split->pts && split->splits &&
CheckSplitOwner(outrec, split->splits)) return true; //#942
split = GetRealOutRec(split);
if (!split || split == outrec || split->recursive_split == outrec) continue;
if(!split || split == outrec || split->recursive_split == outrec) continue;
split->recursive_split = outrec; // prevent infinite loops
if (split->splits && CheckSplitOwner(outrec, split->splits))
return true;
if (!CheckBounds(split) || !split->bounds.Contains(outrec->bounds) ||
!Path2ContainsPath1(outrec->pts, split->pts)) continue;
if (!IsValidOwner(outrec, split)) // split is owned by outrec! (#957)
split->owner = outrec->owner;
outrec->owner = split;
return true;
return true;
else if (CheckBounds(split) &&
IsValidOwner(outrec, split) &&
split->bounds.Contains(outrec->bounds) &&
Path1InsidePath2(outrec->pts, split->pts))
{
outrec->owner = split; //found in split
return true;
}
}
return false;
}
@@ -2998,12 +2960,13 @@ namespace Clipper2Lib {
// post-condition: if a valid path, outrec will have a polypath
if (outrec->polypath || outrec->bounds.IsEmpty()) return;
while (outrec->owner)
{
if (outrec->owner->splits && CheckSplitOwner(outrec, outrec->owner->splits)) break;
if (outrec->owner->pts && CheckBounds(outrec->owner) &&
outrec->owner->bounds.Contains(outrec->bounds) &&
Path2ContainsPath1(outrec->pts, outrec->owner->pts)) break;
Path1InsidePath2(outrec->pts, outrec->owner->pts)) break;
outrec->owner = outrec->owner->owner;
}
@@ -3066,7 +3029,6 @@ namespace Clipper2Lib {
{
OutRec* outrec = outrec_list_[i];
if (!outrec || !outrec->pts) continue;
if (outrec->is_open)
{
Path64 path;
@@ -1,6 +1,6 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 11 October 2025 *
* Date : 22 January 2025 *
* Website : https://www.angusj.com *
* Copyright : Angus Johnson 2010-2025 *
* Purpose : Path Offset (Inflate/Shrink) *
@@ -37,35 +37,29 @@ const double arc_const = 0.002; // <-- 1/500
// Miscellaneous methods
//------------------------------------------------------------------------------
void GetLowestClosedPathInfo(const Paths64& paths, std::optional<size_t>& idx, bool& is_neg_area)
std::optional<size_t> GetLowestClosedPathIdx(const Paths64& paths)
{
idx.reset();
std::optional<size_t> result;
Point64 botPt = Point64(INT64_MAX, INT64_MIN);
for (size_t i = 0; i < paths.size(); ++i)
{
double a = MAX_DBL;
for (const Point64& pt : paths[i])
{
if ((pt.y < botPt.y) ||
((pt.y == botPt.y) && (pt.x >= botPt.x))) continue;
if (a == MAX_DBL)
{
a = Area(paths[i]);
if (a == 0) break; // invalid closed path, so break from inner loop
is_neg_area = a < 0;
}
idx = i;
result = i;
botPt.x = pt.x;
botPt.y = pt.y;
}
}
return result;
}
inline double Hypot(double x, double y)
{
// given that this is an internal function, and given the x and y parameters
// will always be coordinate values (or the difference between coordinate values),
// x and y should always be within INT64_MIN to INT64_MAX. Consequently,
// x and y should always be within INT64_MIN to INT64_MAX. Consequently,
// there should be no risk that the following computation will overflow
// see https://stackoverflow.com/a/32436148/359538
return std::sqrt(x * x + y * y);
@@ -151,16 +145,15 @@ ClipperOffset::Group::Group(const Paths64& _paths, JoinType _join_type, EndType
if (end_type == EndType::Polygon)
{
bool is_neg_area;
GetLowestClosedPathInfo(paths_in, lowest_path_idx, is_neg_area);
lowest_path_idx = GetLowestClosedPathIdx(paths_in);
// the lowermost path must be an outer path, so if its orientation is negative,
// then flag the whole group is 'reversed' (will negate delta etc.)
// as this is much more efficient than reversing every path.
is_reversed = lowest_path_idx.has_value() && is_neg_area;
is_reversed = (lowest_path_idx.has_value()) && Area(paths_in[lowest_path_idx.value()]) < 0;
}
else
{
lowest_path_idx.reset();
lowest_path_idx = std::nullopt;
is_reversed = false;
}
}
@@ -243,7 +236,7 @@ void ClipperOffset::DoSquare(const Path64& path, size_t j, size_t k)
{
PointD pt4 = PointD(pt3.x + vec.x * group_delta_, pt3.y + vec.y * group_delta_);
PointD pt = ptQ;
GetLineIntersectPt(pt1, pt2, pt3, pt4, pt);
GetSegmentIntersectPt(pt1, pt2, pt3, pt4, pt);
//get the second intersect point through reflecion
path_out.emplace_back(ReflectPoint(pt, ptQ));
path_out.emplace_back(pt);
@@ -252,7 +245,7 @@ void ClipperOffset::DoSquare(const Path64& path, size_t j, size_t k)
{
PointD pt4 = GetPerpendicD(path[j], norms[k], group_delta_);
PointD pt = ptQ;
GetLineIntersectPt(pt1, pt2, pt3, pt4, pt);
GetSegmentIntersectPt(pt1, pt2, pt3, pt4, pt);
path_out.emplace_back(pt);
//get the second intersect point through reflecion
path_out.emplace_back(ReflectPoint(pt, ptQ));
@@ -298,8 +291,7 @@ void ClipperOffset::DoRound(const Path64& path, size_t j, size_t k, double angle
#else
path_out.emplace_back(pt.x + offsetVec.x, pt.y + offsetVec.y);
#endif
// Orca: round the step count like Clipper1 did, so round offsets keep their vertices.
int steps = std::max(static_cast<int>(std::round(steps_per_rad_ * std::abs(angle))), 1);
int steps = static_cast<int>(std::ceil(steps_per_rad_ * std::abs(angle))); // #448, #456
for (int i = 1; i < steps; ++i) // ie 1 less than steps
{
offsetVec = PointD(offsetVec.x * step_cos_ - step_sin_ * offsetVec.y,
@@ -341,9 +333,9 @@ void ClipperOffset::OffsetPoint(Group& group, const Path64& path, size_t j, size
if (cos_a > -0.999 && (sin_a * group_delta_ < 0)) // test for concavity first (#593)
{
// is concave
// by far the simplest way to construct concave joins, especially those joining very
// short segments, is to insert 3 points that produce negative regions. These regions
// will be removed later by the finishing union operation. This is also the best way
// by far the simplest way to construct concave joins, especially those joining very
// short segments, is to insert 3 points that produce negative regions. These regions
// will be removed later by the finishing union operation. This is also the best way
// to ensure that path reversals (ie over-shrunk paths) are removed.
#ifdef USINGZ
path_out.emplace_back(GetPerpendic(path[j], norms[k], group_delta_), path[j].z);
@@ -374,31 +366,11 @@ void ClipperOffset::OffsetPoint(Group& group, const Path64& path, size_t j, size
DoSquare(path, j, k);
}
// Orca: join concave corners at the crossing of both edge offsets where safe, 3-point loops make dense inward offsets slow.
static bool OffsetConcaveCrossing(const Path64& path, const PathD& norms, size_t j, size_t k, size_t next,
double delta, Path64& path_out)
{
const double sin_a = CrossProduct(norms[j], norms[k]);
const double cos_a = DotProduct(norms[j], norms[k]);
if (cos_a <= -0.999 || sin_a * delta >= 0) return false;
const double x = std::fabs(delta * sin_a) / (1 + cos_a);
if (4 * x * x > DistanceSqr(path[k], path[j]) || 4 * x * x > DistanceSqr(path[j], path[next])) return false;
const double q = delta / (1 + cos_a);
#ifdef USINGZ
path_out.emplace_back(path[j].x + (norms[k].x + norms[j].x) * q, path[j].y + (norms[k].y + norms[j].y) * q, path[j].z);
#else
path_out.emplace_back(path[j].x + (norms[k].x + norms[j].x) * q, path[j].y + (norms[k].y + norms[j].y) * q);
#endif
return true;
}
void ClipperOffset::OffsetPolygon(Group& group, const Path64& path)
{
path_out.clear();
for (Path64::size_type j = 0, k = path.size() - 1; j < path.size(); k = j, ++j)
if (deltaCallback64_ || path[j] == path[k] ||
!OffsetConcaveCrossing(path, norms, j, k, j + 1 == path.size() ? 0 : j + 1, group_delta_, path_out))
OffsetPoint(group, path, j, k);
OffsetPoint(group, path, j, k);
solution->emplace_back(path_out);
}
@@ -408,7 +380,7 @@ void ClipperOffset::OffsetOpenJoined(Group& group, const Path64& path)
Path64 reverse_path(path);
std::reverse(reverse_path.begin(), reverse_path.end());
//rebuild normals
//rebuild normals
std::reverse(norms.begin(), norms.end());
norms.emplace_back(norms[0]);
norms.erase(norms.begin());
@@ -629,10 +601,10 @@ void ClipperOffset::ExecuteInternal(double delta)
if (!solution->size()) return;
bool paths_reversed = CheckReverseOrientation();
bool paths_reversed = CheckReverseOrientation();
//clean up self-intersections ...
Clipper64 c;
c.PreserveCollinear(preserve_collinear_);
c.PreserveCollinear(false);
//the solution should retain the orientation of the input
c.ReverseSolution(reverse_solution_ != paths_reversed);
#ifdef USINGZ
@@ -1,8 +1,8 @@
/*******************************************************************************
* Author : Angus Johnson *
* Date : 11 October 2025 *
* Date : 5 July 2024 *
* Website : https://www.angusj.com *
* Copyright : Angus Johnson 2010-2025 *
* Copyright : Angus Johnson 2010-2024 *
* Purpose : FAST rectangular clipping *
* License : https://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/
@@ -77,8 +77,8 @@ namespace Clipper2Lib {
bool GetSegmentIntersection(const Point64& p1,
const Point64& p2, const Point64& p3, const Point64& p4, Point64& ip)
{
int res1 = CrossProductSign(p1, p3, p4);
int res2 = CrossProductSign(p2, p3, p4);
double res1 = CrossProduct(p1, p3, p4);
double res2 = CrossProduct(p2, p3, p4);
if (res1 == 0)
{
ip = p1;
@@ -97,8 +97,8 @@ namespace Clipper2Lib {
}
if ((res1 > 0) == (res2 > 0)) return false;
int res3 = CrossProductSign(p3, p1, p2);
int res4 = CrossProductSign(p4, p1, p2);
double res3 = CrossProduct(p3, p1, p2);
double res4 = CrossProduct(p4, p1, p2);
if (res3 == 0)
{
ip = p3;
@@ -116,7 +116,7 @@ namespace Clipper2Lib {
if ((res3 > 0) == (res4 > 0)) return false;
// segments must intersect to get here
return GetLineIntersectPt(p1, p2, p3, p4, ip);
return GetSegmentIntersectPt(p1, p2, p3, p4, ip);
}
inline bool GetIntersection(const Path64& rectPath,
@@ -227,7 +227,7 @@ namespace Clipper2Lib {
const Point64& prev_pt, const Point64& curr_pt, const Point64& rect_mp)
{
if (AreOpposites(prev, curr))
return CrossProductSign(prev_pt, rect_mp, curr_pt) < 0;
return CrossProduct(prev_pt, rect_mp, curr_pt) < 0;
else
return HeadingClockwise(prev, curr);
}
File diff suppressed because it is too large Load Diff
@@ -6,4 +6,3 @@
#include "clipper.engine.cpp"
#include "clipper.offset.cpp"
#include "clipper.rectclip.cpp"
#include "clipper.triangulation.cpp"
@@ -132,14 +132,14 @@ template<>
inline void offset(Slic3r::ExPolygon& sh, coord_t distance, const PolygonTag&)
{
#define DISABLE_BOOST_OFFSET
auto res = Slic3r::offset_ex(sh, distance, Slic3r::jtSquare);
auto res = Slic3r::offset_ex(sh, distance, Slic3r::ClipperLib::jtSquare);
if (!res.empty()) sh = res.front();
}
template<>
inline void offset(Slic3r::Polygon& sh, coord_t distance, const PathTag&)
{
auto res = Slic3r::offset(sh, distance, Slic3r::jtSquare);
auto res = Slic3r::offset(sh, distance, Slic3r::ClipperLib::jtSquare);
if (!res.empty()) sh = res.front();
}
+5 -1
View File
@@ -19,7 +19,11 @@ if(Qhull_FOUND)
message(STATUS "Using qhull from system.")
if(SLIC3R_STATIC)
slic3r_remap_configs("Qhull::qhullcpp;Qhull::qhullstatic_r" RelWithDebInfo Release)
target_link_libraries(qhull INTERFACE Qhull::qhullcpp Qhull::qhullstatic_r)
if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
target_link_libraries(qhull INTERFACE Qhull::qhullcpp_d Qhull::qhullstatic_rd)
else()
target_link_libraries(qhull INTERFACE Qhull::qhullcpp Qhull::qhullstatic_r)
endif()
else()
slic3r_remap_configs("Qhull::qhullcpp;Qhull::qhull_r" RelWithDebInfo Release)
target_link_libraries(qhull INTERFACE Qhull::qhullcpp Qhull::qhull_r)
-131
View File
@@ -1,131 +0,0 @@
# Polygon Clipping — High Level Design
## Purpose and scope
Almost every stage of slicing works on 2D regions: slices, perimeters, infill
areas, bridges, supports and brims are all produced by boolean operations and
offsets on polygons. libslic3r does this through two interfaces, both built on
the Clipper2 library vendored in `deps_src/clipper2`:
- `ClipperUtils` (`src/libslic3r/ClipperUtils.hpp`) takes and returns Slic3r
geometry: `Polygon(s)`, `ExPolygon(s)`, `Polyline(s)`, `Lines` and
`Surfaces`. It provides unions, intersections, differences and xor, closed
and open offsets, morphological opening and closing, variable width offsets
and polyline clipping.
- `ClipperZUtils` (`src/libslic3r/ClipperZUtils.hpp`) clips paths whose
vertices carry a Z value, which callers use to tag vertices with a source
index or an extrusion width.
No other code calls Clipper2.
`ClipperUtils` declares its own `JoinType`, `EndType`, `PolyFillType` and
`ClipType` enums and maps them to Clipper2's. Every call builds its own
Clipper2 objects and shares no state, so slicing threads can clip
concurrently.
Clipping is one of the largest costs of slicing, and nearly all of it goes
through `ClipperUtils`. The layer is therefore designed for throughput as much
as for predictable geometry.
## Vendored Clipper2
`deps_src/clipper2` builds the static target `Clipper2`. It carries four
changes to the upstream sources that must be carried over when Clipper2 is
updated. The namespace switch sits at the top of every header and source, the
other three are marked with `Orca:` comments.
| Change | Files | Why |
| --- | --- | --- |
| Z build in its own namespace | all headers and sources, `clipper2_z.cpp`, `clipper2_z.hpp` | The library is compiled a second time with `USINGZ` in namespace `Clipper2Lib_Z`, so the 2D and the Z variants link into one binary. |
| Engine nodes from tbbmalloc | `clipper.engine.h`, `clipper.engine.cpp` | Vertices, active edges, output points and records, local minima and `PolyTree` nodes are allocated one by one. `CLIPPER2_NODE_ALLOCATOR` routes them through `scalable_malloc`, because the default heap does not scale when all slicing threads clip at once. |
| Concave joins at the edge crossing | `clipper.offset.cpp` | For closed paths, a concave corner is joined at the crossing of the two offset edges when that point lies within half of both adjacent edges. The upstream 3-point loop makes inward offsets of dense curves very slow to union. |
| Rounded arc steps | `clipper.offset.cpp` | Round joins use the rounded number of steps, not the ceiling, which keeps the vertex count of round offsets that the rest of the code is tuned for. |
## ClipperUtils semantics
The callers of `ClipperUtils` rely on a fixed set of behaviours. Where
Clipper2 behaves differently by default, the wrapper adjusts it.
### Booleans
- The fill rule is non-zero unless the function takes a `PolyFillType`. One
rule applies to both subject and clip; Clipper2 has no per-operand rule.
- Collinear vertices are removed from the result. Clipper2 keeps them by
default, so every boolean sets `PreserveCollinear(false)`.
- Outer contours are CCW and holes are CW. No output contour touches
itself: where one would pass twice through a vertex, it is split there into
two contours.
- `ExPolygons` results are built from one `PolyTree64` pass. An island inside
a hole becomes an `ExPolygon` of its own.
- `ApplySafetyOffset::Yes` grows the clip polygons by `ClipperSafetyOffset`
before an intersection or a difference, so that edges shared by subject and
clip do not leave slivers.
- Open polylines are clipped with the non-zero rule and keep their direction.
### Offsets
- Before offsetting, input vertices closer than
`ClipperOffsetShortestEdgeFactor` × |delta| to the previously kept vertex
are dropped. This bounds the work on dense contours, and the error it
introduces is far below the offset distance.
- The miter limit is at least 2. For `jtRound`, a positive `miterLimit`
argument is the arc tolerance, capped at |delta| / 4, and 0.25 is used
otherwise. Other joins use the smaller of 0.25 and |delta| / 4 for round end
caps.
- A single `Polygon` keeps its orientation: a CCW polygon grows with a
positive delta, a CW polygon is a hole and shrinks.
- `Polygons` follow the same rule per path. When every CW path lies strictly
inside the bounding box of a CCW path, which is the usual case of contours
with their holes, all paths are offset in one Clipper2 group. Otherwise
each path is offset on its own and the results are united, with the
non-zero rule when growing and the positive rule when shrinking.
- `ExPolygons` and `Surfaces` are offset as one group after the contours are
oriented CCW and the holes CW, whatever their input orientation.
- Zero-area paths vanish under a negative offset instead of growing.
- Polyline offsets use the requested end type. Clipper2 already unites the
result, so no further union is done.
### Coordinate range
Clipper2 computes intersections and slopes in doubles, which hold integers
exactly only up to 2^53 (about 9e15 units, 9,000 km). Geometry passed to
`ClipperUtils` must stay well inside that range; near the int64 limit the
results shift by hundreds of units. This is why the arrange `InfiniteBed` is a
box of ±2^50 units around its centre rather than libnest2d's infinite box,
which reaches ±2.3e18.
## ClipperZUtils
`ZPoint` is a `Vec3crd`, and a `ZPath` is a vector of them.
`clip_zpaths()` runs one boolean with the non-zero rule on the Clipper2 Z
build. The subject may be open, the clip is closed, and the result lists the
closed paths before the open ones.
The Z of each output vertex follows these rules:
- An input vertex keeps its Z.
- An intersection that lies on an end point of one of the two crossing edges
takes that end point's Z, preferring the subject edge.
- Any other intersection gets its Z from the callback, which receives both
crossing edges, the subject edge first.
Clipper2 calls the callback only when it creates an output vertex at an
intersection, not for every crossing it processes. A callback that records
intersections, like `ClipperZIntersectionVisitor`, therefore sees only those.
The users are:
| User | Z carries |
| --- | --- |
| `Algorithm::wave_seeds()` (region expansion) | source and boundary index; intersections get a negative index into the visitor's list of crossing pairs |
| `Algorithm::split_line()` | index of the source vertex; an intersection gets the negated index of its source edge, so the pieces can be put back in path order |
| `PerimeterGenerator` overhang and top-surface clipping of Arachne walls | extrusion width, interpolated along the edge at intersections |
| Tree support anchors in `SupportCommon` | index of the source contour, -1 at intersections |
| `extrusion_paths_append()` | extrusion width, turned into extrusion paths |
## Testing
`tests/libslic3r/test_clipper_utils.cpp` and `test_clipper_offset.cpp` cover
the wrapper's booleans, orientation and offset rules. The perimeter, support
and region expansion users are exercised by the slicing tests in
`tests/fff_print`.
-93
View File
@@ -1,93 +0,0 @@
# 3D Scene Benchmark: High Level Design
## Why it exists
Rendering changes, such as the realistic view, shadows or SSAO, need a number to compare
before and after, and user reports of a slow viewport need a way to say how slow. The FPS
overlay and the render timings overlay show live values while someone drags the camera,
which varies from run to run with the model, the path of the mouse and the view.
The benchmark renders a fixed model along a fixed camera path in both 3D views, so two
runs on the same machine differ only by the code or the settings, and prints a report that
can be pasted into an issue.
## What it does
`run_scene_benchmark()` in `src/slic3r/GUI/SceneBenchmark.cpp` is reached from Help >
Benchmark 3D Scene, the command palette and Preferences > Graphics. After a confirmation
it starts a new project, which asks to save the current one if needed, loads the
OrcaSliced Combo handy model and arranges it. A small dialog in a corner of the 3D view
then shows the progress; every other window is disabled until the run ends, so a click
cannot change the scene being measured. Cancel or Esc stops the run.
The run goes through these stages, driven by a timer while it waits and by idle events
while it renders:
1. Loading: waits until the UI job worker is idle, so the arrange job has moved the
objects. The orbit target is the center of the objects on the current plate, and the
base zoom fits their bounding box in the viewport.
2. Prepare: renders the scene in the Prepare view.
3. Slicing: slices the plate and switches to Preview, then waits for the G-code preview
to load. If slicing fails, the report holds Prepare alone.
4. Preview: renders the scene in the Preview view, with the slicing progress notification
hidden.
5. Layers: renders the Preview view again while the layer slider moves, which is what
makes dragging it feel slow on large prints.
The dialog then shows the report, with a button to copy it. A scene cut short, because its
view was hidden, is left out of the report.
## Rendering a scene
Each scene renders 30 warm-up frames, then the camera path twice, 360 frames each time.
- The first pass times the frames. A frame's time is the interval between the starts of
consecutive benchmark frames, so it includes the event loop between them.
- The second pass averages the render timings. The frame profiler flushes the GL command
queue after each section, which slows a frame down, so it only runs in this pass.
`FrameProfiler::start_averaging()` flags every frame begun afterwards, and
`finish_averaging()` waits for the flagged frames still on the GPU and returns the mean
CPU and GPU time of each section per profiled frame.
- A section's GPU time is taken between a timestamp before its commands and one after
them. The first is only sent along with those commands, so when the GPU finishes a
section before the CPU has issued the next one, the wait counts in neither.
The dialog renders one frame per idle event by calling `GLCanvas3D::render()`, which
redraws the whole scene. While `GLCanvas3D::set_benchmarking()` is on, the canvas does not
render from its own idle handler, so no other frame is drawn in between, and it skips the
picking pass and the FPS and render timings overlays, which depend on the mouse and on
preferences. The FPS cap does not apply, since it only paces idle redraws.
VSync is turned off for the scene through `wxGLCanvas::SetSwapInterval(0)`, so the frame
rate is what the GPU and CPU can reach rather than the display's refresh rate, and the
previous interval is restored afterwards. When the platform cannot report the current
interval (EGL), it is left as it is and the report says so.
The camera path makes two turns around the target while the view rises three times from
25 degrees below the plate to 85 degrees above it and the zoom goes twice between 0.6 and
1.4 times the base zoom. The camera stays at the default distance, so the perspective is
the same in every run. The camera the scene started with is restored at its end.
The Layers scene holds the camera at the start of that path and moves the top of the layer
slider instead, from the last layer down to the first and back up in each pass. It goes
through `IMSlider::SetHigherValue()`, as a drag does, so every frame applies a new layer
range to the toolpaths and the objects before drawing them, including a new shadow map when
the shadows are static. Its warm-up frames lead into the start of the path, so the slider
moves in every frame. The slider position it started from is restored at its end.
## The report
The report is plain English text, so it reads the same in every language:
- The version and build commit, the GPU and OpenGL version, the viewport size and camera
type, and the graphics settings that change the cost of a frame: MSAA samples as read
from the framebuffer, FXAA, the scene cache, VSync and the realistic view options.
- The printer and process presets the model was sliced with, marked when they have
unsaved changes, and the toolpath vertices and layers they produced, since the Preview
scenes cost more with more toolpaths.
- For each scene, the average FPS and the average, median, 95th percentile, 99th
percentile and maximum frame time. Percentiles are nearest-rank, so each is a measured
frame (`frame_time_stats()`).
- For each scene, the render timings table: the CPU and GPU milliseconds of each section
of a frame, and their total. Without timer queries (OpenGL 3.3 or `ARB_timer_query`) the
table says that the driver does not support them.
-105
View File
@@ -1,105 +0,0 @@
# Section view — High Level Design
## Purpose and scope
Section view hides whatever lies between the camera and a plane, so the user can look inside
objects in Prepare and in the assembly view, and inside the toolpaths in Preview. It is a view
setting: it changes nothing in the model, the slice or the project file, and it does not reach
plate thumbnails.
The user controls it from the section button of the canvas toolbar in the bottom left corner of
the 3D view. The button opens a panel above it with a slider for the depth of the cut, a "Set
viewing angle" button that turns the plane to face the camera at the same depth, and a button that
resets the depth to zero. The panel is an ordinary overlay window, not a popup, so the scene keeps
taking clicks and drags while it is open; the button or Esc closes it again. Esc closes the panel
before it closes a gizmo or clears the selection. The button is highlighted
while a section cuts the scene and has shortcuts of its own: the mouse wheel over it moves the
plane, a right click switches the section off and back on, and a middle click sets the viewing
angle. Alt + mouse wheel moves the plane anywhere in the 3D view, with or without a gizmo open.
## State
Prepare and Preview share one section view, so a cut made in either tab is the same cut in the
other. The assembly view, whose objects sit apart from their places on the plate, and the Design
tab keep their own. The section itself is two values.
- **Ratio**, from 0 to 1. At 0 the section is off. As the ratio grows, the plane sweeps the
sphere around the objects, from its side facing the camera to the opposite side, so at 1
everything is cut away.
- **Normal**, taken from the camera direction the first time the section is switched on, and
again whenever the user sets the viewing angle. The plane keeps that orientation while the
camera orbits and while the section is off, so the cut face can be seen from any side and
bringing the depth back to 0 does not lose the angle.
The ratio in use when the section is switched off is kept, and the right click on the button
brings the section back at that ratio, which restores the same cut. Whether the panel is open is
shared along with the section.
The sphere is recomputed every frame from the volumes of the canvas the section view belongs to:
the objects on the current plate, or every object when that plate is empty. Preview holds no
objects of its own, so it places the plane across the volumes of Prepare, which makes it cut the
toolpaths exactly where Prepare cuts the objects. Only G-code opened on its own, without objects,
is measured by its toolpaths. In the assembly view the sphere is around the whole assembly. The
ratio therefore keeps its meaning when objects move or the user switches plates. It is not a
fixed position in world space.
Only the tab on screen can change the section, and switching tabs redraws the whole scene and
closes the open gizmo, so neither tab ever shows a stale cut.
## Where the plane applies
`GLCanvas3D::_get_section_view_plane()` turns the state into a plane in the convention of
`ClippingPlane::is_point_clipped()`. Everything that draws or picks the scene reads that plane.
- **Volumes.** The plane goes to the `clipping_plane` uniform of the volume shaders. The same
uniform serves the gouraud, phong and X-ray passes and the colour picking pass.
- **Cut faces.** Clipping only discards fragments, which would leave the cut volumes hollow.
`_render_section_view_caps()` draws their cut faces with one `MeshClipper` per model part the
plane passes through. A clipper recomputes its face only when the plane or the volume moves.
Modifiers, the wipe tower and SLA auxiliaries get no face.
- **Toolpaths.** libvgcode takes the plane through `Viewer::set_clipping_plane()`. It draws each
extrusion as only the faces of a diamond-section prism that turn towards the camera, so
discarding the fragments on the clipped side would leave open shells. Instead, the segment
shader follows the view ray from a fragment that is cut away to the plane. When the
extrusion's diamond section still holds that point, the fragment is shaded as the cut face, lit
as the plane faces; otherwise it is discarded. The cut face keeps the depth of the fragment it
replaces, which is safe: along that ray everything else still shown lies behind the plane. The
shader writes no `gl_FragDepth`, so early depth testing survives. Option markers are cut away
whole, by their centres. The shadow casters draw with a program of their own, which takes the
plane and discards the fragments on the clipped side, so what is cut away casts no shadow either.
Their cut faces are not drawn, since the part left behind casts the shadow of its own section.
Preview shells are drawn by another shader and are not clipped.
- **Picking.** `get_raycaster_clipping_plane()` returns the same plane, so hover, selection and
the perspective pan anchor ignore what the user cannot see.
## Gizmos
A gizmo that clips its object itself owns the gizmo data pool's `ObjectClipper`, and its plane
replaces the canvas section while the gizmo is open. `GLGizmosManager::get_clipping_plane()`
reports that plane, or nothing when no open gizmo has a clipper. There are two cases.
- **Painting tools and brim ears** show the canvas section on the object they edit.
`GLGizmosManager::update_section_view()` copies the ratio and normal into their clipper
whenever the pool is updated or the section changes. The clipper then places the plane across
the edited instance, which is the only object shown. The painting tools keep clipping their
own triangles, raycasts and cut face through it. Brim ears always cut horizontally from the
top, because the ears sit on the plate. At ratio 0 the clipper holds no plane at all, so the
raycasts are not clipped.
- **Cut and mesh boolean** use the clipper for their own purposes, so the canvas section is
suspended while they are open.
Every other gizmo, including move, rotate and scale, leaves the canvas section in place.
## Alt + mouse wheel
The canvas handles Alt + wheel after the gizmos had their turn, so it works the same in every
tab and with any gizmo open. On Windows, releasing Alt when no key was pressed since it went down
opens the window menu, and a wheel turn does not count as a key. Under the custom title bar that
menu is invisible, yet it takes the keyboard and the next click, which looks like a frozen 3D
view. After Alt + wheel the canvas therefore consumes the Alt release instead of passing it on.
## Redraw
The button and the panel are part of the ImGui overlay, which is built after the frame's scene is
drawn. A change to the section therefore marks the scene dirty and asks for one more frame. The
cached scene is never reused across a change.
-1
View File
@@ -180,7 +180,6 @@ src/slic3r/GUI/PrivacyUpdateDialog.cpp
src/slic3r/GUI/PublishDialog.cpp
src/slic3r/GUI/PublishSettingsDialog.cpp
src/slic3r/GUI/SavePresetDialog.cpp
src/slic3r/GUI/SceneBenchmark.cpp
src/slic3r/GUI/Search.cpp
src/slic3r/GUI/SettingsIndex.cpp
src/slic3r/GUI/SpeedDialDialog.cpp
-1
View File
@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 36 36"><circle cx="17.5" cy="17.5" r="17.5" style="fill:#fafafa;"/><polygon points="13.82,11.62 21.18,15.88 21.18,23.38 13.82,19.12" style="fill:#2b3436; fill-opacity:.55;"/><polygon points="8.75,14.55 16.11,18.80 16.11,26.30 8.75,22.05" style="fill:none; stroke:#2b3436; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polyline points="8.75,14.55 12.43,12.43 19.79,16.68 16.11,18.80" style="fill:none; stroke:#2b3436; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polyline points="19.79,16.68 19.79,24.18 16.11,26.30" style="fill:none; stroke:#2b3436; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polygon points="13.82,11.62 17.50,9.50 24.86,13.75 24.86,21.25 21.18,23.38 13.82,19.12" style="fill:none; stroke:#2b3436; stroke-linecap:round; stroke-linejoin:round;"/><polyline points="13.82,11.62 21.18,15.88 24.86,13.75" style="fill:none; stroke:#2b3436; stroke-linecap:round; stroke-linejoin:round;"/><line x1="21.18" y1="15.88" x2="21.18" y2="23.38" style="fill:none; stroke:#2b3436; stroke-linecap:round; stroke-linejoin:round;"/></svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 36 36"><circle cx="17.5" cy="17.5" r="17.5" style="fill:#009688;"/><polygon points="13.82,11.62 21.18,15.88 21.18,23.38 13.82,19.12" style="fill:#ffffff; fill-opacity:.55;"/><polygon points="8.75,14.55 16.11,18.80 16.11,26.30 8.75,22.05" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polyline points="8.75,14.55 12.43,12.43 19.79,16.68 16.11,18.80" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polyline points="19.79,16.68 19.79,24.18 16.11,26.30" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polygon points="13.82,11.62 17.50,9.50 24.86,13.75 24.86,21.25 21.18,23.38 13.82,19.12" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round;"/><polyline points="13.82,11.62 21.18,15.88 24.86,13.75" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round;"/><line x1="21.18" y1="15.88" x2="21.18" y2="23.38" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round;"/></svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 36 36"><circle cx="17.5" cy="17.5" r="17.5" style="fill:#00675b;"/><polygon points="13.82,11.62 21.18,15.88 21.18,23.38 13.82,19.12" style="fill:#ffffff; fill-opacity:.55;"/><polygon points="8.75,14.55 16.11,18.80 16.11,26.30 8.75,22.05" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polyline points="8.75,14.55 12.43,12.43 19.79,16.68 16.11,18.80" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polyline points="19.79,16.68 19.79,24.18 16.11,26.30" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polygon points="13.82,11.62 17.50,9.50 24.86,13.75 24.86,21.25 21.18,23.38 13.82,19.12" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round;"/><polyline points="13.82,11.62 21.18,15.88 24.86,13.75" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round;"/><line x1="21.18" y1="15.88" x2="21.18" y2="23.38" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round;"/></svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 36 36"><circle cx="17.5" cy="17.5" r="17.5" style="fill:#008172;"/><polygon points="13.82,11.62 21.18,15.88 21.18,23.38 13.82,19.12" style="fill:#ffffff; fill-opacity:.55;"/><polygon points="8.75,14.55 16.11,18.80 16.11,26.30 8.75,22.05" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polyline points="8.75,14.55 12.43,12.43 19.79,16.68 16.11,18.80" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polyline points="19.79,16.68 19.79,24.18 16.11,26.30" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polygon points="13.82,11.62 17.50,9.50 24.86,13.75 24.86,21.25 21.18,23.38 13.82,19.12" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round;"/><polyline points="13.82,11.62 21.18,15.88 24.86,13.75" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round;"/><line x1="21.18" y1="15.88" x2="21.18" y2="23.38" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round;"/></svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 36 36"><circle cx="17.5" cy="17.5" r="17.5" style="fill:#26a69a;"/><polygon points="13.82,11.62 21.18,15.88 21.18,23.38 13.82,19.12" style="fill:#ffffff; fill-opacity:.55;"/><polygon points="8.75,14.55 16.11,18.80 16.11,26.30 8.75,22.05" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polyline points="8.75,14.55 12.43,12.43 19.79,16.68 16.11,18.80" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polyline points="19.79,16.68 19.79,24.18 16.11,26.30" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polygon points="13.82,11.62 17.50,9.50 24.86,13.75 24.86,21.25 21.18,23.38 13.82,19.12" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round;"/><polyline points="13.82,11.62 21.18,15.88 24.86,13.75" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round;"/><line x1="21.18" y1="15.88" x2="21.18" y2="23.38" style="fill:none; stroke:#ffffff; stroke-linecap:round; stroke-linejoin:round;"/></svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

-1
View File
@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 36 36"><circle cx="17.5" cy="17.5" r="17.5" style="fill:#393c42;"/><polygon points="13.82,11.62 21.18,15.88 21.18,23.38 13.82,19.12" style="fill:#b6b6b6; fill-opacity:.55;"/><polygon points="8.75,14.55 16.11,18.80 16.11,26.30 8.75,22.05" style="fill:none; stroke:#b6b6b6; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polyline points="8.75,14.55 12.43,12.43 19.79,16.68 16.11,18.80" style="fill:none; stroke:#b6b6b6; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polyline points="19.79,16.68 19.79,24.18 16.11,26.30" style="fill:none; stroke:#b6b6b6; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polygon points="13.82,11.62 17.50,9.50 24.86,13.75 24.86,21.25 21.18,23.38 13.82,19.12" style="fill:none; stroke:#b6b6b6; stroke-linecap:round; stroke-linejoin:round;"/><polyline points="13.82,11.62 21.18,15.88 24.86,13.75" style="fill:none; stroke:#b6b6b6; stroke-linecap:round; stroke-linejoin:round;"/><line x1="21.18" y1="15.88" x2="21.18" y2="23.38" style="fill:none; stroke:#b6b6b6; stroke-linecap:round; stroke-linejoin:round;"/></svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 36 36"><circle cx="17.5" cy="17.5" r="17.5" style="fill:#283232;"/><polygon points="13.82,11.62 21.18,15.88 21.18,23.38 13.82,19.12" style="fill:#b6b6b6; fill-opacity:.55;"/><polygon points="8.75,14.55 16.11,18.80 16.11,26.30 8.75,22.05" style="fill:none; stroke:#b6b6b6; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polyline points="8.75,14.55 12.43,12.43 19.79,16.68 16.11,18.80" style="fill:none; stroke:#b6b6b6; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polyline points="19.79,16.68 19.79,24.18 16.11,26.30" style="fill:none; stroke:#b6b6b6; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polygon points="13.82,11.62 17.50,9.50 24.86,13.75 24.86,21.25 21.18,23.38 13.82,19.12" style="fill:none; stroke:#b6b6b6; stroke-linecap:round; stroke-linejoin:round;"/><polyline points="13.82,11.62 21.18,15.88 24.86,13.75" style="fill:none; stroke:#b6b6b6; stroke-linecap:round; stroke-linejoin:round;"/><line x1="21.18" y1="15.88" x2="21.18" y2="23.38" style="fill:none; stroke:#b6b6b6; stroke-linecap:round; stroke-linejoin:round;"/></svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

@@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg id="a" xmlns="http://www.w3.org/2000/svg" width="36" height="36" viewBox="0 0 36 36"><circle cx="17.5" cy="17.5" r="17.5" style="fill:#e5f0ee;"/><polygon points="13.82,11.62 21.18,15.88 21.18,23.38 13.82,19.12" style="fill:#2b3436; fill-opacity:.55;"/><polygon points="8.75,14.55 16.11,18.80 16.11,26.30 8.75,22.05" style="fill:none; stroke:#2b3436; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polyline points="8.75,14.55 12.43,12.43 19.79,16.68 16.11,18.80" style="fill:none; stroke:#2b3436; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polyline points="19.79,16.68 19.79,24.18 16.11,26.30" style="fill:none; stroke:#2b3436; stroke-linecap:round; stroke-linejoin:round; stroke-opacity:.45;"/><polygon points="13.82,11.62 17.50,9.50 24.86,13.75 24.86,21.25 21.18,23.38 13.82,19.12" style="fill:none; stroke:#2b3436; stroke-linecap:round; stroke-linejoin:round;"/><polyline points="13.82,11.62 21.18,15.88 24.86,13.75" style="fill:none; stroke:#2b3436; stroke-linecap:round; stroke-linejoin:round;"/><line x1="21.18" y1="15.88" x2="21.18" y2="23.38" style="fill:none; stroke:#2b3436; stroke-linecap:round; stroke-linejoin:round;"/></svg>

Before

Width:  |  Height:  |  Size: 1.2 KiB

+1 -121
View File
@@ -1,6 +1,6 @@
{
"name": "Snapmaker",
"version": "02.04.00.17",
"version": "02.04.00.16",
"force_update": "0",
"description": "Snapmaker configurations",
"machine_model_list": [
@@ -1448,10 +1448,6 @@
"name": "Snapmaker PETG HF @U1 0.2 nozzle",
"sub_path": "filament/Snapmaker PETG HF @U1 0.2 nozzle.json"
},
{
"name": "Snapmaker PETG HF @U1 0.4 nozzle",
"sub_path": "filament/Snapmaker PETG HF @U1 0.4 nozzle.json"
},
{
"name": "Snapmaker PETG HF @U1 0.6 nozzle",
"sub_path": "filament/Snapmaker PETG HF @U1 0.6 nozzle.json"
@@ -1924,122 +1920,6 @@
"name": "Snapmaker Breakaway Support For PLA @U1 0.8 nozzle",
"sub_path": "filament/Snapmaker Breakaway Support For PLA @U1 0.8 nozzle.json"
},
{
"name": "Generic PETG @U1 0.2 nozzle",
"sub_path": "filament/Generic PETG @U1 0.2 nozzle.json"
},
{
"name": "Generic PETG @U1 0.4 nozzle",
"sub_path": "filament/Generic PETG @U1 0.4 nozzle.json"
},
{
"name": "Generic PETG @U1 0.6 nozzle",
"sub_path": "filament/Generic PETG @U1 0.6 nozzle.json"
},
{
"name": "Generic PETG @U1 0.8 nozzle",
"sub_path": "filament/Generic PETG @U1 0.8 nozzle.json"
},
{
"name": "Generic PETG HF @U1 0.2 nozzle",
"sub_path": "filament/Generic PETG HF @U1 0.2 nozzle.json"
},
{
"name": "Generic PETG HF @U1 0.4 nozzle",
"sub_path": "filament/Generic PETG HF @U1 0.4 nozzle.json"
},
{
"name": "Generic PETG HF @U1 0.6 nozzle",
"sub_path": "filament/Generic PETG HF @U1 0.6 nozzle.json"
},
{
"name": "Generic PETG HF @U1 0.8 nozzle",
"sub_path": "filament/Generic PETG HF @U1 0.8 nozzle.json"
},
{
"name": "Generic PETG-CF @U1 0.4 nozzle",
"sub_path": "filament/Generic PETG-CF @U1 0.4 nozzle.json"
},
{
"name": "Generic PETG-CF @U1 0.6 nozzle",
"sub_path": "filament/Generic PETG-CF @U1 0.6 nozzle.json"
},
{
"name": "Generic PETG-CF @U1 0.8 nozzle",
"sub_path": "filament/Generic PETG-CF @U1 0.8 nozzle.json"
},
{
"name": "Generic PLA @U1 0.2 nozzle",
"sub_path": "filament/Generic PLA @U1 0.2 nozzle.json"
},
{
"name": "Generic PLA @U1 0.4 nozzle",
"sub_path": "filament/Generic PLA @U1 0.4 nozzle.json"
},
{
"name": "Generic PLA @U1 0.6 nozzle",
"sub_path": "filament/Generic PLA @U1 0.6 nozzle.json"
},
{
"name": "Generic PLA @U1 0.8 nozzle",
"sub_path": "filament/Generic PLA @U1 0.8 nozzle.json"
},
{
"name": "Generic PLA High Speed @U1 0.2 nozzle",
"sub_path": "filament/Generic PLA High Speed @U1 0.2 nozzle.json"
},
{
"name": "Generic PLA High Speed @U1 0.4 nozzle",
"sub_path": "filament/Generic PLA High Speed @U1 0.4 nozzle.json"
},
{
"name": "Generic PLA High Speed @U1 0.6 nozzle",
"sub_path": "filament/Generic PLA High Speed @U1 0.6 nozzle.json"
},
{
"name": "Generic PLA High Speed @U1 0.8 nozzle",
"sub_path": "filament/Generic PLA High Speed @U1 0.8 nozzle.json"
},
{
"name": "Generic PLA Silk @U1 0.2 nozzle",
"sub_path": "filament/Generic PLA Silk @U1 0.2 nozzle.json"
},
{
"name": "Generic PLA Silk @U1 0.4 nozzle",
"sub_path": "filament/Generic PLA Silk @U1 0.4 nozzle.json"
},
{
"name": "Generic PLA Silk @U1 0.6 nozzle",
"sub_path": "filament/Generic PLA Silk @U1 0.6 nozzle.json"
},
{
"name": "Generic PLA Silk @U1 0.8 nozzle",
"sub_path": "filament/Generic PLA Silk @U1 0.8 nozzle.json"
},
{
"name": "Generic PLA-CF @U1 0.4 nozzle",
"sub_path": "filament/Generic PLA-CF @U1 0.4 nozzle.json"
},
{
"name": "Generic PLA-CF @U1 0.6 nozzle",
"sub_path": "filament/Generic PLA-CF @U1 0.6 nozzle.json"
},
{
"name": "Generic PLA-CF @U1 0.8 nozzle",
"sub_path": "filament/Generic PLA-CF @U1 0.8 nozzle.json"
},
{
"name": "Generic TPU @U1 0.4 nozzle",
"sub_path": "filament/Generic TPU @U1 0.4 nozzle.json"
},
{
"name": "Generic TPU @U1 0.6 nozzle",
"sub_path": "filament/Generic TPU @U1 0.6 nozzle.json"
},
{
"name": "Generic TPU @U1 0.8 nozzle",
"sub_path": "filament/Generic TPU @U1 0.8 nozzle.json"
},
{
"name": "PolyTerra J1 PLA",
"sub_path": "filament/Polymaker/PolyTerra J1 PLA.json"
@@ -1,27 +0,0 @@
{
"type": "filament",
"name": "Generic PETG @U1 0.2 nozzle",
"inherits": "Generic PETG @System",
"from": "system",
"setting_id": "Iw7T4qqDf1fbSAa1",
"instantiation": "true",
"compatible_printers": [
"Snapmaker U1 (0.2 nozzle)"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"filament_max_volumetric_speed": [
"10",
"1"
],
"fan_min_speed": [
"0",
"0"
],
"fan_max_speed": [
"40",
"40"
]
}
@@ -1,27 +0,0 @@
{
"type": "filament",
"name": "Generic PETG @U1 0.4 nozzle",
"inherits": "Generic PETG @System",
"from": "system",
"setting_id": "VHRa4TXU7HPz7Fld",
"instantiation": "true",
"compatible_printers": [
"Snapmaker U1 (0.4 nozzle)"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"filament_max_volumetric_speed": [
"10",
"12"
],
"fan_min_speed": [
"0",
"0"
],
"fan_max_speed": [
"40",
"40"
]
}
@@ -1,27 +0,0 @@
{
"type": "filament",
"name": "Generic PETG @U1 0.6 nozzle",
"inherits": "Generic PETG @System",
"from": "system",
"setting_id": "Ubr8v7BTxiQMntVk",
"instantiation": "true",
"compatible_printers": [
"Snapmaker U1 (0.6 nozzle)"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"filament_max_volumetric_speed": [
"10",
"12"
],
"fan_min_speed": [
"0",
"0"
],
"fan_max_speed": [
"40",
"40"
]
}
@@ -1,27 +0,0 @@
{
"type": "filament",
"name": "Generic PETG @U1 0.8 nozzle",
"inherits": "Generic PETG @System",
"from": "system",
"setting_id": "ro0mXCLzC5rY95oX",
"instantiation": "true",
"compatible_printers": [
"Snapmaker U1 (0.8 nozzle)"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"filament_max_volumetric_speed": [
"10",
"12"
],
"fan_min_speed": [
"0",
"0"
],
"fan_max_speed": [
"40",
"40"
]
}
@@ -1,27 +0,0 @@
{
"type": "filament",
"name": "Generic PETG HF @U1 0.2 nozzle",
"inherits": "Generic PETG HF @System",
"from": "system",
"setting_id": "7yjLWAdcQe4ndo1z",
"instantiation": "true",
"compatible_printers": [
"Snapmaker U1 (0.2 nozzle)"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"filament_max_volumetric_speed": [
"20",
"1"
],
"fan_min_speed": [
"0",
"0"
],
"fan_max_speed": [
"40",
"40"
]
}
@@ -1,27 +0,0 @@
{
"type": "filament",
"name": "Generic PETG HF @U1 0.4 nozzle",
"inherits": "Generic PETG HF @System",
"from": "system",
"setting_id": "XWWLYZWkD9ZNRPeO",
"instantiation": "true",
"compatible_printers": [
"Snapmaker U1 (0.4 nozzle)"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"filament_max_volumetric_speed": [
"20",
"16"
],
"fan_min_speed": [
"0",
"0"
],
"fan_max_speed": [
"40",
"40"
]
}
@@ -1,27 +0,0 @@
{
"type": "filament",
"name": "Generic PETG HF @U1 0.6 nozzle",
"inherits": "Generic PETG HF @System",
"from": "system",
"setting_id": "1dj4JdFabSql3cEg",
"instantiation": "true",
"compatible_printers": [
"Snapmaker U1 (0.6 nozzle)"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"filament_max_volumetric_speed": [
"20",
"16"
],
"fan_min_speed": [
"0",
"0"
],
"fan_max_speed": [
"40",
"40"
]
}
@@ -1,27 +0,0 @@
{
"type": "filament",
"name": "Generic PETG HF @U1 0.8 nozzle",
"inherits": "Generic PETG HF @System",
"from": "system",
"setting_id": "1LYZ0KG5kQtaOOKt",
"instantiation": "true",
"compatible_printers": [
"Snapmaker U1 (0.8 nozzle)"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"filament_max_volumetric_speed": [
"20",
"16"
],
"fan_min_speed": [
"0",
"0"
],
"fan_max_speed": [
"40",
"40"
]
}
@@ -1,27 +0,0 @@
{
"type": "filament",
"name": "Generic PETG-CF @U1 0.4 nozzle",
"inherits": "Generic PETG-CF @System",
"from": "system",
"setting_id": "wIKcBM1qLgzLEUJP",
"instantiation": "true",
"compatible_printers": [
"Snapmaker U1 (0.4 nozzle)"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"filament_max_volumetric_speed": [
"11.5",
"11"
],
"fan_min_speed": [
"5",
"5"
],
"fan_max_speed": [
"40",
"40"
]
}
@@ -1,23 +0,0 @@
{
"type": "filament",
"name": "Generic PETG-CF @U1 0.6 nozzle",
"inherits": "Generic PETG-CF @System",
"from": "system",
"setting_id": "FvrnBiPbjSvIF3kN",
"instantiation": "true",
"compatible_printers": [
"Snapmaker U1 (0.6 nozzle)"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"5",
"5"
],
"fan_max_speed": [
"40",
"40"
]
}
@@ -1,23 +0,0 @@
{
"type": "filament",
"name": "Generic PETG-CF @U1 0.8 nozzle",
"inherits": "Generic PETG-CF @System",
"from": "system",
"setting_id": "SCfvzDgUWkFHmbtP",
"instantiation": "true",
"compatible_printers": [
"Snapmaker U1 (0.8 nozzle)"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"5",
"5"
],
"fan_max_speed": [
"40",
"40"
]
}
@@ -1,31 +0,0 @@
{
"type": "filament",
"name": "Generic PLA @U1 0.2 nozzle",
"inherits": "Generic PLA @System",
"from": "system",
"setting_id": "8pb3tert1D4w0SKP",
"instantiation": "true",
"compatible_printers": [
"Snapmaker U1 (0.2 nozzle)"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"filament_max_volumetric_speed": [
"12",
"1.6"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -1,31 +0,0 @@
{
"type": "filament",
"name": "Generic PLA @U1 0.4 nozzle",
"inherits": "Generic PLA @System",
"from": "system",
"setting_id": "Uilm8oVmZzyxE3ba",
"instantiation": "true",
"compatible_printers": [
"Snapmaker U1 (0.4 nozzle)"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"filament_max_volumetric_speed": [
"12",
"12"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -1,31 +0,0 @@
{
"type": "filament",
"name": "Generic PLA @U1 0.6 nozzle",
"inherits": "Generic PLA @System",
"from": "system",
"setting_id": "aj4G9OSdgtQPqpkI",
"instantiation": "true",
"compatible_printers": [
"Snapmaker U1 (0.6 nozzle)"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"filament_max_volumetric_speed": [
"12",
"12"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -1,31 +0,0 @@
{
"type": "filament",
"name": "Generic PLA @U1 0.8 nozzle",
"inherits": "Generic PLA @System",
"from": "system",
"setting_id": "zom29gBgIZmvjFmh",
"instantiation": "true",
"compatible_printers": [
"Snapmaker U1 (0.8 nozzle)"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"filament_max_volumetric_speed": [
"12",
"12"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -1,31 +0,0 @@
{
"type": "filament",
"name": "Generic PLA High Speed @U1 0.2 nozzle",
"inherits": "Generic PLA High Speed @System",
"from": "system",
"setting_id": "rT8e5f81vwUpwPuq",
"instantiation": "true",
"compatible_printers": [
"Snapmaker U1 (0.2 nozzle)"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"filament_max_volumetric_speed": [
"18",
"2"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -1,31 +0,0 @@
{
"type": "filament",
"name": "Generic PLA High Speed @U1 0.4 nozzle",
"inherits": "Generic PLA High Speed @System",
"from": "system",
"setting_id": "ZUetX0WveqW8fy6f",
"instantiation": "true",
"compatible_printers": [
"Snapmaker U1 (0.4 nozzle)"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"filament_max_volumetric_speed": [
"18",
"18"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -1,31 +0,0 @@
{
"type": "filament",
"name": "Generic PLA High Speed @U1 0.6 nozzle",
"inherits": "Generic PLA High Speed @System",
"from": "system",
"setting_id": "y2WY6qLA3xKZM1Mm",
"instantiation": "true",
"compatible_printers": [
"Snapmaker U1 (0.6 nozzle)"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"filament_max_volumetric_speed": [
"18",
"18"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -1,31 +0,0 @@
{
"type": "filament",
"name": "Generic PLA High Speed @U1 0.8 nozzle",
"inherits": "Generic PLA High Speed @System",
"from": "system",
"setting_id": "VwXJrdwxvzjlIGal",
"instantiation": "true",
"compatible_printers": [
"Snapmaker U1 (0.8 nozzle)"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"filament_max_volumetric_speed": [
"18",
"18"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -1,31 +0,0 @@
{
"type": "filament",
"name": "Generic PLA Silk @U1 0.2 nozzle",
"inherits": "Generic PLA Silk @System",
"from": "system",
"setting_id": "381SJIDBre8HxHy2",
"instantiation": "true",
"compatible_printers": [
"Snapmaker U1 (0.2 nozzle)"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"filament_max_volumetric_speed": [
"7.5",
"2"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -1,27 +0,0 @@
{
"type": "filament",
"name": "Generic PLA Silk @U1 0.4 nozzle",
"inherits": "Generic PLA Silk @System",
"from": "system",
"setting_id": "Fd1g9OdeM3pUx8f5",
"instantiation": "true",
"compatible_printers": [
"Snapmaker U1 (0.4 nozzle)"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -1,27 +0,0 @@
{
"type": "filament",
"name": "Generic PLA Silk @U1 0.6 nozzle",
"inherits": "Generic PLA Silk @System",
"from": "system",
"setting_id": "8rhmpBFmMj9vVZwG",
"instantiation": "true",
"compatible_printers": [
"Snapmaker U1 (0.6 nozzle)"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -1,27 +0,0 @@
{
"type": "filament",
"name": "Generic PLA Silk @U1 0.8 nozzle",
"inherits": "Generic PLA Silk @System",
"from": "system",
"setting_id": "Fo7U7GYHZh1aZPLs",
"instantiation": "true",
"compatible_printers": [
"Snapmaker U1 (0.8 nozzle)"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -1,31 +0,0 @@
{
"type": "filament",
"name": "Generic PLA-CF @U1 0.4 nozzle",
"inherits": "Generic PLA-CF @System",
"from": "system",
"setting_id": "6AWubtDE4PqrKVNJ",
"instantiation": "true",
"compatible_printers": [
"Snapmaker U1 (0.4 nozzle)"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"filament_max_volumetric_speed": [
"12",
"12"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -1,31 +0,0 @@
{
"type": "filament",
"name": "Generic PLA-CF @U1 0.6 nozzle",
"inherits": "Generic PLA-CF @System",
"from": "system",
"setting_id": "AvM44HgMCkOCnmzY",
"instantiation": "true",
"compatible_printers": [
"Snapmaker U1 (0.6 nozzle)"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"filament_max_volumetric_speed": [
"12",
"12"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -1,31 +0,0 @@
{
"type": "filament",
"name": "Generic PLA-CF @U1 0.8 nozzle",
"inherits": "Generic PLA-CF @System",
"from": "system",
"setting_id": "tvYAUvQEbbdBCIg2",
"instantiation": "true",
"compatible_printers": [
"Snapmaker U1 (0.8 nozzle)"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"filament_max_volumetric_speed": [
"12",
"12"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -1,27 +0,0 @@
{
"type": "filament",
"name": "Generic TPU @U1 0.4 nozzle",
"inherits": "Generic TPU @System",
"from": "system",
"setting_id": "9d0jRdMgkPMahYL7",
"instantiation": "true",
"compatible_printers": [
"Snapmaker U1 (0.4 nozzle)"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"70",
"70"
],
"fan_max_speed": [
"70",
"70"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -1,27 +0,0 @@
{
"type": "filament",
"name": "Generic TPU @U1 0.6 nozzle",
"inherits": "Generic TPU @System",
"from": "system",
"setting_id": "LLjEPSU4l09N3Zb8",
"instantiation": "true",
"compatible_printers": [
"Snapmaker U1 (0.6 nozzle)"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"70",
"70"
],
"fan_max_speed": [
"70",
"70"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -1,27 +0,0 @@
{
"type": "filament",
"name": "Generic TPU @U1 0.8 nozzle",
"inherits": "Generic TPU @System",
"from": "system",
"setting_id": "Wfhd3jLEjSKzCeHE",
"instantiation": "true",
"compatible_printers": [
"Snapmaker U1 (0.8 nozzle)"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"70",
"70"
],
"fan_max_speed": [
"70",
"70"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -10,18 +10,15 @@
"Snapmaker U1 (0.4 nozzle)"
],
"enable_pressure_advance": [
"0",
"0"
],
"filament_max_volumetric_speed": [
"15",
"15"
],
"filament_vendor": [
"Polymaker"
],
"nozzle_temperature_range_high": [
"230",
"230"
],
"temperature_vitrification": [
@@ -32,21 +29,5 @@
],
"textured_plate_temp_initial_layer": [
"65"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -16,15 +16,12 @@
"Polymaker"
],
"nozzle_temperature": [
"230",
"230"
],
"nozzle_temperature_initial_layer": [
"230",
"230"
],
"nozzle_temperature_range_high": [
"230",
"230"
],
"textured_plate_temp": [
@@ -32,21 +29,5 @@
],
"textured_plate_temp_initial_layer": [
"65"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -13,7 +13,6 @@
"1.23"
],
"filament_flow_ratio": [
"0.96",
"0.96"
],
"filament_vendor": [
@@ -30,21 +29,5 @@
],
"textured_plate_temp_initial_layer": [
"65"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -9,27 +9,9 @@
"Snapmaker U1 (0.4 nozzle)"
],
"filament_minimal_purge_on_wipe_tower": [
"15",
"15"
],
"pressure_advance": [
"0.05",
"0.05"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"10",
"10"
],
"fan_max_speed": [
"30",
"30"
],
"additional_cooling_fan_speed": [
"0",
"0"
]
}
@@ -9,35 +9,18 @@
"Snapmaker U1 (0.4 nozzle)"
],
"filament_minimal_purge_on_wipe_tower": [
"15",
"15"
],
"filament_retraction_length": [
"1.0",
"1.0"
],
"filament_z_hop": [
"0.0",
"0.0"
],
"enable_pressure_advance": [
"1",
"1"
],
"pressure_advance": [
"0.05",
"0.05"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"20",
"20"
],
"fan_max_speed": [
"50",
"50"
]
}
@@ -9,35 +9,18 @@
"Snapmaker U1 (0.4 nozzle)"
],
"filament_minimal_purge_on_wipe_tower": [
"15",
"15"
],
"filament_retraction_length": [
"1.0",
"1.0"
],
"filament_z_hop": [
"0.0",
"0.0"
],
"enable_pressure_advance": [
"1",
"1"
],
"pressure_advance": [
"0.08",
"0.08"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"0",
"0"
],
"fan_max_speed": [
"10",
"10"
]
}
@@ -9,32 +9,16 @@
"Snapmaker U1 (0.4 nozzle)"
],
"filament_minimal_purge_on_wipe_tower": [
"15",
"15"
],
"filament_retraction_length": [
"1.0",
"1.0"
],
"filament_z_hop": [
"0.0",
"0.0"
],
"description": " ",
"enable_pressure_advance": [
"1",
"1"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"0",
"0"
],
"fan_max_speed": [
"10",
"10"
]
}
@@ -9,7 +9,6 @@
"Snapmaker U1 (0.4 nozzle)"
],
"filament_minimal_purge_on_wipe_tower": [
"15",
"15"
],
"supertack_plate_temp": [
@@ -19,19 +18,6 @@
"70"
],
"pressure_advance": [
"0.04",
"0.04"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"10",
"10"
],
"fan_max_speed": [
"20",
"20"
]
}
@@ -9,24 +9,7 @@
"Snapmaker U1 (0.4 nozzle)"
],
"filament_minimal_purge_on_wipe_tower": [
"15",
"15"
],
"description": " ",
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"0",
"0"
],
"fan_max_speed": [
"10",
"10"
],
"additional_cooling_fan_speed": [
"0",
"0"
]
"description": " "
}
@@ -10,23 +10,6 @@
"Snapmaker U1 (0.4 nozzle)"
],
"filament_minimal_purge_on_wipe_tower": [
"15",
"15"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -10,27 +10,9 @@
"Snapmaker U1 (0.4 nozzle)"
],
"filament_minimal_purge_on_wipe_tower": [
"15",
"15"
],
"filament_wipe": [
"0",
"0"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -10,23 +10,6 @@
"Snapmaker U1 (0.4 nozzle)"
],
"filament_minimal_purge_on_wipe_tower": [
"15",
"15"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -10,23 +10,6 @@
"Snapmaker U1 (0.4 nozzle)"
],
"filament_minimal_purge_on_wipe_tower": [
"15",
"15"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -10,23 +10,6 @@
"Snapmaker U1 (0.4 nozzle)"
],
"filament_minimal_purge_on_wipe_tower": [
"15",
"15"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -10,23 +10,6 @@
"Snapmaker U1 (0.4 nozzle)"
],
"filament_minimal_purge_on_wipe_tower": [
"15",
"15"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -10,23 +10,6 @@
"Snapmaker U1 (0.4 nozzle)"
],
"filament_minimal_purge_on_wipe_tower": [
"15",
"15"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -10,23 +10,6 @@
"Snapmaker U1 (0.4 nozzle)"
],
"filament_minimal_purge_on_wipe_tower": [
"15",
"15"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -10,23 +10,6 @@
"Snapmaker U1 (0.4 nozzle)"
],
"filament_minimal_purge_on_wipe_tower": [
"15",
"15"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -10,23 +10,6 @@
"Snapmaker U1 (0.4 nozzle)"
],
"filament_minimal_purge_on_wipe_tower": [
"15",
"15"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -10,23 +10,6 @@
"Snapmaker U1 (0.4 nozzle)"
],
"filament_minimal_purge_on_wipe_tower": [
"15",
"15"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -10,23 +10,6 @@
"Snapmaker U1 (0.4 nozzle)"
],
"filament_minimal_purge_on_wipe_tower": [
"15",
"15"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -10,23 +10,6 @@
"Snapmaker U1 (0.4 nozzle)"
],
"filament_minimal_purge_on_wipe_tower": [
"15",
"15"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -10,30 +10,18 @@
"Snapmaker U1 (0.4 nozzle)"
],
"fan_max_speed": [
"100",
"100"
],
"fan_min_speed": [
"100",
"100"
],
"filament_flow_ratio": [
"0.98",
"0.98"
],
"filament_max_volumetric_speed": [
"15",
"15"
],
"slow_down_layer_time": [
"4"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -10,23 +10,6 @@
"Snapmaker U1 (0.4 nozzle)"
],
"filament_minimal_purge_on_wipe_tower": [
"15",
"15"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -10,23 +10,6 @@
"Snapmaker U1 (0.4 nozzle)"
],
"filament_minimal_purge_on_wipe_tower": [
"15",
"15"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -9,23 +9,6 @@
"Snapmaker U1 (0.4 nozzle)"
],
"filament_minimal_purge_on_wipe_tower": [
"15",
"15"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"100",
"100"
],
"fan_max_speed": [
"100",
"100"
],
"additional_cooling_fan_speed": [
"70",
"70"
]
}
@@ -9,7 +9,6 @@
"Snapmaker U1 (0.4 nozzle)"
],
"filament_minimal_purge_on_wipe_tower": [
"15",
"15"
],
"supertack_plate_temp": [
@@ -19,19 +18,6 @@
"70"
],
"pressure_advance": [
"0.05",
"0.05"
],
"filament_extruder_variant": [
"Direct Drive Standard",
"Direct Drive High Flow"
],
"fan_min_speed": [
"0",
"0"
],
"fan_max_speed": [
"40",
"40"
]
}

Some files were not shown because too many files have changed in this diff Show More