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
803 changed files with 8491 additions and 18112 deletions
+1 -4
View File
@@ -14,9 +14,7 @@ CheckOptions:
# admesh's stl.h, which the include path also exposes without its directory. # 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'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 # clipper_z.hpp, which configure it first, and Boost.Polygon's headers only
# work through boost/polygon/polygon.hpp or voronoi.hpp. Clipper2's headers # work through boost/polygon/polygon.hpp or voronoi.hpp. minilzo's config
# are included through clipper2/clipper.h, or clipper2_z.hpp, which defines
# USINGZ first. minilzo's config
# headers are internal to minilzo.h. # headers are internal to minilzo.h.
# FFmpeg's C headers are left alone because they are only included inside # 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, # extern "C", which an inserted include would miss. OS-specific headers (GLib,
@@ -61,5 +59,4 @@ CheckOptions:
png(lib)?conf\.h; png(lib)?conf\.h;
mcut[/\\]platform\.h; mcut[/\\]platform\.h;
boost[/\\]polygon[/\\].*; boost[/\\]polygon[/\\].*;
clipper2[/\\]clipper\.(core|engine|offset|minkowski|rectclip|export|triangulation|version)\.h;
lzo(conf|defs)\.h lzo(conf|defs)\.h
+3 -12
View File
@@ -30,9 +30,8 @@ how you read the wx docs:
Look things up in the source the app is built from — it beats memory, and 3.3 changed real behaviour: Look things up in the source the app is built from — it beats memory, and 3.3 changed real behaviour:
```bash ```bash
WX=$(find -L deps -maxdepth 5 -type d -path '*dep_wxWidgets-prefix/src/dep_wxWidgets' 2>/dev/null | head -1) WX=$(find deps -maxdepth 5 -type d -path '*dep_wxWidgets-prefix/src/dep_wxWidgets' | head -1)
# macOS: deps/build/<arch>/dep_wxWidgets-prefix/src/dep_wxWidgets Linux, Windows: deps/<tree>/dep_wxWidgets-prefix/... # macOS: deps/build/<arch>/dep_wxWidgets-prefix/src/dep_wxWidgets Linux: deps/build/dep_wxWidgets-prefix/...
# -L follows a worktree's deps/<tree> symlinked to the main checkout. Not a glob: zsh aborts on one that matches nothing.
# If deps are not built: git clone --depth 1 -b v3.3.2 https://github.com/SoftFever/Orca-deps-wxWidgets # If deps are not built: git clone --depth 1 -b v3.3.2 https://github.com/SoftFever/Orca-deps-wxWidgets
grep -n "CaptureMouse" -A 30 $WX/interface/wx/window.h # documented contract (doxygen source) grep -n "CaptureMouse" -A 30 $WX/interface/wx/window.h # documented contract (doxygen source)
grep -rn "@onlyfor\|not implemented" $WX/interface/wx/popupwin.h # documented platform limits grep -rn "@onlyfor\|not implemented" $WX/interface/wx/popupwin.h # documented platform limits
@@ -41,13 +40,6 @@ grep -n "IsDark" $WX/docs/changes.txt # what changed in
grep -n "NotifyCaptureLost" -r $WX/src/osx $WX/src/gtk $WX/src/msw # what each port actually does grep -n "NotifyCaptureLost" -r $WX/src/osx $WX/src/gtk $WX/src/msw # what each port actually does
``` ```
On Windows these lookups are bash: run them from Git Bash. PowerShell has no `grep`, and its `find` is
Windows' text-search `find.exe`. To locate the wx tree from PowerShell:
```powershell
$WX = Resolve-Path deps\*\dep_wxWidgets-prefix\src\dep_wxWidgets, deps\*\*\dep_wxWidgets-prefix\src\dep_wxWidgets -ErrorAction SilentlyContinue | Select-Object -First 1 -ExpandProperty Path
```
`interface/wx/<class>.h` is the documentation; `src/common` holds shared behaviour and `interface/wx/<class>.h` is the documentation; `src/common` holds shared behaviour and
`src/{msw,osx,gtk,unix,generic}` the per-port implementation. When the docs and the source disagree, `src/{msw,osx,gtk,unix,generic}` the per-port implementation. When the docs and the source disagree,
the source is what runs — the references mark such facts **[source]**. Orca-side design docs live in the source is what runs — the references mark such facts **[source]**. Orca-side design docs live in
@@ -214,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- - **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 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 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 afterwards with a deferred, liveness-guarded `Raise()`; a live menu accelerator consumes the key before
it the key window; a live menu accelerator consumes the key before
any wx key event; Control+click arrives as a right-click. 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 - **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 `dark_mode()`; menu bitmaps follow `check_dark_mode()`; windows are not double-buffered by default in
@@ -331,10 +331,6 @@ OrcaSlicer: integer spinners are `::SpinInput` ([below](#spininput)); progress b
Contract: Contract:
- A page must be created with the book as its parent and added once; the book owns and deletes it - A page must be created with the book as its parent and added once; the book owns and deletes it
(`interface/wx/bookctrl.h:253-254, 273`). `RemovePage` detaches without deleting, and you then own it (`:324-330`). (`interface/wx/bookctrl.h:253-254, 273`). `RemovePage` detaches without deleting, and you then own it (`:324-330`).
- Removing the selected page selects the page before it (the new first page if it was first) through `SetSelection`,
so that page is shown and PAGE_CHANGING/CHANGED are sent; removing a page before the selection only shifts the
index. This is `wxBookCtrlBase::DoSetSelectionAfterRemoval` (`src/common/bookctrl.cpp:477-495` **[source]**),
called from `DoRemovePage` by `wxSimplebook`, `wxChoicebook`, `wxListbook`, `wxToolbook` and Orca's `Notebook`.
- `GetSelection()` inside a `PAGE_CHANGED` handler may return the old or the new page depending on the platform; use - `GetSelection()` inside a `PAGE_CHANGED` handler may return the old or the new page depending on the platform; use
`event.GetSelection()` (`interface/wx/bookctrl.h:160-166`). `event.GetSelection()` (`interface/wx/bookctrl.h:160-166`).
- `wxSimplebook` has no UI; switch with `ChangeSelection()`. `SetSelection()` sends PAGE_CHANGING/CHANGED - `wxSimplebook` has no UI; switch with `ChangeSelection()`. `SetSelection()` sends PAGE_CHANGING/CHANGED
@@ -10,7 +10,7 @@ wx asserts are compiled out in Orca (`wxDEBUG_LEVEL=0`), so every misuse below t
an assert fails silently. "GTK" means wxGTK3, Orca's Linux default (X11 and Wayland); GTK2 is only an assert fails silently. "GTK" means wxGTK3, Orca's Linux default (X11 and Wayland); GTK2 is only
an opt-out build (`-DDEP_WX_GTK3=OFF`), noted where it differs. Paths starting `interface/`, an opt-out build (`-DDEP_WX_GTK3=OFF`), noted where it differs. Paths starting `interface/`,
`include/`, `src/`, `docs/` are in the wx tree `include/`, `src/`, `docs/` are in the wx tree
(located as in `SKILL.md` §Ground truth); Orca paths are (`find deps -maxdepth 5 -type d -path '*dep_wxWidgets-prefix/src/dep_wxWidgets'`); Orca paths are
relative to `src/slic3r/GUI/`. relative to `src/slic3r/GUI/`.
Contents: [Rules](#rules) · [Mouse capture](#mouse-capture) · [Mouse events](#mouse-events) · Contents: [Rules](#rules) · [Mouse capture](#mouse-capture) · [Mouse events](#mouse-events) ·
@@ -332,9 +332,7 @@ indices: pages come and go per printer and per feature flag.
pages depending on the printer and on `use_printer_agents`; a removed page stays registered but is pages depending on the printer and on `use_printer_agents`; a removed page stays registered but is
not prebuilt (its `LazyPage::in_book()` is false). not prebuilt (its `LazyPage::in_book()` is false).
- Plugin pages are appended by `PluginPages::initialize` (`plugin/host/PluginPages.hpp`) with - Plugin pages are appended by `PluginPages::initialize` (`plugin/host/PluginPages.hpp`) with
namespaced ids (`plugin.<plugin_key>.<name>`) that cannot collide with `TAB_ID_*`. Each is a namespaced ids (`plugin.<plugin_key>.<name>`) that cannot collide with `TAB_ID_*`.
`LazyPage<PluginPage>` with order −1, destroyed when its capability goes away.
→ [Deferred construction](#deferred-construction-lazy-lazypage-stagedbuild-idlescheduler)
### Preset tabs ### Preset tabs
@@ -531,32 +529,6 @@ the main frame does nothing to a panel after creating it.
m_idle.add(m_diff_dialog); m_idle.add(m_diff_dialog);
``` ```
Cite: `IdleScheduler::tick`, `docs/HLSD/deferred-page-construction.md`. Cite: `IdleScheduler::tick`, `docs/HLSD/deferred-page-construction.md`.
- **Rule:** A lazy page that can be destroyed while the main frame lives takes a negative order and
stays out of `m_lazy_pages`.
**Why:** `m_lazy_pages` and `PrebuildQueue` hold raw `LazyBase*` and nothing removes one
(`PrebuildQueue` has only `add` and `clear`). The queue calls `pending()` on every task each slice,
and `prebuild_pages_when_idle` reads every entry of `m_lazy_pages`, so a page destroyed while still
listed can be read after it is freed. A page only taken out of the book is fine: it stays registered
and its `pending()` is false (`MainFrame::show_device`).
```cpp
// Right (PluginPages::create_page): order -1, and no m_lazy_pages.push_back
auto* page = new GUI::LazyPage<PluginPage>(m_parent, name, -1, [capability](wxWindow* parent) {
return new PluginPage(parent, capability);
});
```
Cite: `PluginPages::create_page`, `PluginPages::remove_page`.
- **Rule:** Remove several lazy pages from a book left to right.
**Why:** removing the selected page selects and shows the page before it
(`references/controls-dataview.md` §Book controls), and showing an unbuilt `LazyPage` while the frame
is shown builds it. In any other order the page before the selected one can be one removed next,
built only to be destroyed; left to right it is one that stays (unless the selected page is the
book's first).
```cpp
// Right (PluginPages::shutdown): m_order is the tabs' left-to-right order
for (const PluginCapabilityId& id : std::vector<PluginCapabilityId>(m_order))
remove_page(id);
```
Cite: `PluginPages::shutdown`, `PluginPages::relayout`, `PluginPages::on_plugin_deregister`.
## Plater and Sidebar ## Plater and Sidebar
@@ -6,7 +6,7 @@ to write a custom control on the wx side and how to author an Orca widget on the
`StaticBox`/`StateHandler` foundation. Read it before writing or reviewing any `wxEVT_PAINT` `StaticBox`/`StateHandler` foundation. Read it before writing or reviewing any `wxEVT_PAINT`
handler, `render`/`doRender` method, `messureSize`, or a new class under `src/slic3r/GUI/Widgets/`. handler, `render`/`doRender` method, `messureSize`, or a new class under `src/slic3r/GUI/Widgets/`.
wx cites are relative to the pinned wx 3.3.2 tree (located as in `SKILL.md` §Ground truth). wx cites are relative to the pinned wx 3.3.2 tree (`deps/build/<arch>/dep_wxWidgets-prefix/src/dep_wxWidgets`).
Orca builds wx with `wxBUILD_DEBUG_LEVEL=0` and `libslic3r_gui` with `wxDEBUG_LEVEL=0`: every Orca builds wx with `wxBUILD_DEBUG_LEVEL=0` and `libslic3r_gui` with `wxDEBUG_LEVEL=0`: every
wx assert below is compiled out and `wxCHECK*` returns silently, so paint misuse shows up only as wx assert below is compiled out and `wxCHECK*` returns silently, so paint misuse shows up only as
wrong, missing or stale pixels, never as an assert dialog. wrong, missing or stale pixels, never as an assert dialog.
@@ -78,9 +78,8 @@ Contents: [Rules](#rules) · [The wx build Orca uses](#the-wx-build-orca-uses)
`references/colours-dark-mode.md`. `references/colours-dark-mode.md`.
- The checked-out source is the tree that every wx citation in this skill refers to: - The checked-out source is the tree that every wx citation in this skill refers to:
`deps/build/<arch>/dep_wxWidgets-prefix/src/dep_wxWidgets` on macOS and `deps/build/<arch>/dep_wxWidgets-prefix/src/dep_wxWidgets` on macOS and
`deps/<tree>/dep_wxWidgets-prefix/src/dep_wxWidgets` on Linux and Windows (`deps/build` for a `deps/build/dep_wxWidgets-prefix/src/dep_wxWidgets` on Linux. Find it with
release build; `build_win.bat` names the others). Locate it with the bash or PowerShell lookup in `find deps -maxdepth 5 -type d -path '*dep_wxWidgets-prefix/src/dep_wxWidgets'`. On macOS its
`SKILL.md` §Ground truth. On macOS its
`src/osx/cocoa/colour.mm` already has the patch applied. `src/osx/cocoa/colour.mm` already has the patch applied.
- **Flatpak builds wx separately.** `deps/CMakeLists.txt` leaves `dep_wxWidgets` out of the deps - **Flatpak builds wx separately.** `deps/CMakeLists.txt` leaves `dep_wxWidgets` out of the deps
target when `FLATPAK` is set. Instead, `scripts/flatpak/com.orcaslicer.OrcaSlicer.yml` has its own target when `FLATPAK` is set. Instead, `scripts/flatpak/com.orcaslicer.OrcaSlicer.yml` has its own
@@ -5,7 +5,7 @@ How wx 3.3.2 popups and menus behave on each port, and the Orca wrappers built o
`append_menu_item`, `Plater::PopupMenu` and the macOS menubar versus `BBLTopbar`. Read it before you add `append_menu_item`, `Plater::PopupMenu` and the macOS menubar versus `BBLTopbar`. Read it before you add
or change anything that opens over other UI and must close by itself, or any context menu or menubar item. or change anything that opens over other UI and must close by itself, or any context menu or menubar item.
wx cites are relative to the wx tree root (located as in `SKILL.md` §Ground truth). wx cites are relative to the wx tree root (`deps/build/<arch>/dep_wxWidgets-prefix/src/dep_wxWidgets`).
**[source]** marks behaviour derived from the implementation that the wx docs do not state or contradict. **[source]** marks behaviour derived from the implementation that the wx docs do not state or contradict.
Orca builds wx with `wxBUILD_DEBUG_LEVEL=0`, so every "asserts" below means "fails silently in Orca". Orca builds wx with `wxBUILD_DEBUG_LEVEL=0`, so every "asserts" below means "fails silently in Orca".
"GTK" means wxGTK3 (X11 and Wayland), the default Linux build; GTK2 is only an opt-out (`-DDEP_WX_GTK3=OFF`). "GTK" means wxGTK3 (X11 and Wayland), the default Linux build; GTK2 is only an opt-out (`-DDEP_WX_GTK3=OFF`).
@@ -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) 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 18. Content that needs typing focus or hosts a `wxWebView` uses a frameless `wxDialog` that hides on
deactivation, not a transient popup. (§10) deactivation, not a transient popup. (§10)
19. Display-only overlays (HUDs, toasts) use a plain `wxPopupWindow`: `Show()` never gives it focus and it 19. Display-only overlays (HUDs, toasts) use a plain `wxPopupWindow`: it never takes focus and never
never auto-dismisses. Never `Raise()` any `wxPopupWindow`: `Raise()` is for top-level windows only, and on auto-dismisses. (§4, §10)
macOS it makes the popup the key window. (§4, §5, §10)
20. On MSW, don't `SetFocus()` another window on hover while `wxCurrentPopupWindow` is non-null. (§8) 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) 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 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`, 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. `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. 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 - 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 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 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 - **Rule:** For overlays that must never take keyboard focus (above a GL surface), use a plain
`wxPopupWindow(top, wxBORDER_NONE)`, not a `wxFrame`. `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 **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`). 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 ## 11. wxComboCtrl / wxComboPopup
@@ -7,7 +7,7 @@ yields and nested event loops, progress dialogs, startup, shutdown and exception
screen, and the access rules for `wxGetApp()` and `app_config`. Read it whenever code runs off the screen, and the access rules for `wxGetApp()` and `app_config`. Read it whenever code runs off the
main thread, defers work, starts a timer, yields, shows progress, or runs during startup or shutdown. main thread, defers work, starts a timer, yields, shows progress, or runs during startup or shutdown.
wx cites are relative to the pinned wx 3.3.2 tree (located as in `SKILL.md` §Ground truth). wx cites are relative to the pinned wx 3.3.2 tree (`deps/build/<arch>/dep_wxWidgets-prefix/src/dep_wxWidgets`).
Orca builds wx with `wxBUILD_DEBUG_LEVEL=0` and `libslic3r_gui` with `wxDEBUG_LEVEL=0`, so `wxASSERT` Orca builds wx with `wxBUILD_DEBUG_LEVEL=0` and `libslic3r_gui` with `wxDEBUG_LEVEL=0`, so `wxASSERT`
is compiled out and `wxCHECK*` returns silently: a timer started off the main thread never fires on macOS, is compiled out and `wxCHECK*` returns silently: a timer started off the main thread never fires on macOS,
`Exit()` on a loop that is not the active one is ignored, `Start(0)` on macOS fails — all without `Exit()` on a loop that is not the active one is ignored, `Start(0)` on macOS fails — all without
@@ -6,9 +6,10 @@ port; what Orca's `DPIDialog`/`DPIFrame` add; the Orca dialog recipe; and the `M
Read it before writing or reviewing any dialog, frame, close handler, `Destroy()`/`delete`, or code Read it before writing or reviewing any dialog, frame, close handler, `Destroy()`/`delete`, or code
that keeps a pointer to a window across an event, a `CallAfter` or a modal loop. that keeps a pointer to a window across an event, a `CallAfter` or a modal loop.
wx cites are relative to the pinned wx 3.3.2 tree (located as in `SKILL.md` §Ground truth). wx is wx cites are relative to the pinned wx 3.3.2 tree (`find deps -maxdepth 5 -type d -path
built with `wxBUILD_DEBUG_LEVEL=0` and `libslic3r_gui` with `wxDEBUG_LEVEL=0`: every wx assert '*dep_wxWidgets-prefix/src/dep_wxWidgets'`). wx is built with `wxBUILD_DEBUG_LEVEL=0` and
quoted below is compiled out, so misuse fails silently (dropped call, stuck loop, freed memory), never with an assert dialog. "GTK" below `libslic3r_gui` with `wxDEBUG_LEVEL=0`: every wx assert quoted below is compiled out, so misuse
fails silently (dropped call, stuck loop, freed memory), never with an assert dialog. "GTK" below
means wxGTK as Orca builds it on Linux: GTK3 by default (X11 or Wayland); GTK2 is only an opt-out. means wxGTK as Orca builds it on Linux: GTK3 by default (X11 or Wayland); GTK2 is only an opt-out.
Contents: [Rules](#rules) · [1 Creating and parenting](#1-creating-and-parenting-windows) · Contents: [Rules](#rules) · [1 Creating and parenting](#1-creating-and-parenting-windows) ·
@@ -427,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 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 = 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 `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`), already counts as shown); macOS = `makeKeyAndOrderFront` only if shown (`src/osx/nonownedwnd_osx.cpp:289-295`, `src/osx/cocoa/nonownedwnd.mm:896-899`).
which also makes a `wxPopupWindow` the key window — never `Raise()` a popup (`references/popups-menus.md` §5, §10).
**Enable.** `Enable(false)` on a parent disables children logically: `IsEnabled()` reflects ancestors, **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 `IsThisEnabled()` the window's own flag (`interface/wx/window.h:3060-3070, 3116-3138`). **[source]** On MSW/macOS wx
@@ -14,7 +14,7 @@ Contents: [Rules](#rules) · [1 Reading the change logs](#1-reading-the-change-l
[8 Migration done in Orca](#8-migration-already-done-in-orca) [8 Migration done in Orca](#8-migration-already-done-in-orca)
All `docs/`, `interface/`, `include/`, `src/`, `build/` cites are relative to the pinned wx tree All `docs/`, `interface/`, `include/`, `src/`, `build/` cites are relative to the pinned wx tree
(located as in `SKILL.md` §Ground truth), except paths (`find deps -maxdepth 5 -type d -path '*dep_wxWidgets-prefix/src/dep_wxWidgets'`), except paths
explicitly called Orca's (`deps/…`, Orca's `src/CMakeLists.txt`) and bare Orca file + symbol cites. explicitly called Orca's (`deps/…`, Orca's `src/CMakeLists.txt`) and bare Orca file + symbol cites.
## Rules ## Rules
+17
View File
@@ -49,11 +49,28 @@ jobs:
key: ${{ inputs.cache-key }} key: ${{ inputs.cache-key }}
- uses: lukka/get-cmake@latest - 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: with:
cmakeVersion: "~4.3.0" # use most recent 4.3.x version cmakeVersion: "~4.3.0" # use most recent 4.3.x version
useLocalCache: true # <--= Use the local cache (default is 'false'). useLocalCache: true # <--= Use the local cache (default is 'false').
useCloudCache: true 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 - name: setup dev on Windows
if: runner.os == 'Windows' if: runner.os == 'Windows'
uses: microsoft/setup-msbuild@v3 uses: microsoft/setup-msbuild@v3
+17 -1
View File
@@ -54,11 +54,28 @@ jobs:
fail-on-cache-miss: true fail-on-cache-miss: true
- uses: lukka/get-cmake@latest - 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: with:
cmakeVersion: "~4.3.0" # use most recent 4.3.x version cmakeVersion: "~4.3.0" # use most recent 4.3.x version
useLocalCache: true # <--= Use the local cache (default is 'false'). useLocalCache: true # <--= Use the local cache (default is 'false').
useCloudCache: true 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 # Compiler cache. Pushes save it, so main keeps it warm; pull requests
# restore it and discard what they compiled. Objects are keyed on the # restore it and discard what they compiled. Objects are keyed on the
# preprocessed source, the compiler and the flags, so a leg only ever # preprocessed source, the compiler and the flags, so a leg only ever
@@ -259,7 +276,6 @@ jobs:
# Thanks to RaySajuuk, it's working now # Thanks to RaySajuuk, it's working now
- name: Sign app and notary - 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 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 }} working-directory: ${{ github.workspace }}
env: env:
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }} BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
+8
View File
@@ -44,6 +44,14 @@ jobs:
uses: actions/download-artifact@v8 uses: actions/download-artifact@v8
with: with:
name: ${{ inputs.artifact }} 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 - uses: lukka/get-cmake@latest
with: with:
cmakeVersion: "~4.3.0" # use most recent 4.3.x version 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_x86_64-apple-darwin "f3c8e5708a84b920c18b691214d54d2b0da6b984789caae95d47c95120cb7765")
set(ORCA_UV_SHA256_aarch64-unknown-linux-gnu "88e800834007cc5efd4675f166eb2a51e7e3ad19876d85fa8805a6fb5c922397") set(ORCA_UV_SHA256_aarch64-unknown-linux-gnu "88e800834007cc5efd4675f166eb2a51e7e3ad19876d85fa8805a6fb5c922397")
set(ORCA_UV_SHA256_x86_64-unknown-linux-gnu "8c88519b0ef0af9801fcdee419bbb12116bd9e6b18e162ae093c932d8b264050") 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") set(ORCA_UV_SHA256_x86_64-pc-windows-msvc "ace861f360c6de2babedc1607d0f454b6b09a820dbc8182dc15af927e4df9589")
# Version-scoped cache dir so a version bump invalidates the cached binary. # 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") set(ORCA_UV_ARCH "x86_64-unknown-linux-gnu")
endif() endif()
elseif(_orca_uv_proc MATCHES "aarch64|arm64|ARM64") elseif(_orca_uv_proc MATCHES "aarch64|arm64|ARM64")
if(WIN32) if(APPLE)
set(ORCA_UV_ARCH "aarch64-pc-windows-msvc")
set(ORCA_UV_EXT "zip")
elseif(APPLE)
set(ORCA_UV_ARCH "aarch64-apple-darwin") set(ORCA_UV_ARCH "aarch64-apple-darwin")
else() else()
set(ORCA_UV_ARCH "aarch64-unknown-linux-gnu") set(ORCA_UV_ARCH "aarch64-unknown-linux-gnu")
@@ -823,9 +819,7 @@ if(SLIC3R_STATIC)
set(TBB_STATIC 1) set(TBB_STATIC 1)
endif() endif()
set(TBB_DEBUG 1) set(TBB_DEBUG 1)
if ("${CMAKE_BUILD_TYPE}" STREQUAL "RelWithDebInfo" OR MSVC) set(CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO RelWithDebInfo Release "")
set(CMAKE_MAP_IMPORTED_CONFIG_RELWITHDEBINFO RelWithDebInfo Release "")
endif()
find_package(TBB REQUIRED) find_package(TBB REQUIRED)
# include_directories(SYSTEM ${TBB_INCLUDE_DIRS}) # include_directories(SYSTEM ${TBB_INCLUDE_DIRS})
# add_definitions(${TBB_DEFINITIONS}) # add_definitions(${TBB_DEFINITIONS})
@@ -1307,14 +1301,6 @@ if (WIN32)
endif() endif()
set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE) set(CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS_SKIP TRUE)
include(InstallRequiredSystemLibraries) 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 ".") install (PROGRAMS ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS} DESTINATION ".")
elseif (SLIC3R_FHS) elseif (SLIC3R_FHS)
# CMAKE_INSTALL_FULL_DATAROOTDIR: read-only architecture-independent data root (share) # 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 :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 :note_failed CMake !errorlevel!
call :print_and_run winget install !winget_args! --id=StrawberryPerl.StrawberryPerl call :print_and_run winget install !winget_args! --id=StrawberryPerl.StrawberryPerl
call :note_failed Perl !errorlevel! call :note_failed Perl !errorlevel!
-10
View File
@@ -29,15 +29,6 @@ if(WIN32)
# driven through nmake from a Ninja configure step. # driven through nmake from a Ninja configure step.
set(_conf_cmd ${CMAKE_COMMAND} -E env ${_openssl_msvc_env} perl Configure ) set(_conf_cmd ${CMAKE_COMMAND} -E env ${_openssl_msvc_env} perl Configure )
set(_cross_comp_prefix_line "") 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(_make_cmd ${CMAKE_COMMAND} -E env ${_openssl_msvc_env} nmake)
set(_install_cmd ${CMAKE_COMMAND} -E env ${_openssl_msvc_env} nmake install_sw ) set(_install_cmd ${CMAKE_COMMAND} -E env ${_openssl_msvc_env} nmake install_sw )
else() else()
@@ -80,7 +71,6 @@ ExternalProject_Add(dep_OpenSSL
# prefix stays single-layout. # prefix stays single-layout.
"--libdir=lib" "--libdir=lib"
${_cross_comp_prefix_line} ${_cross_comp_prefix_line}
${_openssl_extra_cflags}
no-shared no-shared
no-asm no-asm
no-ssl3-method no-ssl3-method
+1
View File
@@ -15,6 +15,7 @@ add_subdirectory(stb_dxt) # Header-only STB DXT compression library
# Static libraries # Static libraries
add_subdirectory(Shiny) add_subdirectory(Shiny)
add_subdirectory(admesh) add_subdirectory(admesh)
add_subdirectory(clipper)
add_subdirectory(clipper2) add_subdirectory(clipper2)
add_subdirectory(expat) add_subdirectory(expat)
add_subdirectory(glu-libtess) 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) 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_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD 17)
@@ -19,7 +19,6 @@ set(CLIPPER2_INC
Clipper2Lib/include/clipper2/clipper.minkowski.h Clipper2Lib/include/clipper2/clipper.minkowski.h
Clipper2Lib/include/clipper2/clipper.offset.h Clipper2Lib/include/clipper2/clipper.offset.h
Clipper2Lib/include/clipper2/clipper.rectclip.h Clipper2Lib/include/clipper2/clipper.rectclip.h
Clipper2Lib/include/clipper2/clipper.triangulation.h
Clipper2Lib/include/clipper2/clipper2_z.hpp Clipper2Lib/include/clipper2/clipper2_z.hpp
) )
@@ -27,7 +26,6 @@ set(CLIPPER2_SRC
Clipper2Lib/src/clipper.engine.cpp Clipper2Lib/src/clipper.engine.cpp
Clipper2Lib/src/clipper.offset.cpp Clipper2Lib/src/clipper.offset.cpp
Clipper2Lib/src/clipper.rectclip.cpp Clipper2Lib/src/clipper.rectclip.cpp
Clipper2Lib/src/clipper.triangulation.cpp
Clipper2Lib/src/clipper2_z.cpp Clipper2Lib/src/clipper2_z.cpp
) )
@@ -38,9 +36,6 @@ target_include_directories(Clipper2
PUBLIC Clipper2Lib/include PUBLIC Clipper2Lib/include
) )
# Engine nodes are allocated through tbbmalloc (see clipper.engine.cpp).
target_link_libraries(Clipper2 PRIVATE TBB::tbbmalloc)
if (WIN32) if (WIN32)
if (MSVC AND NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang") if (MSVC AND NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
target_compile_options(Clipper2 PRIVATE /W4 /WX) target_compile_options(Clipper2 PRIVATE /W4 /WX)
@@ -1,8 +1,8 @@
/******************************************************************************* /*******************************************************************************
* Author : Angus Johnson * * Author : Angus Johnson *
* Date : 12 October 2025 * * Date : 12 May 2024 *
* Website : https://www.angusj.com * * Website : https://www.angusj.com *
* Copyright : Angus Johnson 2010-2025 * * Copyright : Angus Johnson 2010-2024 *
* Purpose : Core Clipper Library structures and functions * * Purpose : Core Clipper Library structures and functions *
* License : https://www.boost.org/LICENSE_1_0.txt * * License : https://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/ *******************************************************************************/
@@ -251,20 +251,6 @@ namespace Clipper2Lib {
template <typename T> template <typename T>
using Paths = std::vector<Path<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 Path64 = Path<int64_t>;
using PathD = Path<double>; using PathD = Path<double>;
using Paths64 = std::vector< Path64>; using Paths64 = std::vector< Path64>;
@@ -702,28 +688,29 @@ namespace Clipper2Lib {
return (x > 0) - (x < 0); return (x > 0) - (x < 0);
} }
struct UInt128Struct struct MultiplyUInt64Result
{ {
const uint64_t lo = 0; const uint64_t result = 0;
const uint64_t hi = 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 lo = [](uint64_t x) { return x & 0xFFFFFFFF; };
const auto hi = [](uint64_t x) { return x >> 32; }; const auto hi = [](uint64_t x) { return x >> 32; };
const uint64_t x1 = lo(a) * lo(b); const uint64_t x1 = lo(a) * lo(b);
const uint64_t x2 = hi(a) * lo(b) + hi(x1); const uint64_t x2 = hi(a) * lo(b) + hi(x1);
const uint64_t x3 = lo(a) * hi(b) + lo(x2); 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 // 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_c = static_cast<uint64_t>(std::abs(c));
const auto abs_d = static_cast<uint64_t>(std::abs(d)); const auto abs_d = static_cast<uint64_t>(std::abs(d));
const auto ab = MultiplyUInt64(abs_a, abs_b); const auto abs_ab = Multiply(abs_a, abs_b);
const auto cd = MultiplyUInt64(abs_c, abs_d); const auto abs_cd = Multiply(abs_c, abs_d);
// nb: it's important to differentiate 0 values here from other values // nb: it's important to differentiate 0 values here from other values
const auto sign_ab = TriSign(a) * TriSign(b); const auto sign_ab = TriSign(a) * TriSign(b);
const auto sign_cd = TriSign(c) * TriSign(d); const auto sign_cd = TriSign(c) * TriSign(d);
return ab == cd && sign_ab == sign_cd; return abs_ab == abs_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;
#endif #endif
} }
@@ -887,10 +838,6 @@ namespace Clipper2Lib {
return Area<T>(poly) >= 0; 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 #if CLIPPER2_HI_PRECISION
// caution: this will compromise performance // caution: this will compromise performance
// https://github.com/AngusJohnson/Clipper2/issues/317#issuecomment-1314023253 // 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_MIN(x,y) ((x)>(y)?(y):(x))
#define CC_MAX(x,y) ((x)<(y)?(y):(x)) #define CC_MAX(x,y) ((x)<(y)?(y):(x))
template<typename T> 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) const Point<T>& ln2a, const Point<T>& ln2b, Point<T>& ip)
{ {
double ln1dy = static_cast<double>(ln1b.y - ln1a.y); double ln1dy = static_cast<double>(ln1b.y - ln1a.y);
@@ -944,14 +891,11 @@ namespace Clipper2Lib {
ip.x = originx + static_cast<T>(hitx); ip.x = originx + static_cast<T>(hitx);
ip.y = originy + static_cast<T>(hity); ip.y = originy + static_cast<T>(hity);
} }
#ifdef USINGZ
ip.z = 0;
#endif
return true; return true;
} }
#else #else
template<typename T> 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) const Point<T>& ln2a, const Point<T>& ln2b, Point<T>& ip)
{ {
// https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection // 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.x = static_cast<T>(ln1a.x + t * dx1);
ip.y = static_cast<T>(ln1a.y + t * dy1); ip.y = static_cast<T>(ln1a.y + t * dy1);
#ifdef USINGZ }
ip.z = 0;
#endif
}
return true; return true;
} }
#endif #endif
@@ -1008,44 +949,21 @@ namespace Clipper2Lib {
inline bool SegmentsIntersect(const Point64& seg1a, const Point64& seg1b, inline bool SegmentsIntersect(const Point64& seg1a, const Point64& seg1b,
const Point64& seg2a, const Point64& seg2b, bool inclusive = false) 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) if (inclusive)
{ {
//result **includes** segments that touch at an end point double res1 = CrossProduct(seg1a, seg2a, seg2b);
double t = ((seg1a.x - seg2a.x) * dy2 - (seg1a.y - seg2a.y) * dx2); double res2 = CrossProduct(seg1b, seg2a, seg2b);
if (t == 0) return true; if (res1 * res2 > 0) return false;
if (t > 0) double res3 = CrossProduct(seg2a, seg1a, seg1b);
{ double res4 = CrossProduct(seg2b, seg1a, seg1b);
if (cp < 0 || t > cp) return false; if (res3 * res4 > 0) return false;
} return (res1 || res2 || res3 || res4); // ensures not collinear
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
} }
else else {
{ return (GetSign(CrossProduct(seg1a, seg2a, seg2b)) *
//result **excludes** segments that touch at an end point GetSign(CrossProduct(seg1b, seg2a, seg2b)) < 0) &&
double t = ((seg1a.x - seg2a.x) * dy2 - (seg1a.y - seg2a.y) * dx2); (GetSign(CrossProduct(seg2a, seg1a, seg1b)) *
if (t == 0) return false; GetSign(CrossProduct(seg2b, seg1a, seg1b)) < 0);
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
} }
} }
@@ -1133,7 +1051,7 @@ namespace Clipper2Lib {
val = 1 - val; // toggle val val = 1 - val; // toggle val
else else
{ {
int d = CrossProductSign(*prev, *curr, pt); double d = CrossProduct(*prev, *curr, pt);
if (d == 0) return PointInPolygonResult::IsOn; if (d == 0) return PointInPolygonResult::IsOn;
if ((d < 0) == is_above) val = 1 - val; if ((d < 0) == is_above) val = 1 - val;
} }
@@ -1147,7 +1065,7 @@ namespace Clipper2Lib {
if (curr == cend) curr = cbegin; if (curr == cend) curr = cbegin;
if (curr == cbegin) prev = cend - 1; if (curr == cbegin) prev = cend - 1;
else prev = curr - 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) return PointInPolygonResult::IsOn;
if ((d < 0) == is_above) val = 1 - val; if ((d < 0) == is_above) val = 1 - val;
} }
@@ -15,13 +15,6 @@
#include <functional> #include <functional>
#include <memory> #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 #ifdef USINGZ
namespace Clipper2Lib_Z { namespace Clipper2Lib_Z {
#else #else
@@ -57,7 +50,6 @@ namespace Clipper2Lib {
} }
struct Vertex { struct Vertex {
CLIPPER2_NODE_ALLOCATOR
Point64 pt; Point64 pt;
Vertex* next = nullptr; Vertex* next = nullptr;
Vertex* prev = nullptr; Vertex* prev = nullptr;
@@ -65,7 +57,6 @@ namespace Clipper2Lib {
}; };
struct OutPt { struct OutPt {
CLIPPER2_NODE_ALLOCATOR
Point64 pt; Point64 pt;
OutPt* next = nullptr; OutPt* next = nullptr;
OutPt* prev = nullptr; OutPt* prev = nullptr;
@@ -90,7 +81,6 @@ namespace Clipper2Lib {
//OutRec: contains a path in the clipping solution. Edges in the AEL will //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. //have OutRec pointers assigned when they form part of the clipping solution.
struct OutRec { struct OutRec {
CLIPPER2_NODE_ALLOCATOR
size_t idx = 0; size_t idx = 0;
OutRec* owner = nullptr; OutRec* owner = nullptr;
Active* front_edge = nullptr; Active* front_edge = nullptr;
@@ -116,7 +106,6 @@ namespace Clipper2Lib {
/////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////
struct Active { struct Active {
CLIPPER2_NODE_ALLOCATOR
Point64 bot; Point64 bot;
Point64 top; Point64 top;
int64_t curr_x = 0; //current (updated at every new scanline) int64_t curr_x = 0; //current (updated at every new scanline)
@@ -144,7 +133,6 @@ namespace Clipper2Lib {
}; };
struct LocalMinima { struct LocalMinima {
CLIPPER2_NODE_ALLOCATOR
Vertex* vertex; Vertex* vertex;
PathType polytype; PathType polytype;
bool is_open; bool is_open;
@@ -315,7 +303,6 @@ namespace Clipper2Lib {
protected: protected:
PolyPath* parent_; PolyPath* parent_;
public: public:
CLIPPER2_NODE_ALLOCATOR
PolyPath(PolyPath* parent = nullptr): parent_(parent){} PolyPath(PolyPath* parent = nullptr): parent_(parent){}
virtual ~PolyPath() {}; virtual ~PolyPath() {};
//https://en.cppreference.com/w/cpp/language/rule_of_three //https://en.cppreference.com/w/cpp/language/rule_of_three
@@ -343,16 +330,15 @@ namespace Clipper2Lib {
//Even levels except level 0 //Even levels except level 0
return lvl && !(lvl & 1); return lvl && !(lvl & 1);
} }
// Area() of the namespace this header is compiled into (Clipper2Lib or Clipper2Lib_Z). template<typename T>
template<typename T> static double Clipper2LibArea(const Path<T> &poly)
static double Clipper2LibArea(const Path<T> &poly) {
{
#ifdef USINGZ #ifdef USINGZ
return Clipper2Lib_Z::Area<T>(poly); return Clipper2Lib_Z::Area<T>(poly);
#else #else
return Clipper2Lib::Area<T>(poly); return Clipper2Lib::Area<T>(poly);
#endif #endif
} }
}; };
typedef typename std::vector<std::unique_ptr<PolyPath64>> PolyPath64List; typedef typename std::vector<std::unique_ptr<PolyPath64>> PolyPath64List;
@@ -402,8 +388,7 @@ namespace Clipper2Lib {
double Area() const double Area() const
{ {
return std::accumulate(childs_.cbegin(), childs_.cend(), return std::accumulate(childs_.cbegin(), childs_.cend(), Clipper2LibArea<int64_t>(polygon_),
Clipper2LibArea<int64_t>(polygon_),
[](double a, const auto& child) {return a + child->Area(); }); [](double a, const auto& child) {return a + child->Area(); });
} }
@@ -477,8 +462,7 @@ namespace Clipper2Lib {
double Area() const double Area() const
{ {
return std::accumulate(childs_.begin(), childs_.end(), return std::accumulate(childs_.begin(), childs_.end(), Clipper2LibArea<double>(polygon_),
Clipper2LibArea<double>(polygon_),
[](double a, const auto& child) {return a + child->Area(); }); [](double a, const auto& child) {return a + child->Area(); });
} }
}; };
@@ -116,10 +116,13 @@ the four vertices that define the two segments that are intersecting.
#include "clipper2/clipper.engine.h" #include "clipper2/clipper.engine.h"
#include "clipper2/clipper.offset.h" #include "clipper2/clipper.offset.h"
#include "clipper2/clipper.rectclip.h" #include "clipper2/clipper.rectclip.h"
#include "clipper2/clipper.triangulation.h"
#include <cstdlib> #include <cstdlib>
#ifdef USINGZ
namespace Clipper2Lib_Z {
#else
namespace Clipper2Lib { namespace Clipper2Lib {
#endif
typedef int64_t* CPath64; typedef int64_t* CPath64;
typedef int64_t* CPaths64; typedef int64_t* CPaths64;
@@ -813,24 +816,6 @@ EXTERN_DLL_EXPORT CPaths64 MinkowskiDiff64(const CPath64& cpattern, const CPath6
return CreateCPathsFromPathsT(solution); 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 #ifdef USINGZ
typedef void (*DLLZCallback64)(const Point64& e1bot, const Point64& e1top, const Point64& e2bot, const Point64& e2top, Point64& pt); 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); typedef void (*DLLZCallbackD)(const PointD& e1bot, const PointD& e1top, const PointD& e2bot, const PointD& e2top, PointD& pt);
@@ -1,8 +1,8 @@
/******************************************************************************* /*******************************************************************************
* Author : Angus Johnson * * Author : Angus Johnson *
* Date : 5 March 2025 * * Date : 27 April 2024 *
* Website : https://www.angusj.com * * 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 * * Purpose : This module provides a simple interface to the Clipper Library *
* License : https://www.boost.org/LICENSE_1_0.txt * * License : https://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/ *******************************************************************************/
@@ -13,15 +13,14 @@
#include "clipper2/clipper.core.h" #include "clipper2/clipper.core.h"
#include "clipper2/clipper.engine.h" #include "clipper2/clipper.engine.h"
#include "clipper2/clipper.offset.h" #include "clipper2/clipper.offset.h"
#include "clipper2/clipper.rectclip.h"
#include "clipper2/clipper.minkowski.h" #include "clipper2/clipper.minkowski.h"
#include "clipper2/clipper.triangulation.h" #include "clipper2/clipper.rectclip.h"
#include <type_traits> #include <type_traits>
#ifdef USINGZ #ifdef USINGZ
namespace Clipper2Lib_Z { namespace Clipper2Lib_Z {
#else #else
namespace Clipper2Lib { namespace Clipper2Lib {
#endif #endif
inline Paths64 BooleanOp(ClipType cliptype, FillRule fillrule, inline Paths64 BooleanOp(ClipType cliptype, FillRule fillrule,
@@ -155,7 +154,7 @@ namespace Clipper2Lib {
if (!delta) return paths; if (!delta) return paths;
if (error_code) return PathsD(); if (error_code) return PathsD();
const double scale = std::pow(10, precision); 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); clip_offset.AddPaths(ScalePaths<int64_t,double>(paths, scale, error_code), jt, et);
if (error_code) return PathsD(); if (error_code) return PathsD();
Paths64 solution; Paths64 solution;
@@ -356,29 +355,6 @@ namespace Clipper2Lib {
#endif #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 } // end details namespace
inline std::ostream& operator<< (std::ostream& os, const PolyTree64& pp) inline std::ostream& operator<< (std::ostream& os, const PolyTree64& pp)
@@ -639,6 +615,29 @@ namespace Clipper2Lib {
return result; 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> template <typename T>
inline Path<T> SimplifyPath(const Path<T> &path, inline Path<T> SimplifyPath(const Path<T> &path,
double epsilon, bool isClosedPath = true) double epsilon, bool isClosedPath = true)
@@ -670,13 +669,13 @@ namespace Clipper2Lib {
start = curr; start = curr;
do do
{ {
curr = details::GetNext(curr, high, flags); curr = GetNext(curr, high, flags);
} while (curr != start && distSqr[curr] > epsSqr); } while (curr != start && distSqr[curr] > epsSqr);
if (curr == start) break; if (curr == start) break;
} }
prior = details::GetPrior(curr, high, flags); prior = GetPrior(curr, high, flags);
next = details::GetNext(curr, high, flags); next = GetNext(curr, high, flags);
if (next == prior) break; if (next == prior) break;
// flag for removal the smaller of adjacent 'distances' // flag for removal the smaller of adjacent 'distances'
@@ -685,14 +684,14 @@ namespace Clipper2Lib {
prior2 = prior; prior2 = prior;
prior = curr; prior = curr;
curr = next; curr = next;
next = details::GetNext(next, high, flags); next = GetNext(next, high, flags);
} }
else else
prior2 = details::GetPrior(prior, high, flags); prior2 = GetPrior(prior, high, flags);
flags[curr] = true; flags[curr] = true;
curr = next; curr = next;
next = details::GetNext(next, high, flags); next = GetNext(next, high, flags);
if (isClosedPath || ((curr != high) && (curr != 0))) if (isClosedPath || ((curr != high) && (curr != 0)))
distSqr[curr] = PerpendicDistFromLineSqrd(path[curr], path[prior], path[next]); distSqr[curr] = PerpendicDistFromLineSqrd(path[curr], path[prior], path[next]);
@@ -717,35 +716,6 @@ namespace Clipper2Lib {
return result; 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> template <typename T>
inline void RDP(const Path<T> path, std::size_t begin, inline void RDP(const Path<T> path, std::size_t begin,
std::size_t end, double epsSqrd, std::vector<bool>& flags) std::size_t end, double epsSqrd, std::vector<bool>& flags)
@@ -39,7 +39,7 @@ private:
class Group { class Group {
public: public:
Paths64 paths_in; Paths64 paths_in;
std::optional<size_t> lowest_path_idx{}; std::optional<size_t> lowest_path_idx{};
bool is_reversed = false; bool is_reversed = false;
JoinType join_type; JoinType join_type;
EndType end_type; EndType end_type;
@@ -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 #ifndef CLIPPER_VERSION_H
#define 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 #endif // CLIPPER_VERSION_H
@@ -1,8 +1,8 @@
/******************************************************************************* /*******************************************************************************
* Author : Angus Johnson * * Author : Angus Johnson *
* Date : 5 November 2025 * * Date : 17 September 2024 *
* Website : https://www.angusj.com * * Website : https://www.angusj.com *
* Copyright : Angus Johnson 2010-2025 * * Copyright : Angus Johnson 2010-2024 *
* Purpose : This is the main polygon clipping module * * Purpose : This is the main polygon clipping module *
* License : https://www.boost.org/LICENSE_1_0.txt * * License : https://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/ *******************************************************************************/
@@ -10,8 +10,6 @@
#include "clipper2/clipper.engine.h" #include "clipper2/clipper.engine.h"
#include "clipper2/clipper.h" #include "clipper2/clipper.h"
#include <stdexcept> #include <stdexcept>
#include <new>
#include <oneapi/tbb/scalable_allocator.h>
// https://github.com/AngusJohnson/Clipper2/discussions/334 // https://github.com/AngusJohnson/Clipper2/discussions/334
// #discussioncomment-4248602 // #discussioncomment-4248602
@@ -29,26 +27,6 @@ namespace Clipper2Lib_Z {
namespace Clipper2Lib { namespace Clipper2Lib {
#endif #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); static const Rect64 invalid_rect = Rect64(false);
// Every closed path (ie polygon) is made up of a series of vertices forming edge // Every closed path (ie polygon) is made up of a series of vertices forming edge
@@ -504,7 +482,8 @@ namespace Clipper2Lib {
inline void SetOwner(OutRec* outrec, OutRec* new_owner) inline void SetOwner(OutRec* outrec, OutRec* new_owner)
{ {
//precondition1: new_owner is never null //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; OutRec* tmp = new_owner;
while (tmp && tmp != outrec) tmp = tmp->owner; while (tmp && tmp != outrec) tmp = tmp->owner;
if (tmp) new_owner->owner = outrec->owner; if (tmp) new_owner->owner = outrec->owner;
@@ -557,9 +536,9 @@ namespace Clipper2Lib {
val = 1 - val; // toggle val val = 1 - val; // toggle val
else else
{ {
int i = CrossProductSign(op2->prev->pt, op2->pt, pt); double d = CrossProduct(op2->prev->pt, op2->pt, pt);
if (i == 0) return PointInPolygonResult::IsOn; if (d == 0) return PointInPolygonResult::IsOn;
if ((i < 0) == is_above) val = 1 - val; if ((d < 0) == is_above) val = 1 - val;
} }
is_above = !is_above; is_above = !is_above;
op2 = op2->next; op2 = op2->next;
@@ -567,9 +546,9 @@ namespace Clipper2Lib {
if (is_above != starting_above) if (is_above != starting_above)
{ {
int i = CrossProductSign(op2->prev->pt, op2->pt, pt); double d = CrossProduct(op2->prev->pt, op2->pt, pt);
if (i == 0) return PointInPolygonResult::IsOn; if (d == 0) return PointInPolygonResult::IsOn;
if ((i < 0) == is_above) val = 1 - val; if ((d < 0) == is_above) val = 1 - val;
} }
if (val == 0) return PointInPolygonResult::IsOutside; if (val == 0) return PointInPolygonResult::IsOutside;
@@ -599,31 +578,30 @@ namespace Clipper2Lib {
return result; return result;
} }
inline bool Path2ContainsPath1(OutPt* op1, OutPt* op2) inline bool Path1InsidePath2(OutPt* op1, OutPt* op2)
{ {
// this function accommodates rounding errors that // we need to make some accommodation for rounding errors
// can cause path micro intersections // so we won't jump if the first vertex is found outside
PointInPolygonResult pip = PointInPolygonResult::IsOn; PointInPolygonResult result;
int outside_cnt = 0;
OutPt* op = op1; OutPt* op = op1;
do { do
switch (PointInOpPolygon(op->pt, op2)) {
{ result = PointInOpPolygon(op->pt, op2);
case PointInPolygonResult::IsOutside: if (result == PointInPolygonResult::IsOutside) ++outside_cnt;
if (pip == PointInPolygonResult::IsOutside) return false; else if (result == PointInPolygonResult::IsInside) --outside_cnt;
pip = PointInPolygonResult::IsOutside;
break;
case PointInPolygonResult::IsInside:
if (pip == PointInPolygonResult::IsInside) return true;
pip = PointInPolygonResult::IsInside;
break;
default: break;
}
op = op->next; op = op->next;
} while (op != op1); } while (op != op1 && std::abs(outside_cnt) < 2);
// result unclear, so try again using cleaned paths if (std::abs(outside_cnt) > 1) return (outside_cnt < 0);
return Path2ContainsPath1(GetCleanPath(op1), GetCleanPath(op2)); // (#973) // 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, void AddLocMin(LocalMinimaList& list,
Vertex& vert, PathType polytype, bool is_open) Vertex& vert, PathType polytype, bool is_open)
{ {
@@ -1148,19 +1126,21 @@ namespace Clipper2Lib {
return newcomer.curr_x > resident.curr_x; return newcomer.curr_x > resident.curr_x;
//get the turning direction a1.top, a2.bot, a2.top //get the turning direction a1.top, a2.bot, a2.top
int i = CrossProductSign(resident.top, newcomer.bot, newcomer.top); double d = CrossProduct(resident.top, newcomer.bot, newcomer.top);
if (i != 0) return i < 0; if (d != 0) return d < 0;
//edges must be collinear to get here //edges must be collinear to get here
//for starting open paths, place them according to //for starting open paths, place them according to
//the direction they're about to turn //the direction they're about to turn
if (!IsMaxima(resident) && (resident.top.y > newcomer.top.y)) 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)) 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; int64_t y = newcomer.bot.y;
@@ -1175,7 +1155,7 @@ namespace Clipper2Lib {
resident.bot, resident.top)) return true; resident.bot, resident.top)) return true;
else else
//compare turning direction of the alternate bound //compare turning direction of the alternate bound
return (CrossProductSign(PrevPrevVertex(resident)->pt, return (CrossProduct(PrevPrevVertex(resident)->pt,
newcomer.bot, PrevPrevVertex(newcomer)->pt) > 0) == newcomerIsLeft; newcomer.bot, PrevPrevVertex(newcomer)->pt) > 0) == newcomerIsLeft;
} }
@@ -1585,7 +1565,7 @@ namespace Clipper2Lib {
FixSelfIntersects(outrec); FixSelfIntersects(outrec);
} }
void ClipperBase::DoSplitOp (OutRec* outrec, OutPt* splitOp) void ClipperBase::DoSplitOp(OutRec* outrec, OutPt* splitOp)
{ {
// splitOp.prev -> splitOp && // splitOp.prev -> splitOp &&
// splitOp.next -> splitOp.next.next are intersecting // splitOp.next -> splitOp.next.next are intersecting
@@ -1594,7 +1574,7 @@ namespace Clipper2Lib {
outrec->pts = prevOp; outrec->pts = prevOp;
Point64 ip; Point64 ip;
GetLineIntersectPt(prevOp->pt, splitOp->pt, GetSegmentIntersectPt(prevOp->pt, splitOp->pt,
splitOp->next->pt, nextNextOp->pt, ip); splitOp->next->pt, nextNextOp->pt, ip);
#ifdef USINGZ #ifdef USINGZ
@@ -1650,7 +1630,7 @@ namespace Clipper2Lib {
if (using_polytree_) if (using_polytree_)
{ {
if (Path2ContainsPath1(prevOp, newOp)) if (Path1InsidePath2(prevOp, newOp))
{ {
newOr->splits = new OutRecList(); newOr->splits = new OutRecList();
newOr->splits->emplace_back(outrec); newOr->splits->emplace_back(outrec);
@@ -1672,32 +1652,19 @@ namespace Clipper2Lib {
void ClipperBase::FixSelfIntersects(OutRec* outrec) void ClipperBase::FixSelfIntersects(OutRec* outrec)
{ {
OutPt* op2 = outrec->pts; OutPt* op2 = outrec->pts;
if (op2->prev == op2->next->next)
return; // because triangles can't self-intersect
for (; ; ) for (; ; )
{ {
// triangles can't self-intersect
if (op2->prev == op2->next->next) break;
if (SegmentsIntersect(op2->prev->pt, if (SegmentsIntersect(op2->prev->pt,
op2->pt, op2->next->pt, op2->next->next->pt)) op2->pt, op2->next->pt, op2->next->next->pt))
{ {
if (SegmentsIntersect(op2->prev->pt, if (op2 == outrec->pts || op2->next == outrec->pts)
op2->pt, op2->next->next->pt, op2->next->next->next->pt)) outrec->pts = outrec->pts->prev;
{ DoSplitOp(outrec, op2);
// adjacent intersections (ie a micro self-intersections) if (!outrec->pts) break;
op2 = DuplicateOp(op2, false); op2 = outrec->pts;
op2->pt = op2->next->next->next->pt; continue;
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;
}
} }
else else
op2 = op2->next; op2 = op2->next;
@@ -2145,9 +2112,10 @@ namespace Clipper2Lib {
e->prev_in_sel = e->prev_in_ael; e->prev_in_sel = e->prev_in_ael;
e->next_in_sel = e->next_in_ael; e->next_in_sel = e->next_in_ael;
e->jump = e->next_in_sel; e->jump = e->next_in_sel;
// it is safe to ignore 'joined' edges here because if (e->join_with == JoinWith::Left)
// if necessary they will be split in IntersectEdges() e->curr_x = e->prev_in_ael->curr_x; // also avoids complications
e->curr_x = TopX(*e, top_y); else
e->curr_x = TopX(*e, top_y);
e = e->next_in_ael; e = e->next_in_ael;
} }
} }
@@ -2294,14 +2262,15 @@ namespace Clipper2Lib {
void MoveSplits(OutRec* fromOr, OutRec* toOr) void MoveSplits(OutRec* fromOr, OutRec* toOr)
{ {
if (!fromOr->splits) return;
if (!toOr->splits) toOr->splits = new OutRecList(); if (!toOr->splits) toOr->splits = new OutRecList();
OutRecList::iterator orIter = fromOr->splits->begin(); OutRecList::iterator orIter = fromOr->splits->begin();
for (; orIter != fromOr->splits->end(); ++orIter) for (; orIter != fromOr->splits->end(); ++orIter)
if (toOr != *orIter) // #987 toOr->splits->emplace_back(*orIter);
toOr->splits->emplace_back(*orIter);
fromOr->splits->clear(); fromOr->splits->clear();
} }
void ClipperBase::ProcessHorzJoins() void ClipperBase::ProcessHorzJoins()
{ {
for (const HorzJoin& j : horz_join_list_) for (const HorzJoin& j : horz_join_list_)
@@ -2331,7 +2300,7 @@ namespace Clipper2Lib {
if (using_polytree_) //#498, #520, #584, D#576, #618 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 //swap or1's & or2's pts
OutPt* tmp = or1->pts; OutPt* tmp = or1->pts;
@@ -2342,7 +2311,7 @@ namespace Clipper2Lib {
//or2 is now inside or1 //or2 is now inside or1
or2->owner = or1; or2->owner = or1;
} }
else if (Path2ContainsPath1(or2->pts, or1->pts)) else if (Path1InsidePath2(or2->pts, or1->pts))
{ {
or2->owner = or1; or2->owner = or1;
} }
@@ -2355,14 +2324,13 @@ namespace Clipper2Lib {
else else
or2->owner = or1; or2->owner = or1;
} }
else // joining, not splitting else
{ {
or2->pts = nullptr; or2->pts = nullptr;
if (using_polytree_) if (using_polytree_)
{ {
SetOwner(or2, or1); SetOwner(or2, or1);
if (or2->splits) MoveSplits(or2, or1); //#618
MoveSplits(or2, or1); //#618
} }
else else
or2->owner = or1; or2->owner = or1;
@@ -2382,7 +2350,7 @@ namespace Clipper2Lib {
void ClipperBase::AddNewIntersectNode(Active& e1, Active& e2, int64_t top_y) void ClipperBase::AddNewIntersectNode(Active& e1, Active& e2, int64_t top_y)
{ {
Point64 ip; 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 ip = Point64(e1.curr_x, top_y); //parallel edges
//rounding errors can occasionally place the calculated intersection //rounding errors can occasionally place the calculated intersection
@@ -2966,28 +2934,22 @@ namespace Clipper2Lib {
bool ClipperBase::CheckSplitOwner(OutRec* outrec, OutRecList* splits) bool ClipperBase::CheckSplitOwner(OutRec* outrec, OutRecList* splits)
{ {
// nb: use indexing (not an iterator) in case 'splits' is modified inside this loop (#1029) for (auto split : *splits)
for (size_t idx = 0; idx < splits->size(); ++idx)
{ {
OutRec* split = (*splits)[idx];
if (!split->pts && split->splits &&
CheckSplitOwner(outrec, split->splits)) return true; //#942
split = GetRealOutRec(split); 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 split->recursive_split = outrec; // prevent infinite loops
if (split->splits && CheckSplitOwner(outrec, split->splits)) if (split->splits && CheckSplitOwner(outrec, split->splits))
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 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 false; return false;
} }
@@ -2998,12 +2960,13 @@ namespace Clipper2Lib {
// post-condition: if a valid path, outrec will have a polypath // post-condition: if a valid path, outrec will have a polypath
if (outrec->polypath || outrec->bounds.IsEmpty()) return; if (outrec->polypath || outrec->bounds.IsEmpty()) return;
while (outrec->owner) while (outrec->owner)
{ {
if (outrec->owner->splits && CheckSplitOwner(outrec, outrec->owner->splits)) break; if (outrec->owner->splits && CheckSplitOwner(outrec, outrec->owner->splits)) break;
if (outrec->owner->pts && CheckBounds(outrec->owner) && if (outrec->owner->pts && CheckBounds(outrec->owner) &&
outrec->owner->bounds.Contains(outrec->bounds) && 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; outrec->owner = outrec->owner->owner;
} }
@@ -3066,7 +3029,6 @@ namespace Clipper2Lib {
{ {
OutRec* outrec = outrec_list_[i]; OutRec* outrec = outrec_list_[i];
if (!outrec || !outrec->pts) continue; if (!outrec || !outrec->pts) continue;
if (outrec->is_open) if (outrec->is_open)
{ {
Path64 path; Path64 path;
@@ -1,6 +1,6 @@
/******************************************************************************* /*******************************************************************************
* Author : Angus Johnson * * Author : Angus Johnson *
* Date : 11 October 2025 * * Date : 22 January 2025 *
* Website : https://www.angusj.com * * Website : https://www.angusj.com *
* Copyright : Angus Johnson 2010-2025 * * Copyright : Angus Johnson 2010-2025 *
* Purpose : Path Offset (Inflate/Shrink) * * Purpose : Path Offset (Inflate/Shrink) *
@@ -37,28 +37,22 @@ const double arc_const = 0.002; // <-- 1/500
// Miscellaneous methods // 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); Point64 botPt = Point64(INT64_MAX, INT64_MIN);
for (size_t i = 0; i < paths.size(); ++i) for (size_t i = 0; i < paths.size(); ++i)
{ {
double a = MAX_DBL;
for (const Point64& pt : paths[i]) for (const Point64& pt : paths[i])
{ {
if ((pt.y < botPt.y) || if ((pt.y < botPt.y) ||
((pt.y == botPt.y) && (pt.x >= botPt.x))) continue; ((pt.y == botPt.y) && (pt.x >= botPt.x))) continue;
if (a == MAX_DBL) result = i;
{
a = Area(paths[i]);
if (a == 0) break; // invalid closed path, so break from inner loop
is_neg_area = a < 0;
}
idx = i;
botPt.x = pt.x; botPt.x = pt.x;
botPt.y = pt.y; botPt.y = pt.y;
} }
} }
return result;
} }
inline double Hypot(double x, double y) inline double Hypot(double x, double y)
@@ -151,16 +145,15 @@ ClipperOffset::Group::Group(const Paths64& _paths, JoinType _join_type, EndType
if (end_type == EndType::Polygon) if (end_type == EndType::Polygon)
{ {
bool is_neg_area; lowest_path_idx = GetLowestClosedPathIdx(paths_in);
GetLowestClosedPathInfo(paths_in, lowest_path_idx, is_neg_area);
// the lowermost path must be an outer path, so if its orientation is negative, // 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.) // then flag the whole group is 'reversed' (will negate delta etc.)
// as this is much more efficient than reversing every path. // 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 else
{ {
lowest_path_idx.reset(); lowest_path_idx = std::nullopt;
is_reversed = false; 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 pt4 = PointD(pt3.x + vec.x * group_delta_, pt3.y + vec.y * group_delta_);
PointD pt = ptQ; PointD pt = ptQ;
GetLineIntersectPt(pt1, pt2, pt3, pt4, pt); GetSegmentIntersectPt(pt1, pt2, pt3, pt4, pt);
//get the second intersect point through reflecion //get the second intersect point through reflecion
path_out.emplace_back(ReflectPoint(pt, ptQ)); path_out.emplace_back(ReflectPoint(pt, ptQ));
path_out.emplace_back(pt); 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 pt4 = GetPerpendicD(path[j], norms[k], group_delta_);
PointD pt = ptQ; PointD pt = ptQ;
GetLineIntersectPt(pt1, pt2, pt3, pt4, pt); GetSegmentIntersectPt(pt1, pt2, pt3, pt4, pt);
path_out.emplace_back(pt); path_out.emplace_back(pt);
//get the second intersect point through reflecion //get the second intersect point through reflecion
path_out.emplace_back(ReflectPoint(pt, ptQ)); 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 #else
path_out.emplace_back(pt.x + offsetVec.x, pt.y + offsetVec.y); path_out.emplace_back(pt.x + offsetVec.x, pt.y + offsetVec.y);
#endif #endif
// Orca: round the step count like Clipper1 did, so round offsets keep their vertices. int steps = static_cast<int>(std::ceil(steps_per_rad_ * std::abs(angle))); // #448, #456
int steps = std::max(static_cast<int>(std::round(steps_per_rad_ * std::abs(angle))), 1);
for (int i = 1; i < steps; ++i) // ie 1 less than steps for (int i = 1; i < steps; ++i) // ie 1 less than steps
{ {
offsetVec = PointD(offsetVec.x * step_cos_ - step_sin_ * offsetVec.y, offsetVec = PointD(offsetVec.x * step_cos_ - step_sin_ * offsetVec.y,
@@ -374,31 +366,11 @@ void ClipperOffset::OffsetPoint(Group& group, const Path64& path, size_t j, size
DoSquare(path, j, k); 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) void ClipperOffset::OffsetPolygon(Group& group, const Path64& path)
{ {
path_out.clear(); path_out.clear();
for (Path64::size_type j = 0, k = path.size() - 1; j < path.size(); k = j, ++j) for (Path64::size_type j = 0, k = path.size() - 1; j < path.size(); k = j, ++j)
if (deltaCallback64_ || path[j] == path[k] || OffsetPoint(group, path, j, k);
!OffsetConcaveCrossing(path, norms, j, k, j + 1 == path.size() ? 0 : j + 1, group_delta_, path_out))
OffsetPoint(group, path, j, k);
solution->emplace_back(path_out); solution->emplace_back(path_out);
} }
@@ -629,10 +601,10 @@ void ClipperOffset::ExecuteInternal(double delta)
if (!solution->size()) return; if (!solution->size()) return;
bool paths_reversed = CheckReverseOrientation(); bool paths_reversed = CheckReverseOrientation();
//clean up self-intersections ... //clean up self-intersections ...
Clipper64 c; Clipper64 c;
c.PreserveCollinear(preserve_collinear_); c.PreserveCollinear(false);
//the solution should retain the orientation of the input //the solution should retain the orientation of the input
c.ReverseSolution(reverse_solution_ != paths_reversed); c.ReverseSolution(reverse_solution_ != paths_reversed);
#ifdef USINGZ #ifdef USINGZ
@@ -1,8 +1,8 @@
/******************************************************************************* /*******************************************************************************
* Author : Angus Johnson * * Author : Angus Johnson *
* Date : 11 October 2025 * * Date : 5 July 2024 *
* Website : https://www.angusj.com * * Website : https://www.angusj.com *
* Copyright : Angus Johnson 2010-2025 * * Copyright : Angus Johnson 2010-2024 *
* Purpose : FAST rectangular clipping * * Purpose : FAST rectangular clipping *
* License : https://www.boost.org/LICENSE_1_0.txt * * License : https://www.boost.org/LICENSE_1_0.txt *
*******************************************************************************/ *******************************************************************************/
@@ -77,8 +77,8 @@ namespace Clipper2Lib {
bool GetSegmentIntersection(const Point64& p1, bool GetSegmentIntersection(const Point64& p1,
const Point64& p2, const Point64& p3, const Point64& p4, Point64& ip) const Point64& p2, const Point64& p3, const Point64& p4, Point64& ip)
{ {
int res1 = CrossProductSign(p1, p3, p4); double res1 = CrossProduct(p1, p3, p4);
int res2 = CrossProductSign(p2, p3, p4); double res2 = CrossProduct(p2, p3, p4);
if (res1 == 0) if (res1 == 0)
{ {
ip = p1; ip = p1;
@@ -97,8 +97,8 @@ namespace Clipper2Lib {
} }
if ((res1 > 0) == (res2 > 0)) return false; if ((res1 > 0) == (res2 > 0)) return false;
int res3 = CrossProductSign(p3, p1, p2); double res3 = CrossProduct(p3, p1, p2);
int res4 = CrossProductSign(p4, p1, p2); double res4 = CrossProduct(p4, p1, p2);
if (res3 == 0) if (res3 == 0)
{ {
ip = p3; ip = p3;
@@ -116,7 +116,7 @@ namespace Clipper2Lib {
if ((res3 > 0) == (res4 > 0)) return false; if ((res3 > 0) == (res4 > 0)) return false;
// segments must intersect to get here // 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, inline bool GetIntersection(const Path64& rectPath,
@@ -227,7 +227,7 @@ namespace Clipper2Lib {
const Point64& prev_pt, const Point64& curr_pt, const Point64& rect_mp) const Point64& prev_pt, const Point64& curr_pt, const Point64& rect_mp)
{ {
if (AreOpposites(prev, curr)) if (AreOpposites(prev, curr))
return CrossProductSign(prev_pt, rect_mp, curr_pt) < 0; return CrossProduct(prev_pt, rect_mp, curr_pt) < 0;
else else
return HeadingClockwise(prev, curr); return HeadingClockwise(prev, curr);
} }
File diff suppressed because it is too large Load Diff
@@ -6,4 +6,3 @@
#include "clipper.engine.cpp" #include "clipper.engine.cpp"
#include "clipper.offset.cpp" #include "clipper.offset.cpp"
#include "clipper.rectclip.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&) inline void offset(Slic3r::ExPolygon& sh, coord_t distance, const PolygonTag&)
{ {
#define DISABLE_BOOST_OFFSET #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(); if (!res.empty()) sh = res.front();
} }
template<> template<>
inline void offset(Slic3r::Polygon& sh, coord_t distance, const PathTag&) 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(); if (!res.empty()) sh = res.front();
} }
+5 -1
View File
@@ -19,7 +19,11 @@ if(Qhull_FOUND)
message(STATUS "Using qhull from system.") message(STATUS "Using qhull from system.")
if(SLIC3R_STATIC) if(SLIC3R_STATIC)
slic3r_remap_configs("Qhull::qhullcpp;Qhull::qhullstatic_r" RelWithDebInfo Release) 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() else()
slic3r_remap_configs("Qhull::qhullcpp;Qhull::qhull_r" RelWithDebInfo Release) slic3r_remap_configs("Qhull::qhullcpp;Qhull::qhull_r" RelWithDebInfo Release)
target_link_libraries(qhull INTERFACE Qhull::qhullcpp Qhull::qhull_r) 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/PublishDialog.cpp
src/slic3r/GUI/PublishSettingsDialog.cpp src/slic3r/GUI/PublishSettingsDialog.cpp
src/slic3r/GUI/SavePresetDialog.cpp src/slic3r/GUI/SavePresetDialog.cpp
src/slic3r/GUI/SceneBenchmark.cpp
src/slic3r/GUI/Search.cpp src/slic3r/GUI/Search.cpp
src/slic3r/GUI/SettingsIndex.cpp src/slic3r/GUI/SettingsIndex.cpp
src/slic3r/GUI/SpeedDialDialog.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 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><rect x="0.5" y="2.5" width="15" height="11" rx="2.5" style="fill:none;stroke:#949494;stroke-linecap:round;stroke-linejoin:round"/><path d="M6.5 5.5l4.5 2.5-4.5 2.5z" style="fill:none;stroke:#009688;stroke-linecap:round;stroke-linejoin:round"/></svg>

Before

Width:  |  Height:  |  Size: 334 B

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><path d="M1.5 2.5h4a2 2 0 0 1 2 2v9a1.5 1.5 0 0 0-1.5-1.5h-4.5zM14.5 2.5h-4a2 2 0 0 0-2 2v9a1.5 1.5 0 0 1 1.5-1.5h4.5z" style="fill:none;stroke:#949494;stroke-linecap:round;stroke-linejoin:round"/><path d="M10.5 5.5h2.5M10.5 7.5h2.5M3 5.5h2.5M3 7.5h2.5" style="fill:none;stroke:#009688;stroke-linecap:round;stroke-linejoin:round"/></svg>

Before

Width:  |  Height:  |  Size: 421 B

+1 -121
View File
@@ -1,6 +1,6 @@
{ {
"name": "Snapmaker", "name": "Snapmaker",
"version": "02.04.00.17", "version": "02.04.00.16",
"force_update": "0", "force_update": "0",
"description": "Snapmaker configurations", "description": "Snapmaker configurations",
"machine_model_list": [ "machine_model_list": [
@@ -1448,10 +1448,6 @@
"name": "Snapmaker PETG HF @U1 0.2 nozzle", "name": "Snapmaker PETG HF @U1 0.2 nozzle",
"sub_path": "filament/Snapmaker PETG HF @U1 0.2 nozzle.json" "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", "name": "Snapmaker PETG HF @U1 0.6 nozzle",
"sub_path": "filament/Snapmaker PETG HF @U1 0.6 nozzle.json" "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", "name": "Snapmaker Breakaway Support For PLA @U1 0.8 nozzle",
"sub_path": "filament/Snapmaker Breakaway Support For PLA @U1 0.8 nozzle.json" "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", "name": "PolyTerra J1 PLA",
"sub_path": "filament/Polymaker/PolyTerra J1 PLA.json" "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)" "Snapmaker U1 (0.4 nozzle)"
], ],
"enable_pressure_advance": [ "enable_pressure_advance": [
"0",
"0" "0"
], ],
"filament_max_volumetric_speed": [ "filament_max_volumetric_speed": [
"15",
"15" "15"
], ],
"filament_vendor": [ "filament_vendor": [
"Polymaker" "Polymaker"
], ],
"nozzle_temperature_range_high": [ "nozzle_temperature_range_high": [
"230",
"230" "230"
], ],
"temperature_vitrification": [ "temperature_vitrification": [
@@ -32,21 +29,5 @@
], ],
"textured_plate_temp_initial_layer": [ "textured_plate_temp_initial_layer": [
"65" "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" "Polymaker"
], ],
"nozzle_temperature": [ "nozzle_temperature": [
"230",
"230" "230"
], ],
"nozzle_temperature_initial_layer": [ "nozzle_temperature_initial_layer": [
"230",
"230" "230"
], ],
"nozzle_temperature_range_high": [ "nozzle_temperature_range_high": [
"230",
"230" "230"
], ],
"textured_plate_temp": [ "textured_plate_temp": [
@@ -32,21 +29,5 @@
], ],
"textured_plate_temp_initial_layer": [ "textured_plate_temp_initial_layer": [
"65" "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" "1.23"
], ],
"filament_flow_ratio": [ "filament_flow_ratio": [
"0.96",
"0.96" "0.96"
], ],
"filament_vendor": [ "filament_vendor": [
@@ -30,21 +29,5 @@
], ],
"textured_plate_temp_initial_layer": [ "textured_plate_temp_initial_layer": [
"65" "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)" "Snapmaker U1 (0.4 nozzle)"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15",
"15" "15"
], ],
"pressure_advance": [ "pressure_advance": [
"0.05",
"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)" "Snapmaker U1 (0.4 nozzle)"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15",
"15" "15"
], ],
"filament_retraction_length": [ "filament_retraction_length": [
"1.0",
"1.0" "1.0"
], ],
"filament_z_hop": [ "filament_z_hop": [
"0.0",
"0.0" "0.0"
], ],
"enable_pressure_advance": [ "enable_pressure_advance": [
"1",
"1" "1"
], ],
"pressure_advance": [ "pressure_advance": [
"0.05",
"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)" "Snapmaker U1 (0.4 nozzle)"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15",
"15" "15"
], ],
"filament_retraction_length": [ "filament_retraction_length": [
"1.0",
"1.0" "1.0"
], ],
"filament_z_hop": [ "filament_z_hop": [
"0.0",
"0.0" "0.0"
], ],
"enable_pressure_advance": [ "enable_pressure_advance": [
"1",
"1" "1"
], ],
"pressure_advance": [ "pressure_advance": [
"0.08",
"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)" "Snapmaker U1 (0.4 nozzle)"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15",
"15" "15"
], ],
"filament_retraction_length": [ "filament_retraction_length": [
"1.0",
"1.0" "1.0"
], ],
"filament_z_hop": [ "filament_z_hop": [
"0.0",
"0.0" "0.0"
], ],
"description": " ", "description": " ",
"enable_pressure_advance": [ "enable_pressure_advance": [
"1",
"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)" "Snapmaker U1 (0.4 nozzle)"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15",
"15" "15"
], ],
"supertack_plate_temp": [ "supertack_plate_temp": [
@@ -19,19 +18,6 @@
"70" "70"
], ],
"pressure_advance": [ "pressure_advance": [
"0.04",
"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)" "Snapmaker U1 (0.4 nozzle)"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15",
"15" "15"
], ],
"description": " ", "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"
]
} }
@@ -10,23 +10,6 @@
"Snapmaker U1 (0.4 nozzle)" "Snapmaker U1 (0.4 nozzle)"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15",
"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)" "Snapmaker U1 (0.4 nozzle)"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15",
"15" "15"
], ],
"filament_wipe": [ "filament_wipe": [
"0",
"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)" "Snapmaker U1 (0.4 nozzle)"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15",
"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)" "Snapmaker U1 (0.4 nozzle)"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15",
"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)" "Snapmaker U1 (0.4 nozzle)"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15",
"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)" "Snapmaker U1 (0.4 nozzle)"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15",
"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)" "Snapmaker U1 (0.4 nozzle)"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15",
"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)" "Snapmaker U1 (0.4 nozzle)"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15",
"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)" "Snapmaker U1 (0.4 nozzle)"
], ],
"filament_minimal_purge_on_wipe_tower": [ "filament_minimal_purge_on_wipe_tower": [
"15",
"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"
] ]
} }

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