OrcaSlicer/src/libslic3r/Arrange.cpp
SoftFever 226450ea6a
Merge some features from BS1.9 beta4 (#5181)
* FIX: linux: fix the building issue on Linux Mint 21.3 Virginia

github: https://github.com/bambulab/BambuStudio/issues/3874
author: https://github.com/lucianoloder

Change-Id: Ia3db6923d5dd68dba532d7bdba6f93f73cc51d59

* FIX: auto-arranging incorrect with rotation enabled

auto-arranging incorrect with rotation enabled and the objects already have been rotated.

jira: STUDIO-6022
Change-Id: I349d663efb1fc71367c8a77aa8ed5047a0bf2017
(cherry picked from commit 75fe40257a274ed83886e1ee20ce8dedd0de48f6)

* ENH: update X1C & X1E start gcode

1.Fix fan problem

jira:NEW

Signed-off-by: xun.zhang <xun.zhang@bambulab.com>
Change-Id: I68ee5be78e142e8a2a210a1a70f5663893390610

* ENH: update A series gcode

1. Update A1 series start gcode and change filament gcode
2. Add G2814 command
3. Add multi-filament extrusion compensation and vibration suppression

jira:NEW

Signed-off-by: xun.zhang <xun.zhang@bambulab.com>
Change-Id: I57d2bc8e98d3e547881dc1369c1fb31413c6205d

* FIX: fix some cali problem of P series

jira: none
Change-Id: Id57ea8d65da22ab653cca49509cb923ff065e43f

* FIX: fix can't enter ',' in multiplicator

github: #3805

Change-Id: I6dd70822d1c2e79d66c70514d6dd580ab029c7ea

* calib wizard

* NEW: FlipLines infill

jira:6701

New infill pattern that combine block lines infill and switching layers for smooth transition.

Change-Id: I2608a2d39b14efcdfe9d39a9437280da350b94c0
(cherry picked from commit 8d0a09c8b763dfc924cbba9913c241e6afadbc7f)

* ENH: add nozzle blob detection and air printing detection

jira: new

Change-Id: Ie4a19a7ad7d0b10a021c516cbc3a84b4ae734302

* FIX: Top surface bridging fail on 3DHC & FL infill

Add 45 degree angle offset when processing the bridge.
Need to raise infill_direction to invalidate posPrepareInfill

jira: 6774
Change-Id: I5e6bef3aa814b01c5f30398ac745937a67e3ef4c
(cherry picked from commit 7b12cab10b88f432a11414f8caa1c6427777a1ba)

* FIX: the error display when reset virtual slot

jira: none
Change-Id: I5ae5899baf1bfc2aaadb832083b277855a669fd5

* FIX: Error "Voronoi cell doesn't contain a sourcepoint"

github: 3859
Change-Id: Idca84992bcba5380bfe05e63ac9a5e40419dcfdf

* fix build error

* FIX: CLI: fix the crash issue caused by get_min_flush_volumes

JIRA: no jira
Change-Id: I0d5bfd605e51ebddac8fddc4d83dab5055b0fbf2

* FIX: can't use support filament in gcode.3mf

1. Add total_filament_volumes, directly access it to get used filaments

github:#3865

Signed-off-by: xun.zhang <xun.zhang@bambulab.com>
Change-Id: I4fae4f1947b4ebd16e394e0f3cf5fb0e9f979717

* ENH: p series support long retraction

1. P series support long retraction in filament
2. Add long retraction params in common.json

jira:NEW

Signed-off-by: xun.zhang <xun.zhang@bambulab.com>
Change-Id: Ib94184fa1f0b5ab151360f1f053d8c8ff92e7e18

* ENH::modify some logs level

jira:[for log]

Change-Id: I6a46b8fcd3a030b4b630e800fe9a9ac5c387f117

* NEW: support multi device

JIRA: STUDIO-6072

Change-Id: Ic514c4097767b0a728368c9ea48ee103c031fbb0
Signed-off-by: Stone Li <stone.li@bambulab.com>

* ENH: update A1 series  gcode

1.Update filament change gcode and machine start gcode for
  A1 and A1 mini

jira:NEW

Signed-off-by: xun.zhang <xun.zhang@bambulab.com>
Change-Id: I2f3be3fd89fef21e717a32f2b89985fc046f7f6e

* FIX: always have 0th filament in ams mapping

1. Only set the filament id in map when flush length is not 0

jira:NEW

Signed-off-by: xun.zhang <xun.zhang@bambulab.com>
Change-Id: I6e0aeaf010f6e6dcbdc3bca5c0034aa60750bb67

* ENH: add filament id in slice info

jira:NEW

Signed-off-by: xun.zhang <xun.zhang@bambulab.com>
Change-Id: Ic5fe4632bca8acacc9ffd072ee2ed207c1da37aa

* ENH: refine ui for multi machine

JIRA: STUDIO-6819 STUDIO-6824
1. Shrink the Send Print dialog box
2. add input box for flipping panel

Change-Id: I4174c79ecd239c374ee11478951e12be399c57ce

* FIX: fix Issues with sending multiple devices

JIRA: STUDIO-6876

Signed-off-by: Kunlong Ma <kunlong.ma@bambulab.com>
Change-Id: I33c6a932863fc715c3f0eb5dfd4b299f980a4918

* NEW: support hms error code

Change-Id: Ic256a83cf501fb05bb9d3203f3d24cb1d1290fa4

* FIX:fixed some multi job issue

Change-Id: I338078ad8fcf809888db9d8daeb470a9bf4eab46

* NEW:support pin code binding

Change-Id: Ida5d47881fbd83f3ffedc80369cfe377114d7f13

* ENH:add printable check for devices

Change-Id: I672988fa9cfa986d924bfc64331752f4aef68067
(cherry picked from commit 69de9e5b8334ec94eec7fcee31038b8ff42d1d3b)

* FIX: add more fonts

jira: none
Change-Id: I6bafed3563083858f29e92a3d84906a2e53dcb5c
(cherry picked from commit afbea693e807dcc1c406a59aa5376b9ea2a5d606)

* ENH: load more fonts

this feature is according to Prusa by Filip Sykala<filip.sykala@prusa3d.cz>, thanks to Filip Sykala
jira: none
Change-Id: I55e92f184f750c0b93b679d4382aaa5b164ec5c3
(cherry picked from commit d05522c4cc5d7ee4cac42de398b88d347a55f74b)

* ENH: add ProfileDescription for translate

1.Add ProfileDescription.hpp simply for translating

jira:NEW

Signed-off-by: XunZhangBambu <xun.zhang@bambulab.com>
Change-Id: Iaa3ced1edccf67eaeebde35c1e8b36442d2e9a6f

* ENH: Improve CrossHatch transation layers

jira: 6701

Change name from Flippingline to CrossHatch.

Reduce noise, improve speed by 6.5%. Improve transation layers by
gradually increasing rotation angle and overshoot the transation
layer while direction changed.

Change-Id: I17fcc45b409074d121bf5bb5702e15553d925b51

* UP

* ENH: modify the default config for multi-device

JIRA: STUDIO-6072

Change-Id: If6e7582a8274eb5e685b8b8545f6eab5d17de3f5
Signed-off-by: Stone Li <stone.li@bambulab.com>

* ENH: add long retraction for P series

jira:NEW

Signed-off-by: xun.zhang <xun.zhang@bambulab.com>
Change-Id: I6890695b67e674fc5cdc2a208e89bd9e41404213

* FIX: all plates stats data missing issue

jira: new

Change-Id: I137a2b6d69ad08791f5a9a9788653621960dc63f

* ENH:update pre print options

jira:[for multi]

Change-Id: I2e9bb8a09436a71749af98a0bad94e9922f95c81

* FIX:fixed can't popup pinbind win on macos

jira:[STUDIO-6895]

Change-Id: I664bba78cf27420d736b586df19e3c09c6f8ed21

* FIX:fixed the task of padding cannot be cancelled

Change-Id: I401a22118c14ca7601be7a925cfd8e4796dfc1e9

* ENH:Play video after redirecting to device page

jira:[STUDIO-6884]

Change-Id: Ia5e2ac84e3d71baacfcf941b782dab2325f35d54

* FIX: fix ui bug in send multi machine page for mac

JIRA: STUDIO-6882
Incorrect background color when renaming during multi machine printing

Change-Id: I6c551f5023ffe747e7a7e2f5703b0707c9505922

* FIX: Fix some bugs in maintaining the selected status of local tasks

JIRA: STUDIO-6824

Change-Id: I12c4da3fc56ac5077b3ccd7e89a4b57c3675eaf5

* ENH: local task sort by send time by default

JIRA: STUDIO-6885

Change-Id: I03b5881a39ab2e90c5b9cf46052ba465ee707ccc

* FIX: Clicking to continue printing does not take effect in error code

JIRA: STUDIO-6830
Detected an incomplete printing task error pop-up when power outage occurred. Clicking to continue printing did not take effect

Change-Id: Ie85a1602093dabac861cd1f41ea21e1c312c83e9

* ENH: use designTitle when designId > 0

JIRA: STUDIO-6072

Change-Id: I8342df053edeab16f930522e099e2eef91e5c5a4
Signed-off-by: Stone Li <stone.li@bambulab.com>

* NEW:import vertex and mtl color from obj file

Jira: STUDIO-6805

Change-Id: Iaacb13ee2451effdb83e5aba4b7fe1637b7fc95f

* FIX:change the strategy of merge_ka_kd

Upgrade ui, users can directly ok to proceed to the next step
jira: STUDIO-6805

Change-Id: Ia81019c2eacb503666680c0b8583d026baa0134c
(cherry picked from commit 38a2434753c8e3b422267283b16c75f6ad195b14)

* FIX:use default_strategy after modifed cluster number

jira: STUDIO-6915
Change-Id: I4e0c3d62f5a766f73d48d1e06c4364fc6babe1ac

* FIX: the bug of incorrect button without restarting

JIRA: STUDIO-6824
The bug can cause the user to not restart when opening the multi-device option, but the button of send multi-devices appears

Change-Id: I0837fa79ecc1d8ab5ce98273ad134fa2f830421e

* FIX: wrong default value for long retraction

jira:NEW

Signed-off-by: xun.zhang <xun.zhang@bambulab.com>
Change-Id: Ifc2ec57a320fdb14e7ca746e5795501ed146ff32

* FIX: error code pop-up window without retry button in some code

JIRA: STUDIO-6922

Change-Id: I67464bebaba4558618301592c455db8824bbfe30

* FIX: air printing and nozzle blob detection issue

jira: STUDIO-6897

Change-Id: I008ddb24b74119d7e4124ae26310b4b86c42a799

* FIX:fix bugs of algo and read quad in obj file

Jira: STUDIO-6805
Change-Id: I6c33e8197225f27dccdfa0681e64d76d1df14f61

* dd

* ENH:Set the default nozzle diameter to 0.4

jira:[for nozzle]

Change-Id: I74a5c9b0460046496b897eae3d9f917ac1b99052

* FIX:fixed backspace error on macos

Change-Id: I76066391783c04857c1a60a6f8438111501b6d7c

* ENH:Subscription list deduplication

jira:[for mulit]

Change-Id: I10e9d849986c9661b587c7b1a509180c2451816e

* ENH:update wiki url for Pin Code

jira:[pin code]

Change-Id: I95faaa396a839b5b159119ef235b650c76706a84

* NEW:add OpenCV.cmake in deps

jira: none
Change-Id: I1ae4a2bd5618e9e620b08a937904d6af5d00bc41

* FIX:cancel obj import restrictions

jira: none
Change-Id: Iaf3e799ca982ad6aeb3ec76e9a416c4c8e4d100c

* NEW:add multiple printer restrictions

jira:[for multiple]

Change-Id: I0bb5a0c1062a543c42f8d67a9347efa358b0864a

* ENH:Added two entrances for adding devices

jira:[multi device]

Change-Id: Ieb6197e067d422979606f93b22b337a2399aec74

* slic3r: Fix wxFont being undefined

[427/494] Building CXX object src/slic3r/CMakeFiles/libslic3r_gui.dir/Utils/FontUtils.cpp.o
FAILED: src/slic3r/CMakeFiles/libslic3r_gui.dir/Utils/FontUtils.cpp.o
/usr/bin/c++ -DBOOST_ATOMIC_NO_LIB -DBOOST_CHRONO_NO_LIB -DBOOST_DATE_TIME_NO_LIB -DBOOST_FILESYSTEM_NO_LIB -DBOOST_IOSTREAMS_NO_LIB -DBOOST_LOCALE_NO_LIB -DBOOST_LOG_NO_LIB -DBOOST_REGEX_NO_LIB -DBOOST_SYSTEM_NO_LIB -DBOOST_THREAD_NO_LIB -DCURL_STATICLIB -DGLEW_STATIC -DLIBNEST2D_GEOMETRIES_libslic3r -DLIBNEST2D_OPTIMIZER_nlopt -DLIBNEST2D_STATIC -DLIBNEST2D_THREADING_tbb -DOPENSSL_CERT_OVERRIDE -DOPENVDB_OPENEXR_STATICLIB -DOPENVDB_STATICLIB -DSLIC3R_CURRENTLY_COMPILING_GUI_MODULE -DSLIC3R_GUI -DTBB_USE_CAPTURED_EXCEPTION=0 -DUNICODE -DUSE_TBB -DWXINTL_NO_GETTEXT_MACRO -D_UNICODE -D__WXGTK3__ -D__WXGTK__ -DwxDEBUG_LEVEL=0 -DwxNO_UNSAFE_WXSTRING_CONV -DwxUSE_UNICODE -I/usr/include/dbus-1.0 -I/usr/lib/x86_64-linux-gnu/dbus-1.0/include -I/run/build/BambuStudio/src -I/run/build/BambuStudio/build/src/platform -I/run/build/BambuStudio/src/hidapi/include -I/run/build/BambuStudio/src/slic3r/Utils -I/usr/include/gtk-3.0 -I/usr/include/pango-1.0 -I/usr/include/cairo -I/usr/include/gdk-pixbuf-2.0 -I/usr/include/webp -I/usr/include/at-spi2-atk/2.0 -I/usr/include/at-spi-2.0 -I/usr/include/atk-1.0 -I/usr/include/fribidi -I/usr/include/pixman-1 -I/usr/include/harfbuzz -I/usr/include/freetype2 -I/usr/include/libpng16 -I/usr/include/gio-unix-2.0 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/libmount -I/usr/include/blkid -I/usr/include/sysprof-6 -I/usr/include/gstreamer-1.0 -I/run/build/BambuStudio/build/src/libslic3r -I/run/build/BambuStudio/deps/build/destdir/usr/local/include/opencascade -I/run/build/BambuStudio/src/libnest2d/include -I/run/build/BambuStudio/src/miniz -I/run/build/BambuStudio/src/glu-libtess/include -I/run/build/BambuStudio/src/clipper2/Clipper2Lib/include -I/run/build/BambuStudio/src/minilzo -isystem /run/build/BambuStudio/src/eigen -isystem /run/build/BambuStudio/src/libigl -isystem /app/lib/wx/include/gtk3-unicode-static-3.1 -isystem /app/include/wx-3.1 -isystem /run/build/BambuStudio/deps/build/destdir/usr/local/include -isystem /run/build/BambuStudio/deps/build/destdir/usr/local/include/opencv4 -isystem /run/build/BambuStudio/deps/build/destdir/usr/local/include/OpenEXR -std=gnu++20 -fext-numeric-literals -Wall -Wno-reorder -pthread -O3 -DNDEBUG -std=gnu++17 -fPIC -fsigned-char -Werror=return-type -Wno-ignored-attributes -Wno-unknown-pragmas -DOPENVDB_ABI_VERSION_NUMBER=8 -MD -MT src/slic3r/CMakeFiles/libslic3r_gui.dir/Utils/FontUtils.cpp.o -MF src/slic3r/CMakeFiles/libslic3r_gui.dir/Utils/FontUtils.cpp.o.d -o src/slic3r/CMakeFiles/libslic3r_gui.dir/Utils/FontUtils.cpp.o -c /run/build/BambuStudio/src/slic3r/Utils/FontUtils.cpp
In file included from /run/build/BambuStudio/src/slic3r/Utils/FontUtils.cpp:1:
/run/build/BambuStudio/src/slic3r/Utils/FontUtils.hpp:51:21: error: ‘wxFont’ does not name a type
   51 | bool can_load(const wxFont &font);
      |                     ^~~~~~

* slic3r: Fix missing BOOST_LOG_TRIVIAL declaration

[427/494] Building CXX object src/slic3r/CMakeFiles/libslic3r_gui.dir/Utils/FontUtils.cpp.o
FAILED: src/slic3r/CMakeFiles/libslic3r_gui.dir/Utils/FontUtils.cpp.o
/usr/bin/c++ -DBOOST_ATOMIC_NO_LIB -DBOOST_CHRONO_NO_LIB -DBOOST_DATE_TIME_NO_LIB -DBOOST_FILESYSTEM_NO_LIB -DBOOST_IOSTREAMS_NO_LIB -DBOOST_LOCALE_NO_LIB -DBOOST_LOG_NO_LIB -DBOOST_REGEX_NO_LIB -DBOOST_SYSTEM_NO_LIB -DBOOST_THREAD_NO_LIB -DCURL_STATICLIB -DGLEW_STATIC -DLIBNEST2D_GEOMETRIES_libslic3r -DLIBNEST2D_OPTIMIZER_nlopt -DLIBNEST2D_STATIC -DLIBNEST2D_THREADING_tbb -DOPENSSL_CERT_OVERRIDE -DOPENVDB_OPENEXR_STATICLIB -DOPENVDB_STATICLIB -DSLIC3R_CURRENTLY_COMPILING_GUI_MODULE -DSLIC3R_GUI -DTBB_USE_CAPTURED_EXCEPTION=0 -DUNICODE -DUSE_TBB -DWXINTL_NO_GETTEXT_MACRO -D_UNICODE -D__WXGTK3__ -D__WXGTK__ -DwxDEBUG_LEVEL=0 -DwxNO_UNSAFE_WXSTRING_CONV -DwxUSE_UNICODE -I/usr/include/dbus-1.0 -I/usr/lib/x86_64-linux-gnu/dbus-1.0/include -I/run/build/BambuStudio/src -I/run/build/BambuStudio/build/src/platform -I/run/build/BambuStudio/src/hidapi/include -I/run/build/BambuStudio/src/slic3r/Utils -I/usr/include/gtk-3.0 -I/usr/include/pango-1.0 -I/usr/include/cairo -I/usr/include/gdk-pixbuf-2.0 -I/usr/include/webp -I/usr/include/at-spi2-atk/2.0 -I/usr/include/at-spi-2.0 -I/usr/include/atk-1.0 -I/usr/include/fribidi -I/usr/include/pixman-1 -I/usr/include/harfbuzz -I/usr/include/freetype2 -I/usr/include/libpng16 -I/usr/include/gio-unix-2.0 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/libmount -I/usr/include/blkid -I/usr/include/sysprof-6 -I/usr/include/gstreamer-1.0 -I/run/build/BambuStudio/build/src/libslic3r -I/run/build/BambuStudio/deps/build/destdir/usr/local/include/opencascade -I/run/build/BambuStudio/src/libnest2d/include -I/run/build/BambuStudio/src/miniz -I/run/build/BambuStudio/src/glu-libtess/include -I/run/build/BambuStudio/src/clipper2/Clipper2Lib/include -I/run/build/BambuStudio/src/minilzo -isystem /run/build/BambuStudio/src/eigen -isystem /run/build/BambuStudio/src/libigl -isystem /app/lib/wx/include/gtk3-unicode-static-3.1 -isystem /app/include/wx-3.1 -isystem /run/build/BambuStudio/deps/build/destdir/usr/local/include -isystem /run/build/BambuStudio/deps/build/destdir/usr/local/include/opencv4 -isystem /run/build/BambuStudio/deps/build/destdir/usr/local/include/OpenEXR -std=gnu++20 -fext-numeric-literals -Wall -Wno-reorder -pthread -O3 -DNDEBUG -std=gnu++17 -fPIC -fsigned-char -Werror=return-type -Wno-ignored-attributes -Wno-unknown-pragmas -DOPENVDB_ABI_VERSION_NUMBER=8 -MD -MT src/slic3r/CMakeFiles/libslic3r_gui.dir/Utils/FontUtils.cpp.o -MF src/slic3r/CMakeFiles/libslic3r_gui.dir/Utils/FontUtils.cpp.o.d -o src/slic3r/CMakeFiles/libslic3r_gui.dir/Utils/FontUtils.cpp.o -c /run/build/BambuStudio/src/slic3r/Utils/FontUtils.cpp
/run/build/BambuStudio/src/slic3r/Utils/FontUtils.cpp: In function ‘std::unique_ptr<Slic3r::FontFile> Slic3r::create_font_file(const char*)’:
/run/build/BambuStudio/src/slic3r/Utils/FontUtils.cpp:127:27: error: ‘error’ was not declared in this scope; did you mean ‘perror’?
  127 |         BOOST_LOG_TRIVIAL(error) << "Couldn't open " << file_path << " for reading.";
      |                           ^~~~~
      |                           perror

[447/494] Building CXX object src/slic3r/CMakeFiles/libslic3r_gui.dir/GUI/TaskManager.cpp.o
FAILED: src/slic3r/CMakeFiles/libslic3r_gui.dir/GUI/TaskManager.cpp.o
/usr/bin/c++ -DBOOST_ATOMIC_NO_LIB -DBOOST_CHRONO_NO_LIB -DBOOST_DATE_TIME_NO_LIB -DBOOST_FILESYSTEM_NO_LIB -DBOOST_IOSTREAMS_NO_LIB -DBOOST_LOCALE_NO_LIB -DBOOST_LOG_NO_LIB -DBOOST_REGEX_NO_LIB -DBOOST_SYSTEM_NO_LIB -DBOOST_THREAD_NO_LIB -DCURL_STATICLIB -DGLEW_STATIC -DLIBNEST2D_GEOMETRIES_libslic3r -DLIBNEST2D_OPTIMIZER_nlopt -DLIBNEST2D_STATIC -DLIBNEST2D_THREADING_tbb -DOPENSSL_CERT_OVERRIDE -DOPENVDB_OPENEXR_STATICLIB -DOPENVDB_STATICLIB -DSLIC3R_CURRENTLY_COMPILING_GUI_MODULE -DSLIC3R_GUI -DTBB_USE_CAPTURED_EXCEPTION=0 -DUNICODE -DUSE_TBB -DWXINTL_NO_GETTEXT_MACRO -D_UNICODE -D__WXGTK3__ -D__WXGTK__ -DwxDEBUG_LEVEL=0 -DwxNO_UNSAFE_WXSTRING_CONV -DwxUSE_UNICODE -I/usr/include/dbus-1.0 -I/usr/lib/x86_64-linux-gnu/dbus-1.0/include -I/run/build/BambuStudio/src -I/run/build/BambuStudio/build/src/platform -I/run/build/BambuStudio/src/hidapi/include -I/run/build/BambuStudio/src/slic3r/Utils -I/usr/include/gtk-3.0 -I/usr/include/pango-1.0 -I/usr/include/cairo -I/usr/include/gdk-pixbuf-2.0 -I/usr/include/webp -I/usr/include/at-spi2-atk/2.0 -I/usr/include/at-spi-2.0 -I/usr/include/atk-1.0 -I/usr/include/fribidi -I/usr/include/pixman-1 -I/usr/include/harfbuzz -I/usr/include/freetype2 -I/usr/include/libpng16 -I/usr/include/gio-unix-2.0 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/libmount -I/usr/include/blkid -I/usr/include/sysprof-6 -I/usr/include/gstreamer-1.0 -I/run/build/BambuStudio/build/src/libslic3r -I/run/build/BambuStudio/deps/build/destdir/usr/local/include/opencascade -I/run/build/BambuStudio/src/libnest2d/include -I/run/build/BambuStudio/src/miniz -I/run/build/BambuStudio/src/glu-libtess/include -I/run/build/BambuStudio/src/clipper2/Clipper2Lib/include -I/run/build/BambuStudio/src/minilzo -isystem /run/build/BambuStudio/src/eigen -isystem /run/build/BambuStudio/src/libigl -isystem /app/lib/wx/include/gtk3-unicode-static-3.1 -isystem /app/include/wx-3.1 -isystem /run/build/BambuStudio/deps/build/destdir/usr/local/include -isystem /run/build/BambuStudio/deps/build/destdir/usr/local/include/opencv4 -isystem /run/build/BambuStudio/deps/build/destdir/usr/local/include/OpenEXR -std=gnu++20 -fext-numeric-literals -Wall -Wno-reorder -pthread -O3 -DNDEBUG -std=gnu++17 -fPIC -fsigned-char -Werror=return-type -Wno-ignored-attributes -Wno-unknown-pragmas -DOPENVDB_ABI_VERSION_NUMBER=8 -MD -MT src/slic3r/CMakeFiles/libslic3r_gui.dir/GUI/TaskManager.cpp.o -MF src/slic3r/CMakeFiles/libslic3r_gui.dir/GUI/TaskManager.cpp.o.d -o src/slic3r/CMakeFiles/libslic3r_gui.dir/GUI/TaskManager.cpp.o -c /run/build/BambuStudio/src/slic3r/GUI/TaskManager.cpp
In file included from /run/build/BambuStudio/src/slic3r/GUI/TaskManager.cpp:1:
/run/build/BambuStudio/src/slic3r/GUI/TaskManager.hpp: In member function ‘void Slic3r::TaskStateInfo::set_state(Slic3r::TaskState)’:
/run/build/BambuStudio/src/slic3r/GUI/TaskManager.hpp:40:9: error: ‘BOOST_LOG_TRIVIAL’ was not declared in this scope
   40 |         BOOST_LOG_TRIVIAL(trace) << "TaskStateInfo set state = " << get_task_state_enum_str(ts);
      |         ^~~~~~~~~~~~~~~~~

* fix OpenCV

* wip - build break

* fix build error wip

* ENH: support preset description(tooltip)

Change-Id: Iff005baac4974c538d1109fb0ba1df20b04a8f69
Jira: STUDIO-5754

* fix more build errors

* Revert "ENH: load more fonts"

This reverts commit 32b6fd199a.

* change colors

* misc fixes

* restore export gcode btn

---------

Signed-off-by: xun.zhang <xun.zhang@bambulab.com>
Signed-off-by: Stone Li <stone.li@bambulab.com>
Signed-off-by: Kunlong Ma <kunlong.ma@bambulab.com>
Signed-off-by: XunZhangBambu <xun.zhang@bambulab.com>
Co-authored-by: lane.wei <lane.wei@bambulab.com>
Co-authored-by: Arthur <arthur.tang@bambulab.com>
Co-authored-by: xun.zhang <xun.zhang@bambulab.com>
Co-authored-by: zhimin.zeng <zhimin.zeng@bambulab.com>
Co-authored-by: Kunlong Ma <kunlong.ma@bambulab.com>
Co-authored-by: jianjia.ma <jianjia.ma@bambulab.com>
Co-authored-by: liz.li <liz.li@bambulab.com>
Co-authored-by: tao wang <tao.wang@bambulab.com>
Co-authored-by: Stone Li <stone.li@bambulab.com>
Co-authored-by: zhou.xu <zhou.xu@bambulab.com>
Co-authored-by: Bastien Nocera <hadess@hadess.net>
Co-authored-by: chunmao.guo <chunmao.guo@bambulab.com>
2024-04-28 22:58:47 +08:00

1146 lines
46 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "Arrange.hpp"
#include "Print.hpp"
#include "BoundingBox.hpp"
#include <libnest2d/backends/libslic3r/geometries.hpp>
#include <libnest2d/optimizers/nlopt/subplex.hpp>
#include <libnest2d/placers/nfpplacer.hpp>
#include <libnest2d/selections/firstfit.hpp>
#include <libnest2d/utils/rotcalipers.hpp>
#include <numeric>
#include <ClipperUtils.hpp>
#include <boost/geometry/index/rtree.hpp>
#if defined(_MSC_VER) && defined(__clang__)
#define BOOST_NO_CXX17_HDR_STRING_VIEW
#endif
#include <boost/log/trivial.hpp>
#include <boost/multiprecision/integer.hpp>
#include <boost/rational.hpp>
namespace libnest2d {
#if !defined(_MSC_VER) && defined(__SIZEOF_INT128__) && !defined(__APPLE__)
using LargeInt = __int128;
#else
using LargeInt = boost::multiprecision::int128_t;
template<> struct _NumTag<LargeInt>
{
using Type = ScalarTag;
};
#endif
template<class T> struct _NumTag<boost::rational<T>>
{
using Type = RationalTag;
};
namespace nfp {
template<class S> struct NfpImpl<S, NfpLevel::CONVEX_ONLY>
{
NfpResult<S> operator()(const S &sh, const S &other)
{
return nfpConvexOnly<S, boost::rational<LargeInt>>(sh, other);
}
};
} // namespace nfp
} // namespace libnest2d
namespace Slic3r {
template<class Tout = double, class = FloatingOnly<Tout>, int...EigenArgs>
inline constexpr Eigen::Matrix<Tout, 2, EigenArgs...> unscaled(
const Slic3r::ClipperLib::IntPoint &v) noexcept
{
return Eigen::Matrix<Tout, 2, EigenArgs...>{unscaled<Tout>(v.x()),
unscaled<Tout>(v.y())};
}
namespace arrangement {
using namespace libnest2d;
// Get the libnest2d types for clipper backend
using Item = _Item<ExPolygon>;
using Box = _Box<Point>;
using Circle = _Circle<Point>;
using Segment = _Segment<Point>;
using MultiPolygon = ExPolygons;
// Summon the spatial indexing facilities from boost
namespace bgi = boost::geometry::index;
using SpatElement = std::pair<Box, unsigned>;
using SpatIndex = bgi::rtree< SpatElement, bgi::rstar<16, 4> >;
using ItemGroup = std::vector<std::reference_wrapper<Item>>;
// A coefficient used in separating bigger items and smaller items.
const double BIG_ITEM_TRESHOLD = 0.02;
#define VITRIFY_TEMP_DIFF_THRSH 15 // bed temp can be higher than vitrify temp, but not higher than this thresh
void update_arrange_params(ArrangeParams& params, const DynamicPrintConfig* print_cfg, const ArrangePolygons& selected)
{
double skirt_distance = get_real_skirt_dist(*print_cfg);
// Note: skirt_distance is now defined between outermost brim and skirt, not the object and skirt.
// So we can't do max but do adding instead.
params.brim_skirt_distance = skirt_distance;
params.bed_shrink_x += params.brim_skirt_distance;
params.bed_shrink_y += params.brim_skirt_distance;
// for sequential print, we need to inflate the bed because cleareance_radius is so large
if (params.is_seq_print) {
params.bed_shrink_x -= params.cleareance_radius / 2;
params.bed_shrink_y -= params.cleareance_radius / 2;
}
}
void update_selected_items_inflation(ArrangePolygons& selected, const DynamicPrintConfig* print_cfg, ArrangeParams& params) {
// do not inflate brim_width. Objects are allowed to have overlapped brim.
Points bedpts = get_shrink_bedpts(print_cfg, params);
BoundingBox bedbb = Polygon(bedpts).bounding_box();
// set obj distance for auto seq_print
if (params.is_seq_print) {
bool all_objects_are_short = std::all_of(selected.begin(), selected.end(), [&](ArrangePolygon& ap) { return ap.height < params.nozzle_height; });
if (all_objects_are_short) {
params.min_obj_distance = std::max(params.min_obj_distance, scaled(double(MAX_OUTER_NOZZLE_DIAMETER)/2+0.001));
}
else
params.min_obj_distance = std::max(params.min_obj_distance, scaled(params.cleareance_radius + 0.001)); // +0.001mm to avoid clearance check fail due to rounding error
}
double brim_max = 0;
bool plate_has_tree_support = false;
std::for_each(selected.begin(), selected.end(), [&](ArrangePolygon& ap) {
brim_max = std::max(brim_max, ap.brim_width);
if (ap.has_tree_support) plate_has_tree_support = true; });
std::for_each(selected.begin(), selected.end(), [&](ArrangePolygon& ap) {
// 1. if user input a distance, use it
// 2. if there is an object with tree support, all objects use the max tree branch radius (brim_max=branch diameter)
// 3. otherwise, use each object's own brim width
ap.inflation = params.min_obj_distance != 0 ? params.min_obj_distance / 2 :
plate_has_tree_support ? scaled(brim_max / 2) : scaled(ap.brim_width);
BoundingBox apbb = ap.poly.contour.bounding_box();
auto diffx = bedbb.size().x() - apbb.size().x() - 5;
auto diffy = bedbb.size().y() - apbb.size().y() - 5;
if (diffx > 0 && diffy > 0) {
auto min_diff = std::min(diffx, diffy);
ap.inflation = std::min(min_diff / 2, ap.inflation);
}
});
}
void update_unselected_items_inflation(ArrangePolygons& unselected, const DynamicPrintConfig* print_cfg, const ArrangeParams& params)
{
float exclusion_gap = 1.f;
if (params.is_seq_print) {
// bed_shrink_x is typically (-params.cleareance_radius / 2+5) for seq_print
exclusion_gap = std::max(exclusion_gap, params.cleareance_radius / 2 + params.bed_shrink_x + 1.f); // +1mm gap so the exclusion region is not too close
// dont forget to move the excluded region
for (auto& region : unselected) {
if (region.is_virt_object) region.poly.translate(scaled(params.bed_shrink_x), scaled(params.bed_shrink_y));
}
}
// For occulusion regions, inflation should be larger to prevent genrating brim on them.
// However, extrusion cali regions are exceptional, since we can allow brim overlaps them.
// 屏蔽区域只需要膨胀brim宽度防止brim长过去挤出标定区域不需要膨胀brim可以长过去。
// 以前我们认为还需要膨胀clearance_radius/2这其实是不需要的因为这些区域并不会真的摆放物体
// 其他物体的膨胀轮廓是可以跟它们重叠的。
std::for_each(unselected.begin(), unselected.end(),
[&](auto& ap) { ap.inflation = !ap.is_virt_object ? (params.min_obj_distance == 0 ? scaled(ap.brim_width) : params.min_obj_distance / 2)
: (ap.is_extrusion_cali_object ? 0 : scale_(exclusion_gap)); });
}
void update_selected_items_axis_align(ArrangePolygons& selected, const DynamicPrintConfig* print_cfg, const ArrangeParams& params)
{
// now only need to consider "Align to x axis"
if (!params.align_to_y_axis)
return;
for (ArrangePolygon& ap : selected) {
bool validResult = false;
double angle = 0.0;
{
const auto& pts = ap.transformed_poly().contour;
int lpt = pts.size();
double a00 = 0, a10 = 0, a01 = 0, a20 = 0, a11 = 0, a02 = 0, a30 = 0, a21 = 0, a12 = 0, a03 = 0;
double xi, yi, xi2, yi2, xi_1, yi_1, xi_12, yi_12, dxy, xii_1, yii_1;
xi_1 = pts.back().x();
yi_1 = pts.back().y();
xi_12 = xi_1 * xi_1;
yi_12 = yi_1 * yi_1;
for (int i = 0; i < lpt; i++) {
xi = pts[i].x();
yi = pts[i].y();
xi2 = xi * xi;
yi2 = yi * yi;
dxy = xi_1 * yi - xi * yi_1;
xii_1 = xi_1 + xi;
yii_1 = yi_1 + yi;
a00 += dxy;
a10 += dxy * xii_1;
a01 += dxy * yii_1;
a20 += dxy * (xi_1 * xii_1 + xi2);
a11 += dxy * (xi_1 * (yii_1 + yi_1) + xi * (yii_1 + yi));
a02 += dxy * (yi_1 * yii_1 + yi2);
a30 += dxy * xii_1 * (xi_12 + xi2);
a03 += dxy * yii_1 * (yi_12 + yi2);
a21 += dxy * (xi_12 * (3 * yi_1 + yi) + 2 * xi * xi_1 * yii_1 + xi2 * (yi_1 + 3 * yi));
a12 += dxy * (yi_12 * (3 * xi_1 + xi) + 2 * yi * yi_1 * xii_1 + yi2 * (xi_1 + 3 * xi));
xi_1 = xi;
yi_1 = yi;
xi_12 = xi2;
yi_12 = yi2;
}
if (std::abs(a00) > EPSILON) {
double db1_2, db1_6, db1_12, db1_24, db1_20, db1_60;
double m00, m10, m01, m20, m11, m02, m30, m21, m12, m03;
if (a00 > 0) {
db1_2 = 0.5;
db1_6 = 0.16666666666666666666666666666667;
db1_12 = 0.083333333333333333333333333333333;
db1_24 = 0.041666666666666666666666666666667;
db1_20 = 0.05;
db1_60 = 0.016666666666666666666666666666667;
}
else {
db1_2 = -0.5;
db1_6 = -0.16666666666666666666666666666667;
db1_12 = -0.083333333333333333333333333333333;
db1_24 = -0.041666666666666666666666666666667;
db1_20 = -0.05;
db1_60 = -0.016666666666666666666666666666667;
}
m00 = a00 * db1_2;
m10 = a10 * db1_6;
m01 = a01 * db1_6;
m20 = a20 * db1_12;
m11 = a11 * db1_24;
m02 = a02 * db1_12;
m30 = a30 * db1_20;
m21 = a21 * db1_60;
m12 = a12 * db1_60;
m03 = a03 * db1_20;
double cx = m10 / m00;
double cy = m01 / m00;
double a = m20 / m00 - cx * cx;
double b = m11 / m00 - cx * cy;
double c = m02 / m00 - cy * cy;
//if a and c are close, there is no dominant axis, then do not rotate
// ratio is always no more than 1
double ratio = std::abs(a) > std::abs(c) ? std::abs(c / a) :
std::abs(c) > 0 ? std::abs(a / c) : 0;
if (ratio>0.66) {
validResult = false;
}
else {
angle = std::atan2(2 * b, (a - c)) / 2;
angle = PI / 2 - angle;
// if the angle is close to PI or -PI, it means the object is vertical, then do not rotate
if (std::abs(std::abs(angle) - PI) < 0.01)
angle = 0;
validResult = true;
}
}
}
if (validResult) { ap.rotation += angle; }
}
}
//it will bed accurate after call update_params
Points get_shrink_bedpts(const DynamicPrintConfig* print_cfg, const ArrangeParams& params)
{
Points bedpts = get_bed_shape(*print_cfg);
// shrink bed by moving to center by dist
auto shrinkFun = [](Points& bedpts, double dist, int direction) {
#define SGN(x) ((x) >= 0 ? 1 : -1)
Point center = Polygon(bedpts).bounding_box().center();
for (auto& pt : bedpts) pt[direction] += dist * SGN(center[direction] - pt[direction]);
};
shrinkFun(bedpts, scaled(params.bed_shrink_x), 0);
shrinkFun(bedpts, scaled(params.bed_shrink_y), 1);
return bedpts;
}
// Fill in the placer algorithm configuration with values carefully chosen for
// Slic3r.
template<class PConf>
void fill_config(PConf& pcfg, const ArrangeParams &params) {
if (params.is_seq_print) {
// Start placing the items from the center of the print bed
pcfg.starting_point = PConf::Alignment::BOTTOM_LEFT;
}
else {
// Start placing the items from the center of the print bed
pcfg.starting_point = PConf::Alignment::TOP_RIGHT;
}
if (params.do_final_align) {
// Align the arranged pile into the center of the bin
pcfg.alignment = PConf::Alignment::CENTER;
}else
pcfg.alignment = PConf::Alignment::DONT_ALIGN;
// Try 4 angles (45 degree step) and find the one with min cost
if (params.allow_rotations)
pcfg.rotations = {0., PI / 4., PI/2, 3. * PI / 4. };
else
pcfg.rotations = {0.};
// The accuracy of optimization.
// Goes from 0.0 to 1.0 and scales performance as well
pcfg.accuracy = params.accuracy;
// Allow parallel execution.
pcfg.parallel = params.parallel;
// BBS: excluded regions in BBS bed
for (auto& poly : params.excluded_regions)
process_arrangeable(poly, pcfg.m_excluded_regions);
// BBS: nonprefered regions in BBS bed
for (auto& poly : params.nonprefered_regions)
process_arrangeable(poly, pcfg.m_nonprefered_regions);
for (auto& itm : pcfg.m_excluded_regions) {
itm.markAsFixedInBin(0);
itm.inflate(scaled(-2. * EPSILON));
}
}
// Apply penalty to object function result. This is used only when alignment
// after arrange is explicitly disabled (PConfig::Alignment::DONT_ALIGN)
// Also, this will only work well for Box shaped beds.
static double fixed_overfit(const std::tuple<double, Box>& result, const Box &binbb)
{
double score = std::get<0>(result);
Box pilebb = std::get<1>(result);
Box fullbb = sl::boundingBox(pilebb, binbb);
auto diff = double(fullbb.area()) - binbb.area();
if(diff > 0) score += diff;
return score;
}
// useful for arranging big circle objects
static double fixed_overfit_topright_sliding(const std::tuple<double, Box> &result, const Box &binbb, const std::vector<Box> &excluded_boxes)
{
double score = std::get<0>(result);
Box pilebb = std::get<1>(result);
auto shift = binbb.maxCorner() - pilebb.maxCorner();
shift.x() = std::max(0, shift.x()); // do not allow left shift
shift.y() = std::max(0, shift.y()); // do not allow bottom shift
pilebb.minCorner() += shift;
pilebb.maxCorner() += shift;
Box fullbb = sl::boundingBox(pilebb, binbb);
auto diff = double(fullbb.area()) - binbb.area();
if (diff > 0) score += diff;
// excluded regions and nonprefered regions should not intersect the translated pilebb
for (auto &bb : excluded_boxes) {
auto area_ = pilebb.intersection(bb).area();
if (area_ > 0) score += area_;
}
return score;
}
// A class encapsulating the libnest2d Nester class and extending it with other
// management and spatial index structures for acceleration.
template<class TBin>
class AutoArranger {
public:
// Useful type shortcuts...
using Placer = typename placers::_NofitPolyPlacer<ExPolygon, TBin>;
using Selector = selections::_FirstFitSelection<ExPolygon>;
using Packer = _Nester<Placer, Selector>;
using PConfig = typename Packer::PlacementConfig;
using Distance = TCoord<PointImpl>;
std::vector<Item> m_excluded_items_in_each_plate; // for V4 bed there are excluded regions at bottom left corner
protected:
Packer m_pck;
PConfig m_pconf; // Placement configuration
TBin m_bin;
double m_bin_area;
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4244)
#pragma warning(disable: 4267)
#endif
SpatIndex m_rtree; // spatial index for the normal (bigger) objects
SpatIndex m_smallsrtree; // spatial index for only the smaller items
#ifdef _MSC_VER
#pragma warning(pop)
#endif
double m_norm; // A coefficient to scale distances
MultiPolygon m_merged_pile; // The already merged pile (vector of items)
Box m_pilebb; // The bounding box of the merged pile.
ItemGroup m_remaining; // Remaining items
ItemGroup m_items; // allready packed items
std::vector<Box> m_excluded_and_extruCali_regions; // excluded and extrusion calib regions
size_t m_item_count = 0; // Number of all items to be packed
ArrangeParams params;
template<class T> ArithmeticOnly<T, double> norm(T val)
{
return double(val) / m_norm;
}
// dist function for sequential print (starting_point=BOTTOM_LEFT) which is composed of
// 1) Y distance of item corner to bed corner. Must be put above bed corner. (high weight)
// 2) X distance of item corner to bed corner (low weight)
// 3) item row occupancy (useful when rotation is enabled)
// 4需要允许往屏蔽区域的左边或下边去一点不然很多物体可能认为摆不进去实际上我们最后是可以做平移的
double dist_for_BOTTOM_LEFT(Box ibb, const ClipperLib::IntPoint& origin_pack)
{
double dist_corner_y = ibb.minCorner().y() - origin_pack.y();
double dist_corner_x = ibb.minCorner().x() - origin_pack.x();
// occupy as few rows as possible if we have rotations
double bindist = double(ibb.maxCorner().y() - ibb.minCorner().y());
if (dist_corner_x >= 0 && dist_corner_y >= 0)
bindist += dist_corner_y + 1 * dist_corner_x;
else {
if (dist_corner_x < 0) bindist += 10 * (-dist_corner_x);
if (dist_corner_y < 0) bindist += 10 * (-dist_corner_y);
}
bindist = norm(bindist);
return bindist;
}
double dist_to_bin(const Box& ibb, const ClipperLib::IntPoint& origin_pack, typename Packer::PlacementConfig::Alignment starting_point_alignment)
{
double bindist = 0;
if (starting_point_alignment == PConfig::Alignment::BOTTOM_LEFT)
bindist = norm(pl::distance(ibb.minCorner(), origin_pack));
else if (starting_point_alignment == PConfig::Alignment::TOP_RIGHT)
bindist = norm(pl::distance(ibb.maxCorner(), origin_pack));
else
bindist = norm(pl::distance(ibb.center(), origin_pack));
return bindist;
}
// This is "the" object function which is evaluated many times for each
// vertex (decimated with the accuracy parameter) of each object.
// Therefore it is upmost crucial for this function to be as efficient
// as it possibly can be but at the same time, it has to provide
// reasonable results.
std::tuple<double /*score*/, Box /*farthest point from bin center*/>
objfunc(const Item &item, const ClipperLib::IntPoint &origin_pack)
{
const double bin_area = m_bin_area;
const SpatIndex& spatindex = m_rtree;
const SpatIndex& smalls_spatindex = m_smallsrtree;
// We will treat big items (compared to the print bed) differently
auto isBig = [bin_area](double a) {
return a/bin_area > BIG_ITEM_TRESHOLD ;
};
// Candidate item bounding box
auto ibb = item.boundingBox();
// Calculate the full bounding box of the pile with the candidate item
auto fullbb = sl::boundingBox(m_pilebb, ibb);
// The bounding box of the big items (they will accumulate in the center
// of the pile
Box bigbb;
if(spatindex.empty()) bigbb = fullbb;
else {
auto boostbb = spatindex.bounds();
boost::geometry::convert(boostbb, bigbb);
}
// Will hold the resulting score
double score = 0;
// Density is the pack density: how big is the arranged pile
double density = 0;
// Distinction of cases for the arrangement scene
enum e_cases {
// This branch is for big items in a mixed (big and small) scene
// OR for all items in a small-only scene.
BIG_ITEM,
// This branch is for the last big item in a mixed scene
LAST_BIG_ITEM,
// For small items in a mixed scene.
SMALL_ITEM
} compute_case;
bool bigitems = isBig(item.area()) || spatindex.empty();
if(!params.is_seq_print && bigitems && !m_remaining.empty()) compute_case = BIG_ITEM; // do not use so complicated logic for sequential printing
else if (bigitems && m_remaining.empty()) compute_case = LAST_BIG_ITEM;
else compute_case = SMALL_ITEM;
switch (compute_case) {
case BIG_ITEM: {
const Point& minc = ibb.minCorner(); // bottom left corner
const Point& maxc = ibb.maxCorner(); // top right corner
// top left and bottom right corners
Point top_left{getX(minc), getY(maxc)};
Point bottom_right{getX(maxc), getY(minc)};
// Now the distance of the gravity center will be calculated to the
// five anchor points and the smallest will be chosen.
std::array<double, 5> dists;
auto cc = fullbb.center(); // The gravity center
dists[0] = pl::distance(minc, cc);
dists[1] = pl::distance(maxc, cc);
dists[2] = pl::distance(ibb.center(), cc);
dists[3] = pl::distance(top_left, cc);
dists[4] = pl::distance(bottom_right, cc);
// The smalles distance from the arranged pile center:
double dist = norm(*(std::min_element(dists.begin(), dists.end())));
if (m_pconf.starting_point == PConfig::Alignment::BOTTOM_LEFT) {
double bindist = dist_for_BOTTOM_LEFT(ibb, origin_pack);
score = 0.2 * dist + 0.8 * bindist;
}
else {
double bindist = dist_to_bin(ibb, origin_pack, m_pconf.starting_point);
dist = 0.8 * dist + 0.2 * bindist;
// Prepare a variable for the alignment score.
// This will indicate: how well is the candidate item
// aligned with its neighbors. We will check the alignment
// with all neighbors and return the score for the best
// alignment. So it is enough for the candidate to be
// aligned with only one item.
auto alignment_score = 1.0;
auto query = bgi::intersects(ibb);
auto& index = isBig(item.area()) ? spatindex : smalls_spatindex;
// Query the spatial index for the neighbors
std::vector<SpatElement> result;
result.reserve(index.size());
index.query(query, std::back_inserter(result));
// now get the score for the best alignment
for (auto& e : result) {
auto idx = e.second;
Item& p = m_items[idx];
auto parea = p.area();
if (std::abs(1.0 - parea / item.area()) < 1e-6) {
auto bb = sl::boundingBox(p.boundingBox(), ibb);
auto bbarea = bb.area();
auto ascore = 1.0 - (item.area() + parea) / bbarea;
if (ascore < alignment_score) alignment_score = ascore;
}
}
density = std::sqrt(norm(fullbb.width()) * norm(fullbb.height()));
double R = double(m_remaining.size()) / m_item_count;
// alighment score is more important for rectangle items
double alignment_weight = std::max(0.3, 0.6 * item.area() / ibb.area());
// The final mix of the score is the balance between the
// distance from the full pile center, the pack density and
// the alignment with the neighbors
if (result.empty())
score = 0.50 * dist + 0.50 * density;
else
// Let the density matter more when fewer objects remain
score = (1 - 0.2 - alignment_weight) * dist + (1.0 - R) * 0.20 * density +
alignment_weight * alignment_score;
}
break;
}
case LAST_BIG_ITEM: {
if (m_pconf.starting_point == PConfig::Alignment::BOTTOM_LEFT) {
score = dist_for_BOTTOM_LEFT(ibb, origin_pack);
}
else {
if (m_pilebb.defined)
score = 0.5 * norm(pl::distance(ibb.center(), m_pilebb.center()));
else
score = 0.5 * norm(pl::distance(ibb.center(), origin_pack));
}
break;
}
case SMALL_ITEM: {
// Here there are the small items that should be placed around the
// already processed bigger items.
// No need to play around with the anchor points, the center will be
// just fine for small items
if (m_pconf.starting_point == PConfig::Alignment::BOTTOM_LEFT)
score = dist_for_BOTTOM_LEFT(ibb, origin_pack);
else {
// Align mainly around existing items
score = 0.8 * norm(pl::distance(ibb.center(), bigbb.center()))+ 0.2*norm(pl::distance(ibb.center(), origin_pack));
// Align to 135 degree line {calc distance to the line x+y-(xc+yc)=0}
//auto ic = ibb.center(), bigbbc = origin_pack;// bigbb.center();
//score = norm(std::abs(ic.x() + ic.y() - bigbbc.x() - bigbbc.y()));
}
break;
}
}
if (params.is_seq_print) {
double clearance_height_to_lid = params.clearance_height_to_lid;
double clearance_height_to_rod = params.clearance_height_to_rod;
bool hasRowHeightConflict = false;
bool hasLidHeightConflict = false;
auto iy1 = item.boundingBox().minCorner().y();
auto iy2 = item.boundingBox().maxCorner().y();
auto ix1 = item.boundingBox().minCorner().x();
for (int i = 0; i < m_items.size(); i++) {
Item& p = m_items[i];
if (p.is_virt_object) continue;
auto px1 = p.boundingBox().minCorner().x();
auto py1 = p.boundingBox().minCorner().y();
auto py2 = p.boundingBox().maxCorner().y();
auto inter_min = std::max(iy1, py1); // min y of intersection
auto inter_max = std::min(iy2, py2); // max y of intersection. length=max_y-min_y>0 means intersection exists
// if this item is lower than existing ones, this item will be printed first, so should not exceed height_to_rod
if (iy2 < py1) { hasRowHeightConflict |= (item.height > clearance_height_to_rod); }
else if (inter_max - inter_min > 0) {
// if they inter, the one on the left will be printed first
double h = ix1 < px1 ? item.height : p.height;
hasRowHeightConflict |= (h > clearance_height_to_rod);
}
// only last item can be heigher than clearance_height_to_lid, so if the existing items are higher than clearance_height_to_lid, there is height conflict
hasLidHeightConflict |= (p.height > clearance_height_to_lid);
}
double lambda3 = LARGE_COST_TO_REJECT*1.1;
double lambda4 = LARGE_COST_TO_REJECT*1.2;
for (int i = 0; i < m_items.size(); i++) {
Item& p = m_items[i];
if (p.is_virt_object) continue;
//score += lambda3 * (item.bed_temp - p.vitrify_temp > VITRIFY_TEMP_DIFF_THRSH);
if (!Print::is_filaments_compatible({item.filament_temp_type,p.filament_temp_type}))
score += lambda3;
}
//score += lambda3 * (item.bed_temp - item.vitrify_temp > VITRIFY_TEMP_DIFF_THRSH);
score += lambda4 * hasRowHeightConflict + lambda4 * hasLidHeightConflict;
}
else {
for (int i = 0; i < m_items.size(); i++) {
Item& p = m_items[i];
if (p.is_virt_object) {
// Better not put items above wipe tower
if (p.is_wipe_tower) {
if (ibb.maxCorner().y() > p.boundingBox().maxCorner().y())
score += 1;
else if(m_pilebb.defined)
score += norm(pl::distance(ibb.center(), m_pilebb.center()));
}
else
continue;
} else {
// 高度接近的件尽量摆到一起
score += (1- std::abs(item.height - p.height) / params.printable_height)
* norm(pl::distance(ibb.center(), p.boundingBox().center()));
//score += LARGE_COST_TO_REJECT * (item.bed_temp - p.bed_temp != 0);
if (!Print::is_filaments_compatible({ item.filament_temp_type,p.filament_temp_type }))
score += LARGE_COST_TO_REJECT;
}
}
}
std::set<int> extruder_ids;
for (int i = 0; i < m_items.size(); i++) {
Item& p = m_items[i];
if (p.is_virt_object) continue;
extruder_ids.insert(p.extrude_ids.begin(),p.extrude_ids.end());
}
// add a large cost if not multi materials on same plate is not allowed
if (!params.allow_multi_materials_on_same_plate) {
// it's the first object, which can be multi-color
bool first_object = extruder_ids.empty();
// the two objects (previously packed items and the current item) are considered having same color if either one's colors are a subset of the other
std::set<int> item_extruder_ids(item.extrude_ids.begin(), item.extrude_ids.end());
bool same_color_with_previous_items = std::includes(item_extruder_ids.begin(), item_extruder_ids.end(), extruder_ids.begin(), extruder_ids.end())
|| std::includes(extruder_ids.begin(), extruder_ids.end(), item_extruder_ids.begin(), item_extruder_ids.end());
if (!(first_object || same_color_with_previous_items)) score += LARGE_COST_TO_REJECT * 1.3;
}
// for layered printing, we want extruder change as few as possible
// this has very weak effect, CAN NOT use a large weight
extruder_ids.insert(item.extrude_ids.begin(), item.extrude_ids.end());
if (!params.is_seq_print) {
score += 1 * std::max(0, ((int) extruder_ids.size() - 1));
}
return std::make_tuple(score, fullbb);
}
std::function<double(const Item&, const ItemGroup&)> get_objfn();
public:
AutoArranger(const TBin & bin,
const ArrangeParams &params,
std::function<void(unsigned,std::string)> progressind,
std::function<bool(void)> stopcond)
: m_pck(bin, params.min_obj_distance)
, m_bin(bin)
{
m_bin_area = abs(sl::area(bin)); // due to clockwise or anti-clockwise, the result of sl::area may be negative
m_norm = std::sqrt(m_bin_area);
fill_config(m_pconf, params);
this->params = params;
// if best object center is not bed center, specify starting point here
if (std::abs(this->params.align_center.x() - 0.5) > 0.001 || std::abs(this->params.align_center.y() - 0.5) > 0.001) {
auto binbb = sl::boundingBox(m_bin);
m_pconf.best_object_pos = binbb.minCorner() + Point{ binbb.width() * this->params.align_center.x(), binbb.height() * this->params.align_center.y() };
m_pconf.alignment = PConfig::Alignment::USER_DEFINED;
}
for (auto& region : m_pconf.m_excluded_regions) {
Box bb = region.boundingBox();
m_excluded_and_extruCali_regions.emplace_back(bb);
}
for (auto& region : m_pconf.m_nonprefered_regions) {
Box bb = region.boundingBox();
m_excluded_and_extruCali_regions.emplace_back(bb);
}
// Set up a callback that is called just before arranging starts
// This functionality is provided by the Nester class (m_pack).
m_pconf.before_packing =
[this](const MultiPolygon& merged_pile, // merged pile
const ItemGroup& items, // packed items
const ItemGroup& remaining) // future items to be packed
{
m_items = items;
m_merged_pile = merged_pile;
m_remaining = remaining;
m_pilebb.defined = false;
if (!merged_pile.empty())
{
m_pilebb = sl::boundingBox(merged_pile);
m_pilebb.defined = true;
}
m_rtree.clear();
m_smallsrtree.clear();
// We will treat big items (compared to the print bed) differently
auto isBig = [this](double a) {
return a / m_bin_area > BIG_ITEM_TRESHOLD ;
};
for(unsigned idx = 0; idx < items.size(); ++idx) {
Item& itm = items[idx];
if (itm.is_virt_object) continue;
if(isBig(itm.area())) m_rtree.insert({itm.boundingBox(), idx});
m_smallsrtree.insert({itm.boundingBox(), idx});
}
};
m_pconf.object_function = get_objfn();
// preload fixed items (and excluded regions) on plate
m_pconf.on_preload = [this](const ItemGroup &items, PConfig &cfg) {
if (items.empty()) return;
auto binbb = sl::boundingBox(m_bin);
auto starting_point = cfg.starting_point == PConfig::Alignment::BOTTOM_LEFT ? binbb.minCorner() : binbb.center();
// if we have wipe tower, items should be arranged around wipe tower
for (Item itm : items) {
if (itm.is_wipe_tower) {
starting_point = itm.boundingBox().center();
BOOST_LOG_TRIVIAL(debug) << "arrange we have wipe tower, change starting point to: " << starting_point;
break;
}
}
cfg.object_function = [this, binbb, starting_point](const Item &item, const ItemGroup &packed_items) {
return fixed_overfit(objfunc(item, starting_point), binbb);
};
};
auto on_packed = params.on_packed;
if (progressind || on_packed)
m_pck.progressIndicator(
[this, progressind, on_packed](unsigned num_finished) {
int last_bed = m_pck.lastPackedBinId();
if (last_bed >= 0) {
Item& last_packed = m_pck.lastResult()[last_bed].back();
ArrangePolygon ap;
ap.bed_idx = last_packed.binId();
ap.priority = last_packed.priority();
if (progressind) progressind(num_finished, last_packed.name);
if (on_packed)
on_packed(ap);
BOOST_LOG_TRIVIAL(debug) << "arrange " + last_packed.name + " succeed!"
<< ", plate id=" << ap.bed_idx << ", pos=" << last_packed.translation()
<< ", temp_type=" << last_packed.filament_temp_type;
}
});
m_pck.unfitIndicator([this](std::string name) {
BOOST_LOG_TRIVIAL(debug) << "arrange progress: " + name;
});
if (stopcond) m_pck.stopCondition(stopcond);
m_pconf.sortfunc= [&params](Item& i1, Item& i2) {
int p1 = i1.priority(), p2 = i2.priority();
if (p1 != p2)
return p1 > p2;
if (params.is_seq_print) {
return i1.bed_temp != i2.bed_temp ? (i1.bed_temp > i2.bed_temp) :
(i1.height != i2.height ? (i1.height < i2.height) : (i1.area() > i2.area()));
}
else {
return i1.bed_temp != i2.bed_temp ? (i1.bed_temp > i2.bed_temp) :
(i1.extrude_ids != i2.extrude_ids ? (i1.extrude_ids.front() < i2.extrude_ids.front()) : (i1.area() > i2.area()));
}
};
m_pck.configure(m_pconf);
}
template<class It> inline void operator()(It from, It to) {
m_rtree.clear();
m_item_count += size_t(to - from);
m_pck.execute(from, to);
m_item_count = 0;
}
PConfig& config() { return m_pconf; }
const PConfig& config() const { return m_pconf; }
inline void preload(std::vector<Item>& fixeditems) {
for(unsigned idx = 0; idx < fixeditems.size(); ++idx) {
Item& itm = fixeditems[idx];
itm.markAsFixedInBin(itm.binId());
}
m_item_count += fixeditems.size();
}
};
template<> std::function<double(const Item&, const ItemGroup&)> AutoArranger<Box>::get_objfn()
{
auto origin_pack = m_pconf.starting_point == PConfig::Alignment::CENTER ? m_bin.center() :
m_pconf.starting_point == PConfig::Alignment::TOP_RIGHT ? m_bin.maxCorner() : m_bin.minCorner();
return [this, origin_pack](const Item &itm, const ItemGroup&) {
auto result = objfunc(itm, origin_pack);
double score = std::get<0>(result);
auto& fullbb = std::get<1>(result);
//if (m_pconf.starting_point == PConfig::Alignment::BOTTOM_LEFT)
//{
// if (!sl::isInside(fullbb, m_bin))
// score += LARGE_COST_TO_REJECT;
//}
//else
{
double miss = Placer::overfit(fullbb, m_bin);
miss = miss > 0 ? miss : 0;
score += miss * miss;
if (score > LARGE_COST_TO_REJECT)
score = 1.5 * LARGE_COST_TO_REJECT;
}
return score;
};
}
template<> std::function<double(const Item&, const ItemGroup&)> AutoArranger<Circle>::get_objfn()
{
auto bb = sl::boundingBox(m_bin);
auto origin_pack = m_pconf.starting_point == PConfig::Alignment::CENTER ? bb.center() : bb.minCorner();
return [this, origin_pack](const Item &item, const ItemGroup&) {
auto result = objfunc(item, origin_pack);
double score = std::get<0>(result);
auto isBig = [this](const Item& itm) {
return itm.area() / m_bin_area > BIG_ITEM_TRESHOLD ;
};
if(isBig(item)) {
auto mp = m_merged_pile;
mp.push_back(item.transformedShape());
auto chull = sl::convexHull(mp);
double miss = Placer::overfit(chull, m_bin);
if(miss < 0) miss = 0;
score += miss*miss;
}
return score;
};
}
// Specialization for a generalized polygon.
// Warning: this is much slower than with Box bed. Need further speedup.
template<>
std::function<double(const Item &, const ItemGroup&)> AutoArranger<ExPolygon>::get_objfn()
{
auto bb = sl::boundingBox(m_bin);
auto origin_pack = m_pconf.starting_point == PConfig::Alignment::CENTER ? bb.center() : bb.minCorner();
return [this, origin_pack](const Item &itm, const ItemGroup&) {
auto result = objfunc(itm, origin_pack);
double score = std::get<0>(result);
auto mp = m_merged_pile;
mp.emplace_back(itm.transformedShape());
auto chull = sl::convexHull(mp);
if (m_pconf.starting_point == PConfig::Alignment::BOTTOM_LEFT)
{
if (!sl::isInside(chull, m_bin))
score += LARGE_COST_TO_REJECT;
}
else
{
double miss = Placer::overfit(chull, m_bin);
miss = miss > 0 ? miss : 0;
score += miss * miss;
}
return score;
};
}
template<class Bin> void remove_large_items(std::vector<Item> &items, Bin &&bin)
{
auto it = items.begin();
while (it != items.end())
{
//BBS: skip virtual object
if (!it->is_virt_object && !sl::isInside(it->transformedShape(), bin))
it = items.erase(it);
else
it++;
}
}
template<class S> Radians min_area_boundingbox_rotation(const S &sh)
{
return minAreaBoundingBox<S, TCompute<S>, boost::rational<LargeInt>>(sh)
.angleToX();
}
template<class S>
Radians fit_into_box_rotation(const S &sh, const _Box<TPoint<S>> &box)
{
return fitIntoBoxRotation<S, TCompute<S>, boost::rational<LargeInt>>(sh, box);
}
template<class BinT> // Arrange for arbitrary bin type
void _arrange(
std::vector<Item> & shapes,
std::vector<Item> & excludes,
const BinT & bin,
const ArrangeParams &params,
std::function<void(unsigned,std::string)> progressfn,
std::function<bool()> stopfn)
{
// Integer ceiling the min distance from the bed perimeters
coord_t md = params.min_obj_distance;
md = md / 2;
auto corrected_bin = bin;
//sl::offset(corrected_bin, md);
ArrangeParams mod_params = params;
mod_params.min_obj_distance = 0; // items are already inflated
AutoArranger<BinT> arranger{corrected_bin, mod_params, progressfn, stopfn};
remove_large_items(excludes, corrected_bin);
// If there is something on the plate
if (!excludes.empty()) arranger.preload(excludes);
std::vector<std::reference_wrapper<Item>> inp;
inp.reserve(shapes.size() + excludes.size());
for (auto &itm : shapes ) inp.emplace_back(itm);
for (auto &itm : excludes) inp.emplace_back(itm);
// Use the minimum bounding box rotation as a starting point.
// TODO: This only works for convex hull. If we ever switch to concave
// polygon nesting, a convex hull needs to be calculated.
if (params.allow_rotations) {
for (auto &itm : shapes) {
itm.rotation(min_area_boundingbox_rotation(itm.transformedShape()));
// If the item is too big, try to find a rotation that makes it fit
if constexpr (std::is_same_v<BinT, Box>) {
auto bb = itm.boundingBox();
if (bb.width() >= bin.width() || bb.height() >= bin.height())
itm.rotate(fit_into_box_rotation(itm.transformedShape(), bin));
}
}
}
arranger(inp.begin(), inp.end());
for (Item &itm : inp) itm.inflation(0);
}
inline Box to_nestbin(const BoundingBox &bb) { return Box{{bb.min(X), bb.min(Y)}, {bb.max(X), bb.max(Y)}};}
inline Circle to_nestbin(const CircleBed &c) { return Circle({c.center()(0), c.center()(1)}, c.radius()); }
inline ExPolygon to_nestbin(const Polygon &p) { return ExPolygon{p}; }
inline Box to_nestbin(const InfiniteBed &bed) { return Box::infinite({bed.center.x(), bed.center.y()}); }
inline coord_t width(const BoundingBox& box) { return box.max.x() - box.min.x(); }
inline coord_t height(const BoundingBox& box) { return box.max.y() - box.min.y(); }
inline double area(const BoundingBox& box) { return double(width(box)) * height(box); }
inline double poly_area(const Points &pts) { return std::abs(Polygon::area(pts)); }
inline double distance_to(const Point& p1, const Point& p2)
{
double dx = p2.x() - p1.x();
double dy = p2.y() - p1.y();
return std::sqrt(dx*dx + dy*dy);
}
static CircleBed to_circle(const Point &center, const Points& points) {
std::vector<double> vertex_distances;
double avg_dist = 0;
for (auto pt : points)
{
double distance = distance_to(center, pt);
vertex_distances.push_back(distance);
avg_dist += distance;
}
avg_dist /= vertex_distances.size();
CircleBed ret(center, avg_dist);
for(auto el : vertex_distances)
{
if (std::abs(el - avg_dist) > 10 * SCALED_EPSILON) {
ret = {};
break;
}
}
return ret;
}
// Create Item from Arrangeable
static void process_arrangeable(const ArrangePolygon &arrpoly,
std::vector<Item> & outp)
{
Polygon p = arrpoly.poly.contour;
const Vec2crd &offs = arrpoly.translation;
double rotation = arrpoly.rotation;
if (p.is_counter_clockwise()) p.reverse();
if (p.size() < 3)
return;
outp.emplace_back(std::move(p));
Item& item = outp.back();
item.rotation(rotation);
item.translation({offs.x(), offs.y()});
item.binId(arrpoly.bed_idx);
item.priority(arrpoly.priority);
item.itemId(arrpoly.itemid);
item.extrude_ids = arrpoly.extrude_ids;
item.height = arrpoly.height;
item.name = arrpoly.name;
//BBS: add virtual object logic
item.is_virt_object = arrpoly.is_virt_object;
item.is_wipe_tower = arrpoly.is_wipe_tower;
item.bed_temp = arrpoly.first_bed_temp;
item.print_temp = arrpoly.print_temp;
item.vitrify_temp = arrpoly.vitrify_temp;
item.inflation(arrpoly.inflation);
item.filament_temp_type = arrpoly.filament_temp_type;
}
template<class Fn> auto call_with_bed(const Points &bed, Fn &&fn)
{
if (bed.empty())
return fn(InfiniteBed{});
else if (bed.size() == 1)
return fn(InfiniteBed{bed.front()});
else {
auto bb = BoundingBox(bed);
CircleBed circ = to_circle(bb.center(), bed);
auto parea = poly_area(bed);
if ((1.0 - parea / area(bb)) < 1e-3)
return fn(bb);
else if (!std::isnan(circ.radius()))
return fn(circ);
else
return fn(Polygon(bed));
}
}
template<>
void arrange(ArrangePolygons & items,
const ArrangePolygons &excludes,
const Points & bed,
const ArrangeParams & params)
{
call_with_bed(bed, [&](const auto &bin) {
arrange(items, excludes, bin, params);
});
}
template<class BedT>
void arrange(ArrangePolygons & arrangables,
const ArrangePolygons &excludes,
const BedT & bed,
const ArrangeParams & params)
{
namespace clppr = Slic3r::ClipperLib;
std::vector<Item> items, fixeditems;
items.reserve(arrangables.size());
for (ArrangePolygon &arrangeable : arrangables)
process_arrangeable(arrangeable, items);
for (const ArrangePolygon &fixed: excludes)
process_arrangeable(fixed, fixeditems);
for (Item &itm : fixeditems) itm.inflate(scaled(-2. * EPSILON));
_arrange(items, fixeditems, to_nestbin(bed), params, params.progressind, params.stopcondition);
for(size_t i = 0; i < items.size(); ++i) {
Point tr = items[i].translation();
arrangables[i].translation = {coord_t(tr.x()), coord_t(tr.y())};
arrangables[i].rotation = items[i].rotation();
arrangables[i].bed_idx = items[i].binId();
arrangables[i].itemid = items[i].itemId(); // arrange order is useful for sequential printing
}
}
template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const BoundingBox &bed, const ArrangeParams &params);
template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const CircleBed &bed, const ArrangeParams &params);
template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const Polygon &bed, const ArrangeParams &params);
template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const InfiniteBed &bed, const ArrangeParams &params);
} // namespace arr
} // namespace Slic3r