mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-04 22:31:02 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
64460d7f85 |
-65
@@ -1,65 +0,0 @@
|
|||||||
# clang-tidy configuration. Only missing includes are reported for now: a file
|
|
||||||
# should include the header for every symbol it uses, not rely on the
|
|
||||||
# precompiled header or another header's includes. Run with --fix to add them.
|
|
||||||
Checks: '-*,misc-include-cleaner'
|
|
||||||
CheckOptions:
|
|
||||||
# Missing includes only. Builds without the precompiled header break on these.
|
|
||||||
misc-include-cleaner.UnusedIncludes: false
|
|
||||||
# Headers that declare a symbol but are not the one to include: per-platform
|
|
||||||
# implementations of wxWidgets and Boost.Thread (a Linux run would suggest the
|
|
||||||
# GTK or pthread one), library internals and forward declarations (MSVC's STL
|
|
||||||
# __msvc_* and the Windows UCRT's corecrt_* included), CPython's headers behind
|
|
||||||
# Python.h (python3.x/ on Linux and macOS, libpython/include/ on Windows),
|
|
||||||
# curl's behind curl.h, oneTBB's behind tbb/, and
|
|
||||||
# admesh's stl.h, which the include path also exposes without its directory.
|
|
||||||
# Clipper's own clipper.hpp is only included through libslic3r/clipper.hpp or
|
|
||||||
# clipper_z.hpp, which configure it first, and Boost.Polygon's headers only
|
|
||||||
# work through boost/polygon/polygon.hpp or voronoi.hpp. Clipper2's headers
|
|
||||||
# are included through clipper2/clipper.h, or clipper2_z.hpp, which defines
|
|
||||||
# USINGZ first. minilzo's config
|
|
||||||
# headers are internal to minilzo.h.
|
|
||||||
# FFmpeg's C headers are left alone because they are only included inside
|
|
||||||
# extern "C", which an inserted include would miss. OS-specific headers (GLib,
|
|
||||||
# GTK, D-Bus, POSIX, the Windows SDK) are only used inside platform #if blocks,
|
|
||||||
# and an include added at the top of the file would break the other platforms'
|
|
||||||
# builds. A symbol these provide is not reported missing.
|
|
||||||
# Write every / as [/\\] so the patterns also match Windows paths.
|
|
||||||
# No comments inside the block below, because a # there silently becomes part of a pattern.
|
|
||||||
misc-include-cleaner.IgnoreHeaders: >-
|
|
||||||
wx[/\\](gtk|gtk1|msw|osx|unix|x11|motif|univ|qt|dfb|generic|private)[/\\].*;
|
|
||||||
.*[/\\]detail[/\\].*;
|
|
||||||
.*[/\\]impl[/\\].*;
|
|
||||||
.*_fwd\.hpp;
|
|
||||||
python3\.[0-9]+[/\\].*;
|
|
||||||
libpython[/\\]include[/\\].*;
|
|
||||||
bits[/\\].*;
|
|
||||||
corecrt_.*\.h;
|
|
||||||
__msvc_.*\.hpp;
|
|
||||||
boost[/\\]multiprecision[/\\]fwd\.hpp;
|
|
||||||
imconfig\.h;
|
|
||||||
expat_external\.h;
|
|
||||||
admesh[/\\]stl\.h;
|
|
||||||
boost[/\\]thread[/\\](pthread|win32)[/\\].*;
|
|
||||||
boost[/\\]regex[/\\]v[0-9]+[/\\].*;
|
|
||||||
curl[/\\](easy|multi|system|urlapi|header|options|websockets|mprintf)\.h;
|
|
||||||
oneapi[/\\]tbb[/\\].*;
|
|
||||||
opencv2[/\\]core[/\\]hal[/\\].*;
|
|
||||||
openssl[/\\]ossl_typ\.h;
|
|
||||||
libav[a-z]+[/\\].*;
|
|
||||||
libsw[a-z]+[/\\].*;
|
|
||||||
glib-2\.0[/\\].*;
|
|
||||||
gtk-3\.0[/\\].*;
|
|
||||||
dbus-1\.0[/\\].*;
|
|
||||||
sys[/\\].*;
|
|
||||||
unistd\.h;
|
|
||||||
strings\.h;
|
|
||||||
fcntl\.h;
|
|
||||||
termios\.h;
|
|
||||||
[/\\](um|shared)[/\\].*;
|
|
||||||
sal\.h;
|
|
||||||
clipper[/\\]clipper\.hpp;
|
|
||||||
png(lib)?conf\.h;
|
|
||||||
mcut[/\\]platform\.h;
|
|
||||||
boost[/\\]polygon[/\\].*;
|
|
||||||
clipper2[/\\]clipper\.(core|engine|offset|minkowski|rectclip|export|triangulation|version)\.h;
|
|
||||||
lzo(conf|defs)\.h
|
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -10,6 +10,3 @@
|
|||||||
# resume after `call :label`. With LF endings that offset can land wrong and the
|
# resume after `call :label`. With LF endings that offset can land wrong and the
|
||||||
# label lookup fails, so keep these CRLF whatever the platform.
|
# label lookup fails, so keep these CRLF whatever the platform.
|
||||||
*.bat text eol=crlf
|
*.bat text eol=crlf
|
||||||
|
|
||||||
# OCCT BRep fixtures, kept byte for byte as OCCT wrote them.
|
|
||||||
*.brep -text
|
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ on:
|
|||||||
|
|
||||||
|
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '15 2 * * *' # 10:15 AM Singapore time (UTC+8), when macOS runners are least busy
|
- cron: '0 17 * * *' # run once a day at 1 AM Singapore time (UTC+8)
|
||||||
|
|
||||||
workflow_dispatch: # allows for manual dispatch
|
workflow_dispatch: # allows for manual dispatch
|
||||||
inputs:
|
inputs:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 }}
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
# Reports the newest push build of main that was not cancelled, for the README badge.
|
|
||||||
name: Main build status
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_run:
|
|
||||||
workflows: ["Build all"]
|
|
||||||
types: [completed]
|
|
||||||
branches: [main]
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
actions: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
status:
|
|
||||||
if: github.repository == 'OrcaSlicer/OrcaSlicer'
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- env:
|
|
||||||
GH_TOKEN: ${{ github.token }}
|
|
||||||
run: |
|
|
||||||
for attempt in 1 2 3; do
|
|
||||||
conclusion=$(gh api "repos/${{ github.repository }}/actions/workflows/build_all.yml/runs?branch=main&event=push&status=completed&per_page=100" \
|
|
||||||
--jq '[.workflow_runs[] | select(.conclusion != "cancelled")][0].conclusion') && break
|
|
||||||
sleep 10
|
|
||||||
done
|
|
||||||
echo "Latest finished push build of main: $conclusion"
|
|
||||||
[ "$conclusion" = success ]
|
|
||||||
@@ -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
|
||||||
|
|||||||
+3
-18
@@ -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})
|
||||||
@@ -1131,8 +1125,7 @@ function(orcaslicer_copy_dlls target config postfix output_dlls)
|
|||||||
if (NOT OCCT_LIBS)
|
if (NOT OCCT_LIBS)
|
||||||
message(FATAL_ERROR "OCCT_LIBS is not set; libslic3r must be configured first.")
|
message(FATAL_ERROR "OCCT_LIBS is not set; libslic3r must be configured first.")
|
||||||
endif ()
|
endif ()
|
||||||
string(TOUPPER "${config}" _config_upper)
|
set(_occt_bin "${CMAKE_PREFIX_PATH}/bin/occt")
|
||||||
set(_occt_bin "${OCCT_BIN_DIR_${_config_upper}}")
|
|
||||||
set(_occt_dlls "")
|
set(_occt_dlls "")
|
||||||
set(_occt_staged "")
|
set(_occt_staged "")
|
||||||
set(_missing_occt "")
|
set(_missing_occt "")
|
||||||
@@ -1308,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)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<a href="https://trendshift.io/repositories/15552" target="_blank"><img src="https://trendshift.io/api/badge/repositories/15552" alt="OrcaSlicer%2FOrcaSlicer | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
<a href="https://trendshift.io/repositories/15552" target="_blank"><img src="https://trendshift.io/api/badge/repositories/15552" alt="OrcaSlicer%2FOrcaSlicer | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||||
|
|
||||||
[](https://github.com/OrcaSlicer/OrcaSlicer/stargazers) [](https://github.com/OrcaSlicer/OrcaSlicer/actions/workflows/build_all.yml)
|
[](https://github.com/OrcaSlicer/OrcaSlicer/stargazers) [](https://github.com/OrcaSlicer/OrcaSlicer/actions/workflows/build_all.yml)
|
||||||
|
|
||||||
OrcaSlicer: an open source Next-Gen Slicing Software for Precision 3D Prints.
|
OrcaSlicer: an open source Next-Gen Slicing Software for Precision 3D Prints.
|
||||||
Optimize your prints with ultra-fast slicing, intelligent support generation, and seamless printer compatibility—engineered for perfection.
|
Optimize your prints with ultra-fast slicing, intelligent support generation, and seamless printer compatibility—engineered for perfection.
|
||||||
|
|||||||
+5
-1
@@ -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!
|
||||||
|
|||||||
Vendored
+264
@@ -0,0 +1,264 @@
|
|||||||
|
diff --git a/adm/cmake/occt_defs_flags.cmake b/adm/cmake/occt_defs_flags.cmake
|
||||||
|
index 00000000..00000001 100644
|
||||||
|
--- a/adm/cmake/occt_defs_flags.cmake
|
||||||
|
+++ b/adm/cmake/occt_defs_flags.cmake
|
||||||
|
@@ -134,7 +134,11 @@
|
||||||
|
set (CMAKE_CXX_FLAGS "-std=c++0x ${CMAKE_CXX_FLAGS}")
|
||||||
|
endif()
|
||||||
|
# Optimize size of binaries
|
||||||
|
- set (CMAKE_SHARED_LINKER_FLAGS "-Wl,-s ${CMAKE_SHARED_LINKER_FLAGS}")
|
||||||
|
+ # clang-cl reports the Clang compiler ID, and OCCT builds shared on Windows,
|
||||||
|
+ # where the MSVC-style linker gets this flag as an argument it does not know.
|
||||||
|
+ if (NOT WIN32)
|
||||||
|
+ set (CMAKE_SHARED_LINKER_FLAGS "-Wl,-s ${CMAKE_SHARED_LINKER_FLAGS}")
|
||||||
|
+ endif()
|
||||||
|
elseif(MINGW)
|
||||||
|
add_definitions(-D_WIN32_WINNT=0x0601)
|
||||||
|
# _WIN32_WINNT=0x0601 (use Windows 7 SDK)
|
||||||
|
diff --git a/CMakeLists.txt b/CMakeLists.txt
|
||||||
|
index d98acc0f..28eb8eb4 100644
|
||||||
|
--- a/CMakeLists.txt
|
||||||
|
+++ b/CMakeLists.txt
|
||||||
|
@@ -225,7 +225,7 @@ if (NOT DEFINED INSTALL_DIR_BIN)
|
||||||
|
if ("${INSTALL_DIR_LAYOUT}" STREQUAL "Unix")
|
||||||
|
set (INSTALL_DIR_BIN "bin" CACHE PATH "${INSTALL_DIR_BIN_DESCR}")
|
||||||
|
else()
|
||||||
|
- set (INSTALL_DIR_BIN "${OS_WITH_BIT}/${COMPILER}/bin" CACHE PATH "${INSTALL_DIR_BIN_DESCR}")
|
||||||
|
+ set (INSTALL_DIR_BIN "bin/occt" CACHE PATH "${INSTALL_DIR_BIN_DESCR}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
@@ -243,11 +243,11 @@ if (NOT DEFINED INSTALL_DIR_LIB)
|
||||||
|
if ("${INSTALL_DIR_LAYOUT}" STREQUAL "Unix")
|
||||||
|
set (INSTALL_DIR_LIB "lib" CACHE PATH "${INSTALL_DIR_LIB_DESCR}")
|
||||||
|
else()
|
||||||
|
- set (INSTALL_DIR_LIB "${OS_WITH_BIT}/${COMPILER}/lib" CACHE PATH "${INSTALL_DIR_LIB_DESCR}")
|
||||||
|
+ set (INSTALL_DIR_LIB "lib/occt" CACHE PATH "${INSTALL_DIR_LIB_DESCR}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
-# OCCT headers: <prefix>/inc for windows,
|
||||||
|
+# OCCT headers: <prefix>/include for windows,
|
||||||
|
# <prefix>/include/opencascade-7.0.0 for unix
|
||||||
|
if (NOT DEFINED INSTALL_DIR_INCLUDE)
|
||||||
|
if ("${INSTALL_DIR_LAYOUT}" STREQUAL "Unix")
|
||||||
|
@@ -256,7 +256,7 @@ if (NOT DEFINED INSTALL_DIR_INCLUDE)
|
||||||
|
set (INSTALL_DIR_INCLUDE "include/opencascade-${OCC_VERSION_STRING_EXT}" CACHE PATH "${INSTALL_DIR_INCLUDE_DESCR}" FORCE)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
- set (INSTALL_DIR_INCLUDE "inc" CACHE PATH "${INSTALL_DIR_INCLUDE_DESCR}")
|
||||||
|
+ set (INSTALL_DIR_INCLUDE "include/occt" CACHE PATH "${INSTALL_DIR_INCLUDE_DESCR}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
@@ -330,7 +330,7 @@ if (NOT DEFINED INSTALL_DIR_CMAKE)
|
||||||
|
set (INSTALL_DIR_CMAKE "lib/cmake/opencascade" CACHE PATH "${INSTALL_DIR_CMAKE_DESCR}")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
- set (INSTALL_DIR_CMAKE "cmake" CACHE PATH "${INSTALL_DIR_CMAKE_DESCR}")
|
||||||
|
+ set (INSTALL_DIR_CMAKE "lib/cmake/occt" CACHE PATH "${INSTALL_DIR_CMAKE_DESCR}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
@@ -338,13 +338,13 @@ endif()
|
||||||
|
OCCT_INCLUDE_CMAKE_FILE ("adm/cmake/occt_resources")
|
||||||
|
|
||||||
|
# install LICENSE_LGPL_21.txt and OCCT_LGPL_EXCEPTION.txt files
|
||||||
|
-if ("${INSTALL_DIR_LAYOUT}" STREQUAL "Unix")
|
||||||
|
- OCCT_INSTALL_FILE_OR_DIR ("LICENSE_LGPL_21.txt" "${INSTALL_DIR_DOC}")
|
||||||
|
- OCCT_INSTALL_FILE_OR_DIR ("OCCT_LGPL_EXCEPTION.txt" "${INSTALL_DIR_DOC}")
|
||||||
|
-else()
|
||||||
|
- OCCT_INSTALL_FILE_OR_DIR ("LICENSE_LGPL_21.txt" ".")
|
||||||
|
- OCCT_INSTALL_FILE_OR_DIR ("OCCT_LGPL_EXCEPTION.txt" ".")
|
||||||
|
-endif()
|
||||||
|
+#if ("${INSTALL_DIR_LAYOUT}" STREQUAL "Unix")
|
||||||
|
+# OCCT_INSTALL_FILE_OR_DIR ("LICENSE_LGPL_21.txt" "${INSTALL_DIR_DOC}")
|
||||||
|
+# OCCT_INSTALL_FILE_OR_DIR ("OCCT_LGPL_EXCEPTION.txt" "${INSTALL_DIR_DOC}")
|
||||||
|
+#else()
|
||||||
|
+# OCCT_INSTALL_FILE_OR_DIR ("LICENSE_LGPL_21.txt" ".")
|
||||||
|
+# OCCT_INSTALL_FILE_OR_DIR ("OCCT_LGPL_EXCEPTION.txt" ".")
|
||||||
|
+#endif()
|
||||||
|
|
||||||
|
if(APPLE)
|
||||||
|
set (INSTALL_NAME_DIR "" CACHE STRING "install_name library suffix on OS X (e.g. @executable_path/../Frameworks)")
|
||||||
|
@@ -850,34 +850,34 @@ endif()
|
||||||
|
|
||||||
|
# build directories
|
||||||
|
if (SINGLE_GENERATOR)
|
||||||
|
- set (CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${OS_WITH_BIT}/${COMPILER}/lib${BIN_LETTER}")
|
||||||
|
- set (CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${OS_WITH_BIT}/${COMPILER}/bin${BIN_LETTER}")
|
||||||
|
- set (CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${OS_WITH_BIT}/${COMPILER}/lib${BIN_LETTER}")
|
||||||
|
+ set (CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/occt")
|
||||||
|
+ set (CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/occt")
|
||||||
|
+ set (CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib/occt")
|
||||||
|
if (WIN32)
|
||||||
|
- set (CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${OS_WITH_BIT}/${COMPILER}/bin${BIN_LETTER}")
|
||||||
|
+ set (CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin/occt")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
-set (CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/${OS_WITH_BIT}/${COMPILER}/lib")
|
||||||
|
-set (CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/${OS_WITH_BIT}/${COMPILER}/bin")
|
||||||
|
-set (CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/${OS_WITH_BIT}/${COMPILER}/lib")
|
||||||
|
+set (CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/lib/occt")
|
||||||
|
+set (CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin/occt")
|
||||||
|
+set (CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/lib/occt")
|
||||||
|
|
||||||
|
-set (CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_BINARY_DIR}/${OS_WITH_BIT}/${COMPILER}/libi")
|
||||||
|
-set (CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_BINARY_DIR}/${OS_WITH_BIT}/${COMPILER}/bini")
|
||||||
|
-set (CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_BINARY_DIR}/${OS_WITH_BIT}/${COMPILER}/libi")
|
||||||
|
+set (CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_BINARY_DIR}/lib/occt/RelWithDebInfo")
|
||||||
|
+set (CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_BINARY_DIR}/bin/occt/RelWithDebInfo")
|
||||||
|
+set (CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_BINARY_DIR}/lib/occt/RelWithDebInfo")
|
||||||
|
|
||||||
|
-set (CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/${OS_WITH_BIT}/${COMPILER}/libd")
|
||||||
|
-set (CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/${OS_WITH_BIT}/${COMPILER}/bind")
|
||||||
|
-set (CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/${OS_WITH_BIT}/${COMPILER}/libd")
|
||||||
|
+set (CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/lib/occt/Debug")
|
||||||
|
+set (CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin/occt/Debug")
|
||||||
|
+set (CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/lib/occt/Debug")
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
- set (CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/${OS_WITH_BIT}/${COMPILER}/bin")
|
||||||
|
- set (CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_BINARY_DIR}/${OS_WITH_BIT}/${COMPILER}/bini")
|
||||||
|
- set (CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/${OS_WITH_BIT}/${COMPILER}/bind")
|
||||||
|
+ set (CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/bin/occt")
|
||||||
|
+ set (CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_BINARY_DIR}/bin/occt/RelWithDebInfo")
|
||||||
|
+ set (CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/bin/occt/Debug")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
string(TIMESTAMP CURRENT_TIME "%H:%M:%S")
|
||||||
|
-message (STATUS "\nInfo: \(${CURRENT_TIME}\) Start collecting all OCCT header files into ${CMAKE_BINARY_DIR}/inc ...")
|
||||||
|
+message (STATUS "\nInfo: \(${CURRENT_TIME}\) Start collecting all OCCT header files into ${CMAKE_BINARY_DIR}/include/occt ...")
|
||||||
|
|
||||||
|
# collect all the headers to <binary dir>/inc folder
|
||||||
|
COLLECT_AND_INSTALL_OCCT_HEADER_FILES ("${CMAKE_BINARY_DIR}" "${BUILD_TOOLKITS}" "${CMAKE_SOURCE_DIR}/src" "${INSTALL_DIR_INCLUDE}")
|
||||||
|
@@ -984,9 +984,9 @@ if (EXISTS "${INSTALL_DIR}/${INSTALL_DIR_SCRIPT}/custom.${SCRIPT_EXT}")
|
||||||
|
|
||||||
|
set (CUSTOM_CONTENT "${CUSTOM_CONTENT} ${ADDITIONAL_CUSTOM_CONTENT}")
|
||||||
|
|
||||||
|
- file (WRITE "${INSTALL_DIR}/${INSTALL_DIR_SCRIPT}/custom.${SCRIPT_EXT}" "${CUSTOM_CONTENT}")
|
||||||
|
+ #file (WRITE "${INSTALL_DIR}/${INSTALL_DIR_SCRIPT}/custom.${SCRIPT_EXT}" "${CUSTOM_CONTENT}")
|
||||||
|
else()
|
||||||
|
- OCCT_CONFIGURE_AND_INSTALL ("adm/templates/custom.${SCRIPT_EXT}.main" "custom.${SCRIPT_EXT}" "custom.${SCRIPT_EXT}" "${INSTALL_DIR_SCRIPT}")
|
||||||
|
+ #OCCT_CONFIGURE_AND_INSTALL ("adm/templates/custom.${SCRIPT_EXT}.main" "custom.${SCRIPT_EXT}" "custom.${SCRIPT_EXT}" "${INSTALL_DIR_SCRIPT}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
@@ -1007,7 +1007,7 @@ endforeach()
|
||||||
|
|
||||||
|
# write current custom.bat/sh (for install directory)
|
||||||
|
set (SUB_CUSTOM_BUILD_NAME "custom_${COMPILER}_${COMPILER_BITNESS}.install.${SCRIPT_EXT}")
|
||||||
|
-OCCT_CONFIGURE_AND_INSTALL ("adm/templates/custom.install.${SCRIPT_EXT}.in" "${SUB_CUSTOM_BUILD_NAME}" "${SUB_CUSTOM_NAME}" "${INSTALL_DIR_SCRIPT}")
|
||||||
|
+#OCCT_CONFIGURE_AND_INSTALL ("adm/templates/custom.install.${SCRIPT_EXT}.in" "${SUB_CUSTOM_BUILD_NAME}" "${SUB_CUSTOM_NAME}" "${INSTALL_DIR_SCRIPT}")
|
||||||
|
|
||||||
|
# write current custom.bat/sh (for build directory)
|
||||||
|
OCCT_CONFIGURE ("adm/templates/custom.build.${SCRIPT_EXT}.in" "${SUB_CUSTOM_NAME}")
|
||||||
|
@@ -1019,9 +1019,9 @@ endif()
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
# env script for draw in building environment
|
||||||
|
- OCCT_CONFIGURE ("adm/templates/env.${SCRIPT_EXT}.in" "env.${SCRIPT_EXT}")
|
||||||
|
+ #OCCT_CONFIGURE ("adm/templates/env.${SCRIPT_EXT}.in" "env.${SCRIPT_EXT}")
|
||||||
|
# install env script
|
||||||
|
- install (FILES "${CMAKE_BINARY_DIR}/env.${SCRIPT_EXT}" DESTINATION "${INSTALL_DIR_SCRIPT}")
|
||||||
|
+ #install (FILES "${CMAKE_BINARY_DIR}/env.${SCRIPT_EXT}" DESTINATION "${INSTALL_DIR_SCRIPT}")
|
||||||
|
# copy build.bat and install.bat scripts to CMake binary folder
|
||||||
|
OCCT_COPY_FILE_OR_DIR ("adm/templates/build.bat" "${CMAKE_BINARY_DIR}")
|
||||||
|
OCCT_COPY_FILE_OR_DIR ("adm/templates/install.bat" "${CMAKE_BINARY_DIR}")
|
||||||
|
@@ -1043,12 +1043,12 @@ endif()
|
||||||
|
FILE_TO_LIST ("adm/RESOURCES" RESOURCES)
|
||||||
|
foreach(RESOURCE ${RESOURCES})
|
||||||
|
get_filename_component(RESOURCE_FOLDER ${RESOURCE} DIRECTORY)
|
||||||
|
- if(NOT "${RESOURCE_FOLDER}" STREQUAL "")
|
||||||
|
- get_filename_component(RESOURCE_FOLDER ${RESOURCE_FOLDER} NAME)
|
||||||
|
- OCCT_INSTALL_FILE_OR_DIR ("src/${RESOURCE}" "${INSTALL_DIR_RESOURCE}/${RESOURCE_FOLDER}")
|
||||||
|
- else()
|
||||||
|
- OCCT_INSTALL_FILE_OR_DIR ("src/${RESOURCE}" "${INSTALL_DIR_RESOURCE}")
|
||||||
|
- endif()
|
||||||
|
+ #if(NOT "${RESOURCE_FOLDER}" STREQUAL "")
|
||||||
|
+ # get_filename_component(RESOURCE_FOLDER ${RESOURCE_FOLDER} NAME)
|
||||||
|
+ # OCCT_INSTALL_FILE_OR_DIR ("src/${RESOURCE}" "${INSTALL_DIR_RESOURCE}/${RESOURCE_FOLDER}")
|
||||||
|
+ #else()
|
||||||
|
+ # OCCT_INSTALL_FILE_OR_DIR ("src/${RESOURCE}" "${INSTALL_DIR_RESOURCE}")
|
||||||
|
+ #endif()
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
if (BUILD_SAMPLES_QT)
|
||||||
|
diff --git a/adm/cmake/occt_macros.cmake b/adm/cmake/occt_macros.cmake
|
||||||
|
index 224c96b1..8c94a1c5 100644
|
||||||
|
--- a/adm/cmake/occt_macros.cmake
|
||||||
|
+++ b/adm/cmake/occt_macros.cmake
|
||||||
|
@@ -608,7 +608,7 @@ macro (OCCT_INSERT_CODE_FOR_TARGET)
|
||||||
|
install(CODE "if (\"\${CMAKE_INSTALL_CONFIG_NAME}\" MATCHES \"^([Rr][Ee][Ll][Ee][Aa][Ss][Ee])$\")
|
||||||
|
set (OCCT_INSTALL_BIN_LETTER \"\")
|
||||||
|
elseif (\"\${CMAKE_INSTALL_CONFIG_NAME}\" MATCHES \"^([Rr][Ee][Ll][Ww][Ii][Tt][Hh][Dd][Ee][Bb][Ii][Nn][Ff][Oo])$\")
|
||||||
|
- set (OCCT_INSTALL_BIN_LETTER \"i\")
|
||||||
|
+ set (OCCT_INSTALL_BIN_LETTER \"\")
|
||||||
|
elseif (\"\${CMAKE_INSTALL_CONFIG_NAME}\" MATCHES \"^([Dd][Ee][Bb][Uu][Gg])$\")
|
||||||
|
set (OCCT_INSTALL_BIN_LETTER \"d\")
|
||||||
|
endif()")
|
||||||
|
diff --git a/adm/cmake/occt_toolkit.cmake b/adm/cmake/occt_toolkit.cmake
|
||||||
|
index 550e0e2f..7ac1a3b8 100644
|
||||||
|
--- a/adm/cmake/occt_toolkit.cmake
|
||||||
|
+++ b/adm/cmake/occt_toolkit.cmake
|
||||||
|
@@ -241,7 +241,7 @@
|
||||||
|
else()
|
||||||
|
set (aReleasePdbConf)
|
||||||
|
endif()
|
||||||
|
- install (FILES ${CMAKE_BINARY_DIR}/${OS_WITH_BIT}/${COMPILER}/bin\${OCCT_INSTALL_BIN_LETTER}/${PROJECT_NAME}.pdb
|
||||||
|
+ install (FILES $<TARGET_PDB_FILE:${PROJECT_NAME}>
|
||||||
|
CONFIGURATIONS Debug ${aReleasePdbConf} RelWithDebInfo
|
||||||
|
DESTINATION "${INSTALL_DIR_BIN}\${OCCT_INSTALL_BIN_LETTER}")
|
||||||
|
endif()
|
||||||
|
diff --git a/src/Font/Font_FTFont.cxx b/src/Font/Font_FTFont.cxx
|
||||||
|
index 5ae9899f..0a17372b 100644
|
||||||
|
--- a/src/Font/Font_FTFont.cxx
|
||||||
|
+++ b/src/Font/Font_FTFont.cxx
|
||||||
|
@@ -103,9 +103,11 @@ bool Font_FTFont::Init (const Handle(NCollection_Buffer)& theData,
|
||||||
|
{
|
||||||
|
throw Standard_ProgramError ("Font_FTFont, Light and Normal hinting styles are mutually exclusive");
|
||||||
|
}
|
||||||
|
+#ifdef HAVE_FREETYPE
|
||||||
|
setLoadFlag (FT_LOAD_TARGET_LIGHT, (theParams.FontHinting & Font_Hinting_Light) != 0);
|
||||||
|
setLoadFlag (FT_LOAD_NO_HINTING, (theParams.FontHinting & Font_Hinting_Normal) == 0
|
||||||
|
&& (theParams.FontHinting & Font_Hinting_Light) == 0);
|
||||||
|
+#endif
|
||||||
|
|
||||||
|
// manage native / autohinting
|
||||||
|
if ((theParams.FontHinting & Font_Hinting_ForceAutohint) != 0
|
||||||
|
@@ -113,8 +115,10 @@ bool Font_FTFont::Init (const Handle(NCollection_Buffer)& theData,
|
||||||
|
{
|
||||||
|
throw Standard_ProgramError ("Font_FTFont, ForceAutohint and NoAutohint are mutually exclusive");
|
||||||
|
}
|
||||||
|
+#ifdef HAVE_FREETYPE
|
||||||
|
setLoadFlag (FT_LOAD_FORCE_AUTOHINT, (theParams.FontHinting & Font_Hinting_ForceAutohint) != 0);
|
||||||
|
setLoadFlag (FT_LOAD_NO_AUTOHINT, (theParams.FontHinting & Font_Hinting_NoAutohint) != 0);
|
||||||
|
+#endif
|
||||||
|
|
||||||
|
if (!myFTLib->IsValid())
|
||||||
|
{
|
||||||
|
From 7236e83dcc1e7284e66dc61e612154617ef715d6 Mon Sep 17 00:00:00 2001
|
||||||
|
From: dpasukhi <dpasukhi@opencascade.com>
|
||||||
|
Date: Tue, 27 Aug 2024 11:33:29 +0100
|
||||||
|
Subject: [PATCH] 0033808: Coding - FreeType Use unsigned point and contour
|
||||||
|
indexing in `FT_Outline`
|
||||||
|
|
||||||
|
Changes to auto instead of specific type
|
||||||
|
---
|
||||||
|
src/StdPrs/StdPrs_BRepFont.cxx | 2 +-
|
||||||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/src/StdPrs/StdPrs_BRepFont.cxx b/src/StdPrs/StdPrs_BRepFont.cxx
|
||||||
|
index ab2d9b3c9f..cd701879b1 100644
|
||||||
|
--- a/src/StdPrs/StdPrs_BRepFont.cxx
|
||||||
|
+++ b/src/StdPrs/StdPrs_BRepFont.cxx
|
||||||
|
@@ -457,7 +457,7 @@ Standard_Boolean StdPrs_BRepFont::renderGlyph (const Standard_Utf32Char theChar,
|
||||||
|
for (short aContour = 0, aStartIndex = 0; aContour < anOutline->n_contours; ++aContour)
|
||||||
|
{
|
||||||
|
const FT_Vector* aPntList = &anOutline->points[aStartIndex];
|
||||||
|
- const char* aTags = &anOutline->tags[aStartIndex];
|
||||||
|
+ const auto* aTags = &anOutline->tags[aStartIndex];
|
||||||
|
const short anEndIndex = anOutline->contours[aContour];
|
||||||
|
const short aPntsNb = (anEndIndex - aStartIndex) + 1;
|
||||||
|
aStartIndex = anEndIndex + 1;
|
||||||
Vendored
+18
-18
@@ -1,5 +1,5 @@
|
|||||||
# clang-cl cannot emit some OCCT sources for ARM64 (llvm/llvm-project#62081).
|
# clang-cl cannot emit IGESAppli_GeneralModule.cxx on ARM64
|
||||||
# cl and clang-cl share an ABI.
|
# (llvm/llvm-project#62081). cl and clang-cl share an ABI.
|
||||||
set(_occt_compiler_args "")
|
set(_occt_compiler_args "")
|
||||||
if ("${DEPS_ARCH}" STREQUAL "arm64" AND CMAKE_CXX_COMPILER_ID STREQUAL Clang)
|
if ("${DEPS_ARCH}" STREQUAL "arm64" AND CMAKE_CXX_COMPILER_ID STREQUAL Clang)
|
||||||
set(_occt_compiler_args -DCMAKE_C_COMPILER:STRING=cl -DCMAKE_CXX_COMPILER:STRING=cl)
|
set(_occt_compiler_args -DCMAKE_C_COMPILER:STRING=cl -DCMAKE_CXX_COMPILER:STRING=cl)
|
||||||
@@ -15,31 +15,31 @@ endif()
|
|||||||
# (fillet/offset/loft), whose only consumer is the parametric Design/CAD tab. With it OFF
|
# (fillet/offset/loft), whose only consumer is the parametric Design/CAD tab. With it OFF
|
||||||
# the deps prefix matches upstream exactly.
|
# the deps prefix matches upstream exactly.
|
||||||
#
|
#
|
||||||
# With it ON OCCT also builds TKFillet (used via BRepFilletAPI), TKOffset (used via
|
# With it ON the delta is THREE toolkits, not two: TKFillet (7.40 MiB archive, used via
|
||||||
# BRepOffsetAPI), and TKFeat, TKHelix, TKXMesh and TKExpress, which nothing here references
|
# BRepFilletAPI), TKOffset (5.38 MiB, used via BRepOffsetAPI) and TKFeat (4.42 MiB), which
|
||||||
# but which the module flag builds anyway, since module flags are all-or-nothing. The
|
# nothing here references but which the module flag builds anyway -- it is all-or-nothing
|
||||||
# module's other toolkits are built either way, because DataExchange (the STEP path)
|
# per module. The module's other nine toolkits are built either way, because DataExchange
|
||||||
# depends on them.
|
# (the STEP path upstream already ships) depends on them.
|
||||||
#
|
#
|
||||||
# On macOS/Linux OCCT links statically, so an unreferenced toolkit costs build time and no
|
# On macOS/Linux OCCT links statically, so an unreferenced toolkit costs build time and no
|
||||||
# shipped bytes. Windows ships only the DLLs libslic3r links, so the tab adds the TKFillet,
|
# shipped bytes. The Windows figure is a real DLL cost and has NOT been measured -- an
|
||||||
# TKOffset and TKBool DLLs. See docs/HLSD/design-tab.md.
|
# earlier "3.77 MiB, Windows only" note here covered only two of the three toolkits and is
|
||||||
|
# not a number to quote. See docs/HLSD/design-tab.md.
|
||||||
|
|
||||||
|
if (IN_GIT_REPO)
|
||||||
|
set(OCCT_DIRECTORY_FLAG --directory ${BINARY_DIR_REL}/dep_OCCT-prefix/src/dep_OCCT)
|
||||||
|
endif ()
|
||||||
|
|
||||||
orcaslicer_add_cmake_project(OCCT
|
orcaslicer_add_cmake_project(OCCT
|
||||||
URL https://github.com/Open-Cascade-SAS/OCCT/archive/refs/tags/V8_0_1.zip
|
URL https://github.com/Open-Cascade-SAS/OCCT/archive/refs/tags/V7_6_0.zip
|
||||||
URL_HASH SHA256=7c033d917ee8f040c0512d289dcc5f02c148889d5bac17c3e25639accb44f0da
|
URL_HASH SHA256=28334f0e98f1b1629799783e9b4d21e05349d89e695809d7e6dfa45ea43e1dbc
|
||||||
|
#PATCH_COMMAND ${PATCH_CMD} ${CMAKE_CURRENT_LIST_DIR}/0001-OCCT-fix.patch
|
||||||
|
PATCH_COMMAND git apply ${OCCT_DIRECTORY_FLAG} --verbose --ignore-space-change --whitespace=fix ${CMAKE_CURRENT_LIST_DIR}/0001-OCCT-fix.patch
|
||||||
#DEPENDS dep_Boost
|
#DEPENDS dep_Boost
|
||||||
DEPENDS ${FREETYPE_PKG}
|
DEPENDS ${FREETYPE_PKG}
|
||||||
CMAKE_ARGS
|
CMAKE_ARGS
|
||||||
-DCMAKE_CXX_STANDARD=17
|
-DCMAKE_CXX_STANDARD=17
|
||||||
-DBUILD_LIBRARY_TYPE=${library_build_type}
|
-DBUILD_LIBRARY_TYPE=${library_build_type}
|
||||||
# With the Unix layout, OCCT's resources and licenses go under share/ and its scripts
|
|
||||||
# into bin/occt on Windows too. libslic3r finds the CMake package in lib/cmake/occt.
|
|
||||||
-DINSTALL_DIR_LAYOUT=Unix
|
|
||||||
-DINSTALL_DIR_BIN=bin/occt
|
|
||||||
-DINSTALL_DIR_LIB=lib/occt
|
|
||||||
-DINSTALL_DIR_INCLUDE=include/occt
|
|
||||||
-DINSTALL_DIR_CMAKE=lib/cmake/occt
|
|
||||||
-DUSE_TK=OFF
|
-DUSE_TK=OFF
|
||||||
-DUSE_TBB=OFF
|
-DUSE_TBB=OFF
|
||||||
#-DUSE_FREETYPE=OFF
|
#-DUSE_FREETYPE=OFF
|
||||||
|
|||||||
Vendored
-10
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
@@ -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"
|
||||||
@@ -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,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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -51,13 +51,14 @@ widens the existing OCCT build by one module flag in `deps/OCCT/OCCT.cmake`:
|
|||||||
-DBUILD_MODULE_ModelingAlgorithms=${SLIC3R_CAD}
|
-DBUILD_MODULE_ModelingAlgorithms=${SLIC3R_CAD}
|
||||||
```
|
```
|
||||||
|
|
||||||
Most of that module's toolkits are built either way, because `DataExchange`, which the STEP
|
Most of that module's twelve toolkits were already being built, because `DataExchange` — the
|
||||||
importer uses, depends on them. With the flag on, OCCT also builds `TKFillet` (used through
|
STEP path upstream ships — depends on them. The delta is `TKFillet` (used through
|
||||||
`BRepFilletAPI`), `TKOffset` (`BRepOffsetAPI`), and `TKFeat`, `TKHelix`, `TKXMesh` and
|
`BRepFilletAPI`), `TKOffset` (`BRepOffsetAPI`) and `TKFeat`, which nothing here references but
|
||||||
`TKExpress`, which nothing here references but which the module flag builds anyway, because
|
which the module flag builds anyway, because OCCT's module flags are all-or-nothing. On macOS
|
||||||
OCCT's module flags are all-or-nothing. On macOS and Linux OCCT links statically, so an
|
and Linux OCCT links statically, so an unreferenced toolkit costs build time and no shipped
|
||||||
unreferenced toolkit costs build time and no shipped bytes. On Windows OCCT builds shared and
|
bytes; on Windows OCCT builds shared, so the cost there is real DLL bytes. That Windows figure
|
||||||
only the linked toolkits ship, so the tab adds the `TKFillet`, `TKOffset` and `TKBool` DLLs.
|
has not been measured, and `OCCT.cmake` says so rather than carrying a number that was derived
|
||||||
|
from an incomplete toolkit list.
|
||||||
|
|
||||||
On Windows the packaging step asserts that every linked OCCT toolkit has a shipped DLL and
|
On Windows the packaging step asserts that every linked OCCT toolkit has a shipped DLL and
|
||||||
fails the configure with the name of any that is missing, because the alternative failure — a
|
fails the configure with the name of any that is missing, because the alternative failure — a
|
||||||
@@ -116,8 +117,6 @@ that keep it survivable are:
|
|||||||
`Import` features embed the imported solid as an OCCT BRep string inside the recipe rather than
|
`Import` features embed the imported solid as an OCCT BRep string inside the recipe rather than
|
||||||
referencing the source file, so a project opens without the STEP or mesh it was built from.
|
referencing the source file, so a project opens without the STEP or mesh it was built from.
|
||||||
The cost is that saved projects are coupled to an OCCT BRep revision.
|
The cost is that saved projects are coupled to an OCCT BRep revision.
|
||||||
`tests/data/cad_brep_occt76.brep` holds a solid written by OCCT 7.6, and its test fails if the
|
|
||||||
bundled OCCT can no longer read it.
|
|
||||||
|
|
||||||
## The interaction contract
|
## The interaction contract
|
||||||
|
|
||||||
|
|||||||
@@ -1,148 +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.
|
|
||||||
|
|
||||||
### Tiled booleans
|
|
||||||
|
|
||||||
The sweep slows down with the number of edges crossing a scan line, so a layer
|
|
||||||
cut into thousands of pieces makes every whole-layer boolean expensive.
|
|
||||||
`diff_ex_by_piece()` and `intersection_ex_by_piece()` take a subject of
|
|
||||||
non-overlapping `ExPolygons`, group them into tiles with
|
|
||||||
`ClipperUtils::tile_expolygons()`, and run each tile in parallel against only
|
|
||||||
the clip polygons near it, cut to the tile's box. Below 128 pieces there is a
|
|
||||||
single tile, and they are the plain `diff_ex()` / `intersection_ex()`.
|
|
||||||
|
|
||||||
The result covers the same area as the plain call. Without the safety offset
|
|
||||||
the rings are the same. With it, each tile unites only the clip polygons near
|
|
||||||
it, so a clip edge that the whole-layer union splits where it crosses a distant
|
|
||||||
clip polygon stays whole, and a crossing with the subject can round 1 unit
|
|
||||||
differently. The tiles' results are concatenated in tile order, so the order of
|
|
||||||
the output `ExPolygons` differs from the plain call.
|
|
||||||
|
|
||||||
### 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`.
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
# Prusa CORE One INDX profiles
|
|
||||||
|
|
||||||
The Prusa bundle provides separate four-tool and eight-tool CORE One INDX models
|
|
||||||
with 0.4 mm high-flow nozzles and a 248 x 205 x 270 mm printable volume. These are
|
|
||||||
independent-tool printers, not single-extruder MMU3 machines. Both inherit
|
|
||||||
`fdm_machine_common_coreone_indx`, which inherits the ordinary CORE One HF
|
|
||||||
printer; the concrete printer presets supply their tool-count-dependent arrays. The bed and wizard resources come from Prusa.
|
|
||||||
|
|
||||||
The source configuration is PrusaResearch 2.5.10 from
|
|
||||||
[Prusa's profile repository](https://github.com/prusa3d/PrusaSlicer-settings-prusa-fff/blob/65c5c8f1e1c3836f306119c49d717759cbc368db/PrusaResearch/2.5.10.ini).
|
|
||||||
Its printer, process and filament settings are translated to Orca option names.
|
|
||||||
The supplied process presets cover 0.10, 0.15, 0.20 and 0.25 mm layers. Their
|
|
||||||
shared `fdm_process_coreone_indx` base inherits the ordinary CORE One HF SPEED
|
|
||||||
process, with explicit INDX differences and layer-specific child settings.
|
|
||||||
Material presets are shared with the ordinary CORE One family.
|
|
||||||
|
|
||||||
## Tool changes and extrusion state
|
|
||||||
|
|
||||||
The start script initializes a persistent `tool_init` vector, counts the tools
|
|
||||||
used by the job, homes and probes using a loaded tool, calibrates the used tools,
|
|
||||||
and primes the initial tool at the cleaning station. The change script preserves
|
|
||||||
Prusa's `G27`, `P0`, `T`, `G12`, `G750` and `M906` sequences. It updates
|
|
||||||
`e_retracted` so Orca's subsequent unretraction agrees with the script's extrusion.
|
|
||||||
Per-tool temperature commands are guarded by `is_extruder_used`.
|
|
||||||
|
|
||||||
The off-bed purge station is the default. With a prime tower enabled, a newly
|
|
||||||
used tool still receives its initial station purge, while an initialized tool
|
|
||||||
uses the source's wipe-tower preparation path. `tool_init` persists across these
|
|
||||||
changes; treating every tool selection as first use would change the purge and
|
|
||||||
deretraction sequence.
|
|
||||||
|
|
||||||
Purge volume uses the filament's minimal purge setting. Orca's optional flush
|
|
||||||
volumetric speed is divided by filament cross-sectional area before it is used
|
|
||||||
as a linear extrusion-speed override. Prusa's separate `filament_flush_volume`
|
|
||||||
override is not available in these profiles. The source's `EXCLUDE_E_START` and
|
|
||||||
`EXCLUDE_E_END` internal markers become comments rather than printer commands.
|
|
||||||
|
|
||||||
Pressure-advance restoration and automatic pressure-advance emission use the
|
|
||||||
selected filament preset's settings. These profiles do not impose machine-owned
|
|
||||||
filament overrides. Dock-fan control retains the source's material and layer
|
|
||||||
conditions; shutdown parks the tool and turns off the used heaters and dock fan.
|
|
||||||
|
|
||||||
## Configuration boundaries
|
|
||||||
|
|
||||||
The scripts use Orca's temperature, retraction, fan and speed option names. The
|
|
||||||
nozzle-check high-flow flag is fixed because these presets describe HF nozzles;
|
|
||||||
the abrasive-material flag is derived from the filament's required nozzle HRC.
|
|
||||||
An unset idle temperature uses Orca's zero sentinel. ABS's source XY shrinkage
|
|
||||||
compensation is represented using Orca's retained-size percentage.
|
|
||||||
|
|
||||||
Prusa's consistent-surface cooling strategy and filament-specific infill crossing
|
|
||||||
speed limits have no direct Orca profile equivalent. These presets use Orca's
|
|
||||||
native layer-time cooling and material volumetric limits. They do not add slicer
|
|
||||||
features or change existing Prusa printer profiles.
|
|
||||||
@@ -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.
|
|
||||||
@@ -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.
|
|
||||||
@@ -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
|
||||||
|
|||||||
+1830
-1830
File diff suppressed because it is too large
Load Diff
+1189
-1189
File diff suppressed because it is too large
Load Diff
+2943
-2943
File diff suppressed because it is too large
Load Diff
+1338
-1338
File diff suppressed because it is too large
Load Diff
+2014
-2014
File diff suppressed because it is too large
Load Diff
+321
-321
@@ -1,323 +1,323 @@
|
|||||||
{
|
{
|
||||||
"result": 0,
|
"result": 0,
|
||||||
"t": 1765968095,
|
"t": 1765968095,
|
||||||
"ver": 202511162200,
|
"ver": 202511162200,
|
||||||
"data": [
|
"data": [
|
||||||
{
|
{
|
||||||
"ecode": "03008041",
|
"ecode": "03008041",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
4,
|
4,
|
||||||
6
|
6
|
||||||
],
|
],
|
||||||
"device": "26A"
|
"device": "26A"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "03008060",
|
"ecode": "03008060",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
22
|
22
|
||||||
],
|
],
|
||||||
"device": "26A"
|
"device": "26A"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "12008010",
|
"ecode": "12008010",
|
||||||
"image": "c31daf51b36e446698f14c6df879ea21.png",
|
"image": "c31daf51b36e446698f14c6df879ea21.png",
|
||||||
"actions": [
|
"actions": [
|
||||||
6,
|
6,
|
||||||
4
|
4
|
||||||
],
|
],
|
||||||
"device": "26A"
|
"device": "26A"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "12FF8010",
|
"ecode": "12FF8010",
|
||||||
"image": "f87557495f2f2163a44bde8f4ddd9b8c.png",
|
"image": "f87557495f2f2163a44bde8f4ddd9b8c.png",
|
||||||
"actions": [
|
"actions": [
|
||||||
4,
|
4,
|
||||||
6
|
6
|
||||||
],
|
],
|
||||||
"device": "26A"
|
"device": "26A"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "18008016",
|
"ecode": "18008016",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
4,
|
4,
|
||||||
6
|
6
|
||||||
],
|
],
|
||||||
"device": "26A"
|
"device": "26A"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "07008016",
|
"ecode": "07008016",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
4,
|
4,
|
||||||
6
|
6
|
||||||
],
|
],
|
||||||
"device": "26A"
|
"device": "26A"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "07018016",
|
"ecode": "07018016",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
4,
|
4,
|
||||||
6
|
6
|
||||||
],
|
],
|
||||||
"device": "26A"
|
"device": "26A"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "03002E0000030001",
|
"ecode": "03002E0000030001",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
52
|
52
|
||||||
],
|
],
|
||||||
"device": "26A"
|
"device": "26A"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "03004016",
|
"ecode": "03004016",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
6
|
6
|
||||||
],
|
],
|
||||||
"device": "26A"
|
"device": "26A"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "03008019",
|
"ecode": "03008019",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
4
|
4
|
||||||
],
|
],
|
||||||
"device": "default"
|
"device": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "0502C026",
|
"ecode": "0502C026",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
11
|
11
|
||||||
],
|
],
|
||||||
"device": "default"
|
"device": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "05004003",
|
"ecode": "05004003",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
11
|
11
|
||||||
],
|
],
|
||||||
"device": "default"
|
"device": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "05004040",
|
"ecode": "05004040",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
11
|
11
|
||||||
],
|
],
|
||||||
"device": "default"
|
"device": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "05004042",
|
"ecode": "05004042",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
36,
|
36,
|
||||||
37
|
37
|
||||||
],
|
],
|
||||||
"device": "default"
|
"device": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "03008000",
|
"ecode": "03008000",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
2
|
2
|
||||||
],
|
],
|
||||||
"device": "default"
|
"device": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "0C00402D",
|
"ecode": "0C00402D",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
11
|
11
|
||||||
],
|
],
|
||||||
"device": "default"
|
"device": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "05004037",
|
"ecode": "05004037",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
11
|
11
|
||||||
],
|
],
|
||||||
"device": "default"
|
"device": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "05004015",
|
"ecode": "05004015",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
14
|
14
|
||||||
],
|
],
|
||||||
"device": "default"
|
"device": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "05004007",
|
"ecode": "05004007",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
11
|
11
|
||||||
],
|
],
|
||||||
"device": "default"
|
"device": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "0502C014",
|
"ecode": "0502C014",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
11
|
11
|
||||||
],
|
],
|
||||||
"device": "default"
|
"device": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "0300800C",
|
"ecode": "0300800C",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
2
|
2
|
||||||
],
|
],
|
||||||
"device": "default"
|
"device": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "05004004",
|
"ecode": "05004004",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
11
|
11
|
||||||
],
|
],
|
||||||
"device": "default"
|
"device": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "0500402E",
|
"ecode": "0500402E",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
14
|
14
|
||||||
],
|
],
|
||||||
"device": "default"
|
"device": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "05024001",
|
"ecode": "05024001",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
11
|
11
|
||||||
],
|
],
|
||||||
"device": "default"
|
"device": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "0502C012",
|
"ecode": "0502C012",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
11
|
11
|
||||||
],
|
],
|
||||||
"device": "default"
|
"device": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "0502C010",
|
"ecode": "0502C010",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
35
|
35
|
||||||
],
|
],
|
||||||
"device": "default"
|
"device": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "05008092",
|
"ecode": "05008092",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
27,
|
27,
|
||||||
5
|
5
|
||||||
],
|
],
|
||||||
"device": "default"
|
"device": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "05004014",
|
"ecode": "05004014",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
11
|
11
|
||||||
],
|
],
|
||||||
"device": "default"
|
"device": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "0300800D",
|
"ecode": "0300800D",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
2,
|
2,
|
||||||
6
|
6
|
||||||
],
|
],
|
||||||
"device": "default"
|
"device": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "0500402F",
|
"ecode": "0500402F",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
14
|
14
|
||||||
],
|
],
|
||||||
"device": "default"
|
"device": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "05004041",
|
"ecode": "05004041",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
11
|
11
|
||||||
],
|
],
|
||||||
"device": "default"
|
"device": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "0500400E",
|
"ecode": "0500400E",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
11
|
11
|
||||||
],
|
],
|
||||||
"device": "default"
|
"device": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "05004043",
|
"ecode": "05004043",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
11
|
11
|
||||||
],
|
],
|
||||||
"device": "default"
|
"device": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "0C00402C",
|
"ecode": "0C00402C",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
11
|
11
|
||||||
],
|
],
|
||||||
"device": "default"
|
"device": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "03008017",
|
"ecode": "03008017",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
4
|
4
|
||||||
],
|
],
|
||||||
"device": "default"
|
"device": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "03008013",
|
"ecode": "03008013",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
2
|
2
|
||||||
],
|
],
|
||||||
"device": "default"
|
"device": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "03008007",
|
"ecode": "03008007",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
2,
|
2,
|
||||||
5
|
5
|
||||||
],
|
],
|
||||||
"device": "default"
|
"device": "default"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"ecode": "03008016",
|
"ecode": "03008016",
|
||||||
"image": "",
|
"image": "",
|
||||||
"actions": [
|
"actions": [
|
||||||
4,
|
4,
|
||||||
5,
|
5,
|
||||||
6
|
6
|
||||||
],
|
],
|
||||||
"device": "default"
|
"device": "default"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
+2847
-2847
File diff suppressed because it is too large
Load Diff
+20903
-20903
File diff suppressed because it is too large
Load Diff
+20315
-20315
File diff suppressed because it is too large
Load Diff
+22215
-22215
File diff suppressed because it is too large
Load Diff
+20215
-20215
File diff suppressed because it is too large
Load Diff
+21799
-21799
File diff suppressed because it is too large
Load Diff
+19555
-19555
File diff suppressed because it is too large
Load Diff
+23351
-23351
File diff suppressed because it is too large
Load Diff
+20903
-20903
File diff suppressed because it is too large
Load Diff
+20315
-20315
File diff suppressed because it is too large
Load Diff
+22215
-22215
File diff suppressed because it is too large
Load Diff
+20215
-20215
File diff suppressed because it is too large
Load Diff
+21799
-21799
File diff suppressed because it is too large
Load Diff
+19555
-19555
File diff suppressed because it is too large
Load Diff
+23351
-23351
File diff suppressed because it is too large
Load Diff
+20903
-20903
File diff suppressed because it is too large
Load Diff
+20315
-20315
File diff suppressed because it is too large
Load Diff
+22215
-22215
File diff suppressed because it is too large
Load Diff
+20215
-20215
File diff suppressed because it is too large
Load Diff
+21799
-21799
File diff suppressed because it is too large
Load Diff
+19555
-19555
File diff suppressed because it is too large
Load Diff
+23351
-23351
File diff suppressed because it is too large
Load Diff
+20903
-20903
File diff suppressed because it is too large
Load Diff
+20315
-20315
File diff suppressed because it is too large
Load Diff
+22215
-22215
File diff suppressed because it is too large
Load Diff
+20215
-20215
File diff suppressed because it is too large
Load Diff
+21799
-21799
File diff suppressed because it is too large
Load Diff
+19555
-19555
File diff suppressed because it is too large
Load Diff
+23351
-23351
File diff suppressed because it is too large
Load Diff
+20903
-20903
File diff suppressed because it is too large
Load Diff
+20315
-20315
File diff suppressed because it is too large
Load Diff
+22215
-22215
File diff suppressed because it is too large
Load Diff
+20215
-20215
File diff suppressed because it is too large
Load Diff
+21799
-21799
File diff suppressed because it is too large
Load Diff
+19555
-19555
File diff suppressed because it is too large
Load Diff
+23351
-23351
File diff suppressed because it is too large
Load Diff
+20903
-20903
File diff suppressed because it is too large
Load Diff
+20315
-20315
File diff suppressed because it is too large
Load Diff
+22215
-22215
File diff suppressed because it is too large
Load Diff
+20215
-20215
File diff suppressed because it is too large
Load Diff
+21799
-21799
File diff suppressed because it is too large
Load Diff
+19555
-19555
File diff suppressed because it is too large
Load Diff
+23351
-23351
File diff suppressed because it is too large
Load Diff
+20903
-20903
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user