mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-07 15:07:31 -06:00

* 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>
1146 lines
46 KiB
C++
1146 lines
46 KiB
C++
#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 ¶ms) {
|
||
|
||
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 ¶ms,
|
||
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= [¶ms](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 ¶ms,
|
||
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 ¢er, 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 ¶ms);
|
||
template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const CircleBed &bed, const ArrangeParams ¶ms);
|
||
template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const Polygon &bed, const ArrangeParams ¶ms);
|
||
template void arrange(ArrangePolygons &items, const ArrangePolygons &excludes, const InfiniteBed &bed, const ArrangeParams ¶ms);
|
||
|
||
} // namespace arr
|
||
} // namespace Slic3r
|