* FIX: do not touch the plate with different printing sequence jira: STUDIO-5424 Change-Id: I8ad00fa991b753de126a5bef0d320c452033e2e7 (cherry picked from commit c4adfe16e285f238f2c5cd8938b2167fdfb6b1b0) * FIX: global arrange setting is wrong global arrange setting is wrong if a plate's setting is changed from object list jira: STUDIO-5438 Change-Id: Iaa7f35837edbacff9b97ca17a8ab34c8e6bb023d (cherry picked from commit fa2f56575b2e4305e35dd59ff55e0881720de025) * FIX: temperature symbols not shown correctly Need to use wxString::FromUTF8 to convert unicode symbols to wxString. jira: none Change-Id: Ia8b559d437c956a2cc28916d8963823356402d05 * FIX:Repair calculation process of plate_box Jira: STUDIO-5520 Change-Id: I4c3f9597542ad2dfec4d7849e75fa28272fa4ea3 * FIX:frequent calls to _update_imgui_select_plate_toolbar Jira: STUDIO-5488 Change-Id: I12e6f37c2fe94de004aa6da43421970d6df10f0f * FIX: & is not displayed on the sending print page Jira: STUDIO-5343 Signed-off-by: wenjie.guo <wenjie.guo@bambulab.com> Change-Id: I1736bb97433581ff117bfe09afe8ee70c1b08fc4 * FIX: file name is not fully displayed if it is too long Jira: STUDIO-5230 Signed-off-by: wenjie.guo <wenjie.guo@bambulab.com> Change-Id: I992fa0c0575afbd2eecb2af02c8a305eda028f7f (cherry picked from commit d0d7fb0b1394429ee9d28d8ef4060a286ba0112d) * FIX: The warning box still exits when the temperature has reset. Jira: STUDIO-5562 Signed-off-by: wenjie.guo <wenjie.guo@bambulab.com> Change-Id: I7532db69880449eb3fa0a14fc4dfc61e7f6d518e (cherry picked from commit 589ed5fe045b5e7ec3effe437c9685085960c0fc) * FIX: White circle is not clear on auto refill page Jira: STUDIO-3262 Signed-off-by: wenjie.guo <wenjie.guo@bambulab.com> Change-Id: I05ac6257638063d32a9943c09bb7c14cc9229b3a * FIX: Groove text ctrl is not wide engough Jira: STUDIO-5434 Signed-off-by: wenjie.guo <wenjie.guo@bambulab.com> Change-Id: I93c0995473a72b5c19bc413c38c090906e360455 (cherry picked from commit e4a8b0ef5e62ba0053dc782c30ea79b237a46ac3) * FIX: values are not saved when clicking on an empty space Jira: STUDIO-4637 Signed-off-by: wenjie.guo <wenjie.guo@bambulab.com> Change-Id: I837050029635f673b3ae671ea1ad049aaf4fdd16 * FIX: Temperature warning is not fully displayed Jira: STUDIO-5038 Signed-off-by: wenjie.guo <wenjie.guo@bambulab.com> Change-Id: I460cbe2a5d0a092c4257b7bd5192058bf2e4707b * NEW: display bitmap when calibrating Jira: STUDIO-4661 Signed-off-by: wenjie.guo <wenjie.guo@bambulab.com> Change-Id: I60cf4f9769feca74699012418880e93fcfe34432 (cherry picked from commit 1213aea816694405311dc0c1061655a4c2a1d067) * FIX: remember the flow ratio calibration type Jira: STUDIO-5181 Signed-off-by: wenjie.guo <wenjie.guo@bambulab.com> Change-Id: Id6125d1d4ea58972ce55c2c2498259596b25111e (cherry picked from commit 1af1038fd4824d989e992cb630cf34e00c787af7) * FIX: File panel crash on scroll Change-Id: I56833a376fa52c960efea5fbd60003367ba410c2 Jira: STUDIO-5337, STUDIO-5513 * FIX: auto arranging skip unprintable high items Jira: STUDIO-5646 Change-Id: I72dc3d8c71a075bab8204f4418e869a7a34c0c8e (cherry picked from commit 0afdf8361493485da2254c426719594fd9a982ed) * FIX: MediaFilePanel error state Change-Id: I318ef59fb97478ffee16dff594022b2b9029964a Jira: STUDIO-5638 * FIX: sync whole preset vendor directory Change-Id: I191dbe979a87ff35d38cab1149b7975664344838 Jira: STUDIO-5534 * ENH: support turn off liveview auto retry Change-Id: I24b39f74e0a40a13277d6eae3830c95c5c9de333 Jira: none (cherry picked from commit f6ceb3fb8e4df3f876c50a1c4ba96b4a1be60190) * FIX: SwitchButton auto scale font Change-Id: If4004c0963cc8bb2f41e8e71c304d5239bf252ab Jira: STUDIO-4969 STUDIO-4921 * FIX: set WEBKIT_DISABLE_COMPOSITING_MODE=1 for linux gtk Change-Id: I8a500585ca815948bab1210578ba5c45858ed78e Jira: STUDIO-5199 * FIX: Prefer old selection when sync AMS not compatible Change-Id: I6b18db51887132a997cf78d70fff9a92e23bc44a Jira: STUDIO-5416 * ENH: show liveview stat Change-Id: I70d1f458aa2ed379ad7fe07dee76fbe035316420 Jira: none * NEW:remember custom color Jira: STUDIO-5635 Change-Id: I439080f6a8ddb6fde3899cffbabc3b6e66afbd96 * FIX: copy live555 dll Change-Id: Idf727b8e26107e93aa9934299e87dc71531d1c63 Jira: STUDIO-4480 * FIX: optimize batch update object list on macOS Change-Id: I92e24cc53c0b3bf0658d15abc64292f0e17c0a82 Jira: STUDIO-5440 STUDIO-5515 * FIX: network plugins tip disappear on dark mode Change-Id: I422ab63f71158a49920438f01dd9c39774c27744 Jira: STUDIO-4891 * FIX: Display inconsistence in parameter table JIra: STUDIO-3716 Signed-off-by: wenjie.guo <wenjie.guo@bambulab.com> Change-Id: I986473bcbb3efff4abd9c5917926d9e888a4f28c * FIX: Incomplete copy display in Transfer or discard dialog Jira: 5569 5549 Change-Id: I757b636259d7e1a222b9fc09276c12235360fd57 * FIX: Limit the max length of k when calibrating Jira: STUDIO-4291 Signed-off-by: wenjie.guo <wenjie.guo@bambulab.com> Change-Id: Ie7cff086cf2a3c744213525d5d83f9ac4b55333d * fix build break * FIX: delete sdcard file crash Change-Id: I814fd4b557fa92ac4060cbeb18a53f5616e49662 Jira: STUDIO-5977 * FIX: Yield when join media thread Change-Id: I746d7df88a0de8363da7d9507cb63c9e0ffe970a Jira: STUDIO-5952 * FIX: Guide page can't show in screen with mainframe Jira: STUDIO-4911 Change-Id: I7e89614e0f1585263456c847a1b38dcfd0ad59e6 * FIX: filament combox has blank line Change-Id: Ia39ddb564b3c9cc943d0ea4c0cf7cc4d24bef799 * FIX: load 3mf crash when studio has no base filament Jira: none Change-Id: I4387f425f60e6a53a53cf68addb1ab2d6f8f8901 Signed-off-by: maosheng.wei <maosheng.wei@bambulab.com> * FIX:add resume button JIRA2860 Change-Id: I39035d929876ab3c84c5f5c3494376967300938c * FIX: CLI: fix an arrange issue when duplicate failed restore the wipe_tower position to original when duplicate fail JIRA: MAK-2638 Change-Id: I355056f1d87648cc1f6aafa15a98ff569359b44f * FIX: fix printer list without nozzle such as 0.35 or 0.75 Jira: 5409 Change-Id: I1a258fd10bcc03e297b791256880f2518d602905 * ENH:The first object should locate at plate center Jira: STUDIO-6023 Change-Id: If4284136fe63ca576463445f3ab16b6e18ead30f * FIX: Colored filament is not matched against. github: #2190 Colored filament is not matched against the same color in AMS slot. Signed-off-by: Kunlong Ma <kunlong.ma@bambulab.com> Change-Id: Id4588fc9c8115a46881e2f5d198d79fe831f4371 * FIX: Revert "[STUDIO-4284] not set max height of liveview window" This reverts commit 0312aee4d9b92e23884be8802da9801ff3b9fe93. Reason for revert: STUDIO-5653 Jira: STUDIO-5653 Change-Id: If9d5f3e63968a0a54f9af1a2dae8f95f7f1f3f80 * ENH:modify file name rules when export stl Jira: STUDIO-6091 Change-Id: Ic27e4e341cc09099e98a5eab7dfd48416f2922ae * FIX: Flow calibration stage incorrect when switching printers Jira: 6093 Change-Id: I41f1ac10ac9422ac808eab3254f32ea14a0d3b76 * FIX: UserGuide Can not Click When Computer User name has chinese JIRA: None Change-Id: If50baa8c6a13eb501918fd5cdaf0ea3da7c788ef (cherry picked from commit 4e5ccc9f2de5ac429af6541c6a8bd412848801d0) * ENH: Little Optimize JS Code Execute Progress JIRA: STUDIO-5792 Change-Id: I12b03d8b968a9dd8dfce9eb3ef925fa8768e2046 (cherry picked from commit 2bf861092c9e306e1311eda8ac36fd981e73b6c2) * FIX: Delete Test Code JIRA: NONE Change-Id: I838a348edb22e09d2b1d5c41600c6fade535d184 (cherry picked from commit 51e664da0209ae8a3de5cbf30a72505c0b5bd028) * FIX: the object list order changed after clone github: 2798 Change-Id: I10a05ee7e00b05cb1255cfb708876ed784cabac7 * ENH: add alias for custom Filament preset Jira: XXXX Change-Id: I2fecc8b2bdb63618155e3d21f9db374a6119e416 * FIX: [5779] fix show alias logic when load preset Jira: 5779 Change-Id: I4fefe3c1ffbca9bd8296f1b3fdd5de48c6a36a28 * ENH: Optimize the logic for deleting third-party printers Mark the Filament and Process presets to be deleted first then delete the child presets first and then the parent presets. Jira: none Change-Id: I100b873baae96c6ba27af258e708e6ab8e6ee4ab * ENH:default selection of virtual tray jira:[for def selected] Change-Id: I0661f179f8e4bcac33ae12fbbeaeaf95c5b7c110 * ENH:add protection when no thumbnail data jira:[for protection] Change-Id: I3834a5ffde11ff54567dd854271184f06f94547f * FIX:fixed issue with chinese path jira:[Fixed the issue of failed loading of configuration files under Chinese path] Change-Id: I9badd8fc158fcf49f46411ac4e5f72d58823eeb2 * NEW:add new msg notification for hms jira:[STUDIO-6154] Change-Id: If1aa33030a99550d0c859d594a2711aea4dcea4a * NEW:using new humidity display ui jira:[STUDIO-5967] Change-Id: I13be4212e6b97f646d21e0af64cbc5006753fdeb * NEW:Dye materials above grade 10 with shortcut keys JIRA:STUDIO-5827 Change-Id: I002ecdd19167fb36772e4b4e9e2f7760e21079db * NEW:update automatically when inserting materials JIRA: STUDIO-6157 Change-Id: I2cefbb7b330ca4f13e841066548992b3fb3740f1 * FIX: check sdcard exists for file connect Change-Id: I69199a29294c04d1fe46ee66682085b1f1d1d049 Jira: none * FIX: not load printer files when it's busy Change-Id: Ie5a58befcfc0d7fa0d4e587e8429c0b1bfeff72a Jira: STUDIO-6105 * ENH: save video ctrl size to reduce layout change Change-Id: I470f29d7f029d304c9badeeb8f94bed281080b29 Jira: STUDIO-6141 * ENH: stop liveview track record Change-Id: Id4f236b239740bd919f2aa2f2892c1e63ce233bd Jira: STUDIO-6131 * FIX: thread safe of http extra headers Change-Id: I6ffa424be7ccb6abd78a66cc8be535f038b05469 Jira: none * optimize MeshBoolean * FIX: parse printer_model_id from 3mf Change-Id: Ib149c986885ee6412898f1f51dd5a4aaad0a596d Jira: none * ENH: find grid empty cells for fill bed if the item is too small jira: STUDIO-6015 Change-Id: I4e5eafdadd77482a27a8903d32bb83325283088d (cherry picked from commit 8df4da4a863cdc42c790a9d5da37f8633423e406) * ENH: always return product for firmware and lifycycle JIRA: STUDIO-6282 Change-Id: I1f942babdcb7afee2c9a9076ac539063c5406ad7 Signed-off-by: Stone Li <stone.li@bambulab.com> * ENH:STL tracking restricted area jira:[STUDIO-6155] Change-Id: I289c8b8aa8f62f0e5cc7004fb60437aa3337ca85 * NEW:add nozzle settings jira:[STUDIO-6226] Change-Id: I0db8333e5b5c8195add111fdcfa2e92387997815 * ENH:display the current humidity of AMS jira:[ENH] Change-Id: I98bdd6d70cd173ed640f0d96692fcb6836416bb8 * FIX: [6123] create printer for exist printer can not into next page Jira: 6123 Change-Id: I338ac0fde4f69b6f312f20e53851d91339e8156f * ENH: Display value of flushing volumes JIRA:STUDIO-6139 Signed-off-by: Kunlong Ma <kunlong.ma@bambulab.com> Change-Id: I273fb22b0d378a839c34e9e0e9c414f0e5134799 * FIX: show printer file path & title Change-Id: Ie5eff188c3039deeca5da96b54407194bf8910a0 Jira: STUDIO-6268 * FIX: liveview error message Change-Id: Ie437e07916d7b6feae2dbcfa166c4e73bdcf31a1 Jira: STUDIO-6107 * FIX: file proto error message Change-Id: I2c4117961c615e424780fb3830441e6a93c50bcc Jira: none * ENH: earse sensitive fields when export configs Jira: None Change-Id: Id9ca0637240b80773f39d2308192f8c78a5de3c6 * fix build errors * FIX: unexpected layers in multi color print github: 3131 Change-Id: I2a42e3bbd2247fbc0957022e1baae43c9375a8fb * ENH: Add "New" button for PA cali Jira: XXXX Change-Id: Ic39f2508f2f9d390c2b9246fb3d3e281cde9b064 * NEW:add printer compatible check from sd card view jira:[STUDIO-5969] Change-Id: I86d10ebe2e9bc77e6350e26aeed6b4f0f9fdcecb * NEW:enable loadl/unload when printing pause jira:[STUDIO-5968] Change-Id: Ieb3ef2423378e44b81a61a2b18c16f68aa335922 * FIX:fixed HMS message not cleared jira:[STUDIO-6296] Change-Id: Ic7692ce337fd00ece4ab8d65214a8c406f8543f8 * ENH:error code setting default value jira:[for error code] Change-Id: Ica61344c8217d41adb2947a40f633dc8d19a197a * ENH:display conflict information jira:[STUDIO-6297] Change-Id: Ie1501323a7e8d9ceb4060ae6c0b4eab20f8b088a * ENH: refresh printer file list Change-Id: Ic86942d2b0b2e8383ef0f06311164aad59e837ad Github: 3383 * FIX: Unnecessary prime tower error prompts Custom gcode on other plate causes unnecessary prime tower error prompts Jira: 6305 Change-Id: If499659b364a6b6898db1587b7b2aeed03758667 * FIX:multi colour displayed as gradient color on AMS JIRA:5925 Change-Id: Ic7a925dda2e3bde066ba40ba27002569040f9518 * NEW:Color painting shortcut keys 10~16 JIRA:STUDIO-6238 Change-Id: I3cce838fad5e73d41f109b32f2e563716fd5b0da * ENH: Print when unnamed project, task named as object names github: #2286 Change-Id: I9be3fd25d16a00b78326ec43db9afcf3645d90f1 * ENH:reset user access code jira:[for lan mode] Change-Id: I2d0ed48411d683c3f20b2febc0d54747287870a7 * FIX:fixed crash when selecting new printer jira:[fix] Change-Id: I6a81186e822eb6bf6ce7aa70561dfae35d4de0e7 * FIX: not show printer's camera error when updating Jira: STUDIO-6232 Change-Id: I985d75b3772849e07100799c4f13db5d4cbafde3 * FIX: clear error after reload file list ok Change-Id: I5d5e4f2870302b198d3a9d40603a6fa8010b7e76 Jira: STUDIO-6306 * ENH: custom filament sync with printer 1. prompt sync user presets when create custom filament 2. Fix the issue of not displaying printers when creating custom Filaments based on presets when selecting PLA Aero Type. 3. Optimizing the traversal logic during AMS Setting Pop up reduces time complexity and allows for quick pop ups. Additionally, using nozzle calibers for retrieval and repairing custom materials may result in inaccurate retrieval. 4. Implement synchronization logic with the printer -a. Received slot information, reset the slot when the "filament_id" in the information does not exist in Studio -b. Received slot information, the nozzle temperature in the information is different from the preset nozzle temperature in Studio, reset the current temperature. Jira: none Change-Id: I511dc82563ec77a341839671d398607048ce1985 Signed-off-by: maosheng.wei <maosheng.wei@bambulab.com> * NEW: add api of "toggle_selected_volume_visibility" Jira: STUDIO-6166 Change-Id: I77eb988a3ea43cd37d50888d1753b973795d8b36 * FIX: No data in the drop-down menu of the AMS settings page Jira: 6342 6343 Change-Id: I6938fb4a7ae2816a4675d8d739622e25f219f469 * fix build error * FIX: label wrap all & ping test for liveview Change-Id: I7767ed0740e20bb578b6ef9f5e9873c8c79d172a Jira: STUDIO-5821 * ENH: reuse controls in param Field Change-Id: I42bb4da01e1e9b64c343b7fda4357a9553cf8684 Jira: STUDIO-5983 * FIX: use wide path to create camera process Change-Id: I5de31fce0dea14df9a0ad363f3cb16dc40c275bc Jira: STUDIO-4946 * ENH: optimize the get_tool_order func Use Dp to refine performance jira:[NEW] Signed-off-by: xun.zhang <xun.zhang@bambulab.com> Change-Id: I38b0c875e4deee9d9fbe926087fb5b2e274f8f90 (cherry picked from commit 9b7b66dc7a1f5e3efa318227ae7694bec5ec1216) * Fix build errors * ENH: add customize other layers print sequence Jira: 6338 Change-Id: Ic14b2671ade37ab37583b81c5b509447b6c0d8f8 * ENH: [#3236] Unsaved changes to interface copy adjustment github: #3236 Change-Id: I53931859bdcdfedfa9f63f6239d0fd2fd6d2766c * NEW: support to adjust other layers print sequence Jira: 6338 Change-Id: I5e6aef71aa9e6e97c1859aaaeb9ada5f1340414a * FIX: imgui support toolbar window text wrapping issue jira: STUDIO-5821 Change-Id: I57ee984baffbb2f00a7ecc5d5c8061074b06aff6 * FIX: updater: fix force upgrade logic JIRA: STUDIO-6393 Change-Id: I46c51e09e7390e5ab0de40215911aac9635ab476 (cherry picked from commit 673ba6ff4ebda039d71dcbfdaa28c1252f5b8821) * FIX:final step of slicing is to execute post-processing script JIRA: STUDIO-5828 Change-Id: I8c33e2a66ac5c692244c778586040663b7b54bd7 * NEW:enable 3dMouse detect in .conf JIRA: 5830 Change-Id: I8731e0244d2f551130c84bcfbbb46967ae6b19cd * FIX:finish init "return" icon and hide it Jira: STUDIO-6350 Change-Id: I0f1efd4a64ea204daeac7de822602ef6dfa3e4a5 * FIX: seq_print: fix an invalid warning caused by sinking github: https://github.com/bambulab/BambuStudio/issues/3007 Change-Id: I1111910f2c625d5a871ea01b37dbfa7b04a849ee (cherry picked from commit a3db95bb0940d5afe07ef0bb07113cc2acd7cd0a) * ENH: plater: optimize the loading time of 3mf with large objects JIRA: STUDIO-6021 Change-Id: Ia97f681041bb553c5c4b5b1d9109e5e5c42daf6b * FIX:Fixed HMS issue jira:[STUDIO-6344 STUDIO-6310 STUDIO-6356 STUDIO-6348] Change-Id: I9d6660e7c349775004b69bfe41b651bfa8b359b7 * ENH:handling dirty data after nozzle settings jira:[STUDIO-6332] Change-Id: I00d6d1324376f973ec3cf9f2154ae83ef3302705 * ENH: use Bambu_StartStreamEx for agora tunnel Change-Id: I5c28dea49d267bf7ff967d0982dd83555899c8c4 Jira: none * FIX: use safe language code for http Change-Id: Id1f4927308350ee35b891a5352cbf1e2d0c2577e Github: 3655 * FIX: add cli_id, cli_ver to bambu url Change-Id: Ic527d1497c6dee0c723d7b4629f0be825a8f7545 Jira: none * FIX: not throw when _add_auxiliary_dir_to_archive Change-Id: Idf54bbbd0ef557ec5e1a8e51ed669a1eb1fb4261 Jira: STUDIO-6339 * NEW: vase mode can be applied to one plate jira: STUDIO-5838 Change-Id: Ifb315f7d79b570aeb7ee31d3495b4d465e3af0c6 * fix crashes * ENH: update overhang degree method on calssic mode Jira: none Signed-off-by: qing.zhang <qing.zhang@bambulab.com> Change-Id: I90f6e4c2ef618fdaef00bdaf1ca309893f484c1e * FIX: auto-arranging unprintable items may crash github: #3676 Change-Id: I68eb87c73ad2c0c269f60e661136fd1a72ee5e2f (cherry picked from commit 7e3c57eaa811424935fe8db6a4e77dd142ee2b58) * FIX: use old slicer_uuid for client_id Change-Id: I6c45e83213d613fc28eef04115f9cfb19dea703e Jira: none * ci: update network module based on commit 542ced8 Change-Id: I3ad5032cc56a99d1c3a687b2891d147b13af066d * NEW:Support OLTP file Jira: STUDIO-6421 Change-Id: I58bc94e978e6d2dd136ea370fb01f6ec80e14b23 * ENH: detect in_head_wrap_zone more precisly 1.Union first layer convex hull with object's bbox to detect whether model enter head_wrap_detect_zone jira:NEW Signed-off-by: XunZhangBambu <xun.zhang@bambulab.com> Change-Id: I11f26967d7421f41e9c824e62794c96591e6ae71 * FIX: fix the plate cannot be searched JIRA: STUDIO-6283 Signed-off-by: Kunlong Ma <kunlong.ma@bambulab.com> Change-Id: I88206c91ea24c6a41a0bd06f05f0f3c2fdc58a36 * NEW:hms error code JIRA: STUDIO-6302 Change-Id: Ia33511f4c636c8ada39ed5a4e52d9b185da9c00b * FIX:Color adaptation for numpad JIRA:STUDIO-6410 Change-Id: If6e49638af8616fd349367073883592e6bebb503 * FIX: error overhang degree mapping Jira: none Signed-off-by: qing.zhang <qing.zhang@bambulab.com> Change-Id: Ifa24aa0cad0a06b09ee62a8be8781188a765d1d0 * FIX:display correct humidity jira:[fix] Change-Id: I27aae54a8355911b5d88ed45be320d3c9178081c * ENH:Hide confirmation button when unable to send print jira:[STUDIO-6355, STUDIO-6332] Change-Id: I8f9c0edea4d5ee70e9fef1e9d42838d598dc32c4 * NEW: new type for Custom Filament Type: "PE", "PP", "EVA", "PHA", "BVOH", "PE-CF", "PP-CF", "PP-GF" Github: 3205 3169 3127 Change-Id: I8a30dd806c35460d9dae0f808190ce013b125d51 Signed-off-by: maosheng.wei <maosheng.wei@bambulab.com> * FIX:fixed filament settings page display error in French jira:[STUDIO-5821] Change-Id: I6cc6dd9b83c7570688c2adc55efe2407cbcb4390 * FIX:fixed thumbnail not updating when using multiple plates jira:[STUDIO-6313] Change-Id: If49daa5b38b9a580ae226ff00a1e0085d167c15c * FIX: Color Bleed in slicer github: 3681 jira: 6450 Change-Id: Icb6274f7ddb238c238c133b95167310b1af905f7 * ci: update network module based on commit 8befd46 Change-Id: I3a6420684f106bdde5897a50d27dfec69e0aa37f * ci: update network module based on commit e411785 Change-Id: I3a9c7bfa5ac5a942f339ad0194a24d9170847371 * FIX:reload paint after background process apply Jira: STUDIO-6493 Change-Id: I9a1986152f05163f236f58bb24210b690ca3d562 * FIX: use object name of plate when send task in untitled project Jira: 6430 Change-Id: I78ec811fab1cf028c0d5f81ac7738abdbeb6145f * FIX: auto arranging spacing can't be adjusted correctly jira: none Change-Id: Ibddfe85aab9f3fad6a1612e8db437e52c40e20a3 (cherry picked from commit 136bca01f45e62042bd699a9a0a9f6d13519712c) * FIX: fix change nozzle temp in Studio but printer not change Jira: 6510 Change-Id: Ia0e1ac586ff41ddbabdac0845415e70774299387 Signed-off-by: maosheng.wei <maosheng.wei@bambulab.com> * ENH:rename some img files jira:[STUDIO-6512] Change-Id: I69872533cccda37b94384bc219cc35c5dec9310b * ENH:PEI bed is no longer unchecked by default jira:[STUDIO-6508] Change-Id: Ic9ca99860d46c27ca4c36a735df3f57fe71417df * FIX:fix the load status of vtray jira:[STUDIO-6435] Change-Id: I8cafcc0b6caf19492aae6c153fb509f470dc7e83 * FIX: Supports automatic calibration of textured PEI jira: 6504 Change-Id: I3234fb555b9bf0ea97e73387651874733e761ee7 * ENH:add tooltip for search item JIRA: STUDIO-6459 Signed-off-by: Kunlong Ma <kunlong.ma@bambulab.com> Change-Id: I7602a32159d21de8f37ea0208dd6a9f59b90dcce * ENH: CLI: add version check logic add option allow_newer_file Change-Id: I8e8e4a45f77ebdd6dae6189841e4a9952e95ca82 * ci: update build version to 01.09.00.52 Change-Id: Id6e32b5afcf5eaabce9c0c7ab2c422e97b00e632 * NEW: switch to object panel if double click on object jira: none switch to object panel if double click on object, otherwise switch to global panel if double click on background. Change-Id: I6e54d7957aa19f1ebb1f993bc38125bbee8a1c98 (cherry picked from commit cc2e07bc9489c76a7d767acff0406c83c996504c) * FIX:fixed loading img resource failure jira:[for fix img load] Change-Id: Ifb26b2ca23029abeda000322bf2ef7d2b3cda3b4 * FIX: Project Title can Click JIRA: none Change-Id: I614c60e76efe04875e36e3a8ef7a10acd3ef9ecf * FIX:Prioritize selecting filament with smaller serial numbers in AMS JIRA: 5909 Change-Id: If3030d4dd8d59af36bc1ae1801be1b89b0027a71 * NEW:material adaptation in select machine dialog JIRA:xxxx Change-Id: I625eac75c88cad804dd3741f750c5ea68a975421 * FIX:mac ams setting display JIRA: STUDIO-6228\6409 Change-Id: I432a3aa96601a8e223b5949bc0ad5234c1374dca * FIX: Image Scale Mode and Online Display JIRA: none Change-Id: I528f16e93b82748d86dc93e2dd3d85f317babaa7 * FIX: sequential_print_clearance_valid not working not working correctly with short objects jira: STUDIO-6489 Change-Id: I33e1a165f448e1c3e272d4045934c63ad345db2f (cherry picked from commit 9348eaa22a056db5384a38ea966cec9ba4a533a7) * NEW: add nozzle_height to machine profile and do not detect conflict Jira: request from 1.9 1. add nozzle_height to machine profile 2. auto arranging and sequential_print_clearance_valid don't consider objects conflicting if they are all shorter than nozzle_height and close. 3. do not detect conflict when all models are short. Change-Id: I8d1eebb15d5bfa8c40d7491e033149e360531b89 (cherry picked from commit 6b4b52653db5f08d724a556c5c766c0bfa00f34d) * FIX: sequential_print_clearance_valid not working not working correctly with short objects jira: STUDIO-6489 Change-Id: I33e1a165f448e1c3e272d4045934c63ad345db2f (cherry picked from commit 9348eaa22a056db5384a38ea966cec9ba4a533a7) * FIX: [6510] set nozzle temp incorrectly when popup AMS Setting Change-Id: I898f0b94794a3d67017b1917ce196c4019f5eb4a * FIX: auto-calculate flushing volumes JIRA: STUDIO-6547 FIX the first modification of consumable color after synchronizing filaments, without automatically calculating the flushing volumes Signed-off-by: Kunlong Ma <kunlong.ma@bambulab.com> Change-Id: I2bc76a29afde5241d100cc42a5161db0f9b901c4 * FIX: custom layer sequence from End to End comboBox display issue jira: new Change-Id: I413cd5896d7e921f2c7c03b91b08788fefb9a4f3 * FIX:fix the v tray's filament unload logic jira:[STUDIO-6627] Change-Id: I34420bc4d1d27b6b36defb9852bba2eaf77fdcf2 * NEW:reducing purge through retracting filament 1.reducing purge through retracting filament.Currently only applicable to X&P series github: PR#3100 Signed-off-by: XunZhangBambu <xun.zhang@bambulab.com> Change-Id: Ie328039872e50e699dc5e5082fa99f68ac5f5fd1 * FIX: wrong role cache in wipe tower 1. Add wipe tower role cache in GCodeProcessor result 2. Add wiki link for prime tower jira:NEW Signed-off-by: xun.zhang <xun.zhang@bambulab.com> Change-Id: Ia766c7218df68fb1ffba567af193d6bfecacf588 * Fix plate settng icon * NEW:revert hms error code Change-Id: Ib5cc8bb8b8ced0f70d5bbe4751a1f97258218c6f * FIX: calibration page button broken display issue jira: STUDIO-3913 Change-Id: I2fd488e829d898b7d81d09db814ed6518f0c54a8 * FIX: do not check spiral vase mode config if an object is loaded jira: STUDIO-6514 Change-Id: Ib44ec8322ff178b5765f7fe94b588aa38339691d * FIX: implicitly set spiral vase config for objects just loading jira: 6514 Change-Id: I04bb2b1abeb62d4dfff4e526b723b1cf1bd5fd7f * FIX: filling bed fails if the bed is already full JIRA: STUDIO-6490 Signed-off-by: Kunlong Ma <kunlong.ma@bambulab.com> Change-Id: I71b5a01a95cdffef7c0750e6347fa8911dcd781d * ci: update network module based on commit 868f5d7 Change-Id: I5584e4441e1f2ab400addaa87ee8013927fb9e15 * FIX: add query_real_volume_idx_from_other_view api Jira: STUDIO-6545 Change-Id: Ib8216981c5d2945a0221a5caa1fbc14ed74e930b * FIX: Can't edit text github: 3750 Change-Id: I1caecaa968e60cadcdbe9f7aa67cba141bb88230 * FIX: Slicer creates invalid color pattern github: 3749 Change-Id: I3fd74a9ca59b75873fcbca4437e4858c749ee853 * ENH: hide tuck did Change-Id: I9021d3f51c9a73bc9208b479f96b1ddbe7a2f8f8 Jira: none * FIX: PrinterFileSystem: retry connect on user action Change-Id: I3e8902298385ed2e5906fd15d1817b6e33522a76 Jira: STUDIO-6354 * FIX: Remove user ID and other information Jira: XXXX Change-Id: Ia63ec88a335d88fd40a29952abe6d40d8991efee * ENH: refine retraction before cut 1. Add filament retraction before cut control jira:NEW Signed-off-by: xun.zhang <xun.zhang@bambulab.com> Change-Id: Ifcb087c9791c0461b793ef811b21ebd4c007d880 * FIX: enable resumed read only Field Change-Id: Id09e671932458699c020f0a061d8cfc11a6958ab Jira: STUDIO-6641 * ENH: add precise_z_height jira: none Change-Id: Idb9fcf0063e773f1531a49961478460b91ded10f * ENH: modify the multi-material segmentation and voronoi This patch is cherry pick from Prusa, thanks to Prusa Rework multi-material segmentation to work directly on the Voronoi diagram without creating a copy of it. Previous algorithms assume that they can get an invalid Voronoi diagram. Because of that, during the multi-material segmentation, a copy of the Voronoi diagram was created, and there were several attempts to fix missing vertices and edges. But as it shows, this wasn't a good enough approach and sometimes led to several issues like bleeding layers. After generalization, our approach for detection and repairs of invalid Voronoi diagrams from Arachne, we could assume that multi-material segmentation gets non-invalid Voronoi diagrams. With this assumption, we reimplement multi-materials segmentation to work directly on the Voronoi diagram. That should make multi-material segmentation more stable. So, this should fix several issues like bleeding layers. Also, memory consumption should decrease by a lot. Also, there should be some speedup of multi-materials segmentation. Jira: none Change-Id: I72aa6e1f9634d9ee8759aa469a0b39a36ace62f5 * FIX: infill speed not work on region level Jira: none Signed-off-by: qing.zhang <qing.zhang@bambulab.com> Change-Id: Ie3d17c5e3cbf91a8854e3b4cd80babeb2b1bd121 * ENH: support saving PA calibration results for P series Jira: none Change-Id: I9402b8bcce7b48a63d0e97e0708080701d065e7a * ENH: refine long retraction ui 1. associate button display logic 2. Add valid range tip 3. seperate the printer into three types jira:NEW Signed-off-by: xun.zhang <xun.zhang@bambulab.com> Change-Id: Ie14c8746eb20456dacd5c129a5449c1e7c7db372 * FIX:height range cut = volume_count * 2 Jira: none Change-Id: I539c2f9cda7985b4b3c318ca8aa1eb7c52fdce82 * FIX: auto arranging gets wrong object height obj->bounding_box().size() is not the real object size if the object has been rotated. jira: STUDIO-5999 Change-Id: I6553d4c990696efd674e3e57063802127d5d5282 (cherry picked from commit 479ea9fb02f55d24f27c94633f3d852bd5c62c83) * ENH: seperate support weight from model jira:NEW Signed-off-by: XunZhangBambu <xun.zhang@bambulab.com> Change-Id: I86bb34941269bf1aa29436a94ebbdff675497e85 * ENH: add support for gcodeviewer statistics jira: new Change-Id: Ied6d61e8c48ac82daf16579d9caed9723cf8e29d * FIX: invalid support weight per extruder jira:NEW Signed-off-by: xun.zhang <xun.zhang@bambulab.com> Change-Id: I0e4b857c9c758ab7c54ef13aee1bf596f975640b * FIX: do not need reset bed_type for pa calibration jira: none Change-Id: I411064cf14d94a9bd1f0f6668ee23aa10d372f3d * FIX: P1P/S can not modify the k value in old version jira: 6745 Change-Id: I5c9dffe8e998213e6af6e1d01a6b0ae82521e8db * Add rotation support for 3D Honeycomb Ported from BS * ENH: add default params for long retraction 1. Only auto calculate flush when enabled 2. Add default params for long retraction 3. Disable filament override for unsupport machines jira:NEW Signed-off-by: xun.zhang <xun.zhang@bambulab.com> Change-Id: Ib5d51505b58101839527e944f9a237483951f9fe * misc fixes * ENH: remove long retraction warning jira:NEW Signed-off-by: tao wang <tao.wang@bambulab.com> Change-Id: If60236b3282991a2d94df7d125427cff86899536 * avoid zero length path * FIX: check recommended nozzle temperature Jira: XXXX Change-Id: I4dbb274cf27ef9c6d20a8479b29af1069652b2bc * FIX: fix not popping up a prompt when the temperature is set to 0 Jira: 6497 Change-Id: I6498fc6962e7da376d4c652dab0a99a161932eef Signed-off-by: maosheng.wei <maosheng.wei@bambulab.com> * ENH: When creating a custom Filament, use the system Filament type. Jira: 6301 Change-Id: I1bfddcf43d2ebaebca4eb494d1f64165c3d59e9e Signed-off-by: maosheng.wei <maosheng.wei@bambulab.com> * FIX: seam and unretarct pos error on smooth vase casused by invalid path of smooth vase mode Signed-off-by: qing.zhang <qing.zhang@bambulab.com> Change-Id: Ib597e8c05760886aae2c42e42e8d46e82b844578 * FIX: unable to map if filament not used in model 1.Fix filament can not map if it's not used in model body jira:NEW Signed-off-by: xun.zhang <xun.zhang@bambulab.com> Change-Id: Ibd2685ffd198b2e17dbf44289d0144b5b7c25788 * NEW:Update data only on device pages jira:[STUDIO-6776] Change-Id: I33b0c9f35c1dc6df2db3b6bd4f446f46b31ecf6c * set(SLIC3R_VERSION "01.09.00.70") * update BBL machine profile 01.09.00.04 * scarf clip start and end Ported from BambuStudio * fix linux build error --------- Co-authored-by: Arthur <arthur.tang@bambulab.com> Co-authored-by: zhou.xu <zhou.xu@bambulab.com> Co-authored-by: wenjie.guo <wenjie.guo@bambulab.com> Co-authored-by: chunmao.guo <chunmao.guo@bambulab.com> Co-authored-by: maosheng.wei <maosheng.wei@bambulab.com> Co-authored-by: hu.wang <hu.wang@bambulab.com> Co-authored-by: lane.wei <lane.wei@bambulab.com> Co-authored-by: Kunlong Ma <kunlong.ma@bambulab.com> Co-authored-by: zhimin.zeng <zhimin.zeng@bambulab.com> Co-authored-by: zorro.zhang <zorro.zhang@bambulab.com> Co-authored-by: tao wang <tao.wang@bambulab.com> Co-authored-by: Stone Li <stone.li@bambulab.com> Co-authored-by: xun.zhang <xun.zhang@bambulab.com> Co-authored-by: liz.li <liz.li@bambulab.com> Co-authored-by: qing.zhang <qing.zhang@bambulab.com> Co-authored-by: gerrit <gerrit@bambulab.com> Co-authored-by: Leon Fisher-Skipper <47602359+LeonFisherSkipper@users.noreply.github.com> Co-authored-by: Lukas Matena <lukasmatena@seznam.cz> Co-authored-by: jianjia.ma <jianjia.ma@bambulab.com>
4174 lines
199 KiB
C++
4174 lines
199 KiB
C++
///|/ Copyright (c) Prusa Research 2016 - 2023 Lukáš Matěna @lukasmatena, Tomáš Mészáros @tamasmeszaros, Enrico Turri @enricoturri1966, Vojtěch Bubník @bubnikv, Pavel Mikuš @Godrak, Oleksandra Iushchenko @YuSanka, Lukáš Hejl @hejllukas, Filip Sykala @Jony01, Roman Beránek @zavorka, David Kocík @kocikdav
|
||
///|/ Copyright (c) BambuStudio 2023 manch1n @manch1n
|
||
///|/ Copyright (c) SuperSlicer 2023 Remi Durand @supermerill
|
||
///|/ Copyright (c) 2021 Martin Budden
|
||
///|/ Copyright (c) 2020 Paul Arden @ardenpm
|
||
///|/ Copyright (c) 2019 Thomas Moore
|
||
///|/ Copyright (c) 2019 Bryan Smith
|
||
///|/ Copyright (c) Slic3r 2013 - 2016 Alessandro Ranellucci @alranel
|
||
///|/ Copyright (c) 2014 Petr Ledvina @ledvinap
|
||
///|/
|
||
///|/ ported from lib/Slic3r/Print.pm:
|
||
///|/ Copyright (c) Prusa Research 2016 - 2018 Vojtěch Bubník @bubnikv, Tomáš Mészáros @tamasmeszaros
|
||
///|/ Copyright (c) Slic3r 2011 - 2016 Alessandro Ranellucci @alranel
|
||
///|/ Copyright (c) 2012 - 2013 Mark Hindess
|
||
///|/ Copyright (c) 2013 Devin Grady
|
||
///|/ Copyright (c) 2012 - 2013 Mike Sheldrake @mesheldrake
|
||
///|/ Copyright (c) 2012 Henrik Brix Andersen @henrikbrixandersen
|
||
///|/ Copyright (c) 2012 Michael Moon
|
||
///|/ Copyright (c) 2011 Richard Goodwin
|
||
///|/
|
||
///|/ PrusaSlicer is released under the terms of the AGPLv3 or higher
|
||
///|/
|
||
#include "Config.hpp"
|
||
#include "Exception.hpp"
|
||
#include "Print.hpp"
|
||
#include "BoundingBox.hpp"
|
||
#include "Brim.hpp"
|
||
#include "ClipperUtils.hpp"
|
||
#include "Extruder.hpp"
|
||
#include "Flow.hpp"
|
||
#include "Geometry/ConvexHull.hpp"
|
||
#include "I18N.hpp"
|
||
#include "ShortestPath.hpp"
|
||
#include "Support/SupportMaterial.hpp"
|
||
#include "Thread.hpp"
|
||
#include "Time.hpp"
|
||
#include "GCode.hpp"
|
||
#include "GCode/WipeTower.hpp"
|
||
#include "GCode/WipeTower2.hpp"
|
||
#include "Utils.hpp"
|
||
#include "PrintConfig.hpp"
|
||
#include "Model.hpp"
|
||
#include "format.hpp"
|
||
#include <float.h>
|
||
|
||
#include <algorithm>
|
||
#include <limits>
|
||
#include <unordered_set>
|
||
#include <boost/filesystem/path.hpp>
|
||
#include <boost/format.hpp>
|
||
#include <boost/log/trivial.hpp>
|
||
#include <boost/regex.hpp>
|
||
#include <boost/nowide/fstream.hpp>
|
||
|
||
#include <tbb/blocked_range.h>
|
||
#include <tbb/parallel_for.h>
|
||
|
||
//BBS: add json support
|
||
#include "nlohmann/json.hpp"
|
||
|
||
#include "GCode/ConflictChecker.hpp"
|
||
|
||
#include <codecvt>
|
||
|
||
using namespace nlohmann;
|
||
|
||
// Mark string for localization and translate.
|
||
#define L(s) Slic3r::I18N::translate(s)
|
||
|
||
namespace Slic3r {
|
||
|
||
template class PrintState<PrintStep, psCount>;
|
||
template class PrintState<PrintObjectStep, posCount>;
|
||
|
||
PrintRegion::PrintRegion(const PrintRegionConfig &config) : PrintRegion(config, config.hash()) {}
|
||
PrintRegion::PrintRegion(PrintRegionConfig &&config) : PrintRegion(std::move(config), config.hash()) {}
|
||
|
||
//BBS
|
||
float Print::min_skirt_length = 0;
|
||
|
||
void Print::clear()
|
||
{
|
||
std::scoped_lock<std::mutex> lock(this->state_mutex());
|
||
// The following call should stop background processing if it is running.
|
||
this->invalidate_all_steps();
|
||
for (PrintObject *object : m_objects)
|
||
delete object;
|
||
m_objects.clear();
|
||
m_print_regions.clear();
|
||
m_model.clear_objects();
|
||
}
|
||
|
||
// Called by Print::apply().
|
||
// This method only accepts PrintConfig option keys.
|
||
bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* new_config */, const std::vector<t_config_option_key> &opt_keys)
|
||
{
|
||
if (opt_keys.empty())
|
||
return false;
|
||
|
||
// Cache the plenty of parameters, which influence the G-code generator only,
|
||
// or they are only notes not influencing the generated G-code.
|
||
static std::unordered_set<std::string> steps_gcode = {
|
||
//BBS
|
||
"additional_cooling_fan_speed",
|
||
"reduce_crossing_wall",
|
||
"max_travel_detour_distance",
|
||
"printable_area",
|
||
//BBS: add bed_exclude_area
|
||
"bed_exclude_area",
|
||
"thumbnail_size",
|
||
"before_layer_change_gcode",
|
||
"enable_pressure_advance",
|
||
"pressure_advance",
|
||
"enable_overhang_bridge_fan",
|
||
"overhang_fan_speed",
|
||
"overhang_fan_threshold",
|
||
"slow_down_for_layer_cooling",
|
||
"default_acceleration",
|
||
"deretraction_speed",
|
||
"close_fan_the_first_x_layers",
|
||
"machine_end_gcode",
|
||
"printing_by_object_gcode",
|
||
"filament_end_gcode",
|
||
"post_process",
|
||
"extruder_clearance_height_to_rod",
|
||
"extruder_clearance_height_to_lid",
|
||
"extruder_clearance_radius",
|
||
"nozzle_height",
|
||
"extruder_colour",
|
||
"extruder_offset",
|
||
"filament_flow_ratio",
|
||
"reduce_fan_stop_start_freq",
|
||
"fan_cooling_layer_time",
|
||
"full_fan_speed_layer",
|
||
"fan_kickstart",
|
||
"fan_speedup_overhangs",
|
||
"fan_speedup_time",
|
||
"filament_colour",
|
||
"default_filament_colour",
|
||
"filament_diameter",
|
||
"filament_density",
|
||
"filament_cost",
|
||
"filament_notes",
|
||
"outer_wall_acceleration",
|
||
"inner_wall_acceleration",
|
||
"initial_layer_acceleration",
|
||
"top_surface_acceleration",
|
||
"bridge_acceleration",
|
||
"travel_acceleration",
|
||
"sparse_infill_acceleration",
|
||
"internal_solid_infill_acceleration"
|
||
// BBS
|
||
"cool_plate_temp_initial_layer",
|
||
"eng_plate_temp_initial_layer",
|
||
"hot_plate_temp_initial_layer",
|
||
"textured_plate_temp_initial_layer",
|
||
"gcode_add_line_number",
|
||
"layer_change_gcode",
|
||
"time_lapse_gcode",
|
||
"fan_min_speed",
|
||
"fan_max_speed",
|
||
"printable_height",
|
||
"slow_down_min_speed",
|
||
"max_volumetric_extrusion_rate_slope",
|
||
"max_volumetric_extrusion_rate_slope_segment_length",
|
||
"reduce_infill_retraction",
|
||
"filename_format",
|
||
"retraction_minimum_travel",
|
||
"retract_before_wipe",
|
||
"retract_when_changing_layer",
|
||
"retraction_length",
|
||
"retract_length_toolchange",
|
||
"z_hop",
|
||
"retract_lift_above",
|
||
"retract_lift_below",
|
||
"retract_lift_enforce",
|
||
"retract_restart_extra",
|
||
"retract_restart_extra_toolchange",
|
||
"retraction_speed",
|
||
"use_firmware_retraction",
|
||
"slow_down_layer_time",
|
||
"standby_temperature_delta",
|
||
"machine_start_gcode",
|
||
"filament_start_gcode",
|
||
"change_filament_gcode",
|
||
"wipe",
|
||
// BBS
|
||
"wipe_distance",
|
||
"curr_bed_type",
|
||
"nozzle_volume",
|
||
"nozzle_hrc",
|
||
"required_nozzle_HRC",
|
||
"upward_compatible_machine",
|
||
"is_infill_first",
|
||
// Orca
|
||
"chamber_temperature",
|
||
"thumbnails",
|
||
"thumbnails_format",
|
||
"seam_gap",
|
||
"role_based_wipe_speed",
|
||
"wipe_speed",
|
||
"use_relative_e_distances",
|
||
"accel_to_decel_enable",
|
||
"accel_to_decel_factor",
|
||
"wipe_on_loops",
|
||
"gcode_comments",
|
||
"gcode_label_objects",
|
||
"exclude_object",
|
||
"support_material_interface_fan_speed",
|
||
"single_extruder_multi_material_priming",
|
||
"activate_air_filtration",
|
||
"during_print_exhaust_fan_speed",
|
||
"complete_print_exhaust_fan_speed",
|
||
"activate_chamber_temp_control",
|
||
"manual_filament_change",
|
||
"disable_m73",
|
||
"use_firmware_retraction",
|
||
"enable_long_retraction_when_cut",
|
||
"long_retractions_when_cut",
|
||
"retraction_distances_when_cut",
|
||
"filament_long_retractions_when_cut",
|
||
"filament_retraction_distances_when_cut"
|
||
};
|
||
|
||
static std::unordered_set<std::string> steps_ignore;
|
||
|
||
std::vector<PrintStep> steps;
|
||
std::vector<PrintObjectStep> osteps;
|
||
bool invalidated = false;
|
||
|
||
for (const t_config_option_key &opt_key : opt_keys) {
|
||
if (steps_gcode.find(opt_key) != steps_gcode.end()) {
|
||
// These options only affect G-code export or they are just notes without influence on the generated G-code,
|
||
// so there is nothing to invalidate.
|
||
steps.emplace_back(psGCodeExport);
|
||
} else if (steps_ignore.find(opt_key) != steps_ignore.end()) {
|
||
// These steps have no influence on the G-code whatsoever. Just ignore them.
|
||
} else if (
|
||
opt_key == "skirt_loops"
|
||
|| opt_key == "skirt_speed"
|
||
|| opt_key == "skirt_height"
|
||
|| opt_key == "draft_shield"
|
||
|| opt_key == "skirt_distance"
|
||
|| opt_key == "ooze_prevention"
|
||
|| opt_key == "wipe_tower_x"
|
||
|| opt_key == "wipe_tower_y"
|
||
|| opt_key == "wipe_tower_rotation_angle") {
|
||
steps.emplace_back(psSkirtBrim);
|
||
} else if (
|
||
opt_key == "initial_layer_print_height"
|
||
|| opt_key == "nozzle_diameter"
|
||
|| opt_key == "filament_shrink"
|
||
|| opt_key == "resolution"
|
||
|| opt_key == "precise_z_height"
|
||
// Spiral Vase forces different kind of slicing than the normal model:
|
||
// In Spiral Vase mode, holes are closed and only the largest area contour is kept at each layer.
|
||
// Therefore toggling the Spiral Vase on / off requires complete reslicing.
|
||
|| opt_key == "spiral_mode") {
|
||
osteps.emplace_back(posSlice);
|
||
} else if (
|
||
opt_key == "print_sequence"
|
||
|| opt_key == "filament_type"
|
||
|| opt_key == "chamber_temperature"
|
||
|| opt_key == "nozzle_temperature_initial_layer"
|
||
|| opt_key == "filament_minimal_purge_on_wipe_tower"
|
||
|| opt_key == "filament_max_volumetric_speed"
|
||
|| opt_key == "filament_loading_speed"
|
||
|| opt_key == "filament_loading_speed_start"
|
||
|| opt_key == "filament_unloading_speed"
|
||
|| opt_key == "filament_unloading_speed_start"
|
||
|| opt_key == "filament_toolchange_delay"
|
||
|| opt_key == "filament_cooling_moves"
|
||
|| opt_key == "filament_cooling_initial_speed"
|
||
|| opt_key == "filament_cooling_final_speed"
|
||
|| opt_key == "filament_ramming_parameters"
|
||
|| opt_key == "filament_multitool_ramming"
|
||
|| opt_key == "filament_multitool_ramming_volume"
|
||
|| opt_key == "filament_multitool_ramming_flow"
|
||
|| opt_key == "filament_max_volumetric_speed"
|
||
|| opt_key == "gcode_flavor"
|
||
|| opt_key == "single_extruder_multi_material"
|
||
|| opt_key == "nozzle_temperature"
|
||
|| opt_key == "cool_plate_temp"
|
||
|| opt_key == "eng_plate_temp"
|
||
|| opt_key == "hot_plate_temp"
|
||
|| opt_key == "textured_plate_temp"
|
||
|| opt_key == "enable_prime_tower"
|
||
|| opt_key == "prime_tower_width"
|
||
|| opt_key == "prime_tower_brim_width"
|
||
|| opt_key == "first_layer_print_sequence"
|
||
|| opt_key == "other_layers_print_sequence"
|
||
|| opt_key == "other_layers_print_sequence_nums"
|
||
|| opt_key == "wipe_tower_bridging"
|
||
|| opt_key == "wipe_tower_no_sparse_layers"
|
||
|| opt_key == "flush_volumes_matrix"
|
||
|| opt_key == "prime_volume"
|
||
|| opt_key == "flush_into_infill"
|
||
|| opt_key == "flush_into_support"
|
||
|| opt_key == "initial_layer_infill_speed"
|
||
|| opt_key == "travel_speed"
|
||
|| opt_key == "travel_speed_z"
|
||
|| opt_key == "initial_layer_speed"
|
||
|| opt_key == "initial_layer_travel_speed"
|
||
|| opt_key == "slow_down_layers"
|
||
|| opt_key == "wipe_tower_cone_angle"
|
||
|| opt_key == "wipe_tower_extra_spacing"
|
||
|| opt_key == "wipe_tower_extruder"
|
||
|| opt_key == "wiping_volumes_extruders"
|
||
|| opt_key == "enable_filament_ramming"
|
||
|| opt_key == "purge_in_prime_tower"
|
||
|| opt_key == "z_offset"
|
||
|| opt_key == "support_multi_bed_types"
|
||
) {
|
||
steps.emplace_back(psWipeTower);
|
||
steps.emplace_back(psSkirtBrim);
|
||
} else if (opt_key == "filament_soluble"
|
||
|| opt_key == "filament_is_support"
|
||
|| opt_key == "independent_support_layer_height") {
|
||
steps.emplace_back(psWipeTower);
|
||
// Soluble support interface / non-soluble base interface produces non-soluble interface layers below soluble interface layers.
|
||
// Thus switching between soluble / non-soluble interface layer material may require recalculation of supports.
|
||
//FIXME Killing supports on any change of "filament_soluble" is rough. We should check for each object whether that is necessary.
|
||
osteps.emplace_back(posSupportMaterial);
|
||
osteps.emplace_back(posSimplifySupportPath);
|
||
} else if (
|
||
opt_key == "initial_layer_line_width"
|
||
|| opt_key == "min_layer_height"
|
||
|| opt_key == "max_layer_height"
|
||
//|| opt_key == "resolution"
|
||
//BBS: when enable arc fitting, we must re-generate perimeter
|
||
|| opt_key == "enable_arc_fitting"
|
||
|| opt_key == "print_order"
|
||
|| opt_key == "wall_sequence") {
|
||
osteps.emplace_back(posPerimeters);
|
||
osteps.emplace_back(posEstimateCurledExtrusions);
|
||
osteps.emplace_back(posInfill);
|
||
osteps.emplace_back(posSupportMaterial);
|
||
osteps.emplace_back(posSimplifyPath);
|
||
osteps.emplace_back(posSimplifyInfill);
|
||
osteps.emplace_back(posSimplifySupportPath);
|
||
steps.emplace_back(psSkirtBrim);
|
||
}
|
||
else if (opt_key == "z_hop_types") {
|
||
osteps.emplace_back(posDetectOverhangsForLift);
|
||
} else {
|
||
// for legacy, if we can't handle this option let's invalidate all steps
|
||
//FIXME invalidate all steps of all objects as well?
|
||
invalidated |= this->invalidate_all_steps();
|
||
// Continue with the other opt_keys to possibly invalidate any object specific steps.
|
||
}
|
||
}
|
||
|
||
sort_remove_duplicates(steps);
|
||
for (PrintStep step : steps)
|
||
invalidated |= this->invalidate_step(step);
|
||
sort_remove_duplicates(osteps);
|
||
for (PrintObjectStep ostep : osteps)
|
||
for (PrintObject *object : m_objects)
|
||
invalidated |= object->invalidate_step(ostep);
|
||
|
||
return invalidated;
|
||
}
|
||
|
||
void Print::set_calib_params(const Calib_Params& params) {
|
||
m_calib_params = params;
|
||
m_calib_params.mode = params.mode;
|
||
}
|
||
|
||
bool Print::invalidate_step(PrintStep step)
|
||
{
|
||
bool invalidated = Inherited::invalidate_step(step);
|
||
// Propagate to dependent steps.
|
||
if (step != psGCodeExport)
|
||
invalidated |= Inherited::invalidate_step(psGCodeExport);
|
||
return invalidated;
|
||
}
|
||
|
||
// returns true if an object step is done on all objects
|
||
// and there's at least one object
|
||
bool Print::is_step_done(PrintObjectStep step) const
|
||
{
|
||
if (m_objects.empty())
|
||
return false;
|
||
std::scoped_lock<std::mutex> lock(this->state_mutex());
|
||
for (const PrintObject *object : m_objects)
|
||
if (! object->is_step_done_unguarded(step))
|
||
return false;
|
||
return true;
|
||
}
|
||
|
||
// returns 0-based indices of used extruders
|
||
std::vector<unsigned int> Print::object_extruders() const
|
||
{
|
||
std::vector<unsigned int> extruders;
|
||
extruders.reserve(m_print_regions.size() * m_objects.size() * 3);
|
||
// BBS
|
||
#if 0
|
||
for (const PrintObject *object : m_objects)
|
||
for (const PrintRegion ®ion : object->all_regions())
|
||
region.collect_object_printing_extruders(*this, extruders);
|
||
#else
|
||
for (const PrintObject* object : m_objects) {
|
||
const ModelObject* mo = object->model_object();
|
||
for (const ModelVolume* mv : mo->volumes) {
|
||
std::vector<int> volume_extruders = mv->get_extruders();
|
||
for (int extruder : volume_extruders) {
|
||
assert(extruder > 0);
|
||
extruders.push_back(extruder - 1);
|
||
}
|
||
}
|
||
|
||
// layer range
|
||
for (auto layer_range : mo->layer_config_ranges) {
|
||
if (layer_range.second.has("extruder")) {
|
||
//BBS: actually when user doesn't change filament by height range(value is default 0), height range should not save key "extruder".
|
||
//Don't know why height range always save key "extruder" because of no change(should only save difference)...
|
||
//Add protection here to avoid overflow
|
||
auto value = layer_range.second.option("extruder")->getInt();
|
||
if (value > 0)
|
||
extruders.push_back(value - 1);
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
sort_remove_duplicates(extruders);
|
||
return extruders;
|
||
}
|
||
|
||
// returns 0-based indices of used extruders
|
||
std::vector<unsigned int> Print::support_material_extruders() const
|
||
{
|
||
std::vector<unsigned int> extruders;
|
||
bool support_uses_current_extruder = false;
|
||
// BBS
|
||
auto num_extruders = (unsigned int)m_config.filament_diameter.size();
|
||
|
||
for (PrintObject *object : m_objects) {
|
||
if (object->has_support_material()) {
|
||
assert(object->config().support_filament >= 0);
|
||
if (object->config().support_filament == 0)
|
||
support_uses_current_extruder = true;
|
||
else {
|
||
unsigned int i = (unsigned int)object->config().support_filament - 1;
|
||
extruders.emplace_back((i >= num_extruders) ? 0 : i);
|
||
}
|
||
assert(object->config().support_interface_filament >= 0);
|
||
if (object->config().support_interface_filament == 0)
|
||
support_uses_current_extruder = true;
|
||
else {
|
||
unsigned int i = (unsigned int)object->config().support_interface_filament - 1;
|
||
extruders.emplace_back((i >= num_extruders) ? 0 : i);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (support_uses_current_extruder)
|
||
// Add all object extruders to the support extruders as it is not know which one will be used to print supports.
|
||
append(extruders, this->object_extruders());
|
||
|
||
sort_remove_duplicates(extruders);
|
||
return extruders;
|
||
}
|
||
|
||
// returns 0-based indices of used extruders
|
||
std::vector<unsigned int> Print::extruders(bool conside_custom_gcode) const
|
||
{
|
||
std::vector<unsigned int> extruders = this->object_extruders();
|
||
append(extruders, this->support_material_extruders());
|
||
|
||
if (conside_custom_gcode) {
|
||
//BBS
|
||
int num_extruders = m_config.filament_colour.size();
|
||
if (m_model.plates_custom_gcodes.find(m_model.curr_plate_index) != m_model.plates_custom_gcodes.end()) {
|
||
for (auto item : m_model.plates_custom_gcodes.at(m_model.curr_plate_index).gcodes) {
|
||
if (item.type == CustomGCode::Type::ToolChange && item.extruder <= num_extruders)
|
||
extruders.push_back((unsigned int)(item.extruder - 1));
|
||
}
|
||
}
|
||
}
|
||
|
||
sort_remove_duplicates(extruders);
|
||
return extruders;
|
||
}
|
||
|
||
unsigned int Print::num_object_instances() const
|
||
{
|
||
unsigned int instances = 0;
|
||
for (const PrintObject *print_object : m_objects)
|
||
instances += (unsigned int)print_object->instances().size();
|
||
return instances;
|
||
}
|
||
|
||
double Print::max_allowed_layer_height() const
|
||
{
|
||
double nozzle_diameter_max = 0.;
|
||
for (unsigned int extruder_id : this->extruders())
|
||
nozzle_diameter_max = std::max(nozzle_diameter_max, m_config.nozzle_diameter.get_at(extruder_id));
|
||
return nozzle_diameter_max;
|
||
}
|
||
|
||
std::vector<ObjectID> Print::print_object_ids() const
|
||
{
|
||
std::vector<ObjectID> out;
|
||
// Reserve one more for the caller to append the ID of the Print itself.
|
||
out.reserve(m_objects.size() + 1);
|
||
for (const PrintObject *print_object : m_objects)
|
||
out.emplace_back(print_object->id());
|
||
return out;
|
||
}
|
||
|
||
bool Print::has_infinite_skirt() const
|
||
{
|
||
return (m_config.draft_shield == dsEnabled && m_config.skirt_loops > 0) || (m_config.ooze_prevention && this->extruders().size() > 1);
|
||
}
|
||
|
||
bool Print::has_skirt() const
|
||
{
|
||
return (m_config.skirt_height > 0 && m_config.skirt_loops > 0) || m_config.draft_shield != dsDisabled;
|
||
}
|
||
|
||
bool Print::has_brim() const
|
||
{
|
||
return std::any_of(m_objects.begin(), m_objects.end(), [](PrintObject *object) { return object->has_brim(); });
|
||
}
|
||
|
||
//BBS
|
||
std::vector<size_t> Print::layers_sorted_for_object(float start, float end, std::vector<LayerPtrs> &layers_of_objects, std::vector<BoundingBox> &boundingBox_for_objects, std::vector<Points> &objects_instances_shift)
|
||
{
|
||
std::vector<size_t> idx_of_object_sorted;
|
||
size_t idx = 0;
|
||
for (const auto &object : m_objects) {
|
||
idx_of_object_sorted.push_back(idx++);
|
||
object->get_certain_layers(start, end, layers_of_objects, boundingBox_for_objects);
|
||
}
|
||
std::sort(idx_of_object_sorted.begin(), idx_of_object_sorted.end(),
|
||
[boundingBox_for_objects](auto left, auto right) { return boundingBox_for_objects[left].area() > boundingBox_for_objects[right].area(); });
|
||
|
||
objects_instances_shift.clear();
|
||
objects_instances_shift.reserve(m_objects.size());
|
||
for (const auto& object : m_objects)
|
||
objects_instances_shift.emplace_back(object->get_instances_shift_without_plate_offset());
|
||
|
||
return idx_of_object_sorted;
|
||
};
|
||
|
||
StringObjectException Print::sequential_print_clearance_valid(const Print &print, Polygons *polygons, std::vector<std::pair<Polygon, float>>* height_polygons)
|
||
{
|
||
StringObjectException single_object_exception;
|
||
auto print_config = print.config();
|
||
Pointfs excluse_area_points = print_config.bed_exclude_area.values;
|
||
Polygons exclude_polys;
|
||
Polygon exclude_poly;
|
||
const Vec3d print_origin = print.get_plate_origin();
|
||
for (int i = 0; i < excluse_area_points.size(); i++) {
|
||
auto pt = excluse_area_points[i];
|
||
exclude_poly.points.emplace_back(scale_(pt.x() + print_origin.x()), scale_(pt.y() + print_origin.y()));
|
||
if (i % 4 == 3) { // exclude areas are always rectangle
|
||
exclude_polys.push_back(exclude_poly);
|
||
exclude_poly.points.clear();
|
||
}
|
||
}
|
||
|
||
std::map<ObjectID, Polygon> map_model_object_to_convex_hull;
|
||
struct print_instance_info
|
||
{
|
||
const PrintInstance *print_instance;
|
||
BoundingBox bounding_box;
|
||
Polygon hull_polygon;
|
||
int object_index;
|
||
double arrange_score;
|
||
double height;
|
||
};
|
||
auto find_object_index = [](const Model& model, const ModelObject* obj) {
|
||
for (int index = 0; index < model.objects.size(); index++)
|
||
{
|
||
if (model.objects[index] == obj)
|
||
return index;
|
||
}
|
||
return -1;
|
||
};
|
||
std::vector<struct print_instance_info> print_instance_with_bounding_box;
|
||
{
|
||
// sequential_print_horizontal_clearance_valid
|
||
Polygons convex_hulls_other;
|
||
if (polygons != nullptr)
|
||
polygons->clear();
|
||
std::vector<size_t> intersecting_idxs;
|
||
|
||
bool all_objects_are_short = print.is_all_objects_are_short();
|
||
// Shrink the extruder_clearance_radius a tiny bit, so that if the object arrangement algorithm placed the objects
|
||
// exactly by satisfying the extruder_clearance_radius, this test will not trigger collision.
|
||
float obj_distance = all_objects_are_short ? scale_(0.5*MAX_OUTER_NOZZLE_DIAMETER-0.1) : scale_(0.5*print.config().extruder_clearance_radius.value-0.1);
|
||
|
||
for (const PrintObject *print_object : print.objects()) {
|
||
assert(! print_object->model_object()->instances.empty());
|
||
assert(! print_object->instances().empty());
|
||
ObjectID model_object_id = print_object->model_object()->id();
|
||
auto it_convex_hull = map_model_object_to_convex_hull.find(model_object_id);
|
||
// Get convex hull of all printable volumes assigned to this print object.
|
||
ModelInstance *model_instance0 = print_object->model_object()->instances.front();
|
||
if (it_convex_hull == map_model_object_to_convex_hull.end()) {
|
||
// Calculate the convex hull of a printable object.
|
||
// Grow convex hull with the clearance margin.
|
||
// FIXME: Arrangement has different parameters for offsetting (jtMiter, limit 2)
|
||
// which causes that the warning will be showed after arrangement with the
|
||
// appropriate object distance. Even if I set this to jtMiter the warning still shows up.
|
||
it_convex_hull = map_model_object_to_convex_hull.emplace_hint(it_convex_hull, model_object_id,
|
||
print_object->model_object()->convex_hull_2d(Geometry::assemble_transform(
|
||
{ 0.0, 0.0, model_instance0->get_offset().z() }, model_instance0->get_rotation(), model_instance0->get_scaling_factor(), model_instance0->get_mirror())));
|
||
}
|
||
// Make a copy, so it may be rotated for instances.
|
||
Polygon convex_hull0 = it_convex_hull->second;
|
||
const double z_diff = Geometry::rotation_diff_z(model_instance0->get_rotation(), print_object->instances().front().model_instance->get_rotation());
|
||
if (std::abs(z_diff) > EPSILON)
|
||
convex_hull0.rotate(z_diff);
|
||
// Now we check that no instance of convex_hull intersects any of the previously checked object instances.
|
||
for (const PrintInstance &instance : print_object->instances()) {
|
||
Polygon convex_hull_no_offset = convex_hull0, convex_hull;
|
||
auto tmp = offset(convex_hull_no_offset, obj_distance, jtRound, scale_(0.1));
|
||
if (!tmp.empty()) { // tmp may be empty due to clipper's bug, see STUDIO-2452
|
||
convex_hull = tmp.front();
|
||
// instance.shift is a position of a centered object, while model object may not be centered.
|
||
// Convert the shift from the PrintObject's coordinates into ModelObject's coordinates by removing the centering offset.
|
||
convex_hull.translate(instance.shift - print_object->center_offset());
|
||
}
|
||
convex_hull_no_offset.translate(instance.shift - print_object->center_offset());
|
||
//juedge the exclude area
|
||
if (!intersection(exclude_polys, convex_hull_no_offset).empty()) {
|
||
if (single_object_exception.string.empty()) {
|
||
single_object_exception.string = (boost::format(L("%1% is too close to exclusion area, there may be collisions when printing.")) %instance.model_instance->get_object()->name).str();
|
||
single_object_exception.object = instance.model_instance->get_object();
|
||
}
|
||
else {
|
||
single_object_exception.string += "\n"+(boost::format(L("%1% is too close to exclusion area, there may be collisions when printing.")) %instance.model_instance->get_object()->name).str();
|
||
single_object_exception.object = nullptr;
|
||
}
|
||
//if (polygons) {
|
||
// intersecting_idxs.emplace_back(convex_hulls_other.size());
|
||
//}
|
||
}
|
||
|
||
// if output needed, collect indices (inside convex_hulls_other) of intersecting hulls
|
||
for (size_t i = 0; i < convex_hulls_other.size(); ++i) {
|
||
if (! intersection(convex_hulls_other[i], convex_hull).empty()) {
|
||
bool has_exception = false;
|
||
if (single_object_exception.string.empty()) {
|
||
single_object_exception.string = (boost::format(L("%1% is too close to others, and collisions may be caused.")) %instance.model_instance->get_object()->name).str();
|
||
single_object_exception.object = instance.model_instance->get_object();
|
||
has_exception = true;
|
||
}
|
||
else {
|
||
single_object_exception.string += "\n"+(boost::format(L("%1% is too close to others, and collisions may be caused.")) %instance.model_instance->get_object()->name).str();
|
||
single_object_exception.object = nullptr;
|
||
has_exception = true;
|
||
}
|
||
|
||
if (polygons) {
|
||
intersecting_idxs.emplace_back(i);
|
||
intersecting_idxs.emplace_back(convex_hulls_other.size());
|
||
}
|
||
|
||
if (has_exception) break;
|
||
}
|
||
}
|
||
struct print_instance_info print_info {&instance, convex_hull.bounding_box(), convex_hull};
|
||
print_info.height = instance.print_object->height();
|
||
print_info.object_index = find_object_index(print.model(), print_object->model_object());
|
||
print_instance_with_bounding_box.push_back(std::move(print_info));
|
||
convex_hulls_other.emplace_back(std::move(convex_hull));
|
||
}
|
||
}
|
||
if (!intersecting_idxs.empty()) {
|
||
// use collected indices (inside convex_hulls_other) to update output
|
||
std::sort(intersecting_idxs.begin(), intersecting_idxs.end());
|
||
intersecting_idxs.erase(std::unique(intersecting_idxs.begin(), intersecting_idxs.end()), intersecting_idxs.end());
|
||
for (size_t i : intersecting_idxs) {
|
||
polygons->emplace_back(std::move(convex_hulls_other[i]));
|
||
}
|
||
}
|
||
}
|
||
|
||
// calc sort order
|
||
double hc1 = scale_(print.config().extruder_clearance_height_to_lid); // height to lid
|
||
double hc2 = scale_(print.config().extruder_clearance_height_to_rod); // height to rod
|
||
double printable_height = scale_(print.config().printable_height);
|
||
|
||
#if 0 //do not sort anymore, use the order in object list
|
||
auto bed_points = get_bed_shape(print_config);
|
||
float bed_width = bed_points[1].x() - bed_points[0].x();
|
||
// 如果扩大以后的多边形的距离小于这个值,就需要严格保证从左到右的打印顺序,否则会撞工具头右侧
|
||
float unsafe_dist = scale_(print_config.extruder_clearance_max_radius.value - print_config.extruder_clearance_radius.value);
|
||
struct VecHash
|
||
{
|
||
size_t operator()(const Vec2i &n1) const
|
||
{
|
||
return std::hash<coord_t>()(int(n1(0) * 100 + 100)) + std::hash<coord_t>()(int(n1(1) * 100 + 100)) * 101;
|
||
}
|
||
};
|
||
std::unordered_set<Vec2i, VecHash> left_right_pair; // pairs in this vector must strictly obey the left-right order
|
||
for (size_t i = 0; i < print_instance_with_bounding_box.size();i++) {
|
||
auto &inst = print_instance_with_bounding_box[i];
|
||
inst.index = i;
|
||
Point pt = inst.bounding_box.center();
|
||
inst.arrange_score = pt.x() / 2 + pt.y(); // we prefer print row-by-row, so cost on x-direction is smaller
|
||
}
|
||
for (size_t i = 0; i < print_instance_with_bounding_box.size(); i++) {
|
||
auto &inst = print_instance_with_bounding_box[i];
|
||
auto &l = print_instance_with_bounding_box[i];
|
||
for (size_t j = 0; j < print_instance_with_bounding_box.size(); j++) {
|
||
if (j != i) {
|
||
auto &r = print_instance_with_bounding_box[j];
|
||
auto ly1 = l.bounding_box.min.y();
|
||
auto ly2 = l.bounding_box.max.y();
|
||
auto ry1 = r.bounding_box.min.y();
|
||
auto ry2 = r.bounding_box.max.y();
|
||
auto lx1 = l.bounding_box.min.x();
|
||
auto rx1 = r.bounding_box.min.x();
|
||
auto lx2 = l.bounding_box.max.x();
|
||
auto rx2 = r.bounding_box.max.x();
|
||
auto inter_min = std::max(ly1, ry1);
|
||
auto inter_max = std::min(ly2, ry2);
|
||
auto inter_y = inter_max - inter_min;
|
||
|
||
// 如果y方向的重合超过轮廓的膨胀量,说明两个物体在一行,应该先打左边的物体,即先比较二者的x坐标。
|
||
if (inter_y > scale_(0.5 * print.config().extruder_clearance_radius.value)) {
|
||
if (std::max(rx1 - lx2, lx1 - rx2) < unsafe_dist) {
|
||
if (lx1 > rx1) {
|
||
left_right_pair.insert({j, i});
|
||
BOOST_LOG_TRIVIAL(debug) << "in-a-row, print_instance " << r.print_instance->model_instance->get_object()->name << "(" << r.arrange_score << ")"
|
||
<< " -> " << l.print_instance->model_instance->get_object()->name << "(" << l.arrange_score << ")";
|
||
} else {
|
||
left_right_pair.insert({i, j});
|
||
BOOST_LOG_TRIVIAL(debug) << "in-a-row, print_instance " << l.print_instance->model_instance->get_object()->name << "(" << l.arrange_score << ")"
|
||
<< " -> " << r.print_instance->model_instance->get_object()->name << "(" << r.arrange_score << ")";
|
||
}
|
||
}
|
||
}
|
||
if (l.height > hc1 && r.height < hc1) {
|
||
// 当前物体超过了顶盖高度,必须后打
|
||
left_right_pair.insert({j, i});
|
||
BOOST_LOG_TRIVIAL(debug) << "height>hc1, print_instance " << r.print_instance->model_instance->get_object()->name << "(" << r.arrange_score << ")"
|
||
<< " -> " << l.print_instance->model_instance->get_object()->name << "(" << l.arrange_score << ")";
|
||
}
|
||
else if (l.height > hc2 && l.height > r.height && l.arrange_score<r.arrange_score) {
|
||
// 如果当前物体的高度超过滑杆,且比r高,就给它加一点代价,尽量让高的物体后打(只有物体高度超过滑杆时才有必要按高度来)
|
||
if (l.arrange_score < r.arrange_score)
|
||
l.arrange_score = r.arrange_score + 10;
|
||
BOOST_LOG_TRIVIAL(debug) << "height>hc2, print_instance " << inst.print_instance->model_instance->get_object()->name
|
||
<< ", right=" << r.print_instance->model_instance->get_object()->name << ", l.score: " << l.arrange_score
|
||
<< ", r.score: " << r.arrange_score;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
// 多做几次代价传播,因为前一次有些值没有更新。
|
||
// TODO 更好的办法是建立一颗树,一步到位。不过我暂时没精力搞,先就这样吧
|
||
for (int k=0;k<5;k++)
|
||
for (auto p : left_right_pair) {
|
||
auto &l = print_instance_with_bounding_box[p(0)];
|
||
auto &r = print_instance_with_bounding_box[p(1)];
|
||
if(r.arrange_score<l.arrange_score)
|
||
r.arrange_score = l.arrange_score + 10;
|
||
}
|
||
|
||
BOOST_LOG_TRIVIAL(debug) << "bed width: " << unscale_(bed_width) << ", unsafe_dist:" << unscale_(unsafe_dist) << ", height_to_lid: " << unscale_(hc1) << ", height_to_rod:" << unscale_(hc2) << ", final dependency:";
|
||
for (auto p : left_right_pair) {
|
||
auto &l = print_instance_with_bounding_box[p(0)];
|
||
auto &r = print_instance_with_bounding_box[p(1)];
|
||
BOOST_LOG_TRIVIAL(debug) << "print_instance " << I18N::translate(l.print_instance->model_instance->get_object()->name) << "(" << l.arrange_score << ")"
|
||
<< " -> " << I18N::translate(r.print_instance->model_instance->get_object()->name) << "(" << r.arrange_score << ")";
|
||
}
|
||
// sort the print instance
|
||
std::sort(print_instance_with_bounding_box.begin(), print_instance_with_bounding_box.end(),
|
||
[](print_instance_info& l, print_instance_info& r) {return l.arrange_score < r.arrange_score;});
|
||
|
||
for (auto &inst : print_instance_with_bounding_box)
|
||
BOOST_LOG_TRIVIAL(debug) << "after sorting print_instance " << inst.print_instance->model_instance->get_object()->name << ", score: " << inst.arrange_score
|
||
<< ", height:"<< inst.height;
|
||
#else
|
||
// sort the print instance
|
||
std::sort(print_instance_with_bounding_box.begin(), print_instance_with_bounding_box.end(),
|
||
[](print_instance_info& l, print_instance_info& r) {return l.object_index < r.object_index;});
|
||
|
||
for (auto &inst : print_instance_with_bounding_box)
|
||
BOOST_LOG_TRIVIAL(debug) << "after sorting print_instance " << inst.print_instance->model_instance->get_object()->name << ", object_index: " << inst.object_index
|
||
<< ", height:"<< inst.height;
|
||
|
||
#endif
|
||
// sequential_print_vertical_clearance_valid
|
||
{
|
||
// Ignore the last instance printed.
|
||
//print_instance_with_bounding_box.pop_back();
|
||
/*bool has_interlaced_objects = false;
|
||
for (int k = 0; k < print_instance_count; k++)
|
||
{
|
||
auto inst = print_instance_with_bounding_box[k].print_instance;
|
||
auto bbox = print_instance_with_bounding_box[k].bounding_box;
|
||
auto iy1 = bbox.min.y();
|
||
auto iy2 = bbox.max.y();
|
||
|
||
for (int i = 0; i < k; i++)
|
||
{
|
||
auto& p = print_instance_with_bounding_box[i].print_instance;
|
||
auto bbox2 = print_instance_with_bounding_box[i].bounding_box;
|
||
auto py1 = bbox2.min.y();
|
||
auto py2 = bbox2.max.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 (inter_max - inter_min > 0) {
|
||
has_interlaced_objects = true;
|
||
break;
|
||
}
|
||
}
|
||
if (has_interlaced_objects)
|
||
break;
|
||
}*/
|
||
|
||
// if objects are not overlapped on y-axis, they will not collide even if they are taller than extruder_clearance_height_to_rod
|
||
int print_instance_count = print_instance_with_bounding_box.size();
|
||
std::map<const PrintInstance*, std::pair<Polygon, float>> too_tall_instances;
|
||
for (int k = 0; k < print_instance_count; k++)
|
||
{
|
||
auto inst = print_instance_with_bounding_box[k].print_instance;
|
||
// 只需要考虑喷嘴到滑杆的偏移量,这个比整个工具头的碰撞半径要小得多
|
||
auto bbox = print_instance_with_bounding_box[k].bounding_box.inflated(-scale_(0.5 * print.config().extruder_clearance_radius.value));
|
||
auto iy1 = bbox.min.y();
|
||
auto iy2 = bbox.max.y();
|
||
(const_cast<ModelInstance*>(inst->model_instance))->arrange_order = k+1;
|
||
double height = (k == (print_instance_count - 1))?printable_height:hc1;
|
||
/*if (has_interlaced_objects) {
|
||
if ((k < (print_instance_count - 1)) && (inst->print_object->height() > hc2)) {
|
||
too_tall_instances[inst] = std::make_pair(print_instance_with_bounding_box[k].hull_polygon, unscaled<double>(hc2));
|
||
}
|
||
}
|
||
else {
|
||
if ((k < (print_instance_count - 1)) && (inst->print_object->height() > hc1)) {
|
||
too_tall_instances[inst] = std::make_pair(print_instance_with_bounding_box[k].hull_polygon, unscaled<double>(hc1));
|
||
}
|
||
}*/
|
||
|
||
for (int i = k+1; i < print_instance_count; i++)
|
||
{
|
||
auto& p = print_instance_with_bounding_box[i].print_instance;
|
||
auto bbox2 = print_instance_with_bounding_box[i].bounding_box;
|
||
auto py1 = bbox2.min.y();
|
||
auto py2 = bbox2.max.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 (inter_max - inter_min > 0) {
|
||
height = hc2;
|
||
break;
|
||
}
|
||
}
|
||
if (height < inst->print_object->max_z())
|
||
too_tall_instances[inst] = std::make_pair(print_instance_with_bounding_box[k].hull_polygon, unscaled<double>(height));
|
||
}
|
||
|
||
if (too_tall_instances.size() > 0) {
|
||
//return {, inst->model_instance->get_object()};
|
||
for (auto& iter: too_tall_instances) {
|
||
if (single_object_exception.string.empty()) {
|
||
single_object_exception.string = (boost::format(L("%1% is too tall, and collisions will be caused.")) %iter.first->model_instance->get_object()->name).str();
|
||
single_object_exception.object = iter.first->model_instance->get_object();
|
||
}
|
||
else {
|
||
single_object_exception.string += "\n" + (boost::format(L("%1% is too tall, and collisions will be caused.")) %iter.first->model_instance->get_object()->name).str();
|
||
single_object_exception.object = nullptr;
|
||
}
|
||
if (height_polygons)
|
||
height_polygons->emplace_back(std::move(iter.second));
|
||
}
|
||
}
|
||
}
|
||
|
||
return single_object_exception;
|
||
}
|
||
|
||
//BBS
|
||
static StringObjectException layered_print_cleareance_valid(const Print &print, StringObjectException *warning)
|
||
{
|
||
std::vector<const PrintInstance*> print_instances_ordered = sort_object_instances_by_model_order(print, true);
|
||
if (print_instances_ordered.size() < 1)
|
||
return {};
|
||
|
||
auto print_config = print.config();
|
||
Pointfs excluse_area_points = print_config.bed_exclude_area.values;
|
||
Polygons exclude_polys;
|
||
Polygon exclude_poly;
|
||
const Vec3d print_origin = print.get_plate_origin();
|
||
for (int i = 0; i < excluse_area_points.size(); i++) {
|
||
auto pt = excluse_area_points[i];
|
||
exclude_poly.points.emplace_back(scale_(pt.x() + print_origin.x()), scale_(pt.y() + print_origin.y()));
|
||
if (i % 4 == 3) { // exclude areas are always rectangle
|
||
exclude_polys.push_back(exclude_poly);
|
||
exclude_poly.points.clear();
|
||
}
|
||
}
|
||
|
||
std::map<const PrintInstance*, Polygon> map_model_object_to_convex_hull;
|
||
// sequential_print_horizontal_clearance_valid
|
||
Polygons convex_hulls_other;
|
||
for (int k = 0; k < print_instances_ordered.size(); k++)
|
||
{
|
||
auto& inst = print_instances_ordered[k];
|
||
auto it_convex_hull = map_model_object_to_convex_hull.find(inst);
|
||
// Get convex hull of all printable volumes assigned to this print object.
|
||
const ModelInstance* model_instance0 = inst->model_instance;
|
||
if (it_convex_hull == map_model_object_to_convex_hull.end()) {
|
||
// Calculate the convex hull of a printable object.
|
||
auto convex_hull0 = inst->print_object->model_object()->convex_hull_2d(
|
||
Geometry::assemble_transform(Vec3d::Zero(), model_instance0->get_rotation(), model_instance0->get_scaling_factor(), model_instance0->get_mirror()));
|
||
|
||
double z_diff = Geometry::rotation_diff_z(model_instance0->get_rotation(), inst->model_instance->get_rotation());
|
||
if (std::abs(z_diff) > EPSILON)
|
||
convex_hull0.rotate(z_diff);
|
||
|
||
// instance.shift is a position of a centered object, while model object may not be centered.
|
||
// Conver the shift from the PrintObject's coordinates into ModelObject's coordinates by removing the centering offset.
|
||
convex_hull0.translate(inst->shift - inst->print_object->center_offset());
|
||
|
||
it_convex_hull = map_model_object_to_convex_hull.emplace_hint(it_convex_hull, inst, convex_hull0);
|
||
}
|
||
Polygon& convex_hull = it_convex_hull->second;
|
||
Polygons convex_hulls_temp;
|
||
convex_hulls_temp.push_back(convex_hull);
|
||
if (!intersection(convex_hulls_other, convex_hulls_temp).empty()) {
|
||
if (warning) {
|
||
warning->string = inst->model_instance->get_object()->name + L(" is too close to others, there may be collisions when printing.") + "\n";
|
||
warning->object = inst->model_instance->get_object();
|
||
}
|
||
}
|
||
if (!intersection(exclude_polys, convex_hull).empty()) {
|
||
return {inst->model_instance->get_object()->name + L(" is too close to exclusion area, there may be collisions when printing.") + "\n", inst->model_instance->get_object()};
|
||
/*if (warning) {
|
||
warning->string = inst->model_instance->get_object()->name + L(" is too close to exclusion area, there may be collisions when printing.") + "\n";
|
||
warning->object = inst->model_instance->get_object();
|
||
}*/
|
||
}
|
||
convex_hulls_other.emplace_back(convex_hull);
|
||
}
|
||
|
||
//BBS: add the wipe tower check logic
|
||
const PrintConfig & config = print.config();
|
||
int filaments_count = print.extruders().size();
|
||
int plate_index = print.get_plate_index();
|
||
const Vec3d plate_origin = print.get_plate_origin();
|
||
float x = config.wipe_tower_x.get_at(plate_index) + plate_origin(0);
|
||
float y = config.wipe_tower_y.get_at(plate_index) + plate_origin(1);
|
||
float width = config.prime_tower_width.value;
|
||
float a = config.wipe_tower_rotation_angle.value;
|
||
//float v = config.wiping_volume.value;
|
||
|
||
float depth = print.wipe_tower_data(filaments_count).depth;
|
||
//float brim_width = print.wipe_tower_data(filaments_count).brim_width;
|
||
|
||
Polygons convex_hulls_temp;
|
||
if (print.has_wipe_tower()) {
|
||
Polygon wipe_tower_convex_hull;
|
||
wipe_tower_convex_hull.points.emplace_back(scale_(x), scale_(y));
|
||
wipe_tower_convex_hull.points.emplace_back(scale_(x + width), scale_(y));
|
||
wipe_tower_convex_hull.points.emplace_back(scale_(x + width), scale_(y + depth));
|
||
wipe_tower_convex_hull.points.emplace_back(scale_(x), scale_(y + depth));
|
||
wipe_tower_convex_hull.rotate(a);
|
||
convex_hulls_temp.push_back(wipe_tower_convex_hull);
|
||
}
|
||
if (!intersection(convex_hulls_other, convex_hulls_temp).empty()) {
|
||
if (warning) {
|
||
warning->string += L("Prime Tower") + L(" is too close to others, and collisions may be caused.\n");
|
||
}
|
||
}
|
||
if (!intersection(exclude_polys, convex_hulls_temp).empty()) {
|
||
/*if (warning) {
|
||
warning->string += L("Prime Tower is too close to exclusion area, there may be collisions when printing.\n");
|
||
}*/
|
||
return {L("Prime Tower") + L(" is too close to exclusion area, and collisions will be caused.\n")};
|
||
}
|
||
|
||
return {};
|
||
}
|
||
|
||
bool Print::check_multi_filaments_compatibility(const std::vector<std::string>& filament_types)
|
||
{
|
||
bool has_high_temperature_filament = false;
|
||
bool has_low_temperature_filament = false;
|
||
|
||
for (const auto& type : filament_types) {
|
||
if (get_filament_temp_type(type) ==FilamentTempType::HighTemp)
|
||
has_high_temperature_filament = true;
|
||
else if (get_filament_temp_type(type) == FilamentTempType::LowTemp)
|
||
has_low_temperature_filament = true;
|
||
}
|
||
|
||
if (has_high_temperature_filament && has_low_temperature_filament)
|
||
return false;
|
||
|
||
return true;
|
||
}
|
||
|
||
bool Print::is_filaments_compatible(const std::vector<int>& filament_types)
|
||
{
|
||
bool has_high_temperature_filament = false;
|
||
bool has_low_temperature_filament = false;
|
||
|
||
for (const auto& type : filament_types) {
|
||
if (type == FilamentTempType::HighTemp)
|
||
has_high_temperature_filament = true;
|
||
else if (type == FilamentTempType::LowTemp)
|
||
has_low_temperature_filament = true;
|
||
}
|
||
|
||
if (has_high_temperature_filament && has_low_temperature_filament)
|
||
return false;
|
||
|
||
return true;
|
||
}
|
||
int Print::get_compatible_filament_type(const std::set<int>& filament_types)
|
||
{
|
||
bool has_high_temperature_filament = false;
|
||
bool has_low_temperature_filament = false;
|
||
|
||
for (const auto& type : filament_types) {
|
||
if (type == FilamentTempType::HighTemp)
|
||
has_high_temperature_filament = true;
|
||
else if (type == FilamentTempType::LowTemp)
|
||
has_low_temperature_filament = true;
|
||
}
|
||
|
||
if (has_high_temperature_filament && has_low_temperature_filament)
|
||
return HighLowCompatible;
|
||
else if (has_high_temperature_filament)
|
||
return HighTemp;
|
||
else if (has_low_temperature_filament)
|
||
return LowTemp;
|
||
return HighLowCompatible;
|
||
}
|
||
|
||
//BBS: this function is used to check whether multi filament can be printed
|
||
StringObjectException Print::check_multi_filament_valid(const Print& print)
|
||
{
|
||
auto print_config = print.config();
|
||
std::vector<unsigned int> extruders = print.extruders();
|
||
std::vector<std::string> filament_types;
|
||
filament_types.reserve(extruders.size());
|
||
|
||
for (const auto& extruder_idx : extruders)
|
||
filament_types.push_back(print_config.filament_type.get_at(extruder_idx));
|
||
|
||
if (!check_multi_filaments_compatibility(filament_types))
|
||
return { L("Can not print multiple filaments which have large difference of temperature together. Otherwise, the extruder and nozzle may be blocked or damaged during printing") };
|
||
|
||
return {std::string()};
|
||
}
|
||
|
||
// Orca: this g92e0 regex is used copied from PrusaSlicer
|
||
// Matches "G92 E0" with various forms of writing the zero and with an optional comment.
|
||
boost::regex regex_g92e0 { "^[ \\t]*[gG]92[ \\t]*[eE](0(\\.0*)?|\\.0+)[ \\t]*(;.*)?$" };
|
||
|
||
// Precondition: Print::validate() requires the Print::apply() to be called its invocation.
|
||
//BBS: refine seq-print validation logic
|
||
StringObjectException Print::validate(StringObjectException *warning, Polygons* collison_polygons, std::vector<std::pair<Polygon, float>>* height_polygons) const
|
||
{
|
||
std::vector<unsigned int> extruders = this->extruders();
|
||
unsigned int nozzles = m_config.nozzle_diameter.size();
|
||
|
||
if (m_objects.empty())
|
||
return {std::string()};
|
||
|
||
if (extruders.empty())
|
||
return { L("No extrusions under current settings.") };
|
||
|
||
if (nozzles < 2 && extruders.size() > 1 && m_config.print_sequence != PrintSequence::ByObject) {
|
||
auto ret = check_multi_filament_valid(*this);
|
||
if (!ret.string.empty())
|
||
{
|
||
ret.type = STRING_EXCEPT_FILAMENTS_DIFFERENT_TEMP;
|
||
return ret;
|
||
}
|
||
}
|
||
|
||
if (m_config.print_sequence == PrintSequence::ByObject) {
|
||
if (m_config.timelapse_type == TimelapseType::tlSmooth)
|
||
return {L("Smooth mode of timelapse is not supported when \"by object\" sequence is enabled.")};
|
||
|
||
//BBS: refine seq-print validation logic
|
||
auto ret = sequential_print_clearance_valid(*this, collison_polygons, height_polygons);
|
||
if (!ret.string.empty()) {
|
||
ret.type = STRING_EXCEPT_OBJECT_COLLISION_IN_SEQ_PRINT;
|
||
return ret;
|
||
}
|
||
}
|
||
else {
|
||
//BBS
|
||
auto ret = layered_print_cleareance_valid(*this, warning);
|
||
if (!ret.string.empty()) {
|
||
ret.type = STRING_EXCEPT_OBJECT_COLLISION_IN_LAYER_PRINT;
|
||
return ret;
|
||
}
|
||
}
|
||
|
||
if (m_config.spiral_mode) {
|
||
size_t total_copies_count = 0;
|
||
for (const PrintObject* object : m_objects)
|
||
total_copies_count += object->instances().size();
|
||
// #4043
|
||
if (total_copies_count > 1 && m_config.print_sequence != PrintSequence::ByObject)
|
||
return {L("Please select \"By object\" print sequence to print multiple objects in spiral vase mode."), nullptr, "spiral_mode"};
|
||
assert(m_objects.size() == 1);
|
||
if (m_objects.front()->all_regions().size() > 1)
|
||
return {L("The spiral vase mode does not work when an object contains more than one materials."), nullptr, "spiral_mode"};
|
||
}
|
||
|
||
// Cache of layer height profiles for checking:
|
||
// 1) Whether all layers are synchronized if printing with wipe tower and / or unsynchronized supports.
|
||
// 2) Whether layer height is constant for Organic supports.
|
||
// 3) Whether build volume Z is not violated.
|
||
std::vector<std::vector<coordf_t>> layer_height_profiles;
|
||
auto layer_height_profile = [this, &layer_height_profiles](const size_t print_object_idx) -> const std::vector<coordf_t>& {
|
||
const PrintObject &print_object = *m_objects[print_object_idx];
|
||
if (layer_height_profiles.empty())
|
||
layer_height_profiles.assign(m_objects.size(), std::vector<coordf_t>());
|
||
std::vector<coordf_t> &profile = layer_height_profiles[print_object_idx];
|
||
if (profile.empty())
|
||
PrintObject::update_layer_height_profile(*print_object.model_object(), print_object.slicing_parameters(), profile);
|
||
return profile;
|
||
};
|
||
|
||
// Checks that the print does not exceed the max print height
|
||
for (size_t print_object_idx = 0; print_object_idx < m_objects.size(); ++ print_object_idx) {
|
||
const PrintObject &print_object = *m_objects[print_object_idx];
|
||
//FIXME It is quite expensive to generate object layers just to get the print height!
|
||
if (auto layers = generate_object_layers(print_object.slicing_parameters(), layer_height_profile(print_object_idx), print_object.config().precise_z_height.value);
|
||
! layers.empty() && layers.back() > this->config().printable_height + EPSILON) {
|
||
return
|
||
// Test whether the last slicing plane is below or above the print volume.
|
||
{ 0.5 * (layers[layers.size() - 2] + layers.back()) > this->config().printable_height + EPSILON ?
|
||
Slic3r::format(_u8L("The object %1% exceeds the maximum build volume height."), print_object.model_object()->name) :
|
||
Slic3r::format(_u8L("While the object %1% itself fits the build volume, its last layer exceeds the maximum build volume height."), print_object.model_object()->name) +
|
||
" " + _u8L("You might want to reduce the size of your model or change current print settings and retry.") };
|
||
}
|
||
}
|
||
|
||
// Some of the objects has variable layer height applied by painting or by a table.
|
||
bool has_custom_layering = std::find_if(m_objects.begin(), m_objects.end(),
|
||
[](const PrintObject *object) { return object->model_object()->has_custom_layering(); })
|
||
!= m_objects.end();
|
||
|
||
// Custom layering is not allowed for tree supports as of now.
|
||
for (size_t print_object_idx = 0; print_object_idx < m_objects.size(); ++ print_object_idx)
|
||
if (const PrintObject &print_object = *m_objects[print_object_idx];
|
||
print_object.has_support_material() && is_tree(print_object.config().support_type.value) && (print_object.config().support_style.value == smsOrganic ||
|
||
// Orca: use organic as default
|
||
print_object.config().support_style.value == smsDefault) &&
|
||
print_object.model_object()->has_custom_layering()) {
|
||
if (const std::vector<coordf_t> &layers = layer_height_profile(print_object_idx); ! layers.empty())
|
||
if (! check_object_layers_fixed(print_object.slicing_parameters(), layers))
|
||
return {_u8L("Variable layer height is not supported with Organic supports.") };
|
||
}
|
||
|
||
if (this->has_wipe_tower() && ! m_objects.empty()) {
|
||
// Make sure all extruders use same diameter filament and have the same nozzle diameter
|
||
// EPSILON comparison is used for nozzles and 10 % tolerance is used for filaments
|
||
double first_nozzle_diam = m_config.nozzle_diameter.get_at(extruders.front());
|
||
double first_filament_diam = m_config.filament_diameter.get_at(extruders.front());
|
||
for (const auto& extruder_idx : extruders) {
|
||
double nozzle_diam = m_config.nozzle_diameter.get_at(extruder_idx);
|
||
double filament_diam = m_config.filament_diameter.get_at(extruder_idx);
|
||
if (nozzle_diam - EPSILON > first_nozzle_diam || nozzle_diam + EPSILON < first_nozzle_diam
|
||
|| std::abs((filament_diam - first_filament_diam) / first_filament_diam) > 0.1)
|
||
// BBS: remove L()
|
||
return { L("Different nozzle diameters and different filament diameters is not allowed when prime tower is enabled.") };
|
||
}
|
||
|
||
if (! m_config.use_relative_e_distances)
|
||
return { L("The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1).") };
|
||
if (m_config.ooze_prevention)
|
||
return { L("Ooze prevention is currently not supported with the prime tower enabled.") };
|
||
|
||
// BBS: remove following logic and _L()
|
||
#if 0
|
||
if (m_config.gcode_flavor != gcfRepRapSprinter && m_config.gcode_flavor != gcfRepRapFirmware &&
|
||
m_config.gcode_flavor != gcfRepetier && m_config.gcode_flavor != gcfMarlinLegacy && m_config.gcode_flavor != gcfMarlinFirmware)
|
||
return { L("The prime tower is currently only supported for the Marlin, RepRap/Sprinter, RepRapFirmware and Repetier G-code flavors.")};
|
||
|
||
if ((m_config.print_sequence == PrintSequence::ByObject) && extruders.size() > 1)
|
||
return { L("The prime tower is not supported in \"By object\" print."), nullptr, "enable_prime_tower" };
|
||
|
||
// BBS: When prime tower is on, object layer and support layer must be aligned. So support gap should be multiple of object layer height.
|
||
for (size_t i = 0; i < m_objects.size(); i++) {
|
||
const PrintObject* object = m_objects[i];
|
||
const SlicingParameters& slicing_params = object->slicing_parameters();
|
||
if (object->config().adaptive_layer_height) {
|
||
return { L("The prime tower is not supported when adaptive layer height is on. It requires that all objects have the same layer height."), object, "adaptive_layer_height" };
|
||
}
|
||
|
||
if (!object->config().enable_support)
|
||
continue;
|
||
|
||
double gap_layers = slicing_params.gap_object_support / slicing_params.layer_height;
|
||
if (gap_layers - (int)gap_layers > EPSILON) {
|
||
return { L("The prime tower requires \"support gap\" to be multiple of layer height"), object };
|
||
}
|
||
}
|
||
#endif
|
||
|
||
if (m_objects.size() > 1) {
|
||
const SlicingParameters &slicing_params0 = m_objects.front()->slicing_parameters();
|
||
size_t tallest_object_idx = 0;
|
||
for (size_t i = 1; i < m_objects.size(); ++ i) {
|
||
const PrintObject *object = m_objects[i];
|
||
const SlicingParameters &slicing_params = object->slicing_parameters();
|
||
if (std::abs(slicing_params.first_print_layer_height - slicing_params0.first_print_layer_height) > EPSILON ||
|
||
std::abs(slicing_params.layer_height - slicing_params0.layer_height ) > EPSILON)
|
||
return {L("The prime tower requires that all objects have the same layer heights"), object, "initial_layer_print_height"};
|
||
if (slicing_params.raft_layers() != slicing_params0.raft_layers())
|
||
return {L("The prime tower requires that all objects are printed over the same number of raft layers"), object, "raft_layers"};
|
||
// BBS: support gap can be multiple of object layer height, remove _L()
|
||
#if 0
|
||
if (slicing_params0.gap_object_support != slicing_params.gap_object_support ||
|
||
slicing_params0.gap_support_object != slicing_params.gap_support_object)
|
||
return {("The prime tower is only supported for multiple objects if they are printed with the same support_top_z_distance"), object};
|
||
#endif
|
||
if (!equal_layering(slicing_params, slicing_params0))
|
||
return { L("The prime tower requires that all objects are sliced with the same layer heights."), object };
|
||
if (has_custom_layering) {
|
||
auto &lh = layer_height_profile(i);
|
||
auto &lh_tallest = layer_height_profile(tallest_object_idx);
|
||
if (*(lh.end() - 2) > *(lh_tallest.end() - 2))
|
||
tallest_object_idx = i;
|
||
}
|
||
}
|
||
|
||
// BBS: remove obsolete logics and _L()
|
||
if (has_custom_layering) {
|
||
std::vector<std::vector<coordf_t>> layer_z_series;
|
||
layer_z_series.assign(m_objects.size(), std::vector<coordf_t>());
|
||
|
||
for (size_t idx_object = 0; idx_object < m_objects.size(); ++idx_object) {
|
||
layer_z_series[idx_object] = generate_object_layers(m_objects[idx_object]->slicing_parameters(), layer_height_profiles[idx_object], m_objects[idx_object]->config().precise_z_height.value);
|
||
}
|
||
|
||
for (size_t idx_object = 0; idx_object < m_objects.size(); ++idx_object) {
|
||
if (idx_object == tallest_object_idx) continue;
|
||
// Check that the layer height profiles are equal. This will happen when one object is
|
||
// a copy of another, or when a layer height modifier is used the same way on both objects.
|
||
// The latter case might create a floating point inaccuracy mismatch, so compare
|
||
// element-wise using an epsilon check.
|
||
size_t i = 0;
|
||
const coordf_t eps = 0.5 * EPSILON; // layers closer than EPSILON will be merged later. Let's make
|
||
// this check a bit more sensitive to make sure we never consider two different layers as one.
|
||
while (i < layer_height_profiles[idx_object].size() && i < layer_height_profiles[tallest_object_idx].size()) {
|
||
// BBS: remove the break condition, because a variable layer height object and a new object will not be checked when slicing
|
||
//if (i % 2 == 0 && layer_height_profiles[tallest_object_idx][i] > layer_height_profiles[idx_object][layer_height_profiles[idx_object].size() - 2])
|
||
// break;
|
||
if (std::abs(layer_height_profiles[idx_object][i] - layer_height_profiles[tallest_object_idx][i]) > eps)
|
||
return {L("The prime tower is only supported if all objects have the same variable layer height")};
|
||
++i;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
{
|
||
// Find the smallest used nozzle diameter and the number of unique nozzle diameters.
|
||
double min_nozzle_diameter = std::numeric_limits<double>::max();
|
||
double max_nozzle_diameter = 0;
|
||
for (unsigned int extruder_id : extruders) {
|
||
double dmr = m_config.nozzle_diameter.get_at(extruder_id);
|
||
min_nozzle_diameter = std::min(min_nozzle_diameter, dmr);
|
||
max_nozzle_diameter = std::max(max_nozzle_diameter, dmr);
|
||
}
|
||
|
||
// BBS: remove L()
|
||
#if 0
|
||
// We currently allow one to assign extruders with a higher index than the number
|
||
// of physical extruders the machine is equipped with, as the Printer::apply() clamps them.
|
||
unsigned int total_extruders_count = m_config.nozzle_diameter.size();
|
||
for (const auto& extruder_idx : extruders)
|
||
if ( extruder_idx >= total_extruders_count )
|
||
return ("One or more object were assigned an extruder that the printer does not have.");
|
||
#endif
|
||
|
||
auto validate_extrusion_width = [min_nozzle_diameter, max_nozzle_diameter](const ConfigBase &config, const char *opt_key, double layer_height, std::string &err_msg) -> bool {
|
||
double extrusion_width_min = config.get_abs_value(opt_key, min_nozzle_diameter);
|
||
double extrusion_width_max = config.get_abs_value(opt_key, max_nozzle_diameter);
|
||
if (extrusion_width_min == 0) {
|
||
// Default "auto-generated" extrusion width is always valid.
|
||
} else if (extrusion_width_min <= layer_height) {
|
||
err_msg = L("Too small line width");
|
||
return false;
|
||
} else if (extrusion_width_max > max_nozzle_diameter * 5) {
|
||
err_msg = L("Too large line width");
|
||
return false;
|
||
}
|
||
return true;
|
||
};
|
||
for (PrintObject *object : m_objects) {
|
||
if (object->has_support_material()) {
|
||
// BBS: remove useless logics and L()
|
||
#if 0
|
||
if ((object->config().support_filament == 0 || object->config().support_interface_filament == 0) && max_nozzle_diameter - min_nozzle_diameter > EPSILON) {
|
||
// The object has some form of support and either support_filament or support_interface_filament
|
||
// will be printed with the current tool without a forced tool change. Play safe, assert that all object nozzles
|
||
// are of the same diameter.
|
||
return {("Printing with multiple extruders of differing nozzle diameters. "
|
||
"If support is to be printed with the current filament (support_filament == 0 or support_interface_filament == 0), "
|
||
"all nozzles have to be of the same diameter."), object, "support_filament"};
|
||
}
|
||
#endif
|
||
|
||
// BBS
|
||
#if 0
|
||
if (this->has_wipe_tower() && object->config().independent_support_layer_height) {
|
||
return {L("The prime tower requires that support has the same layer height with object."), object, "support_filament"};
|
||
}
|
||
#endif
|
||
|
||
// Prusa: Fixing crashes with invalid tip diameter or branch diameter
|
||
// https://github.com/prusa3d/PrusaSlicer/commit/96b3ae85013ac363cd1c3e98ec6b7938aeacf46d
|
||
if (is_tree(object->config().support_type.value) && (object->config().support_style == smsOrganic ||
|
||
// Orca: use organic as default
|
||
object->config().support_style == smsDefault)) {
|
||
float extrusion_width = std::min(
|
||
support_material_flow(object).width(),
|
||
support_material_interface_flow(object).width());
|
||
if (object->config().tree_support_tip_diameter < extrusion_width - EPSILON)
|
||
return { L("Organic support tree tip diameter must not be smaller than support material extrusion width."), object, "tree_support_tip_diameter" };
|
||
if (object->config().tree_support_branch_diameter_organic < 2. * extrusion_width - EPSILON)
|
||
return { L("Organic support branch diameter must not be smaller than 2x support material extrusion width."), object, "tree_support_branch_diameter_organic" };
|
||
if (object->config().tree_support_branch_diameter_organic < object->config().tree_support_tip_diameter)
|
||
return { L("Organic support branch diameter must not be smaller than support tree tip diameter."), object, "tree_support_branch_diameter_organic" };
|
||
}
|
||
}
|
||
|
||
// Do we have custom support data that would not be used?
|
||
// Notify the user in that case.
|
||
if (! object->has_support() && warning) {
|
||
for (const ModelVolume* mv : object->model_object()->volumes) {
|
||
bool has_enforcers = mv->is_support_enforcer() ||
|
||
(mv->is_model_part() && mv->supported_facets.has_facets(*mv, EnforcerBlockerType::ENFORCER));
|
||
if (has_enforcers) {
|
||
warning->string = L("Support enforcers are used but support is not enabled. Please enable support.");
|
||
warning->object = object;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
double initial_layer_print_height = m_config.initial_layer_print_height.value;
|
||
double first_layer_min_nozzle_diameter;
|
||
if (object->has_raft()) {
|
||
// if we have raft layers, only support material extruder is used on first layer
|
||
size_t first_layer_extruder = object->config().raft_layers == 1
|
||
? object->config().support_interface_filament-1
|
||
: object->config().support_filament-1;
|
||
first_layer_min_nozzle_diameter = (first_layer_extruder == size_t(-1)) ?
|
||
min_nozzle_diameter :
|
||
m_config.nozzle_diameter.get_at(first_layer_extruder);
|
||
} else {
|
||
// if we don't have raft layers, any nozzle diameter is potentially used in first layer
|
||
first_layer_min_nozzle_diameter = min_nozzle_diameter;
|
||
}
|
||
if (initial_layer_print_height > first_layer_min_nozzle_diameter)
|
||
return {L("Layer height cannot exceed nozzle diameter"), object, "initial_layer_print_height"};
|
||
|
||
// validate layer_height
|
||
double layer_height = object->config().layer_height.value;
|
||
if (layer_height > min_nozzle_diameter)
|
||
return {L("Layer height cannot exceed nozzle diameter"), object, "layer_height"};
|
||
|
||
// Validate extrusion widths.
|
||
std::string err_msg;
|
||
if (!validate_extrusion_width(object->config(), "line_width", layer_height, err_msg))
|
||
return {err_msg, object, "line_width"};
|
||
if (object->has_support() || object->has_raft()) {
|
||
if (!validate_extrusion_width(object->config(), "support_line_width", layer_height, err_msg))
|
||
return {err_msg, object, "support_line_width"};
|
||
}
|
||
for (const char *opt_key : { "inner_wall_line_width", "outer_wall_line_width", "sparse_infill_line_width", "internal_solid_infill_line_width", "top_surface_line_width" })
|
||
for (const PrintRegion ®ion : object->all_regions())
|
||
if (!validate_extrusion_width(region.config(), opt_key, layer_height, err_msg))
|
||
return {err_msg, object, opt_key};
|
||
}
|
||
}
|
||
|
||
// Orca: G92 E0 is not supported when using absolute extruder addressing
|
||
// This check is copied from PrusaSlicer, the original author is Vojtech Bubnik
|
||
if(!is_BBL_printer()) {
|
||
bool before_layer_gcode_resets_extruder =
|
||
boost::regex_search(m_config.before_layer_change_gcode.value, regex_g92e0);
|
||
bool layer_gcode_resets_extruder = boost::regex_search(m_config.layer_change_gcode.value, regex_g92e0);
|
||
if (m_config.use_relative_e_distances) {
|
||
// See GH issues #6336 #5073
|
||
if ((m_config.gcode_flavor == gcfMarlinLegacy || m_config.gcode_flavor == gcfMarlinFirmware) &&
|
||
!before_layer_gcode_resets_extruder && !layer_gcode_resets_extruder)
|
||
return {L("Relative extruder addressing requires resetting the extruder position at each layer to "
|
||
"prevent loss of floating point accuracy. Add \"G92 E0\" to layer_gcode."),
|
||
nullptr, "before_layer_change_gcode"};
|
||
} else if (before_layer_gcode_resets_extruder)
|
||
return {L("\"G92 E0\" was found in before_layer_gcode, which is incompatible with absolute extruder "
|
||
"addressing."),
|
||
nullptr, "before_layer_change_gcode"};
|
||
else if (layer_gcode_resets_extruder)
|
||
return {L("\"G92 E0\" was found in layer_gcode, which is incompatible with absolute extruder addressing."),
|
||
nullptr, "layer_change_gcode"};
|
||
}
|
||
|
||
const ConfigOptionDef* bed_type_def = print_config_def.get("curr_bed_type");
|
||
assert(bed_type_def != nullptr);
|
||
|
||
if (is_BBL_printer()) {
|
||
const t_config_enum_values* bed_type_keys_map = bed_type_def->enum_keys_map;
|
||
for (unsigned int extruder_id : extruders) {
|
||
const ConfigOptionInts* bed_temp_opt = m_config.option<ConfigOptionInts>(get_bed_temp_key(m_config.curr_bed_type));
|
||
for (unsigned int extruder_id : extruders) {
|
||
int curr_bed_temp = bed_temp_opt->get_at(extruder_id);
|
||
if (curr_bed_temp == 0 && bed_type_keys_map != nullptr) {
|
||
std::string bed_type_name;
|
||
for (auto item : *bed_type_keys_map) {
|
||
if (item.second == m_config.curr_bed_type) {
|
||
bed_type_name = item.first;
|
||
break;
|
||
}
|
||
}
|
||
|
||
StringObjectException except;
|
||
except.string = Slic3r::format(L("Plate %d: %s does not support filament %s"), this->get_plate_index() + 1, L(bed_type_name), extruder_id + 1);
|
||
except.string += "\n";
|
||
except.type = STRING_EXCEPT_FILAMENT_NOT_MATCH_BED_TYPE;
|
||
except.params.push_back(std::to_string(this->get_plate_index() + 1));
|
||
except.params.push_back(L(bed_type_name));
|
||
except.params.push_back(std::to_string(extruder_id+1));
|
||
except.object = nullptr;
|
||
return except;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// check if print speed/accel/jerk is higher than the maximum speed of the printer
|
||
if (warning) {
|
||
try {
|
||
auto check_motion_ability_object_setting = [&](const std::vector<std::string>& keys_to_check, double limit) -> std::string {
|
||
std::string warning_key;
|
||
for (const auto& key : keys_to_check) {
|
||
if (m_default_object_config.get_abs_value(key) > limit) {
|
||
warning_key = key;
|
||
break;
|
||
}
|
||
}
|
||
return warning_key;
|
||
};
|
||
auto check_motion_ability_region_setting = [&](const std::vector<std::string>& keys_to_check, double limit) -> std::string {
|
||
std::string warning_key;
|
||
for (const auto& key : keys_to_check) {
|
||
if (m_default_region_config.get_abs_value(key) > limit) {
|
||
warning_key = key;
|
||
break;
|
||
}
|
||
}
|
||
return warning_key;
|
||
};
|
||
std::string warning_key;
|
||
|
||
// check jerk
|
||
if (m_default_object_config.default_jerk == 1 || m_default_object_config.outer_wall_jerk == 1 ||
|
||
m_default_object_config.inner_wall_jerk == 1) {
|
||
warning->string = L("Setting the jerk speed too low could lead to artifacts on curved surfaces");
|
||
if (m_default_object_config.outer_wall_jerk == 1)
|
||
warning_key = "outer_wall_jerk";
|
||
else if (m_default_object_config.inner_wall_jerk == 1)
|
||
warning_key = "inner_wall_jerk";
|
||
else
|
||
warning_key = "default_jerk";
|
||
|
||
warning->opt_key = warning_key;
|
||
}
|
||
|
||
if (warning_key.empty() && m_default_object_config.default_jerk > 0) {
|
||
std::vector<std::string> jerk_to_check = {"default_jerk", "outer_wall_jerk", "inner_wall_jerk", "infill_jerk",
|
||
"top_surface_jerk", "initial_layer_jerk", "travel_jerk"};
|
||
const auto max_jerk = std::min(m_config.machine_max_jerk_x.values[0], m_config.machine_max_jerk_y.values[0]);
|
||
warning_key.clear();
|
||
if (m_default_object_config.default_jerk > 0)
|
||
warning_key = check_motion_ability_object_setting(jerk_to_check, max_jerk);
|
||
if (!warning_key.empty()) {
|
||
warning->string = L(
|
||
"The jerk setting exceeds the printer's maximum jerk (machine_max_jerk_x/machine_max_jerk_y).\nOrca will "
|
||
"automatically cap the jerk speed to ensure it doesn't surpass the printer's capabilities.\nYou can adjust the "
|
||
"maximum jerk setting in your printer's configuration to get higher speeds.");
|
||
warning->opt_key = warning_key;
|
||
}
|
||
}
|
||
|
||
// check acceleration
|
||
const auto max_accel = m_config.machine_max_acceleration_extruding.values[0];
|
||
if (warning_key.empty() && m_default_object_config.default_acceleration > 0 && max_accel > 0) {
|
||
const bool support_travel_acc = (m_config.gcode_flavor == gcfRepetier || m_config.gcode_flavor == gcfMarlinFirmware ||
|
||
m_config.gcode_flavor == gcfRepRapFirmware);
|
||
|
||
std::vector<std::string> accel_to_check;
|
||
if (!support_travel_acc)
|
||
accel_to_check = {
|
||
"default_acceleration",
|
||
"inner_wall_acceleration",
|
||
"outer_wall_acceleration",
|
||
"bridge_acceleration",
|
||
"initial_layer_acceleration",
|
||
"sparse_infill_acceleration",
|
||
"internal_solid_infill_acceleration",
|
||
"top_surface_acceleration",
|
||
"travel_acceleration",
|
||
};
|
||
else
|
||
accel_to_check = {
|
||
"default_acceleration",
|
||
"inner_wall_acceleration",
|
||
"outer_wall_acceleration",
|
||
"bridge_acceleration",
|
||
"initial_layer_acceleration",
|
||
"sparse_infill_acceleration",
|
||
"internal_solid_infill_acceleration",
|
||
"top_surface_acceleration",
|
||
};
|
||
warning_key = check_motion_ability_object_setting(accel_to_check, max_accel);
|
||
if (!warning_key.empty()) {
|
||
warning->string = L("The acceleration setting exceeds the printer's maximum acceleration "
|
||
"(machine_max_acceleration_extruding).\nOrca will "
|
||
"automatically cap the acceleration speed to ensure it doesn't surpass the printer's "
|
||
"capabilities.\nYou can adjust the "
|
||
"machine_max_acceleration_extruding value in your printer's configuration to get higher speeds.");
|
||
warning->opt_key = warning_key;
|
||
}
|
||
if (support_travel_acc) {
|
||
const auto max_travel = m_config.machine_max_acceleration_travel.values[0];
|
||
if (max_travel > 0) {
|
||
accel_to_check = {
|
||
"travel_acceleration",
|
||
};
|
||
warning_key = check_motion_ability_object_setting(accel_to_check, max_travel);
|
||
if (!warning_key.empty()) {
|
||
warning->string = L(
|
||
"The travel acceleration setting exceeds the printer's maximum travel acceleration "
|
||
"(machine_max_acceleration_travel).\nOrca will "
|
||
"automatically cap the travel acceleration speed to ensure it doesn't surpass the printer's "
|
||
"capabilities.\nYou can adjust the "
|
||
"machine_max_acceleration_travel value in your printer's configuration to get higher speeds.");
|
||
warning->opt_key = warning_key;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// check speed
|
||
// Orca: disable the speed check for now as we don't cap the speed
|
||
// if (warning_key.empty()) {
|
||
// auto speed_to_check = {"inner_wall_speed", "outer_wall_speed", "sparse_infill_speed", "internal_solid_infill_speed",
|
||
// "top_surface_speed", "bridge_speed", "internal_bridge_speed", "gap_infill_speed"};
|
||
// const auto max_speed = std::min(m_config.machine_max_speed_x.values[0], m_config.machine_max_speed_y.values[0]);
|
||
// warning_key.clear();
|
||
// warning_key = check_motion_ability_region_setting(speed_to_check, max_speed);
|
||
// if (warning_key.empty() && m_config.travel_speed > max_speed)
|
||
// warning_key = "travel_speed";
|
||
// if (!warning_key.empty()) {
|
||
// warning->string = L(
|
||
// "The speed setting exceeds the printer's maximum speed (machine_max_speed_x/machine_max_speed_y).\nOrca will "
|
||
// "automatically cap the print speed to ensure it doesn't surpass the printer's capabilities.\nYou can adjust the "
|
||
// "maximum speed setting in your printer's configuration to get higher speeds.");
|
||
// warning->opt_key = warning_key;
|
||
// }
|
||
// }
|
||
|
||
} catch (std::exception& e) {
|
||
BOOST_LOG_TRIVIAL(warning) << "Orca: validate motion ability failed: " << e.what() << std::endl;
|
||
}
|
||
}
|
||
return {};
|
||
}
|
||
|
||
#if 0
|
||
// the bounding box of objects placed in copies position
|
||
// (without taking skirt/brim/support material into account)
|
||
BoundingBox Print::bounding_box() const
|
||
{
|
||
BoundingBox bb;
|
||
for (const PrintObject *object : m_objects)
|
||
for (const PrintInstance &instance : object->instances()) {
|
||
BoundingBox bb2(object->bounding_box());
|
||
bb.merge(bb2.min + instance.shift);
|
||
bb.merge(bb2.max + instance.shift);
|
||
}
|
||
return bb;
|
||
}
|
||
|
||
// the total bounding box of extrusions, including skirt/brim/support material
|
||
// this methods needs to be called even when no steps were processed, so it should
|
||
// only use configuration values
|
||
BoundingBox Print::total_bounding_box() const
|
||
{
|
||
// get objects bounding box
|
||
BoundingBox bb = this->bounding_box();
|
||
|
||
// we need to offset the objects bounding box by at least half the perimeters extrusion width
|
||
Flow perimeter_flow = m_objects.front()->get_layer(0)->get_region(0)->flow(frPerimeter);
|
||
double extra = perimeter_flow.width/2;
|
||
|
||
// consider support material
|
||
if (this->has_support_material()) {
|
||
extra = std::max(extra, SUPPORT_MATERIAL_MARGIN);
|
||
}
|
||
|
||
// consider brim and skirt
|
||
if (m_config.brim_width.value > 0) {
|
||
Flow brim_flow = this->brim_flow();
|
||
extra = std::max(extra, m_config.brim_width.value + brim_flow.width/2);
|
||
}
|
||
if (this->has_skirt()) {
|
||
int skirts = m_config.skirt_loops.value;
|
||
if (skirts == 0 && this->has_infinite_skirt()) skirts = 1;
|
||
Flow skirt_flow = this->skirt_flow();
|
||
extra = std::max(
|
||
extra,
|
||
m_config.brim_width.value
|
||
+ m_config.skirt_distance.value
|
||
+ skirts * skirt_flow.spacing()
|
||
+ skirt_flow.width/2
|
||
);
|
||
}
|
||
|
||
if (extra > 0)
|
||
bb.offset(scale_(extra));
|
||
|
||
return bb;
|
||
}
|
||
#endif
|
||
|
||
double Print::skirt_first_layer_height() const
|
||
{
|
||
return m_config.initial_layer_print_height.value;
|
||
}
|
||
|
||
Flow Print::brim_flow() const
|
||
{
|
||
ConfigOptionFloatOrPercent width = m_config.initial_layer_line_width;
|
||
if (width.value <= 0)
|
||
width = m_print_regions.front()->config().inner_wall_line_width;
|
||
if (width.value <= 0)
|
||
width = m_objects.front()->config().line_width;
|
||
|
||
/* We currently use a random region's perimeter extruder.
|
||
While this works for most cases, we should probably consider all of the perimeter
|
||
extruders and take the one with, say, the smallest index.
|
||
The same logic should be applied to the code that selects the extruder during G-code
|
||
generation as well. */
|
||
return Flow::new_from_config_width(
|
||
frPerimeter,
|
||
// Flow::new_from_config_width takes care of the percent to value substitution
|
||
width,
|
||
(float)m_config.nozzle_diameter.get_at(m_print_regions.front()->config().wall_filament-1),
|
||
(float)this->skirt_first_layer_height());
|
||
}
|
||
|
||
Flow Print::skirt_flow() const
|
||
{
|
||
ConfigOptionFloatOrPercent width = m_config.initial_layer_line_width;
|
||
if (width.value <= 0)
|
||
width = m_objects.front()->config().line_width;
|
||
|
||
/* We currently use a random object's support material extruder.
|
||
While this works for most cases, we should probably consider all of the support material
|
||
extruders and take the one with, say, the smallest index;
|
||
The same logic should be applied to the code that selects the extruder during G-code
|
||
generation as well. */
|
||
return Flow::new_from_config_width(
|
||
frPerimeter,
|
||
// Flow::new_from_config_width takes care of the percent to value substitution
|
||
width,
|
||
(float)m_config.nozzle_diameter.get_at(m_objects.front()->config().support_filament-1),
|
||
(float)this->skirt_first_layer_height());
|
||
}
|
||
|
||
bool Print::has_support_material() const
|
||
{
|
||
for (const PrintObject *object : m_objects)
|
||
if (object->has_support_material())
|
||
return true;
|
||
return false;
|
||
}
|
||
|
||
/* This method assigns extruders to the volumes having a material
|
||
but not having extruders set in the volume config. */
|
||
void Print::auto_assign_extruders(ModelObject* model_object) const
|
||
{
|
||
// only assign extruders if object has more than one volume
|
||
if (model_object->volumes.size() < 2)
|
||
return;
|
||
|
||
// size_t extruders = m_config.nozzle_diameter.values.size();
|
||
for (size_t volume_id = 0; volume_id < model_object->volumes.size(); ++ volume_id) {
|
||
ModelVolume *volume = model_object->volumes[volume_id];
|
||
//FIXME Vojtech: This assigns an extruder ID even to a modifier volume, if it has a material assigned.
|
||
if ((volume->is_model_part() || volume->is_modifier()) && ! volume->material_id().empty() && ! volume->config.has("extruder"))
|
||
volume->config.set("extruder", int(volume_id + 1));
|
||
}
|
||
}
|
||
|
||
void PrintObject::set_shared_object(PrintObject *object)
|
||
{
|
||
m_shared_object = object;
|
||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": this=%1%, found shared object from %2%")%this%m_shared_object;
|
||
}
|
||
|
||
void PrintObject::clear_shared_object()
|
||
{
|
||
if (m_shared_object) {
|
||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": this=%1%, clear previous shared object data %2%")%this %m_shared_object;
|
||
m_layers.clear();
|
||
m_support_layers.clear();
|
||
|
||
m_shared_object = nullptr;
|
||
|
||
invalidate_all_steps_without_cancel();
|
||
}
|
||
}
|
||
|
||
void PrintObject::copy_layers_from_shared_object()
|
||
{
|
||
if (m_shared_object) {
|
||
m_layers.clear();
|
||
m_support_layers.clear();
|
||
|
||
firstLayerObjSliceByVolume.clear();
|
||
firstLayerObjSliceByGroups.clear();
|
||
|
||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": this=%1%, copied layers from object %2%")%this%m_shared_object;
|
||
m_layers = m_shared_object->layers();
|
||
m_support_layers = m_shared_object->support_layers();
|
||
|
||
firstLayerObjSliceByVolume = m_shared_object->firstLayerObjSlice();
|
||
firstLayerObjSliceByGroups = m_shared_object->firstLayerObjGroups();
|
||
}
|
||
}
|
||
|
||
void PrintObject::copy_layers_overhang_from_shared_object()
|
||
{
|
||
if (m_shared_object) {
|
||
for (size_t index = 0; index < m_layers.size() && index < m_shared_object->m_layers.size(); index++)
|
||
{
|
||
Layer* layer_src = m_layers[index];
|
||
layer_src->loverhangs = m_shared_object->m_layers[index]->loverhangs;
|
||
layer_src->loverhangs_bbox = m_shared_object->m_layers[index]->loverhangs_bbox;
|
||
}
|
||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": this=%1%, copied layer overhang from object %2%")%this%m_shared_object;
|
||
}
|
||
}
|
||
|
||
|
||
// BBS
|
||
BoundingBox PrintObject::get_first_layer_bbox(float& a, float& layer_height, std::string& name)
|
||
{
|
||
BoundingBox bbox;
|
||
a = 0;
|
||
name = this->model_object()->name;
|
||
if (layer_count() > 0) {
|
||
auto layer = get_layer(0);
|
||
layer_height = layer->height;
|
||
// only work for object with single instance
|
||
auto shift = instances()[0].shift_without_plate_offset();
|
||
for (auto bb : layer->lslices_bboxes)
|
||
{
|
||
bb.translate(shift.x(), shift.y());
|
||
bbox.merge(bb);
|
||
}
|
||
for (auto slice : layer->lslices) {
|
||
a += area(slice);
|
||
}
|
||
}
|
||
if (has_brim())
|
||
bbox = firstLayerObjectBrimBoundingBox;
|
||
return bbox;
|
||
}
|
||
|
||
// BBS: map print object with its first layer's first extruder
|
||
std::map<ObjectID, unsigned int> getObjectExtruderMap(const Print& print) {
|
||
std::map<ObjectID, unsigned int> objectExtruderMap;
|
||
for (const PrintObject* object : print.objects()) {
|
||
// BBS
|
||
if (object->object_first_layer_wall_extruders.empty()){
|
||
unsigned int objectFirstLayerFirstExtruder = print.config().filament_diameter.size();
|
||
auto firstLayerRegions = object->layers().front()->regions();
|
||
if (!firstLayerRegions.empty()) {
|
||
for (const LayerRegion* regionPtr : firstLayerRegions) {
|
||
if (regionPtr->has_extrusions())
|
||
objectFirstLayerFirstExtruder = std::min(objectFirstLayerFirstExtruder,
|
||
regionPtr->region().extruder(frExternalPerimeter));
|
||
}
|
||
}
|
||
objectExtruderMap.insert(std::make_pair(object->id(), objectFirstLayerFirstExtruder));
|
||
}
|
||
else {
|
||
objectExtruderMap.insert(std::make_pair(object->id(), object->object_first_layer_wall_extruders.front()));
|
||
}
|
||
}
|
||
return objectExtruderMap;
|
||
}
|
||
|
||
// Slicing process, running at a background thread.
|
||
void Print::process(long long *time_cost_with_cache, bool use_cache)
|
||
{
|
||
long long start_time = 0, end_time = 0;
|
||
if (time_cost_with_cache)
|
||
*time_cost_with_cache = 0;
|
||
|
||
name_tbb_thread_pool_threads_set_locale();
|
||
|
||
//compute the PrintObject with the same geometries
|
||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": this=%1%, enter, use_cache=%2%, object size=%3%")%this%use_cache%m_objects.size();
|
||
if (m_objects.empty())
|
||
return;
|
||
|
||
for (PrintObject *obj : m_objects)
|
||
obj->clear_shared_object();
|
||
|
||
//add the print_object share check logic
|
||
auto is_print_object_the_same = [this](const PrintObject* object1, const PrintObject* object2) -> bool{
|
||
if (object1->trafo().matrix() != object2->trafo().matrix())
|
||
return false;
|
||
const ModelObject* model_obj1 = object1->model_object();
|
||
const ModelObject* model_obj2 = object2->model_object();
|
||
if (model_obj1->volumes.size() != model_obj2->volumes.size())
|
||
return false;
|
||
bool has_extruder1 = model_obj1->config.has("extruder");
|
||
bool has_extruder2 = model_obj2->config.has("extruder");
|
||
if ((has_extruder1 != has_extruder2)
|
||
|| (has_extruder1 && model_obj1->config.extruder() != model_obj2->config.extruder()))
|
||
return false;
|
||
for (int index = 0; index < model_obj1->volumes.size(); index++) {
|
||
const ModelVolume &model_volume1 = *model_obj1->volumes[index];
|
||
const ModelVolume &model_volume2 = *model_obj2->volumes[index];
|
||
if (model_volume1.type() != model_volume2.type())
|
||
return false;
|
||
if (model_volume1.mesh_ptr() != model_volume2.mesh_ptr())
|
||
return false;
|
||
if (!(model_volume1.get_transformation() == model_volume2.get_transformation()))
|
||
return false;
|
||
has_extruder1 = model_volume1.config.has("extruder");
|
||
has_extruder2 = model_volume2.config.has("extruder");
|
||
if ((has_extruder1 != has_extruder2)
|
||
|| (has_extruder1 && model_volume1.config.extruder() != model_volume2.config.extruder()))
|
||
return false;
|
||
if (!model_volume1.supported_facets.equals(model_volume2.supported_facets))
|
||
return false;
|
||
if (!model_volume1.seam_facets.equals(model_volume2.seam_facets))
|
||
return false;
|
||
if (!model_volume1.mmu_segmentation_facets.equals(model_volume2.mmu_segmentation_facets))
|
||
return false;
|
||
if (model_volume1.config.get() != model_volume2.config.get())
|
||
return false;
|
||
}
|
||
//if (!object1->config().equals(object2->config()))
|
||
// return false;
|
||
if (model_obj1->config.get() != model_obj2->config.get())
|
||
return false;
|
||
return true;
|
||
};
|
||
int object_count = m_objects.size();
|
||
std::set<PrintObject*> need_slicing_objects;
|
||
std::set<PrintObject*> re_slicing_objects;
|
||
if (!use_cache) {
|
||
for (int index = 0; index < object_count; index++)
|
||
{
|
||
PrintObject *obj = m_objects[index];
|
||
for (PrintObject *slicing_obj : need_slicing_objects)
|
||
{
|
||
if (is_print_object_the_same(obj, slicing_obj)) {
|
||
obj->set_shared_object(slicing_obj);
|
||
break;
|
||
}
|
||
}
|
||
if (!obj->get_shared_object())
|
||
need_slicing_objects.insert(obj);
|
||
}
|
||
}
|
||
else {
|
||
for (int index = 0; index < object_count; index++)
|
||
{
|
||
PrintObject *obj = m_objects[index];
|
||
if (obj->layer_count() > 0)
|
||
need_slicing_objects.insert(obj);
|
||
}
|
||
for (int index = 0; index < object_count; index++)
|
||
{
|
||
PrintObject *obj = m_objects[index];
|
||
bool found_shared = false;
|
||
if (need_slicing_objects.find(obj) == need_slicing_objects.end()) {
|
||
for (PrintObject *slicing_obj : need_slicing_objects)
|
||
{
|
||
if (is_print_object_the_same(obj, slicing_obj)) {
|
||
obj->set_shared_object(slicing_obj);
|
||
found_shared = true;
|
||
break;
|
||
}
|
||
}
|
||
if (!found_shared) {
|
||
BOOST_LOG_TRIVIAL(warning) << boost::format("Also can not find the shared object, identify_id %1%, maybe shared object is skipped")%obj->model_object()->instances[0]->loaded_id;
|
||
//throw Slic3r::SlicingError("Can not find the cached data.");
|
||
//don't report errot, set use_cache to false, and reslice these objects
|
||
need_slicing_objects.insert(obj);
|
||
re_slicing_objects.insert(obj);
|
||
//use_cache = false;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": total object counts %1% in current print, need to slice %2%")%m_objects.size()%need_slicing_objects.size();
|
||
BOOST_LOG_TRIVIAL(info) << "Starting the slicing process." << log_memory_info();
|
||
if (!use_cache) {
|
||
for (PrintObject *obj : m_objects) {
|
||
if (need_slicing_objects.count(obj) != 0) {
|
||
obj->make_perimeters();
|
||
}
|
||
else {
|
||
if (obj->set_started(posSlice))
|
||
obj->set_done(posSlice);
|
||
if (obj->set_started(posPerimeters))
|
||
obj->set_done(posPerimeters);
|
||
}
|
||
}
|
||
for (PrintObject *obj : m_objects) {
|
||
if (need_slicing_objects.count(obj) != 0) {
|
||
obj->estimate_curled_extrusions();
|
||
}
|
||
else {
|
||
if (obj->set_started(posEstimateCurledExtrusions))
|
||
obj->set_done(posEstimateCurledExtrusions);
|
||
}
|
||
}
|
||
for (PrintObject *obj : m_objects) {
|
||
if (need_slicing_objects.count(obj) != 0) {
|
||
obj->infill();
|
||
}
|
||
else {
|
||
if (obj->set_started(posPrepareInfill))
|
||
obj->set_done(posPrepareInfill);
|
||
if (obj->set_started(posInfill))
|
||
obj->set_done(posInfill);
|
||
}
|
||
}
|
||
for (PrintObject *obj : m_objects) {
|
||
if (need_slicing_objects.count(obj) != 0) {
|
||
obj->ironing();
|
||
}
|
||
else {
|
||
if (obj->set_started(posIroning))
|
||
obj->set_done(posIroning);
|
||
}
|
||
}
|
||
|
||
tbb::parallel_for(tbb::blocked_range<int>(0, int(m_objects.size())),
|
||
[this, need_slicing_objects](const tbb::blocked_range<int>& range) {
|
||
for (int i = range.begin(); i < range.end(); i++) {
|
||
PrintObject* obj = m_objects[i];
|
||
if (need_slicing_objects.count(obj) != 0) {
|
||
obj->generate_support_material();
|
||
}
|
||
else {
|
||
if (obj->set_started(posSupportMaterial))
|
||
obj->set_done(posSupportMaterial);
|
||
}
|
||
}
|
||
}
|
||
);
|
||
|
||
for (PrintObject* obj : m_objects) {
|
||
if (need_slicing_objects.count(obj) != 0) {
|
||
obj->detect_overhangs_for_lift();
|
||
}
|
||
else {
|
||
if (obj->set_started(posDetectOverhangsForLift))
|
||
obj->set_done(posDetectOverhangsForLift);
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
for (PrintObject *obj : m_objects) {
|
||
if (re_slicing_objects.count(obj) == 0) {
|
||
if (obj->set_started(posSlice))
|
||
obj->set_done(posSlice);
|
||
if (obj->set_started(posPerimeters))
|
||
obj->set_done(posPerimeters);
|
||
if (obj->set_started(posPrepareInfill))
|
||
obj->set_done(posPrepareInfill);
|
||
if (obj->set_started(posInfill))
|
||
obj->set_done(posInfill);
|
||
if (obj->set_started(posIroning))
|
||
obj->set_done(posIroning);
|
||
if (obj->set_started(posSupportMaterial))
|
||
obj->set_done(posSupportMaterial);
|
||
if (obj->set_started(posDetectOverhangsForLift))
|
||
obj->set_done(posDetectOverhangsForLift);
|
||
}
|
||
else {
|
||
obj->make_perimeters();
|
||
obj->infill();
|
||
obj->ironing();
|
||
obj->generate_support_material();
|
||
obj->detect_overhangs_for_lift();
|
||
obj->estimate_curled_extrusions();
|
||
}
|
||
}
|
||
}
|
||
|
||
for (PrintObject *obj : m_objects)
|
||
{
|
||
if (need_slicing_objects.count(obj) == 0) {
|
||
obj->copy_layers_from_shared_object();
|
||
obj->copy_layers_overhang_from_shared_object();
|
||
}
|
||
}
|
||
|
||
if (this->set_started(psWipeTower)) {
|
||
m_wipe_tower_data.clear();
|
||
m_tool_ordering.clear();
|
||
if (this->has_wipe_tower()) {
|
||
this->_make_wipe_tower();
|
||
} else if (this->config().print_sequence != PrintSequence::ByObject) {
|
||
// Initialize the tool ordering, so it could be used by the G-code preview slider for planning tool changes and filament switches.
|
||
m_tool_ordering = ToolOrdering(*this, -1, false);
|
||
if (m_tool_ordering.empty() || m_tool_ordering.last_extruder() == unsigned(-1))
|
||
throw Slic3r::SlicingError("The print is empty. The model is not printable with current print settings.");
|
||
}
|
||
this->set_done(psWipeTower);
|
||
}
|
||
if (this->set_started(psSkirtBrim)) {
|
||
this->set_status(70, L("Generating skirt & brim"));
|
||
|
||
if (time_cost_with_cache)
|
||
start_time = (long long)Slic3r::Utils::get_current_time_utc();
|
||
|
||
m_skirt.clear();
|
||
m_skirt_convex_hull.clear();
|
||
m_first_layer_convex_hull.points.clear();
|
||
const bool draft_shield = config().draft_shield != dsDisabled;
|
||
|
||
if (this->has_skirt() && draft_shield) {
|
||
// In case that draft shield is active, generate skirt first so brim
|
||
// can be trimmed to make room for it.
|
||
_make_skirt();
|
||
}
|
||
|
||
//BBS: get the objects' indices when GCodes are generated
|
||
ToolOrdering tool_ordering;
|
||
unsigned int initial_extruder_id = (unsigned int)-1;
|
||
unsigned int final_extruder_id = (unsigned int)-1;
|
||
bool has_wipe_tower = false;
|
||
std::vector<const PrintInstance*> print_object_instances_ordering;
|
||
std::vector<const PrintInstance*>::const_iterator print_object_instance_sequential_active;
|
||
std::vector<std::pair<coordf_t, std::vector<GCode::LayerToPrint>>> layers_to_print = GCode::collect_layers_to_print(*this);
|
||
std::vector<unsigned int> printExtruders;
|
||
if (this->config().print_sequence == PrintSequence::ByObject) {
|
||
// Order object instances for sequential print.
|
||
print_object_instances_ordering = sort_object_instances_by_model_order(*this);
|
||
// print_object_instances_ordering = sort_object_instances_by_max_z(print);
|
||
print_object_instance_sequential_active = print_object_instances_ordering.begin();
|
||
for (; print_object_instance_sequential_active != print_object_instances_ordering.end(); ++print_object_instance_sequential_active) {
|
||
tool_ordering = ToolOrdering(*(*print_object_instance_sequential_active)->print_object, initial_extruder_id);
|
||
if ((initial_extruder_id = tool_ordering.first_extruder()) != static_cast<unsigned int>(-1)) {
|
||
append(printExtruders, tool_ordering.tools_for_layer(layers_to_print.front().first).extruders);
|
||
}
|
||
}
|
||
}
|
||
else {
|
||
tool_ordering = this->tool_ordering();
|
||
tool_ordering.assign_custom_gcodes(*this);
|
||
has_wipe_tower = this->has_wipe_tower() && tool_ordering.has_wipe_tower();
|
||
//BBS: have no single_extruder_multi_material_priming
|
||
#if 0
|
||
initial_extruder_id = (has_wipe_tower && !this->config().single_extruder_multi_material_priming) ?
|
||
// The priming towers will be skipped.
|
||
tool_ordering.all_extruders().back() :
|
||
// Don't skip the priming towers.
|
||
tool_ordering.first_extruder();
|
||
#endif
|
||
initial_extruder_id = tool_ordering.first_extruder();
|
||
print_object_instances_ordering = chain_print_object_instances(*this);
|
||
append(printExtruders, tool_ordering.tools_for_layer(layers_to_print.front().first).extruders);
|
||
}
|
||
|
||
auto objectExtruderMap = getObjectExtruderMap(*this);
|
||
std::vector<std::pair<ObjectID, unsigned int>> objPrintVec;
|
||
for (const PrintInstance* instance : print_object_instances_ordering) {
|
||
const ObjectID& print_object_ID = instance->print_object->id();
|
||
bool existObject = false;
|
||
for (auto& objIDPair : objPrintVec) {
|
||
if (print_object_ID == objIDPair.first) existObject = true;
|
||
}
|
||
if (!existObject && objectExtruderMap.find(print_object_ID) != objectExtruderMap.end())
|
||
objPrintVec.push_back(std::make_pair(print_object_ID, objectExtruderMap.at(print_object_ID)));
|
||
}
|
||
// BBS: m_brimMap and m_supportBrimMap are used instead of m_brim to generate brim of objs and supports seperately
|
||
m_brimMap.clear();
|
||
m_supportBrimMap.clear();
|
||
m_first_layer_convex_hull.points.clear();
|
||
if (this->has_brim()) {
|
||
Polygons islands_area;
|
||
make_brim(*this, this->make_try_cancel(), islands_area, m_brimMap,
|
||
m_supportBrimMap, objPrintVec, printExtruders);
|
||
for (Polygon& poly_ex : islands_area)
|
||
poly_ex.douglas_peucker(SCALED_RESOLUTION);
|
||
for (Polygon &poly : union_(this->first_layer_islands(), islands_area))
|
||
append(m_first_layer_convex_hull.points, std::move(poly.points));
|
||
}
|
||
|
||
|
||
if (has_skirt() && ! draft_shield) {
|
||
// In case that draft shield is NOT active, generate skirt now.
|
||
// It will be placed around the brim, so brim has to be ready.
|
||
assert(m_skirt.empty());
|
||
_make_skirt();
|
||
}
|
||
|
||
this->finalize_first_layer_convex_hull();
|
||
this->set_done(psSkirtBrim);
|
||
|
||
if (time_cost_with_cache) {
|
||
end_time = (long long)Slic3r::Utils::get_current_time_utc();
|
||
*time_cost_with_cache = *time_cost_with_cache + end_time - start_time;
|
||
}
|
||
}
|
||
//BBS
|
||
for (PrintObject *obj : m_objects) {
|
||
if (((!use_cache)&&(need_slicing_objects.count(obj) != 0))
|
||
|| (use_cache &&(re_slicing_objects.count(obj) != 0))){
|
||
obj->simplify_extrusion_path();
|
||
}
|
||
else {
|
||
if (obj->set_started(posSimplifyPath))
|
||
obj->set_done(posSimplifyPath);
|
||
if (obj->set_started(posSimplifyInfill))
|
||
obj->set_done(posSimplifyInfill);
|
||
if (obj->set_started(posSimplifySupportPath))
|
||
obj->set_done(posSimplifySupportPath);
|
||
}
|
||
}
|
||
|
||
// BBS
|
||
bool has_adaptive_layer_height = false;
|
||
for (PrintObject* obj : m_objects) {
|
||
if (obj->model_object()->layer_height_profile.empty() == false) {
|
||
has_adaptive_layer_height = true;
|
||
break;
|
||
}
|
||
}
|
||
// TODO adaptive layer height won't work with conflict checker because m_fake_wipe_tower's path is generated using fixed layer height
|
||
if(!m_no_check && !has_adaptive_layer_height)
|
||
{
|
||
using Clock = std::chrono::high_resolution_clock;
|
||
auto startTime = Clock::now();
|
||
std::optional<const FakeWipeTower *> wipe_tower_opt = {};
|
||
if (this->has_wipe_tower()) {
|
||
m_fake_wipe_tower.set_pos({m_config.wipe_tower_x.get_at(m_plate_index), m_config.wipe_tower_y.get_at(m_plate_index)});
|
||
wipe_tower_opt = std::make_optional<const FakeWipeTower *>(&m_fake_wipe_tower);
|
||
}
|
||
auto conflictRes = ConflictChecker::find_inter_of_lines_in_diff_objs(m_objects, wipe_tower_opt);
|
||
auto endTime = Clock::now();
|
||
volatile double seconds = std::chrono::duration_cast<std::chrono::milliseconds>(endTime - startTime).count() / (double) 1000;
|
||
BOOST_LOG_TRIVIAL(info) << "gcode path conflicts check takes " << seconds << " secs.";
|
||
|
||
m_conflict_result = conflictRes;
|
||
if (conflictRes.has_value()) {
|
||
BOOST_LOG_TRIVIAL(error) << boost::format("gcode path conflicts found between %1% and %2%")%conflictRes.value()._objName1 %conflictRes.value()._objName2;
|
||
}
|
||
}
|
||
|
||
BOOST_LOG_TRIVIAL(info) << "Slicing process finished." << log_memory_info();
|
||
}
|
||
|
||
// G-code export process, running at a background thread.
|
||
// The export_gcode may die for various reasons (fails to process filename_format,
|
||
// write error into the G-code, cannot execute post-processing scripts).
|
||
// It is up to the caller to show an error message.
|
||
std::string Print::export_gcode(const std::string& path_template, GCodeProcessorResult* result, ThumbnailsGeneratorCallback thumbnail_cb)
|
||
{
|
||
// output everything to a G-code file
|
||
// The following call may die if the filename_format template substitution fails.
|
||
std::string path = this->output_filepath(path_template);
|
||
std::string message;
|
||
if (!path.empty() && result == nullptr) {
|
||
// Only show the path if preview_data is not set -> running from command line.
|
||
message = L("Exporting G-code");
|
||
message += " to ";
|
||
message += path;
|
||
} else
|
||
message = L("Generating G-code");
|
||
this->set_status(80, message);
|
||
|
||
// The following line may die for multiple reasons.
|
||
GCode gcode;
|
||
//BBS: compute plate offset for gcode-generator
|
||
const Vec3d origin = this->get_plate_origin();
|
||
gcode.set_gcode_offset(origin(0), origin(1));
|
||
gcode.do_export(this, path.c_str(), result, thumbnail_cb);
|
||
|
||
//BBS
|
||
result->conflict_result = m_conflict_result;
|
||
return path.c_str();
|
||
}
|
||
|
||
void Print::_make_skirt()
|
||
{
|
||
// First off we need to decide how tall the skirt must be.
|
||
// The skirt_height option from config is expressed in layers, but our
|
||
// object might have different layer heights, so we need to find the print_z
|
||
// of the highest layer involved.
|
||
// Note that unless has_infinite_skirt() == true
|
||
// the actual skirt might not reach this $skirt_height_z value since the print
|
||
// order of objects on each layer is not guaranteed and will not generally
|
||
// include the thickest object first. It is just guaranteed that a skirt is
|
||
// prepended to the first 'n' layers (with 'n' = skirt_height).
|
||
// $skirt_height_z in this case is the highest possible skirt height for safety.
|
||
coordf_t skirt_height_z = 0.;
|
||
for (const PrintObject *object : m_objects) {
|
||
size_t skirt_layers = this->has_infinite_skirt() ?
|
||
object->layer_count() :
|
||
std::min(size_t(m_config.skirt_height.value), object->layer_count());
|
||
skirt_height_z = std::max(skirt_height_z, object->m_layers[skirt_layers-1]->print_z);
|
||
}
|
||
|
||
// Collect points from all layers contained in skirt height.
|
||
Points points;
|
||
|
||
// BBS
|
||
std::map<PrintObject*, Polygon> object_convex_hulls;
|
||
for (PrintObject *object : m_objects) {
|
||
Points object_points;
|
||
// Get object layers up to skirt_height_z.
|
||
for (const Layer *layer : object->m_layers) {
|
||
if (layer->print_z > skirt_height_z)
|
||
break;
|
||
for (const ExPolygon &expoly : layer->lslices)
|
||
// Collect the outer contour points only, ignore holes for the calculation of the convex hull.
|
||
append(object_points, expoly.contour.points);
|
||
}
|
||
// Get support layers up to skirt_height_z.
|
||
for (const SupportLayer *layer : object->support_layers()) {
|
||
if (layer->print_z > skirt_height_z)
|
||
break;
|
||
layer->support_fills.collect_points(object_points);
|
||
}
|
||
|
||
object_convex_hulls.insert({ object, Slic3r::Geometry::convex_hull(object_points) });
|
||
|
||
// Repeat points for each object copy.
|
||
for (const PrintInstance &instance : object->instances()) {
|
||
Points copy_points = object_points;
|
||
for (Point &pt : copy_points)
|
||
pt += instance.shift;
|
||
append(points, copy_points);
|
||
}
|
||
}
|
||
|
||
// Include the wipe tower.
|
||
append(points, this->first_layer_wipe_tower_corners());
|
||
|
||
// Unless draft shield is enabled, include all brims as well.
|
||
if (config().draft_shield == dsDisabled)
|
||
append(points, m_first_layer_convex_hull.points);
|
||
|
||
if (points.size() < 3)
|
||
// At least three points required for a convex hull.
|
||
return;
|
||
|
||
this->throw_if_canceled();
|
||
Polygon convex_hull = Slic3r::Geometry::convex_hull(points);
|
||
|
||
// Skirt may be printed on several layers, having distinct layer heights,
|
||
// but loops must be aligned so can't vary width/spacing
|
||
// TODO: use each extruder's own flow
|
||
double initial_layer_print_height = this->skirt_first_layer_height();
|
||
Flow flow = this->skirt_flow();
|
||
float spacing = flow.spacing();
|
||
double mm3_per_mm = flow.mm3_per_mm();
|
||
|
||
std::vector<size_t> extruders;
|
||
std::vector<double> extruders_e_per_mm;
|
||
{
|
||
auto set_extruders = this->extruders();
|
||
extruders.reserve(set_extruders.size());
|
||
extruders_e_per_mm.reserve(set_extruders.size());
|
||
for (auto &extruder_id : set_extruders) {
|
||
extruders.push_back(extruder_id);
|
||
extruders_e_per_mm.push_back(Extruder((unsigned int)extruder_id, &m_config, m_config.single_extruder_multi_material).e_per_mm(mm3_per_mm));
|
||
}
|
||
}
|
||
|
||
// Number of skirt loops per skirt layer.
|
||
size_t n_skirts = m_config.skirt_loops.value;
|
||
if (this->has_infinite_skirt() && n_skirts == 0)
|
||
n_skirts = 1;
|
||
|
||
// Initial offset of the brim inner edge from the object (possible with a support & raft).
|
||
// The skirt will touch the brim if the brim is extruded.
|
||
auto distance = float(scale_(m_config.skirt_distance.value) - spacing/2.);
|
||
// Draw outlines from outside to inside.
|
||
// Loop while we have less skirts than required or any extruder hasn't reached the min length if any.
|
||
std::vector<coordf_t> extruded_length(extruders.size(), 0.);
|
||
for (size_t i = n_skirts, extruder_idx = 0; i > 0; -- i) {
|
||
this->throw_if_canceled();
|
||
// Offset the skirt outside.
|
||
distance += float(scale_(spacing));
|
||
// Generate the skirt centerline.
|
||
Polygon loop;
|
||
{
|
||
// BBS. skirt_distance is defined as the gap between skirt and outer most brim, so no need to add max_brim_width
|
||
Polygons loops = offset(convex_hull, distance, ClipperLib::jtRound, float(scale_(0.1)));
|
||
Geometry::simplify_polygons(loops, scale_(0.05), &loops);
|
||
if (loops.empty())
|
||
break;
|
||
loop = loops.front();
|
||
}
|
||
// Extrude the skirt loop.
|
||
ExtrusionLoop eloop(elrSkirt);
|
||
eloop.paths.emplace_back(ExtrusionPath(
|
||
ExtrusionPath(
|
||
erSkirt,
|
||
(float)mm3_per_mm, // this will be overridden at G-code export time
|
||
flow.width(),
|
||
(float)initial_layer_print_height // this will be overridden at G-code export time
|
||
)));
|
||
eloop.paths.back().polyline = loop.split_at_first_point();
|
||
m_skirt.append(eloop);
|
||
if (Print::min_skirt_length > 0) {
|
||
// The skirt length is limited. Sum the total amount of filament length extruded, in mm.
|
||
extruded_length[extruder_idx] += unscale<double>(loop.length()) * extruders_e_per_mm[extruder_idx];
|
||
if (extruded_length[extruder_idx] < Print::min_skirt_length) {
|
||
// Not extruded enough yet with the current extruder. Add another loop.
|
||
if (i == 1)
|
||
++ i;
|
||
} else {
|
||
assert(extruded_length[extruder_idx] >= Print::min_skirt_length);
|
||
// Enough extruded with the current extruder. Extrude with the next one,
|
||
// until the prescribed number of skirt loops is extruded.
|
||
if (extruder_idx + 1 < extruders.size())
|
||
++ extruder_idx;
|
||
}
|
||
} else {
|
||
// The skirt lenght is not limited, extrude the skirt with the 1st extruder only.
|
||
}
|
||
}
|
||
// Brims were generated inside out, reverse to print the outmost contour first.
|
||
m_skirt.reverse();
|
||
|
||
// Remember the outer edge of the last skirt line extruded as m_skirt_convex_hull.
|
||
for (Polygon &poly : offset(convex_hull, distance + 0.5f * float(scale_(spacing)), ClipperLib::jtRound, float(scale_(0.1))))
|
||
append(m_skirt_convex_hull, std::move(poly.points));
|
||
|
||
// BBS
|
||
const int n_object_skirts = 1;
|
||
const double object_skirt_distance = scale_(1.0);
|
||
for (auto obj_cvx_hull : object_convex_hulls) {
|
||
PrintObject* object = obj_cvx_hull.first;
|
||
for (int i = 0; i < n_object_skirts; i++) {
|
||
distance += float(scale_(spacing));
|
||
Polygon loop;
|
||
{
|
||
// BBS. skirt_distance is defined as the gap between skirt and outer most brim, so no need to add max_brim_width
|
||
Polygons loops = offset(obj_cvx_hull.second, object_skirt_distance, ClipperLib::jtRound, float(scale_(0.1)));
|
||
Geometry::simplify_polygons(loops, scale_(0.05), &loops);
|
||
if (loops.empty())
|
||
break;
|
||
loop = loops.front();
|
||
}
|
||
|
||
// Extrude the skirt loop.
|
||
ExtrusionLoop eloop(elrSkirt);
|
||
eloop.paths.emplace_back(ExtrusionPath(
|
||
ExtrusionPath(
|
||
erSkirt,
|
||
(float)mm3_per_mm, // this will be overridden at G-code export time
|
||
flow.width(),
|
||
(float)initial_layer_print_height // this will be overridden at G-code export time
|
||
)));
|
||
eloop.paths.back().polyline = loop.split_at_first_point();
|
||
object->m_skirt.append(std::move(eloop));
|
||
}
|
||
}
|
||
}
|
||
|
||
Polygons Print::first_layer_islands() const
|
||
{
|
||
Polygons islands;
|
||
for (PrintObject *object : m_objects) {
|
||
Polygons object_islands;
|
||
for (ExPolygon &expoly : object->m_layers.front()->lslices)
|
||
object_islands.push_back(expoly.contour);
|
||
if (!object->support_layers().empty()) {
|
||
if (object->support_layers().front()->support_type==stInnerNormal)
|
||
object->support_layers().front()->support_fills.polygons_covered_by_spacing(object_islands, float(SCALED_EPSILON));
|
||
else if(object->support_layers().front()->support_type==stInnerTree) {
|
||
ExPolygons &expolys_first_layer = object->m_support_layers.front()->lslices;
|
||
for (ExPolygon &expoly : expolys_first_layer) { object_islands.push_back(expoly.contour); }
|
||
}
|
||
}
|
||
islands.reserve(islands.size() + object_islands.size() * object->instances().size());
|
||
for (const PrintInstance &instance : object->instances())
|
||
for (Polygon &poly : object_islands) {
|
||
islands.push_back(poly);
|
||
islands.back().translate(instance.shift);
|
||
}
|
||
}
|
||
return islands;
|
||
}
|
||
|
||
std::vector<Point> Print::first_layer_wipe_tower_corners(bool check_wipe_tower_existance) const
|
||
{
|
||
std::vector<Point> corners;
|
||
if (check_wipe_tower_existance && (!has_wipe_tower() || m_wipe_tower_data.tool_changes.empty()))
|
||
return corners;
|
||
{
|
||
double width = m_config.prime_tower_width + 2*m_wipe_tower_data.brim_width;
|
||
double depth = m_wipe_tower_data.depth + 2*m_wipe_tower_data.brim_width;
|
||
Vec2d pt0(-m_wipe_tower_data.brim_width, -m_wipe_tower_data.brim_width);
|
||
for (Vec2d pt : {
|
||
pt0,
|
||
Vec2d(pt0.x()+width, pt0.y() ),
|
||
Vec2d(pt0.x()+width, pt0.y()+depth),
|
||
Vec2d(pt0.x(), pt0.y()+depth)
|
||
}) {
|
||
pt = Eigen::Rotation2Dd(Geometry::deg2rad(m_config.wipe_tower_rotation_angle.value)) * pt;
|
||
// BBS: add partplate logic
|
||
pt += Vec2d(m_config.wipe_tower_x.get_at(m_plate_index) + m_origin(0), m_config.wipe_tower_y.get_at(m_plate_index) + m_origin(1));
|
||
corners.emplace_back(Point(scale_(pt.x()), scale_(pt.y())));
|
||
}
|
||
}
|
||
return corners;
|
||
}
|
||
|
||
//SoftFever
|
||
Vec2d Print::translate_to_print_space(const Vec2d &point) const {
|
||
//const BoundingBoxf bed_bbox(config().printable_area.values);
|
||
return Vec2d(point(0) - m_origin(0), point(1) - m_origin(1));
|
||
}
|
||
|
||
Vec2d Print::translate_to_print_space(const Point &point) const {
|
||
return Vec2d(unscaled(point.x()) - m_origin(0), unscaled(point.y()) - m_origin(1));
|
||
}
|
||
|
||
FilamentTempType Print::get_filament_temp_type(const std::string& filament_type)
|
||
{
|
||
const static std::string HighTempFilamentStr = "high_temp_filament";
|
||
const static std::string LowTempFilamentStr = "low_temp_filament";
|
||
const static std::string HighLowCompatibleFilamentStr = "high_low_compatible_filament";
|
||
static std::unordered_map<std::string, std::unordered_set<std::string>>filament_temp_type_map;
|
||
|
||
if (filament_temp_type_map.empty()) {
|
||
fs::path file_path = fs::path(resources_dir()) / "info" / "filament_info.json";
|
||
std::ifstream in(file_path.string());
|
||
json j;
|
||
try{
|
||
j = json::parse(in);
|
||
in.close();
|
||
auto&&high_temp_filament_arr =j[HighTempFilamentStr].get < std::vector<std::string>>();
|
||
filament_temp_type_map[HighTempFilamentStr] = std::unordered_set<std::string>(high_temp_filament_arr.begin(), high_temp_filament_arr.end());
|
||
auto&& low_temp_filament_arr = j[LowTempFilamentStr].get < std::vector<std::string>>();
|
||
filament_temp_type_map[LowTempFilamentStr] = std::unordered_set<std::string>(low_temp_filament_arr.begin(), low_temp_filament_arr.end());
|
||
auto&& high_low_compatible_filament_arr = j[HighLowCompatibleFilamentStr].get < std::vector<std::string>>();
|
||
filament_temp_type_map[HighLowCompatibleFilamentStr] = std::unordered_set<std::string>(high_low_compatible_filament_arr.begin(), high_low_compatible_filament_arr.end());
|
||
}
|
||
catch (const json::parse_error& err){
|
||
in.close();
|
||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": parse " << file_path.string() << " got a nlohmann::detail::parse_error, reason = " << err.what();
|
||
filament_temp_type_map[HighTempFilamentStr] = {"ABS","ASA","PC","PA","PA-CF","PA6-CF","PET-CF","PPS","PPS-CF","PPA-GF","PPA-CF"};
|
||
filament_temp_type_map[LowTempFilamentStr] = {"PLA","TPU","PLA-CF","PLA-AERO","PVA"};
|
||
filament_temp_type_map[HighLowCompatibleFilamentStr] = { "HIPS","PETG" };
|
||
}
|
||
}
|
||
|
||
if (filament_temp_type_map[HighLowCompatibleFilamentStr].find(filament_type) != filament_temp_type_map[HighLowCompatibleFilamentStr].end())
|
||
return HighLowCompatible;
|
||
if (filament_temp_type_map[HighTempFilamentStr].find(filament_type) != filament_temp_type_map[HighTempFilamentStr].end())
|
||
return HighTemp;
|
||
if (filament_temp_type_map[LowTempFilamentStr].find(filament_type) != filament_temp_type_map[LowTempFilamentStr].end())
|
||
return LowTemp;
|
||
return Undefine;
|
||
}
|
||
|
||
int Print::get_hrc_by_nozzle_type(const NozzleType&type)
|
||
{
|
||
static std::map<std::string, int>nozzle_type_to_hrc;
|
||
if (nozzle_type_to_hrc.empty()) {
|
||
fs::path file_path = fs::path(resources_dir()) / "info" / "nozzle_info.json";
|
||
boost::nowide::ifstream in(file_path.string());
|
||
//std::ifstream in(file_path.string());
|
||
json j;
|
||
try {
|
||
j = json::parse(in);
|
||
in.close();
|
||
for (const auto& elem : j["nozzle_hrc"].items())
|
||
nozzle_type_to_hrc[elem.key()] = elem.value();
|
||
}
|
||
catch (const json::parse_error& err) {
|
||
in.close();
|
||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": parse " << file_path.string() << " got a nlohmann::detail::parse_error, reason = " << err.what();
|
||
nozzle_type_to_hrc = {
|
||
{"hardened_steel",55},
|
||
{"stainless_steel",20},
|
||
{"brass",2},
|
||
{"undefine",0}
|
||
};
|
||
}
|
||
}
|
||
auto iter = nozzle_type_to_hrc.find(NozzleTypeEumnToStr[type]);
|
||
if (iter != nozzle_type_to_hrc.end())
|
||
return iter->second;
|
||
//0 represents undefine
|
||
return 0;
|
||
}
|
||
|
||
void Print::finalize_first_layer_convex_hull()
|
||
{
|
||
append(m_first_layer_convex_hull.points, m_skirt_convex_hull);
|
||
if (m_first_layer_convex_hull.empty()) {
|
||
// Neither skirt nor brim was extruded. Collect points of printed objects from 1st layer.
|
||
for (Polygon &poly : this->first_layer_islands())
|
||
append(m_first_layer_convex_hull.points, std::move(poly.points));
|
||
}
|
||
append(m_first_layer_convex_hull.points, this->first_layer_wipe_tower_corners());
|
||
m_first_layer_convex_hull = Geometry::convex_hull(m_first_layer_convex_hull.points);
|
||
}
|
||
|
||
// Wipe tower support.
|
||
bool Print::has_wipe_tower() const
|
||
{
|
||
if (m_config.enable_prime_tower.value == true) {
|
||
if (enable_timelapse_print())
|
||
return true;
|
||
|
||
return !m_config.spiral_mode.value && m_config.filament_diameter.values.size() > 1;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
const WipeTowerData &Print::wipe_tower_data(size_t filaments_cnt) const
|
||
{
|
||
// If the wipe tower wasn't created yet, make sure the depth and brim_width members are set to default.
|
||
if (!is_step_done(psWipeTower) && filaments_cnt != 0) {
|
||
double width = m_config.prime_tower_width;
|
||
double layer_height = 0.2; // hard code layer height
|
||
if (m_config.purge_in_prime_tower) {
|
||
// Calculating depth should take into account currently set wiping volumes.
|
||
// For a long time, the initial preview would just use 900/width per toolchange (15mm on a 60mm wide tower)
|
||
// and it worked well enough. Let's try to do slightly better by accounting for the purging volumes.
|
||
std::vector<std::vector<float>> wipe_volumes = WipeTower2::extract_wipe_volumes(m_config);
|
||
std::vector<float> max_wipe_volumes;
|
||
for (const std::vector<float> &v : wipe_volumes)
|
||
max_wipe_volumes.emplace_back(*std::max_element(v.begin(), v.end()));
|
||
float maximum = std::accumulate(max_wipe_volumes.begin(), max_wipe_volumes.end(), 0.f);
|
||
maximum = maximum * filaments_cnt / max_wipe_volumes.size();
|
||
|
||
// Orca: it's overshooting a bit, so let's reduce it a bit
|
||
maximum *= 0.6;
|
||
const_cast<Print *>(this)->m_wipe_tower_data.depth = maximum / (layer_height * width);
|
||
} else {
|
||
double wipe_volume = m_config.prime_volume;
|
||
if (filaments_cnt == 1 && enable_timelapse_print()) {
|
||
const_cast<Print *>(this)->m_wipe_tower_data.depth = wipe_volume / (layer_height * width);
|
||
} else {
|
||
const_cast<Print *>(this)->m_wipe_tower_data.depth = wipe_volume * (filaments_cnt - 1) / (layer_height * width);
|
||
}
|
||
}
|
||
const_cast<Print *>(this)->m_wipe_tower_data.brim_width = m_config.prime_tower_brim_width;
|
||
}
|
||
|
||
return m_wipe_tower_data;
|
||
}
|
||
|
||
bool Print::enable_timelapse_print() const
|
||
{
|
||
return m_config.timelapse_type.value == TimelapseType::tlSmooth;
|
||
}
|
||
|
||
void Print::_make_wipe_tower()
|
||
{
|
||
m_wipe_tower_data.clear();
|
||
|
||
// Get wiping matrix to get number of extruders and convert vector<double> to vector<float>:
|
||
std::vector<float> flush_matrix(cast<float>(m_config.flush_volumes_matrix.values));
|
||
|
||
// BBS
|
||
const unsigned int number_of_extruders = (unsigned int)(sqrt(flush_matrix.size()) + EPSILON);
|
||
// Extract purging volumes for each extruder pair:
|
||
std::vector<std::vector<float>> wipe_volumes;
|
||
for (unsigned int i = 0; i<number_of_extruders; ++i)
|
||
wipe_volumes.push_back(std::vector<float>(flush_matrix.begin()+i*number_of_extruders, flush_matrix.begin()+(i+1)*number_of_extruders));
|
||
|
||
// Let the ToolOrdering class know there will be initial priming extrusions at the start of the print.
|
||
// BBS: priming logic is removed, so don't consider it in tool ordering
|
||
m_wipe_tower_data.tool_ordering = ToolOrdering(*this, (unsigned int)-1, false);
|
||
|
||
if (!m_wipe_tower_data.tool_ordering.has_wipe_tower())
|
||
// Don't generate any wipe tower.
|
||
return;
|
||
|
||
// Check whether there are any layers in m_tool_ordering, which are marked with has_wipe_tower,
|
||
// they print neither object, nor support. These layers are above the raft and below the object, and they
|
||
// shall be added to the support layers to be printed.
|
||
// see https://github.com/prusa3d/PrusaSlicer/issues/607
|
||
{
|
||
size_t idx_begin = size_t(-1);
|
||
size_t idx_end = m_wipe_tower_data.tool_ordering.layer_tools().size();
|
||
// Find the first wipe tower layer, which does not have a counterpart in an object or a support layer.
|
||
for (size_t i = 0; i < idx_end; ++ i) {
|
||
const LayerTools < = m_wipe_tower_data.tool_ordering.layer_tools()[i];
|
||
if (lt.has_wipe_tower && ! lt.has_object && ! lt.has_support) {
|
||
idx_begin = i;
|
||
break;
|
||
}
|
||
}
|
||
if (idx_begin != size_t(-1)) {
|
||
// Find the position in m_objects.first()->support_layers to insert these new support layers.
|
||
double wipe_tower_new_layer_print_z_first = m_wipe_tower_data.tool_ordering.layer_tools()[idx_begin].print_z;
|
||
auto it_layer = m_objects.front()->support_layers().begin();
|
||
auto it_end = m_objects.front()->support_layers().end();
|
||
for (; it_layer != it_end && (*it_layer)->print_z - EPSILON < wipe_tower_new_layer_print_z_first; ++ it_layer);
|
||
// Find the stopper of the sequence of wipe tower layers, which do not have a counterpart in an object or a support layer.
|
||
for (size_t i = idx_begin; i < idx_end; ++ i) {
|
||
LayerTools < = const_cast<LayerTools&>(m_wipe_tower_data.tool_ordering.layer_tools()[i]);
|
||
if (! (lt.has_wipe_tower && ! lt.has_object && ! lt.has_support))
|
||
break;
|
||
lt.has_support = true;
|
||
// Insert the new support layer.
|
||
double height = lt.print_z - (i == 0 ? 0. : m_wipe_tower_data.tool_ordering.layer_tools()[i-1].print_z);
|
||
//FIXME the support layer ID is set to -1, as Vojtech hopes it is not being used anyway.
|
||
it_layer = m_objects.front()->insert_support_layer(it_layer, -1, 0, height, lt.print_z, lt.print_z - 0.5 * height);
|
||
++ it_layer;
|
||
}
|
||
}
|
||
}
|
||
this->throw_if_canceled();
|
||
|
||
if (is_BBL_printer()) {
|
||
// in BBL machine, wipe tower is only use to prime extruder. So just use a global wipe volume.
|
||
WipeTower wipe_tower(m_config, m_plate_index, m_origin, m_config.prime_volume, m_wipe_tower_data.tool_ordering.first_extruder(),
|
||
m_wipe_tower_data.tool_ordering.empty() ? 0.f : m_wipe_tower_data.tool_ordering.back().print_z);
|
||
|
||
// wipe_tower.set_retract();
|
||
// wipe_tower.set_zhop();
|
||
|
||
// Set the extruder & material properties at the wipe tower object.
|
||
for (size_t i = 0; i < number_of_extruders; ++i)
|
||
wipe_tower.set_extruder(i, m_config);
|
||
|
||
// BBS: remove priming logic
|
||
// m_wipe_tower_data.priming = Slic3r::make_unique<std::vector<WipeTower::ToolChangeResult>>(
|
||
// wipe_tower.prime((float)this->skirt_first_layer_height(), m_wipe_tower_data.tool_ordering.all_extruders(), false));
|
||
|
||
// Lets go through the wipe tower layers and determine pairs of extruder changes for each
|
||
// to pass to wipe_tower (so that it can use it for planning the layout of the tower)
|
||
{
|
||
// BBS: priming logic is removed, so get the initial extruder by first_extruder()
|
||
unsigned int current_extruder_id = m_wipe_tower_data.tool_ordering.first_extruder();
|
||
for (auto &layer_tools : m_wipe_tower_data.tool_ordering.layer_tools()) { // for all layers
|
||
if (!layer_tools.has_wipe_tower)
|
||
continue;
|
||
bool first_layer = &layer_tools == &m_wipe_tower_data.tool_ordering.front();
|
||
wipe_tower.plan_toolchange((float) layer_tools.print_z, (float) layer_tools.wipe_tower_layer_height, current_extruder_id,
|
||
current_extruder_id);
|
||
|
||
for (const auto extruder_id : layer_tools.extruders) {
|
||
// BBS: priming logic is removed, so no need to do toolchange for first extruder
|
||
if (/*(first_layer && extruder_id == m_wipe_tower_data.tool_ordering.all_extruders().back()) || */ extruder_id !=
|
||
current_extruder_id) {
|
||
float volume_to_purge = wipe_volumes[current_extruder_id][extruder_id];
|
||
volume_to_purge *= m_config.flush_multiplier;
|
||
|
||
// Not all of that can be used for infill purging:
|
||
// volume_to_purge -= (float)m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
|
||
|
||
// try to assign some infills/objects for the wiping:
|
||
volume_to_purge = layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, current_extruder_id, extruder_id,
|
||
volume_to_purge);
|
||
|
||
// add back the minimal amount toforce on the wipe tower:
|
||
// volume_to_purge += (float)m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
|
||
|
||
// request a toolchange at the wipe tower with at least volume_to_wipe purging amount
|
||
wipe_tower.plan_toolchange((float) layer_tools.print_z, (float) layer_tools.wipe_tower_layer_height,
|
||
current_extruder_id, extruder_id, m_config.prime_volume, volume_to_purge);
|
||
current_extruder_id = extruder_id;
|
||
}
|
||
}
|
||
layer_tools.wiping_extrusions().ensure_perimeters_infills_order(*this);
|
||
|
||
// if enable timelapse, slice all layer
|
||
if (enable_timelapse_print()) {
|
||
if (layer_tools.wipe_tower_partitions == 0)
|
||
wipe_tower.set_last_layer_extruder_fill(false);
|
||
continue;
|
||
}
|
||
|
||
if (&layer_tools == &m_wipe_tower_data.tool_ordering.back() || (&layer_tools + 1)->wipe_tower_partitions == 0)
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Generate the wipe tower layers.
|
||
m_wipe_tower_data.tool_changes.reserve(m_wipe_tower_data.tool_ordering.layer_tools().size());
|
||
wipe_tower.generate(m_wipe_tower_data.tool_changes);
|
||
m_wipe_tower_data.depth = wipe_tower.get_depth();
|
||
m_wipe_tower_data.brim_width = wipe_tower.get_brim_width();
|
||
|
||
// Unload the current filament over the purge tower.
|
||
coordf_t layer_height = m_objects.front()->config().layer_height.value;
|
||
if (m_wipe_tower_data.tool_ordering.back().wipe_tower_partitions > 0) {
|
||
// The wipe tower goes up to the last layer of the print.
|
||
if (wipe_tower.layer_finished()) {
|
||
// The wipe tower is printed to the top of the print and it has no space left for the final extruder purge.
|
||
// Lift Z to the next layer.
|
||
wipe_tower.set_layer(float(m_wipe_tower_data.tool_ordering.back().print_z + layer_height), float(layer_height), 0, false,
|
||
true);
|
||
} else {
|
||
// There is yet enough space at this layer of the wipe tower for the final purge.
|
||
}
|
||
} else {
|
||
// The wipe tower does not reach the last print layer, perform the pruge at the last print layer.
|
||
assert(m_wipe_tower_data.tool_ordering.back().wipe_tower_partitions == 0);
|
||
wipe_tower.set_layer(float(m_wipe_tower_data.tool_ordering.back().print_z), float(layer_height), 0, false, true);
|
||
}
|
||
m_wipe_tower_data.final_purge = Slic3r::make_unique<WipeTower::ToolChangeResult>(wipe_tower.tool_change((unsigned int) (-1)));
|
||
|
||
m_wipe_tower_data.used_filament = wipe_tower.get_used_filament();
|
||
m_wipe_tower_data.number_of_toolchanges = wipe_tower.get_number_of_toolchanges();
|
||
const Vec3d origin = this->get_plate_origin();
|
||
m_fake_wipe_tower.set_fake_extrusion_data(wipe_tower.position(), wipe_tower.width(), wipe_tower.get_height(),
|
||
wipe_tower.get_layer_height(), m_wipe_tower_data.depth, m_wipe_tower_data.brim_width,
|
||
{scale_(origin.x()), scale_(origin.y())});
|
||
} else {
|
||
// Initialize the wipe tower.
|
||
WipeTower2 wipe_tower(m_config, m_default_region_config, m_plate_index, m_origin, wipe_volumes,
|
||
m_wipe_tower_data.tool_ordering.first_extruder());
|
||
|
||
// wipe_tower.set_retract();
|
||
// wipe_tower.set_zhop();
|
||
|
||
// Set the extruder & material properties at the wipe tower object.
|
||
for (size_t i = 0; i < number_of_extruders; ++i)
|
||
wipe_tower.set_extruder(i, m_config);
|
||
|
||
// m_wipe_tower_data.priming = Slic3r::make_unique<std::vector<WipeTower::ToolChangeResult>>(
|
||
// wipe_tower.prime((float)this->skirt_first_layer_height(), m_wipe_tower_data.tool_ordering.all_extruders(), false));
|
||
|
||
// Lets go through the wipe tower layers and determine pairs of extruder changes for each
|
||
// to pass to wipe_tower (so that it can use it for planning the layout of the tower)
|
||
{
|
||
unsigned int current_extruder_id = m_wipe_tower_data.tool_ordering.first_extruder();
|
||
for (auto &layer_tools : m_wipe_tower_data.tool_ordering.layer_tools()) { // for all layers
|
||
if (!layer_tools.has_wipe_tower)
|
||
continue;
|
||
bool first_layer = &layer_tools == &m_wipe_tower_data.tool_ordering.front();
|
||
wipe_tower.plan_toolchange((float) layer_tools.print_z, (float) layer_tools.wipe_tower_layer_height, current_extruder_id,
|
||
current_extruder_id, false);
|
||
for (const auto extruder_id : layer_tools.extruders) {
|
||
if (/*(first_layer && extruder_id == m_wipe_tower_data.tool_ordering.all_extruders().back()) || */ extruder_id !=
|
||
current_extruder_id) {
|
||
float volume_to_wipe = m_config.prime_volume;
|
||
if (m_config.purge_in_prime_tower) {
|
||
volume_to_wipe = wipe_volumes[current_extruder_id][extruder_id]; // total volume to wipe after this toolchange
|
||
volume_to_wipe *= m_config.flush_multiplier;
|
||
// Not all of that can be used for infill purging:
|
||
volume_to_wipe -= (float) m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
|
||
|
||
// try to assign some infills/objects for the wiping:
|
||
volume_to_wipe = layer_tools.wiping_extrusions().mark_wiping_extrusions(*this, current_extruder_id, extruder_id,
|
||
volume_to_wipe);
|
||
|
||
// add back the minimal amount toforce on the wipe tower:
|
||
volume_to_wipe += (float) m_config.filament_minimal_purge_on_wipe_tower.get_at(extruder_id);
|
||
}
|
||
|
||
// request a toolchange at the wipe tower with at least volume_to_wipe purging amount
|
||
wipe_tower.plan_toolchange((float) layer_tools.print_z, (float) layer_tools.wipe_tower_layer_height,
|
||
current_extruder_id, extruder_id, volume_to_wipe);
|
||
current_extruder_id = extruder_id;
|
||
}
|
||
}
|
||
layer_tools.wiping_extrusions().ensure_perimeters_infills_order(*this);
|
||
if (&layer_tools == &m_wipe_tower_data.tool_ordering.back() || (&layer_tools + 1)->wipe_tower_partitions == 0)
|
||
break;
|
||
}
|
||
}
|
||
|
||
// Generate the wipe tower layers.
|
||
m_wipe_tower_data.tool_changes.reserve(m_wipe_tower_data.tool_ordering.layer_tools().size());
|
||
wipe_tower.generate(m_wipe_tower_data.tool_changes);
|
||
m_wipe_tower_data.depth = wipe_tower.get_depth();
|
||
m_wipe_tower_data.z_and_depth_pairs = wipe_tower.get_z_and_depth_pairs();
|
||
m_wipe_tower_data.brim_width = wipe_tower.get_brim_width();
|
||
m_wipe_tower_data.height = wipe_tower.get_wipe_tower_height();
|
||
|
||
// Unload the current filament over the purge tower.
|
||
coordf_t layer_height = m_objects.front()->config().layer_height.value;
|
||
if (m_wipe_tower_data.tool_ordering.back().wipe_tower_partitions > 0) {
|
||
// The wipe tower goes up to the last layer of the print.
|
||
if (wipe_tower.layer_finished()) {
|
||
// The wipe tower is printed to the top of the print and it has no space left for the final extruder purge.
|
||
// Lift Z to the next layer.
|
||
wipe_tower.set_layer(float(m_wipe_tower_data.tool_ordering.back().print_z + layer_height), float(layer_height), 0, false,
|
||
true);
|
||
} else {
|
||
// There is yet enough space at this layer of the wipe tower for the final purge.
|
||
}
|
||
} else {
|
||
// The wipe tower does not reach the last print layer, perform the pruge at the last print layer.
|
||
assert(m_wipe_tower_data.tool_ordering.back().wipe_tower_partitions == 0);
|
||
wipe_tower.set_layer(float(m_wipe_tower_data.tool_ordering.back().print_z), float(layer_height), 0, false, true);
|
||
}
|
||
m_wipe_tower_data.final_purge = Slic3r::make_unique<WipeTower::ToolChangeResult>(wipe_tower.tool_change((unsigned int) (-1)));
|
||
|
||
m_wipe_tower_data.used_filament = wipe_tower.get_used_filament();
|
||
m_wipe_tower_data.number_of_toolchanges = wipe_tower.get_number_of_toolchanges();
|
||
const Vec3d origin = Vec3d::Zero();
|
||
m_fake_wipe_tower.set_fake_extrusion_data(wipe_tower.position(), wipe_tower.width(), wipe_tower.get_wipe_tower_height(),
|
||
config().initial_layer_print_height, m_wipe_tower_data.depth,
|
||
m_wipe_tower_data.z_and_depth_pairs, m_wipe_tower_data.brim_width,
|
||
config().wipe_tower_rotation_angle, config().wipe_tower_cone_angle,
|
||
{scale_(origin.x()), scale_(origin.y())});
|
||
}
|
||
}
|
||
|
||
// Generate a recommended G-code output file name based on the format template, default extension, and template parameters
|
||
// (timestamps, object placeholders derived from the model, current placeholder prameters and print statistics.
|
||
// Use the final print statistics if available, or just keep the print statistics placeholders if not available yet (before G-code is finalized).
|
||
std::string Print::output_filename(const std::string &filename_base) const
|
||
{
|
||
// Set the placeholders for the data know first after the G-code export is finished.
|
||
// These values will be just propagated into the output file name.
|
||
DynamicConfig config = this->finished() ? this->print_statistics().config() : this->print_statistics().placeholders();
|
||
config.set_key_value("num_filaments", new ConfigOptionInt((int)m_config.nozzle_diameter.size()));
|
||
config.set_key_value("plate_name", new ConfigOptionString(get_plate_name()));
|
||
|
||
return this->PrintBase::output_filename(m_config.filename_format.value, ".gcode", filename_base, &config);
|
||
}
|
||
|
||
//BBS: add gcode file preload logic
|
||
void Print::set_gcode_file_ready()
|
||
{
|
||
this->set_started(psGCodeExport);
|
||
this->set_done(psGCodeExport);
|
||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": done");
|
||
}
|
||
//BBS: add gcode file preload logic
|
||
void Print::set_gcode_file_invalidated()
|
||
{
|
||
this->invalidate_step(psGCodeExport);
|
||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": done");
|
||
}
|
||
|
||
//BBS: add gcode file preload logic
|
||
void Print::export_gcode_from_previous_file(const std::string& file, GCodeProcessorResult* result, ThumbnailsGeneratorCallback thumbnail_cb)
|
||
{
|
||
try {
|
||
GCodeProcessor processor;
|
||
const Vec3d origin = this->get_plate_origin();
|
||
processor.set_xy_offset(origin(0), origin(1));
|
||
//processor.enable_producers(true);
|
||
processor.process_file(file);
|
||
|
||
*result = std::move(processor.extract_result());
|
||
} catch (std::exception & /* ex */) {
|
||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": found errors when process gcode file %1%") %file.c_str();
|
||
throw Slic3r::RuntimeError(
|
||
std::string("Failed to process the G-code file ") + file + " from previous 3mf\n");
|
||
}
|
||
|
||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": process the G-code file %1% successfully")%file.c_str();
|
||
}
|
||
|
||
DynamicConfig PrintStatistics::config() const
|
||
{
|
||
DynamicConfig config;
|
||
std::string normal_print_time = short_time(this->estimated_normal_print_time);
|
||
std::string silent_print_time = short_time(this->estimated_silent_print_time);
|
||
config.set_key_value("print_time", new ConfigOptionString(normal_print_time));
|
||
config.set_key_value("normal_print_time", new ConfigOptionString(normal_print_time));
|
||
config.set_key_value("silent_print_time", new ConfigOptionString(silent_print_time));
|
||
config.set_key_value("used_filament", new ConfigOptionFloat(this->total_used_filament / 1000.));
|
||
config.set_key_value("extruded_volume", new ConfigOptionFloat(this->total_extruded_volume));
|
||
config.set_key_value("total_cost", new ConfigOptionFloat(this->total_cost));
|
||
config.set_key_value("total_toolchanges", new ConfigOptionInt(this->total_toolchanges));
|
||
config.set_key_value("total_weight", new ConfigOptionFloat(this->total_weight));
|
||
config.set_key_value("total_wipe_tower_cost", new ConfigOptionFloat(this->total_wipe_tower_cost));
|
||
config.set_key_value("total_wipe_tower_filament", new ConfigOptionFloat(this->total_wipe_tower_filament));
|
||
config.set_key_value("initial_tool", new ConfigOptionInt(static_cast<int>(this->initial_tool)));
|
||
return config;
|
||
}
|
||
|
||
DynamicConfig PrintStatistics::placeholders()
|
||
{
|
||
DynamicConfig config;
|
||
for (const std::string &key : {
|
||
"print_time", "normal_print_time", "silent_print_time",
|
||
"used_filament", "extruded_volume", "total_cost", "total_weight",
|
||
"initial_tool", "total_toolchanges", "total_wipe_tower_cost", "total_wipe_tower_filament"})
|
||
config.set_key_value(key, new ConfigOptionString(std::string("{") + key + "}"));
|
||
return config;
|
||
}
|
||
|
||
std::string PrintStatistics::finalize_output_path(const std::string &path_in) const
|
||
{
|
||
std::string final_path;
|
||
try {
|
||
boost::filesystem::path path(path_in);
|
||
DynamicConfig cfg = this->config();
|
||
PlaceholderParser pp;
|
||
std::string new_stem = pp.process(path.stem().string(), 0, &cfg);
|
||
final_path = (path.parent_path() / (new_stem + path.extension().string())).string();
|
||
} catch (const std::exception &ex) {
|
||
BOOST_LOG_TRIVIAL(error) << "Failed to apply the print statistics to the export file name: " << ex.what();
|
||
final_path = path_in;
|
||
}
|
||
return final_path;
|
||
}
|
||
|
||
/*add json export/import related functions */
|
||
#define JSON_POLYGON_CONTOUR "contour"
|
||
#define JSON_POLYGON_HOLES "holes"
|
||
#define JSON_POINTS "points"
|
||
#define JSON_EXPOLYGON "expolygon"
|
||
#define JSON_ARC_FITTING "arc_fitting"
|
||
#define JSON_OBJECT_NAME "name"
|
||
#define JSON_IDENTIFY_ID "identify_id"
|
||
|
||
|
||
#define JSON_LAYERS "layers"
|
||
#define JSON_SUPPORT_LAYERS "support_layers"
|
||
#define JSON_TREE_SUPPORT_LAYERS "tree_support_layers"
|
||
#define JSON_LAYER_REGIONS "layer_regions"
|
||
#define JSON_FIRSTLAYER_GROUPS "first_layer_groups"
|
||
|
||
#define JSON_FIRSTLAYER_GROUP_ID "group_id"
|
||
#define JSON_FIRSTLAYER_GROUP_VOLUME_IDS "volume_ids"
|
||
#define JSON_FIRSTLAYER_GROUP_SLICES "slices"
|
||
|
||
#define JSON_LAYER_PRINT_Z "print_z"
|
||
#define JSON_LAYER_SLICE_Z "slice_z"
|
||
#define JSON_LAYER_HEIGHT "height"
|
||
#define JSON_LAYER_ID "layer_id"
|
||
#define JSON_LAYER_SLICED_POLYGONS "sliced_polygons"
|
||
#define JSON_LAYER_SLLICED_BBOXES "sliced_bboxes"
|
||
#define JSON_LAYER_OVERHANG_POLYGONS "overhang_polygons"
|
||
#define JSON_LAYER_OVERHANG_BBOX "overhang_bbox"
|
||
|
||
#define JSON_SUPPORT_LAYER_ISLANDS "support_islands"
|
||
#define JSON_SUPPORT_LAYER_FILLS "support_fills"
|
||
#define JSON_SUPPORT_LAYER_INTERFACE_ID "interface_id"
|
||
#define JSON_SUPPORT_LAYER_TYPE "support_type"
|
||
|
||
#define JSON_LAYER_REGION_CONFIG_HASH "config_hash"
|
||
#define JSON_LAYER_REGION_SLICES "slices"
|
||
#define JSON_LAYER_REGION_RAW_SLICES "raw_slices"
|
||
//#define JSON_LAYER_REGION_ENTITIES "entities"
|
||
#define JSON_LAYER_REGION_THIN_FILLS "thin_fills"
|
||
#define JSON_LAYER_REGION_FILL_EXPOLYGONS "fill_expolygons"
|
||
#define JSON_LAYER_REGION_FILL_SURFACES "fill_surfaces"
|
||
#define JSON_LAYER_REGION_FILL_NO_OVERLAP "fill_no_overlap_expolygons"
|
||
#define JSON_LAYER_REGION_UNSUPPORTED_BRIDGE_EDGES "unsupported_bridge_edges"
|
||
#define JSON_LAYER_REGION_PERIMETERS "perimeters"
|
||
#define JSON_LAYER_REGION_FILLS "fills"
|
||
|
||
|
||
|
||
#define JSON_SURF_TYPE "surface_type"
|
||
#define JSON_SURF_THICKNESS "thickness"
|
||
#define JSON_SURF_THICKNESS_LAYER "thickness_layers"
|
||
#define JSON_SURF_BRIDGE_ANGLE "bridge_angle"
|
||
#define JSON_SURF_EXTRA_PERIMETERS "extra_perimeters"
|
||
|
||
#define JSON_ARC_DATA "arc_data"
|
||
#define JSON_ARC_START_INDEX "start_index"
|
||
#define JSON_ARC_END_INDEX "end_index"
|
||
#define JSON_ARC_PATH_TYPE "path_type"
|
||
|
||
#define JSON_IS_ARC "is_arc"
|
||
#define JSON_ARC_LENGTH "length"
|
||
#define JSON_ARC_ANGLE_RADIUS "angle_radians"
|
||
#define JSON_ARC_POLAY_START_THETA "polar_start_theta"
|
||
#define JSON_ARC_POLAY_END_THETA "polar_end_theta"
|
||
#define JSON_ARC_START_POINT "start_point"
|
||
#define JSON_ARC_END_POINT "end_point"
|
||
#define JSON_ARC_DIRECTION "direction"
|
||
#define JSON_ARC_RADIUS "radius"
|
||
#define JSON_ARC_CENTER "center"
|
||
|
||
//extrusions
|
||
#define JSON_EXTRUSION_ENTITY_TYPE "entity_type"
|
||
#define JSON_EXTRUSION_NO_SORT "no_sort"
|
||
#define JSON_EXTRUSION_PATHS "paths"
|
||
#define JSON_EXTRUSION_ENTITIES "entities"
|
||
#define JSON_EXTRUSION_TYPE_PATH "path"
|
||
#define JSON_EXTRUSION_TYPE_MULTIPATH "multipath"
|
||
#define JSON_EXTRUSION_TYPE_LOOP "loop"
|
||
#define JSON_EXTRUSION_TYPE_COLLECTION "collection"
|
||
#define JSON_EXTRUSION_POLYLINE "polyline"
|
||
#define JSON_EXTRUSION_OVERHANG_DEGREE "overhang_degree"
|
||
#define JSON_EXTRUSION_CURVE_DEGREE "curve_degree"
|
||
#define JSON_EXTRUSION_MM3_PER_MM "mm3_per_mm"
|
||
#define JSON_EXTRUSION_WIDTH "width"
|
||
#define JSON_EXTRUSION_HEIGHT "height"
|
||
#define JSON_EXTRUSION_ROLE "role"
|
||
#define JSON_EXTRUSION_NO_EXTRUSION "no_extrusion"
|
||
#define JSON_EXTRUSION_LOOP_ROLE "loop_role"
|
||
|
||
|
||
static void to_json(json& j, const Points& p_s) {
|
||
for (const Point& p : p_s)
|
||
{
|
||
j.push_back(p.x());
|
||
j.push_back(p.y());
|
||
}
|
||
}
|
||
|
||
static void to_json(json& j, const BoundingBox& bb) {
|
||
j.push_back(bb.min.x());
|
||
j.push_back(bb.min.y());
|
||
j.push_back(bb.max.x());
|
||
j.push_back(bb.max.y());
|
||
}
|
||
|
||
static void to_json(json& j, const ExPolygon& polygon) {
|
||
json contour_json = json::array(), holes_json = json::array();
|
||
|
||
//contour
|
||
const Polygon& slice_contour = polygon.contour;
|
||
contour_json = slice_contour.points;
|
||
j[JSON_POLYGON_CONTOUR] = std::move(contour_json);
|
||
|
||
//holes
|
||
const Polygons& slice_holes = polygon.holes;
|
||
for (const Polygon& hole_polyon : slice_holes)
|
||
{
|
||
json hole_json = json::array();
|
||
hole_json = hole_polyon.points;
|
||
holes_json.push_back(std::move(hole_json));
|
||
}
|
||
j[JSON_POLYGON_HOLES] = std::move(holes_json);
|
||
}
|
||
|
||
static void to_json(json& j, const Surface& surf) {
|
||
j[JSON_EXPOLYGON] = surf.expolygon;
|
||
j[JSON_SURF_TYPE] = surf.surface_type;
|
||
j[JSON_SURF_THICKNESS] = surf.thickness;
|
||
j[JSON_SURF_THICKNESS_LAYER] = surf.thickness_layers;
|
||
j[JSON_SURF_BRIDGE_ANGLE] = surf.bridge_angle;
|
||
j[JSON_SURF_EXTRA_PERIMETERS] = surf.extra_perimeters;
|
||
}
|
||
|
||
static void to_json(json& j, const ArcSegment& arc_seg) {
|
||
json start_point_json = json::array(), end_point_json = json::array(), center_point_json = json::array();
|
||
j[JSON_IS_ARC] = arc_seg.is_arc;
|
||
j[JSON_ARC_LENGTH] = arc_seg.length;
|
||
j[JSON_ARC_ANGLE_RADIUS] = arc_seg.angle_radians;
|
||
j[JSON_ARC_POLAY_START_THETA] = arc_seg.polar_start_theta;
|
||
j[JSON_ARC_POLAY_END_THETA] = arc_seg.polar_end_theta;
|
||
start_point_json.push_back(arc_seg.start_point.x());
|
||
start_point_json.push_back(arc_seg.start_point.y());
|
||
j[JSON_ARC_START_POINT] = std::move(start_point_json);
|
||
end_point_json.push_back(arc_seg.end_point.x());
|
||
end_point_json.push_back(arc_seg.end_point.y());
|
||
j[JSON_ARC_END_POINT] = std::move(end_point_json);
|
||
j[JSON_ARC_DIRECTION] = arc_seg.direction;
|
||
j[JSON_ARC_RADIUS] = arc_seg.radius;
|
||
center_point_json.push_back(arc_seg.center.x());
|
||
center_point_json.push_back(arc_seg.center.y());
|
||
j[JSON_ARC_CENTER] = std::move(center_point_json);
|
||
}
|
||
|
||
|
||
static void to_json(json& j, const Polyline& poly_line) {
|
||
json points_json = json::array(), fittings_json = json::array();
|
||
points_json = poly_line.points;
|
||
|
||
j[JSON_POINTS] = std::move(points_json);
|
||
for (const PathFittingData& path_fitting : poly_line.fitting_result)
|
||
{
|
||
json fitting_json;
|
||
fitting_json[JSON_ARC_START_INDEX] = path_fitting.start_point_index;
|
||
fitting_json[JSON_ARC_END_INDEX] = path_fitting.end_point_index;
|
||
fitting_json[JSON_ARC_PATH_TYPE] = path_fitting.path_type;
|
||
if (path_fitting.arc_data.is_arc)
|
||
fitting_json[JSON_ARC_DATA] = path_fitting.arc_data;
|
||
|
||
fittings_json.push_back(std::move(fitting_json));
|
||
}
|
||
j[JSON_ARC_FITTING] = fittings_json;
|
||
}
|
||
|
||
static void to_json(json& j, const ExtrusionPath& extrusion_path) {
|
||
j[JSON_EXTRUSION_POLYLINE] = extrusion_path.polyline;
|
||
j[JSON_EXTRUSION_OVERHANG_DEGREE] = extrusion_path.overhang_degree;
|
||
j[JSON_EXTRUSION_CURVE_DEGREE] = extrusion_path.curve_degree;
|
||
j[JSON_EXTRUSION_MM3_PER_MM] = extrusion_path.mm3_per_mm;
|
||
j[JSON_EXTRUSION_WIDTH] = extrusion_path.width;
|
||
j[JSON_EXTRUSION_HEIGHT] = extrusion_path.height;
|
||
j[JSON_EXTRUSION_ROLE] = extrusion_path.role();
|
||
j[JSON_EXTRUSION_NO_EXTRUSION] = extrusion_path.is_force_no_extrusion();
|
||
}
|
||
|
||
static bool convert_extrusion_to_json(json& entity_json, json& entity_paths_json, const ExtrusionEntity* extrusion_entity) {
|
||
std::string path_type;
|
||
const ExtrusionPath* path = NULL;
|
||
const ExtrusionMultiPath* multipath = NULL;
|
||
const ExtrusionLoop* loop = NULL;
|
||
const ExtrusionEntityCollection* collection = dynamic_cast<const ExtrusionEntityCollection*>(extrusion_entity);
|
||
|
||
if (!collection)
|
||
path = dynamic_cast<const ExtrusionPath*>(extrusion_entity);
|
||
|
||
if (!collection && !path)
|
||
multipath = dynamic_cast<const ExtrusionMultiPath*>(extrusion_entity);
|
||
|
||
if (!collection && !path && !multipath)
|
||
loop = dynamic_cast<const ExtrusionLoop*>(extrusion_entity);
|
||
|
||
path_type = path?JSON_EXTRUSION_TYPE_PATH:(multipath?JSON_EXTRUSION_TYPE_MULTIPATH:(loop?JSON_EXTRUSION_TYPE_LOOP:JSON_EXTRUSION_TYPE_COLLECTION));
|
||
if (path_type.empty()) {
|
||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(":invalid extrusion path type Found");
|
||
return false;
|
||
}
|
||
|
||
entity_json[JSON_EXTRUSION_ENTITY_TYPE] = path_type;
|
||
|
||
if (path) {
|
||
json entity_path_json = *path;
|
||
entity_paths_json.push_back(std::move(entity_path_json));
|
||
}
|
||
else if (multipath) {
|
||
for (const ExtrusionPath& extrusion_path : multipath->paths)
|
||
{
|
||
json entity_path_json = extrusion_path;
|
||
entity_paths_json.push_back(std::move(entity_path_json));
|
||
}
|
||
}
|
||
else if (loop) {
|
||
entity_json[JSON_EXTRUSION_LOOP_ROLE] = loop->loop_role();
|
||
for (const ExtrusionPath& extrusion_path : loop->paths)
|
||
{
|
||
json entity_path_json = extrusion_path;
|
||
entity_paths_json.push_back(std::move(entity_path_json));
|
||
}
|
||
}
|
||
else {
|
||
//recursive collections
|
||
entity_json[JSON_EXTRUSION_NO_SORT] = collection->no_sort;
|
||
for (const ExtrusionEntity* recursive_extrusion_entity : collection->entities) {
|
||
json recursive_entity_json, recursive_entity_paths_json = json::array();
|
||
bool ret = convert_extrusion_to_json(recursive_entity_json, recursive_entity_paths_json, recursive_extrusion_entity);
|
||
if (!ret) {
|
||
continue;
|
||
}
|
||
entity_paths_json.push_back(std::move(recursive_entity_json));
|
||
}
|
||
}
|
||
|
||
if (collection)
|
||
entity_json[JSON_EXTRUSION_ENTITIES] = std::move(entity_paths_json);
|
||
else
|
||
entity_json[JSON_EXTRUSION_PATHS] = std::move(entity_paths_json);
|
||
return true;
|
||
}
|
||
|
||
static void to_json(json& j, const LayerRegion& layer_region) {
|
||
json unsupported_bridge_edges_json = json::array(), slices_surfaces_json = json::array(), raw_slices_json = json::array(), thin_fills_json, thin_fill_entities_json = json::array();
|
||
json fill_expolygons_json = json::array(), fill_no_overlap_expolygons_json = json::array(), fill_surfaces_json = json::array(), perimeters_json, perimeter_entities_json = json::array(), fills_json, fill_entities_json = json::array();
|
||
|
||
j[JSON_LAYER_REGION_CONFIG_HASH] = layer_region.region().config_hash();
|
||
//slices
|
||
for (const Surface& slice_surface : layer_region.slices.surfaces) {
|
||
json surface_json = slice_surface;
|
||
slices_surfaces_json.push_back(std::move(surface_json));
|
||
}
|
||
j.push_back({JSON_LAYER_REGION_SLICES, std::move(slices_surfaces_json)});
|
||
|
||
//raw_slices
|
||
for (const ExPolygon& raw_slice_explogyon : layer_region.raw_slices) {
|
||
json raw_polygon_json = raw_slice_explogyon;
|
||
|
||
raw_slices_json.push_back(std::move(raw_polygon_json));
|
||
}
|
||
j.push_back({JSON_LAYER_REGION_RAW_SLICES, std::move(raw_slices_json)});
|
||
|
||
//thin fills
|
||
thin_fills_json[JSON_EXTRUSION_NO_SORT] = layer_region.thin_fills.no_sort;
|
||
thin_fills_json[JSON_EXTRUSION_ENTITY_TYPE] = JSON_EXTRUSION_TYPE_COLLECTION;
|
||
for (const ExtrusionEntity* extrusion_entity : layer_region.thin_fills.entities) {
|
||
json thinfills_entity_json, thinfill_entity_paths_json = json::array();
|
||
bool ret = convert_extrusion_to_json(thinfills_entity_json, thinfill_entity_paths_json, extrusion_entity);
|
||
if (!ret) {
|
||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(":error found at print_z %1%") % layer_region.layer()->print_z;
|
||
continue;
|
||
}
|
||
|
||
thin_fill_entities_json.push_back(std::move(thinfills_entity_json));
|
||
}
|
||
thin_fills_json[JSON_EXTRUSION_ENTITIES] = std::move(thin_fill_entities_json);
|
||
j.push_back({JSON_LAYER_REGION_THIN_FILLS, std::move(thin_fills_json)});
|
||
|
||
//fill_expolygons
|
||
for (const ExPolygon& fill_expolygon : layer_region.fill_expolygons) {
|
||
json fill_expolygon_json = fill_expolygon;
|
||
|
||
fill_expolygons_json.push_back(std::move(fill_expolygon_json));
|
||
}
|
||
j.push_back({JSON_LAYER_REGION_FILL_EXPOLYGONS, std::move(fill_expolygons_json)});
|
||
|
||
//fill_surfaces
|
||
for (const Surface& fill_surface : layer_region.fill_surfaces.surfaces) {
|
||
json surface_json = fill_surface;
|
||
fill_surfaces_json.push_back(std::move(surface_json));
|
||
}
|
||
j.push_back({JSON_LAYER_REGION_FILL_SURFACES, std::move(fill_surfaces_json)});
|
||
|
||
//fill_no_overlap_expolygons
|
||
for (const ExPolygon& fill_no_overlap_expolygon : layer_region.fill_no_overlap_expolygons) {
|
||
json fill_no_overlap_expolygon_json = fill_no_overlap_expolygon;
|
||
|
||
fill_no_overlap_expolygons_json.push_back(std::move(fill_no_overlap_expolygon_json));
|
||
}
|
||
j.push_back({JSON_LAYER_REGION_FILL_NO_OVERLAP, std::move(fill_no_overlap_expolygons_json)});
|
||
|
||
//unsupported_bridge_edges
|
||
for (const Polyline& poly_line : layer_region.unsupported_bridge_edges)
|
||
{
|
||
json polyline_json = poly_line;
|
||
|
||
unsupported_bridge_edges_json.push_back(std::move(polyline_json));
|
||
}
|
||
j.push_back({JSON_LAYER_REGION_UNSUPPORTED_BRIDGE_EDGES, std::move(unsupported_bridge_edges_json)});
|
||
|
||
//perimeters
|
||
perimeters_json[JSON_EXTRUSION_NO_SORT] = layer_region.perimeters.no_sort;
|
||
perimeters_json[JSON_EXTRUSION_ENTITY_TYPE] = JSON_EXTRUSION_TYPE_COLLECTION;
|
||
for (const ExtrusionEntity* extrusion_entity : layer_region.perimeters.entities) {
|
||
json perimeters_entity_json, perimeters_entity_paths_json = json::array();
|
||
bool ret = convert_extrusion_to_json(perimeters_entity_json, perimeters_entity_paths_json, extrusion_entity);
|
||
if (!ret)
|
||
continue;
|
||
|
||
perimeter_entities_json.push_back(std::move(perimeters_entity_json));
|
||
}
|
||
perimeters_json[JSON_EXTRUSION_ENTITIES] = std::move(perimeter_entities_json);
|
||
j.push_back({JSON_LAYER_REGION_PERIMETERS, std::move(perimeters_json)});
|
||
|
||
//fills
|
||
fills_json[JSON_EXTRUSION_NO_SORT] = layer_region.fills.no_sort;
|
||
fills_json[JSON_EXTRUSION_ENTITY_TYPE] = JSON_EXTRUSION_TYPE_COLLECTION;
|
||
for (const ExtrusionEntity* extrusion_entity : layer_region.fills.entities) {
|
||
json fill_entity_json, fill_entity_paths_json = json::array();
|
||
bool ret = convert_extrusion_to_json(fill_entity_json, fill_entity_paths_json, extrusion_entity);
|
||
if (!ret)
|
||
continue;
|
||
|
||
fill_entities_json.push_back(std::move(fill_entity_json));
|
||
}
|
||
fills_json[JSON_EXTRUSION_ENTITIES] = std::move(fill_entities_json);
|
||
j.push_back({JSON_LAYER_REGION_FILLS, std::move(fills_json)});
|
||
|
||
return;
|
||
}
|
||
|
||
static void to_json(json& j, const groupedVolumeSlices& first_layer_group) {
|
||
json volumes_json = json::array(), slices_json = json::array();
|
||
j[JSON_FIRSTLAYER_GROUP_ID] = first_layer_group.groupId;
|
||
|
||
for (const ObjectID& obj_id : first_layer_group.volume_ids)
|
||
{
|
||
volumes_json.push_back(obj_id.id);
|
||
}
|
||
j[JSON_FIRSTLAYER_GROUP_VOLUME_IDS] = std::move(volumes_json);
|
||
|
||
for (const ExPolygon& slice_expolygon : first_layer_group.slices) {
|
||
json slice_expolygon_json = slice_expolygon;
|
||
|
||
slices_json.push_back(std::move(slice_expolygon_json));
|
||
}
|
||
j[JSON_FIRSTLAYER_GROUP_SLICES] = std::move(slices_json);
|
||
}
|
||
|
||
//load apis from json
|
||
static void from_json(const json& j, Points& p_s) {
|
||
int array_size = j.size();
|
||
for (int index = 0; index < array_size/2; index++)
|
||
{
|
||
coord_t x = j[2*index], y = j[2*index+1];
|
||
Point p(x, y);
|
||
p_s.push_back(std::move(p));
|
||
}
|
||
return;
|
||
}
|
||
|
||
static void from_json(const json& j, BoundingBox& bbox) {
|
||
bbox.min[0] = j[0];
|
||
bbox.min[1] = j[1];
|
||
bbox.max[0] = j[2];
|
||
bbox.max[1] = j[3];
|
||
bbox.defined = true;
|
||
|
||
return;
|
||
}
|
||
|
||
static void from_json(const json& j, ExPolygon& polygon) {
|
||
polygon.contour.points = j[JSON_POLYGON_CONTOUR];
|
||
|
||
int holes_count = j[JSON_POLYGON_HOLES].size();
|
||
for (int holes_index = 0; holes_index < holes_count; holes_index++)
|
||
{
|
||
Polygon poly;
|
||
|
||
poly.points = j[JSON_POLYGON_HOLES][holes_index];
|
||
polygon.holes.push_back(std::move(poly));
|
||
}
|
||
return;
|
||
}
|
||
|
||
static void from_json(const json& j, Surface& surf) {
|
||
surf.expolygon = j[JSON_EXPOLYGON];
|
||
surf.surface_type = j[JSON_SURF_TYPE];
|
||
surf.thickness = j[JSON_SURF_THICKNESS];
|
||
surf.thickness_layers = j[JSON_SURF_THICKNESS_LAYER];
|
||
surf.bridge_angle = j[JSON_SURF_BRIDGE_ANGLE];
|
||
surf.extra_perimeters = j[JSON_SURF_EXTRA_PERIMETERS];
|
||
|
||
return;
|
||
}
|
||
|
||
static void from_json(const json& j, ArcSegment& arc_seg) {
|
||
arc_seg.is_arc = j[JSON_IS_ARC];
|
||
arc_seg.length = j[JSON_ARC_LENGTH];
|
||
arc_seg.angle_radians = j[JSON_ARC_ANGLE_RADIUS];
|
||
arc_seg.polar_start_theta = j[JSON_ARC_POLAY_START_THETA];
|
||
arc_seg.polar_end_theta = j[JSON_ARC_POLAY_END_THETA];
|
||
arc_seg.start_point.x() = j[JSON_ARC_START_POINT][0];
|
||
arc_seg.start_point.y() = j[JSON_ARC_START_POINT][1];
|
||
arc_seg.end_point.x() = j[JSON_ARC_END_POINT][0];
|
||
arc_seg.end_point.y() = j[JSON_ARC_END_POINT][1];
|
||
arc_seg.direction = j[JSON_ARC_DIRECTION];
|
||
arc_seg.radius = j[JSON_ARC_RADIUS];
|
||
arc_seg.center.x() = j[JSON_ARC_CENTER][0];
|
||
arc_seg.center.y() = j[JSON_ARC_CENTER][1];
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
static void from_json(const json& j, Polyline& poly_line) {
|
||
poly_line.points = j[JSON_POINTS];
|
||
|
||
int arc_fitting_count = j[JSON_ARC_FITTING].size();
|
||
for (int arc_fitting_index = 0; arc_fitting_index < arc_fitting_count; arc_fitting_index++)
|
||
{
|
||
const json& fitting_json = j[JSON_ARC_FITTING][arc_fitting_index];
|
||
PathFittingData path_fitting;
|
||
path_fitting.start_point_index = fitting_json[JSON_ARC_START_INDEX];
|
||
path_fitting.end_point_index = fitting_json[JSON_ARC_END_INDEX];
|
||
path_fitting.path_type = fitting_json[JSON_ARC_PATH_TYPE];
|
||
|
||
if (fitting_json.contains(JSON_ARC_DATA)) {
|
||
path_fitting.arc_data = fitting_json[JSON_ARC_DATA];
|
||
}
|
||
|
||
poly_line.fitting_result.push_back(std::move(path_fitting));
|
||
}
|
||
return;
|
||
}
|
||
|
||
static void from_json(const json& j, ExtrusionPath& extrusion_path) {
|
||
extrusion_path.polyline = j[JSON_EXTRUSION_POLYLINE];
|
||
extrusion_path.overhang_degree = j[JSON_EXTRUSION_OVERHANG_DEGREE];
|
||
extrusion_path.curve_degree = j[JSON_EXTRUSION_CURVE_DEGREE];
|
||
extrusion_path.mm3_per_mm = j[JSON_EXTRUSION_MM3_PER_MM];
|
||
extrusion_path.width = j[JSON_EXTRUSION_WIDTH];
|
||
extrusion_path.height = j[JSON_EXTRUSION_HEIGHT];
|
||
extrusion_path.set_extrusion_role(j[JSON_EXTRUSION_ROLE]);
|
||
extrusion_path.set_force_no_extrusion(j[JSON_EXTRUSION_NO_EXTRUSION]);
|
||
}
|
||
|
||
static bool convert_extrusion_from_json(const json& entity_json, ExtrusionEntityCollection& entity_collection) {
|
||
std::string path_type = entity_json[JSON_EXTRUSION_ENTITY_TYPE];
|
||
bool ret = false;
|
||
|
||
if (path_type == JSON_EXTRUSION_TYPE_PATH) {
|
||
ExtrusionPath* path = new ExtrusionPath();
|
||
if (!path) {
|
||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": oom when new ExtrusionPath");
|
||
return false;
|
||
}
|
||
*path = entity_json[JSON_EXTRUSION_PATHS][0];
|
||
entity_collection.entities.push_back(path);
|
||
}
|
||
else if (path_type == JSON_EXTRUSION_TYPE_MULTIPATH) {
|
||
ExtrusionMultiPath* multipath = new ExtrusionMultiPath();
|
||
if (!multipath) {
|
||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": oom when new ExtrusionMultiPath");
|
||
return false;
|
||
}
|
||
int paths_count = entity_json[JSON_EXTRUSION_PATHS].size();
|
||
for (int path_index = 0; path_index < paths_count; path_index++)
|
||
{
|
||
ExtrusionPath path;
|
||
path = entity_json[JSON_EXTRUSION_PATHS][path_index];
|
||
multipath->paths.push_back(std::move(path));
|
||
}
|
||
entity_collection.entities.push_back(multipath);
|
||
}
|
||
else if (path_type == JSON_EXTRUSION_TYPE_LOOP) {
|
||
ExtrusionLoop* loop = new ExtrusionLoop();
|
||
if (!loop) {
|
||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": oom when new ExtrusionLoop");
|
||
return false;
|
||
}
|
||
loop->set_loop_role(entity_json[JSON_EXTRUSION_LOOP_ROLE]);
|
||
int paths_count = entity_json[JSON_EXTRUSION_PATHS].size();
|
||
for (int path_index = 0; path_index < paths_count; path_index++)
|
||
{
|
||
ExtrusionPath path;
|
||
path = entity_json[JSON_EXTRUSION_PATHS][path_index];
|
||
loop->paths.push_back(std::move(path));
|
||
}
|
||
entity_collection.entities.push_back(loop);
|
||
}
|
||
else if (path_type == JSON_EXTRUSION_TYPE_COLLECTION) {
|
||
ExtrusionEntityCollection* collection = new ExtrusionEntityCollection();
|
||
if (!collection) {
|
||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": oom when new ExtrusionEntityCollection");
|
||
return false;
|
||
}
|
||
collection->no_sort = entity_json[JSON_EXTRUSION_NO_SORT];
|
||
int entities_count = entity_json[JSON_EXTRUSION_ENTITIES].size();
|
||
for (int entity_index = 0; entity_index < entities_count; entity_index++)
|
||
{
|
||
const json& entity_item_json = entity_json[JSON_EXTRUSION_ENTITIES][entity_index];
|
||
ret = convert_extrusion_from_json(entity_item_json, *collection);
|
||
if (!ret) {
|
||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": convert_extrusion_from_json failed");
|
||
return false;
|
||
}
|
||
}
|
||
entity_collection.entities.push_back(collection);
|
||
}
|
||
else {
|
||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": unknown path type %1%")%path_type;
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
static void convert_layer_region_from_json(const json& j, LayerRegion& layer_region) {
|
||
//slices
|
||
int slices_count = j[JSON_LAYER_REGION_SLICES].size();
|
||
for (int slices_index = 0; slices_index < slices_count; slices_index++)
|
||
{
|
||
Surface surface;
|
||
|
||
surface = j[JSON_LAYER_REGION_SLICES][slices_index];
|
||
layer_region.slices.surfaces.push_back(std::move(surface));
|
||
}
|
||
|
||
//raw_slices
|
||
int raw_slices_count = j[JSON_LAYER_REGION_RAW_SLICES].size();
|
||
for (int raw_slices_index = 0; raw_slices_index < raw_slices_count; raw_slices_index++)
|
||
{
|
||
ExPolygon polygon;
|
||
|
||
polygon = j[JSON_LAYER_REGION_RAW_SLICES][raw_slices_index];
|
||
layer_region.raw_slices.push_back(std::move(polygon));
|
||
}
|
||
|
||
//thin fills
|
||
layer_region.thin_fills.no_sort = j[JSON_LAYER_REGION_THIN_FILLS][JSON_EXTRUSION_NO_SORT];
|
||
int thinfills_entities_count = j[JSON_LAYER_REGION_THIN_FILLS][JSON_EXTRUSION_ENTITIES].size();
|
||
for (int thinfills_entities_index = 0; thinfills_entities_index < thinfills_entities_count; thinfills_entities_index++)
|
||
{
|
||
const json& extrusion_entity_json = j[JSON_LAYER_REGION_THIN_FILLS][JSON_EXTRUSION_ENTITIES][thinfills_entities_index];
|
||
bool ret = convert_extrusion_from_json(extrusion_entity_json, layer_region.thin_fills);
|
||
if (!ret) {
|
||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(":error parsing thin_fills found at layer %1%, print_z %2%") %layer_region.layer()->id() %layer_region.layer()->print_z;
|
||
char error_buf[1024];
|
||
::sprintf(error_buf, "Error while parsing thin_fills at layer %zd, print_z %f", layer_region.layer()->id(), layer_region.layer()->print_z);
|
||
throw Slic3r::FileIOError(error_buf);
|
||
}
|
||
}
|
||
|
||
//fill_expolygons
|
||
int fill_expolygons_count = j[JSON_LAYER_REGION_FILL_EXPOLYGONS].size();
|
||
for (int fill_expolygons_index = 0; fill_expolygons_index < fill_expolygons_count; fill_expolygons_index++)
|
||
{
|
||
ExPolygon polygon;
|
||
|
||
polygon = j[JSON_LAYER_REGION_FILL_EXPOLYGONS][fill_expolygons_index];
|
||
layer_region.fill_expolygons.push_back(std::move(polygon));
|
||
}
|
||
|
||
//fill_surfaces
|
||
int fill_surfaces_count = j[JSON_LAYER_REGION_FILL_SURFACES].size();
|
||
for (int fill_surfaces_index = 0; fill_surfaces_index < fill_surfaces_count; fill_surfaces_index++)
|
||
{
|
||
Surface surface;
|
||
|
||
surface = j[JSON_LAYER_REGION_FILL_SURFACES][fill_surfaces_index];
|
||
layer_region.fill_surfaces.surfaces.push_back(std::move(surface));
|
||
}
|
||
|
||
//fill_no_overlap_expolygons
|
||
int fill_no_overlap_expolygons_count = j[JSON_LAYER_REGION_FILL_NO_OVERLAP].size();
|
||
for (int fill_no_overlap_expolygons_index = 0; fill_no_overlap_expolygons_index < fill_no_overlap_expolygons_count; fill_no_overlap_expolygons_index++)
|
||
{
|
||
ExPolygon polygon;
|
||
|
||
polygon = j[JSON_LAYER_REGION_FILL_NO_OVERLAP][fill_no_overlap_expolygons_index];
|
||
layer_region.fill_no_overlap_expolygons.push_back(std::move(polygon));
|
||
}
|
||
|
||
//unsupported_bridge_edges
|
||
int unsupported_bridge_edges_count = j[JSON_LAYER_REGION_UNSUPPORTED_BRIDGE_EDGES].size();
|
||
for (int unsupported_bridge_edges_index = 0; unsupported_bridge_edges_index < unsupported_bridge_edges_count; unsupported_bridge_edges_index++)
|
||
{
|
||
Polyline polyline;
|
||
|
||
polyline = j[JSON_LAYER_REGION_UNSUPPORTED_BRIDGE_EDGES][unsupported_bridge_edges_index];
|
||
layer_region.unsupported_bridge_edges.push_back(std::move(polyline));
|
||
}
|
||
|
||
//perimeters
|
||
layer_region.perimeters.no_sort = j[JSON_LAYER_REGION_PERIMETERS][JSON_EXTRUSION_NO_SORT];
|
||
int perimeters_entities_count = j[JSON_LAYER_REGION_PERIMETERS][JSON_EXTRUSION_ENTITIES].size();
|
||
for (int perimeters_entities_index = 0; perimeters_entities_index < perimeters_entities_count; perimeters_entities_index++)
|
||
{
|
||
const json& extrusion_entity_json = j[JSON_LAYER_REGION_PERIMETERS][JSON_EXTRUSION_ENTITIES][perimeters_entities_index];
|
||
bool ret = convert_extrusion_from_json(extrusion_entity_json, layer_region.perimeters);
|
||
if (!ret) {
|
||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": error parsing perimeters found at layer %1%, print_z %2%") %layer_region.layer()->id() %layer_region.layer()->print_z;
|
||
char error_buf[1024];
|
||
::sprintf(error_buf, "Error while parsing perimeters at layer %zd, print_z %f", layer_region.layer()->id(), layer_region.layer()->print_z);
|
||
throw Slic3r::FileIOError(error_buf);
|
||
}
|
||
}
|
||
|
||
//fills
|
||
layer_region.fills.no_sort = j[JSON_LAYER_REGION_FILLS][JSON_EXTRUSION_NO_SORT];
|
||
int fills_entities_count = j[JSON_LAYER_REGION_FILLS][JSON_EXTRUSION_ENTITIES].size();
|
||
for (int fills_entities_index = 0; fills_entities_index < fills_entities_count; fills_entities_index++)
|
||
{
|
||
const json& extrusion_entity_json = j[JSON_LAYER_REGION_FILLS][JSON_EXTRUSION_ENTITIES][fills_entities_index];
|
||
bool ret = convert_extrusion_from_json(extrusion_entity_json, layer_region.fills);
|
||
if (!ret) {
|
||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": error parsing fills found at layer %1%, print_z %2%") %layer_region.layer()->id() %layer_region.layer()->print_z;
|
||
char error_buf[1024];
|
||
::sprintf(error_buf, "Error while parsing fills at layer %zd, print_z %f", layer_region.layer()->id(), layer_region.layer()->print_z);
|
||
throw Slic3r::FileIOError(error_buf);
|
||
}
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
void extract_layer(const json& layer_json, Layer& layer) {
|
||
//slice_polygons
|
||
int slice_polygons_count = layer_json[JSON_LAYER_SLICED_POLYGONS].size();
|
||
for (int polygon_index = 0; polygon_index < slice_polygons_count; polygon_index++)
|
||
{
|
||
ExPolygon polygon;
|
||
|
||
polygon = layer_json[JSON_LAYER_SLICED_POLYGONS][polygon_index];
|
||
layer.lslices.push_back(std::move(polygon));
|
||
}
|
||
|
||
//slice_bboxes
|
||
int sliced_bboxes_count = layer_json[JSON_LAYER_SLLICED_BBOXES].size();
|
||
for (int bbox_index = 0; bbox_index < sliced_bboxes_count; bbox_index++)
|
||
{
|
||
BoundingBox bbox;
|
||
|
||
bbox = layer_json[JSON_LAYER_SLLICED_BBOXES][bbox_index];
|
||
layer.lslices_bboxes.push_back(std::move(bbox));
|
||
}
|
||
|
||
//overhang_polygons
|
||
int overhang_polygons_count = layer_json[JSON_LAYER_OVERHANG_POLYGONS].size();
|
||
for (int polygon_index = 0; polygon_index < overhang_polygons_count; polygon_index++)
|
||
{
|
||
ExPolygon polygon;
|
||
|
||
polygon = layer_json[JSON_LAYER_OVERHANG_POLYGONS][polygon_index];
|
||
layer.loverhangs.push_back(std::move(polygon));
|
||
}
|
||
|
||
//overhang_box
|
||
layer.loverhangs_bbox = layer_json[JSON_LAYER_OVERHANG_BBOX];
|
||
|
||
//layer_regions
|
||
int layer_region_count = layer.region_count();
|
||
for (int layer_region_index = 0; layer_region_index < layer_region_count; layer_region_index++)
|
||
{
|
||
LayerRegion* layer_region = layer.get_region(layer_region_index);
|
||
const json& layer_region_json = layer_json[JSON_LAYER_REGIONS][layer_region_index];
|
||
convert_layer_region_from_json(layer_region_json, *layer_region);
|
||
|
||
//LayerRegion layer_region = layer_json[JSON_LAYER_REGIONS][layer_region_index];
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
void extract_support_layer(const json& support_layer_json, SupportLayer& support_layer) {
|
||
extract_layer(support_layer_json, support_layer);
|
||
|
||
support_layer.support_type = support_layer_json[JSON_SUPPORT_LAYER_TYPE];
|
||
//support_islands
|
||
int islands_count = support_layer_json[JSON_SUPPORT_LAYER_ISLANDS].size();
|
||
for (int islands_index = 0; islands_index < islands_count; islands_index++)
|
||
{
|
||
ExPolygon polygon;
|
||
|
||
polygon = support_layer_json[JSON_SUPPORT_LAYER_ISLANDS][islands_index];
|
||
support_layer.support_islands.push_back(std::move(polygon));
|
||
}
|
||
|
||
//support_fills
|
||
support_layer.support_fills.no_sort = support_layer_json[JSON_SUPPORT_LAYER_FILLS][JSON_EXTRUSION_NO_SORT];
|
||
int support_fills_entities_count = support_layer_json[JSON_SUPPORT_LAYER_FILLS][JSON_EXTRUSION_ENTITIES].size();
|
||
for (int support_fills_entities_index = 0; support_fills_entities_index < support_fills_entities_count; support_fills_entities_index++)
|
||
{
|
||
const json& extrusion_entity_json = support_layer_json[JSON_SUPPORT_LAYER_FILLS][JSON_EXTRUSION_ENTITIES][support_fills_entities_index];
|
||
bool ret = convert_extrusion_from_json(extrusion_entity_json, support_layer.support_fills);
|
||
if (!ret) {
|
||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": error parsing fills found at support_layer %1%, print_z %2%")%support_layer.id() %support_layer.print_z;
|
||
char error_buf[1024];
|
||
::sprintf(error_buf, "Error while parsing fills at support_layer %zd, print_z %f", support_layer.id(), support_layer.print_z);
|
||
throw Slic3r::FileIOError(error_buf);
|
||
}
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
static void from_json(const json& j, groupedVolumeSlices& firstlayer_group)
|
||
{
|
||
firstlayer_group.groupId = j[JSON_FIRSTLAYER_GROUP_ID];
|
||
|
||
int volume_count = j[JSON_FIRSTLAYER_GROUP_VOLUME_IDS].size();
|
||
for (int volume_index = 0; volume_index < volume_count; volume_index++)
|
||
{
|
||
ObjectID obj_id;
|
||
|
||
obj_id.id = j[JSON_FIRSTLAYER_GROUP_VOLUME_IDS][volume_index];
|
||
firstlayer_group.volume_ids.push_back(std::move(obj_id));
|
||
}
|
||
|
||
int slices_count = j[JSON_FIRSTLAYER_GROUP_SLICES].size();
|
||
for (int slice_index = 0; slice_index < slices_count; slice_index++)
|
||
{
|
||
ExPolygon polygon;
|
||
|
||
polygon = j[JSON_FIRSTLAYER_GROUP_SLICES][slice_index];
|
||
firstlayer_group.slices.push_back(std::move(polygon));
|
||
}
|
||
}
|
||
|
||
int Print::export_cached_data(const std::string& directory, bool with_space)
|
||
{
|
||
int ret = 0;
|
||
boost::filesystem::path directory_path(directory);
|
||
|
||
auto convert_layer_to_json = [](json& layer_json, const Layer* layer) {
|
||
json slice_polygons_json = json::array(), slice_bboxs_json = json::array(), overhang_polygons_json = json::array(), layer_regions_json = json::array();
|
||
layer_json[JSON_LAYER_PRINT_Z] = layer->print_z;
|
||
layer_json[JSON_LAYER_HEIGHT] = layer->height;
|
||
layer_json[JSON_LAYER_SLICE_Z] = layer->slice_z;
|
||
layer_json[JSON_LAYER_ID] = layer->id();
|
||
//layer_json["slicing_errors"] = layer->slicing_errors;
|
||
|
||
//sliced_polygons
|
||
for (const ExPolygon& slice_polygon : layer->lslices) {
|
||
json slice_polygon_json = slice_polygon;
|
||
slice_polygons_json.push_back(std::move(slice_polygon_json));
|
||
}
|
||
layer_json[JSON_LAYER_SLICED_POLYGONS] = std::move(slice_polygons_json);
|
||
|
||
//sliced_bbox
|
||
for (const BoundingBox& slice_bbox : layer->lslices_bboxes) {
|
||
json bbox_json = json::array();
|
||
|
||
bbox_json = slice_bbox;
|
||
slice_bboxs_json.push_back(std::move(bbox_json));
|
||
}
|
||
layer_json[JSON_LAYER_SLLICED_BBOXES] = std::move(slice_bboxs_json);
|
||
|
||
//overhang_polygons
|
||
for (const ExPolygon& overhang_polygon : layer->loverhangs) {
|
||
json overhang_polygon_json = overhang_polygon;
|
||
overhang_polygons_json.push_back(std::move(overhang_polygon_json));
|
||
}
|
||
layer_json[JSON_LAYER_OVERHANG_POLYGONS] = std::move(overhang_polygons_json);
|
||
|
||
//overhang_box
|
||
layer_json[JSON_LAYER_OVERHANG_BBOX] = layer->loverhangs_bbox;
|
||
|
||
for (const LayerRegion *layer_region : layer->regions()) {
|
||
json region_json = *layer_region;
|
||
|
||
layer_regions_json.push_back(std::move(region_json));
|
||
}
|
||
layer_json[JSON_LAYER_REGIONS] = std::move(layer_regions_json);
|
||
|
||
return;
|
||
};
|
||
|
||
//firstly clear this directory
|
||
if (fs::exists(directory_path)) {
|
||
fs::remove_all(directory_path);
|
||
}
|
||
try {
|
||
if (!fs::create_directory(directory_path)) {
|
||
BOOST_LOG_TRIVIAL(error) << boost::format("create directory %1% failed")%directory;
|
||
return CLI_EXPORT_CACHE_DIRECTORY_CREATE_FAILED;
|
||
}
|
||
}
|
||
catch (...)
|
||
{
|
||
BOOST_LOG_TRIVIAL(error) << boost::format("create directory %1% failed")%directory;
|
||
return CLI_EXPORT_CACHE_DIRECTORY_CREATE_FAILED;
|
||
}
|
||
|
||
int count = 0;
|
||
std::vector<std::string> filename_vector;
|
||
std::vector<json> json_vector;
|
||
for (PrintObject *obj : m_objects) {
|
||
const ModelObject* model_obj = obj->model_object();
|
||
if (obj->get_shared_object()) {
|
||
BOOST_LOG_TRIVIAL(info) << boost::format("shared object %1%, skip directly")%model_obj->name;
|
||
continue;
|
||
}
|
||
|
||
const PrintInstance &print_instance = obj->instances()[0];
|
||
const ModelInstance *model_instance = print_instance.model_instance;
|
||
size_t identify_id = (model_instance->loaded_id > 0)?model_instance->loaded_id: model_instance->id().id;
|
||
std::string file_name = directory +"/obj_"+std::to_string(identify_id)+".json";
|
||
|
||
BOOST_LOG_TRIVIAL(info) << boost::format("begin to dump object %1%, identify_id %2% to %3%")%model_obj->name %identify_id %file_name;
|
||
|
||
try {
|
||
json root_json, layers_json = json::array(), support_layers_json = json::array(), first_layer_groups = json::array();
|
||
|
||
root_json[JSON_OBJECT_NAME] = model_obj->name;
|
||
root_json[JSON_IDENTIFY_ID] = identify_id;
|
||
|
||
//export the layers
|
||
std::vector<json> layers_json_vector(obj->layer_count());
|
||
tbb::parallel_for(
|
||
tbb::blocked_range<size_t>(0, obj->layer_count()),
|
||
[&layers_json_vector, obj, convert_layer_to_json](const tbb::blocked_range<size_t>& layer_range) {
|
||
for (size_t layer_index = layer_range.begin(); layer_index < layer_range.end(); ++ layer_index) {
|
||
const Layer *layer = obj->get_layer(layer_index);
|
||
json layer_json;
|
||
convert_layer_to_json(layer_json, layer);
|
||
layers_json_vector[layer_index] = std::move(layer_json);
|
||
}
|
||
}
|
||
);
|
||
for (int l_index = 0; l_index < layers_json_vector.size(); l_index++) {
|
||
layers_json.push_back(std::move(layers_json_vector[l_index]));
|
||
}
|
||
layers_json_vector.clear();
|
||
/*for (const Layer *layer : obj->layers()) {
|
||
// for each layer
|
||
json layer_json;
|
||
|
||
convert_layer_to_json(layer_json, layer);
|
||
|
||
layers_json.push_back(std::move(layer_json));
|
||
}*/
|
||
|
||
root_json[JSON_LAYERS] = std::move(layers_json);
|
||
|
||
//export the support layers
|
||
std::vector<json> support_layers_json_vector(obj->support_layer_count());
|
||
tbb::parallel_for(
|
||
tbb::blocked_range<size_t>(0, obj->support_layer_count()),
|
||
[&support_layers_json_vector, obj, convert_layer_to_json](const tbb::blocked_range<size_t>& support_layer_range) {
|
||
for (size_t s_layer_index = support_layer_range.begin(); s_layer_index < support_layer_range.end(); ++ s_layer_index) {
|
||
const SupportLayer *support_layer = obj->get_support_layer(s_layer_index);
|
||
json support_layer_json, support_islands_json = json::array(), support_fills_json, supportfills_entities_json = json::array();
|
||
|
||
convert_layer_to_json(support_layer_json, support_layer);
|
||
|
||
support_layer_json[JSON_SUPPORT_LAYER_INTERFACE_ID] = support_layer->interface_id();
|
||
support_layer_json[JSON_SUPPORT_LAYER_TYPE] = support_layer->support_type;
|
||
|
||
//support_islands
|
||
for (const ExPolygon& support_island : support_layer->support_islands) {
|
||
json support_island_json = support_island;
|
||
support_islands_json.push_back(std::move(support_island_json));
|
||
}
|
||
support_layer_json[JSON_SUPPORT_LAYER_ISLANDS] = std::move(support_islands_json);
|
||
|
||
//support_fills
|
||
support_fills_json[JSON_EXTRUSION_NO_SORT] = support_layer->support_fills.no_sort;
|
||
support_fills_json[JSON_EXTRUSION_ENTITY_TYPE] = JSON_EXTRUSION_TYPE_COLLECTION;
|
||
for (const ExtrusionEntity* extrusion_entity : support_layer->support_fills.entities) {
|
||
json supportfill_entity_json, supportfill_entity_paths_json = json::array();
|
||
bool ret = convert_extrusion_to_json(supportfill_entity_json, supportfill_entity_paths_json, extrusion_entity);
|
||
if (!ret)
|
||
continue;
|
||
|
||
supportfills_entities_json.push_back(std::move(supportfill_entity_json));
|
||
}
|
||
support_fills_json[JSON_EXTRUSION_ENTITIES] = std::move(supportfills_entities_json);
|
||
support_layer_json[JSON_SUPPORT_LAYER_FILLS] = std::move(support_fills_json);
|
||
|
||
support_layers_json_vector[s_layer_index] = std::move(support_layer_json);
|
||
}
|
||
}
|
||
);
|
||
for (int s_index = 0; s_index < support_layers_json_vector.size(); s_index++) {
|
||
support_layers_json.push_back(std::move(support_layers_json_vector[s_index]));
|
||
}
|
||
support_layers_json_vector.clear();
|
||
|
||
/*for (const SupportLayer *support_layer : obj->support_layers()) {
|
||
json support_layer_json, support_islands_json = json::array(), support_fills_json, supportfills_entities_json = json::array();
|
||
|
||
convert_layer_to_json(support_layer_json, support_layer);
|
||
|
||
support_layer_json[JSON_SUPPORT_LAYER_INTERFACE_ID] = support_layer->interface_id();
|
||
|
||
//support_islands
|
||
for (const ExPolygon& support_island : support_layer->support_islands.expolygons) {
|
||
json support_island_json = support_island;
|
||
support_islands_json.push_back(std::move(support_island_json));
|
||
}
|
||
support_layer_json[JSON_SUPPORT_LAYER_ISLANDS] = std::move(support_islands_json);
|
||
|
||
//support_fills
|
||
support_fills_json[JSON_EXTRUSION_NO_SORT] = support_layer->support_fills.no_sort;
|
||
support_fills_json[JSON_EXTRUSION_ENTITY_TYPE] = JSON_EXTRUSION_TYPE_COLLECTION;
|
||
for (const ExtrusionEntity* extrusion_entity : support_layer->support_fills.entities) {
|
||
json supportfill_entity_json, supportfill_entity_paths_json = json::array();
|
||
bool ret = convert_extrusion_to_json(supportfill_entity_json, supportfill_entity_paths_json, extrusion_entity);
|
||
if (!ret)
|
||
continue;
|
||
|
||
supportfills_entities_json.push_back(std::move(supportfill_entity_json));
|
||
}
|
||
support_fills_json[JSON_EXTRUSION_ENTITIES] = std::move(supportfills_entities_json);
|
||
support_layer_json[JSON_SUPPORT_LAYER_FILLS] = std::move(support_fills_json);
|
||
|
||
support_layers_json.push_back(std::move(support_layer_json));
|
||
} // for each layer*/
|
||
root_json[JSON_SUPPORT_LAYERS] = std::move(support_layers_json);
|
||
|
||
const std::vector<groupedVolumeSlices> &first_layer_obj_groups = obj->firstLayerObjGroups();
|
||
for (size_t s_group_index = 0; s_group_index < first_layer_obj_groups.size(); ++ s_group_index) {
|
||
groupedVolumeSlices group = first_layer_obj_groups[s_group_index];
|
||
|
||
//convert the id
|
||
for (ObjectID& obj_id : group.volume_ids)
|
||
{
|
||
const ModelVolume* currentModelVolumePtr = nullptr;
|
||
//BBS: support shared object logic
|
||
const PrintObject* shared_object = obj->get_shared_object();
|
||
if (!shared_object)
|
||
shared_object = obj;
|
||
const ModelVolumePtrs& volumes_ptr = shared_object->model_object()->volumes;
|
||
size_t volume_count = volumes_ptr.size();
|
||
for (size_t index = 0; index < volume_count; index ++) {
|
||
currentModelVolumePtr = volumes_ptr[index];
|
||
if (currentModelVolumePtr->id() == obj_id) {
|
||
obj_id.id = index;
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
json first_layer_group_json;
|
||
|
||
first_layer_group_json = group;
|
||
first_layer_groups.push_back(std::move(first_layer_group_json));
|
||
}
|
||
root_json[JSON_FIRSTLAYER_GROUPS] = std::move(first_layer_groups);
|
||
|
||
filename_vector.push_back(file_name);
|
||
json_vector.push_back(std::move(root_json));
|
||
/*boost::nowide::ofstream c;
|
||
c.open(file_name, std::ios::out | std::ios::trunc);
|
||
if (with_space)
|
||
c << std::setw(4) << root_json << std::endl;
|
||
else
|
||
c << root_json.dump(0) << std::endl;
|
||
c.close();*/
|
||
count ++;
|
||
BOOST_LOG_TRIVIAL(info) << boost::format("will dump object %1%'s json to %2%.")%model_obj->name%file_name;
|
||
}
|
||
catch(std::exception &err) {
|
||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": save to "<<file_name<<" got a generic exception, reason = " << err.what();
|
||
ret = CLI_EXPORT_CACHE_WRITE_FAILED;
|
||
}
|
||
}
|
||
|
||
boost::mutex mutex;
|
||
tbb::parallel_for(
|
||
tbb::blocked_range<size_t>(0, filename_vector.size()),
|
||
[filename_vector, &json_vector, with_space, &ret, &mutex](const tbb::blocked_range<size_t>& output_range) {
|
||
for (size_t object_index = output_range.begin(); object_index < output_range.end(); ++ object_index) {
|
||
try {
|
||
boost::nowide::ofstream c;
|
||
c.open(filename_vector[object_index], std::ios::out | std::ios::trunc);
|
||
if (with_space)
|
||
c << std::setw(4) << json_vector[object_index] << std::endl;
|
||
else
|
||
c << json_vector[object_index].dump(0) << std::endl;
|
||
c.close();
|
||
}
|
||
catch(std::exception &err) {
|
||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": save to "<<filename_vector[object_index]<<" got a generic exception, reason = " << err.what();
|
||
boost::unique_lock l(mutex);
|
||
ret = CLI_EXPORT_CACHE_WRITE_FAILED;
|
||
}
|
||
}
|
||
}
|
||
);
|
||
|
||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(": total printobject count %1%, saved %2%, ret=%3%")%m_objects.size() %count %ret;
|
||
return ret;
|
||
}
|
||
|
||
|
||
int Print::load_cached_data(const std::string& directory)
|
||
{
|
||
int ret = 0;
|
||
boost::filesystem::path directory_path(directory);
|
||
|
||
if (!fs::exists(directory_path)) {
|
||
BOOST_LOG_TRIVIAL(info) << boost::format("directory %1% not exist.")%directory;
|
||
return CLI_IMPORT_CACHE_NOT_FOUND;
|
||
}
|
||
|
||
auto find_region = [this](PrintObject* object, size_t config_hash) -> const PrintRegion* {
|
||
int regions_count = object->num_printing_regions();
|
||
for (int index = 0; index < regions_count; index++ )
|
||
{
|
||
const PrintRegion& print_region = object->printing_region(index);
|
||
if (print_region.config_hash() == config_hash ) {
|
||
return &print_region;
|
||
}
|
||
}
|
||
return NULL;
|
||
};
|
||
|
||
int count = 0;
|
||
std::vector<std::pair<std::string, PrintObject*>> object_filenames;
|
||
for (PrintObject *obj : m_objects) {
|
||
const ModelObject* model_obj = obj->model_object();
|
||
const PrintInstance &print_instance = obj->instances()[0];
|
||
const ModelInstance *model_instance = print_instance.model_instance;
|
||
|
||
obj->clear_layers();
|
||
obj->clear_support_layers();
|
||
|
||
int identify_id = model_instance->loaded_id;
|
||
if (identify_id <= 0) {
|
||
//for old 3mf
|
||
identify_id = model_instance->id().id;
|
||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(": object %1%'s loaded_id is 0, need to use the instance_id %2%")%model_obj->name %identify_id;
|
||
//continue;
|
||
}
|
||
std::string file_name = directory +"/obj_"+std::to_string(identify_id)+".json";
|
||
|
||
if (!fs::exists(file_name)) {
|
||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<<boost::format(": file %1% not exist, maybe a shared object, skip it")%file_name;
|
||
continue;
|
||
}
|
||
object_filenames.push_back({file_name, obj});
|
||
}
|
||
|
||
boost::mutex mutex;
|
||
std::vector<json> object_jsons(object_filenames.size());
|
||
tbb::parallel_for(
|
||
tbb::blocked_range<size_t>(0, object_filenames.size()),
|
||
[object_filenames, &ret, &object_jsons, &mutex](const tbb::blocked_range<size_t>& filename_range) {
|
||
for (size_t filename_index = filename_range.begin(); filename_index < filename_range.end(); ++ filename_index) {
|
||
try {
|
||
json root_json;
|
||
boost::nowide::ifstream ifs(object_filenames[filename_index].first);
|
||
ifs >> root_json;
|
||
object_jsons[filename_index] = std::move(root_json);
|
||
}
|
||
catch(std::exception &err) {
|
||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": load from "<<object_filenames[filename_index].first<<" got a generic exception, reason = " << err.what();
|
||
boost::unique_lock l(mutex);
|
||
ret = CLI_IMPORT_CACHE_LOAD_FAILED;
|
||
}
|
||
}
|
||
}
|
||
);
|
||
|
||
if (ret) {
|
||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< boost::format(": load json failed.");
|
||
return ret;
|
||
}
|
||
|
||
for (int obj_index = 0; obj_index < object_jsons.size(); obj_index++) {
|
||
json& root_json = object_jsons[obj_index];
|
||
PrintObject *obj = object_filenames[obj_index].second;
|
||
|
||
try {
|
||
//boost::nowide::ifstream ifs(file_name);
|
||
//ifs >> root_json;
|
||
|
||
std::string name = root_json.at(JSON_OBJECT_NAME);
|
||
int identify_id = root_json.at(JSON_IDENTIFY_ID);
|
||
int layer_count = 0, support_layer_count = 0, firstlayer_group_count = 0;
|
||
|
||
layer_count = root_json[JSON_LAYERS].size();
|
||
support_layer_count = root_json[JSON_SUPPORT_LAYERS].size();
|
||
firstlayer_group_count = root_json[JSON_FIRSTLAYER_GROUPS].size();
|
||
|
||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<<boost::format(":will load %1%, identify_id %2%, layer_count %3%, support_layer_count %4%, firstlayer_group_count %5%")
|
||
%name %identify_id %layer_count %support_layer_count %firstlayer_group_count;
|
||
|
||
Layer* previous_layer = NULL;
|
||
//create layer and layer regions
|
||
for (int index = 0; index < layer_count; index++)
|
||
{
|
||
json& layer_json = root_json[JSON_LAYERS][index];
|
||
Layer* new_layer = obj->add_layer(layer_json[JSON_LAYER_ID], layer_json[JSON_LAYER_HEIGHT], layer_json[JSON_LAYER_PRINT_Z], layer_json[JSON_LAYER_SLICE_Z]);
|
||
if (!new_layer) {
|
||
BOOST_LOG_TRIVIAL(error) <<__FUNCTION__<< boost::format(":create_layer failed, out of memory");
|
||
return CLI_OUT_OF_MEMORY;
|
||
}
|
||
if (previous_layer) {
|
||
previous_layer->upper_layer = new_layer;
|
||
new_layer->lower_layer = previous_layer;
|
||
}
|
||
previous_layer = new_layer;
|
||
|
||
//layer regions
|
||
int layer_regions_count = layer_json[JSON_LAYER_REGIONS].size();
|
||
for (int region_index = 0; region_index < layer_regions_count; region_index++)
|
||
{
|
||
json& region_json = layer_json[JSON_LAYER_REGIONS][region_index];
|
||
size_t config_hash = region_json[JSON_LAYER_REGION_CONFIG_HASH];
|
||
const PrintRegion *print_region = find_region(obj, config_hash);
|
||
|
||
if (!print_region){
|
||
BOOST_LOG_TRIVIAL(error) <<__FUNCTION__<< boost::format(":can not find print region of object %1%, layer %2%, print_z %3%, layer_region %4%")
|
||
%name % index %new_layer->print_z %region_index;
|
||
//delete new_layer;
|
||
return CLI_IMPORT_CACHE_DATA_CAN_NOT_USE;
|
||
}
|
||
|
||
new_layer->add_region(print_region);
|
||
}
|
||
|
||
}
|
||
|
||
//load the layer data parallel
|
||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<<boost::format(": load the layers in parallel");
|
||
tbb::parallel_for(
|
||
tbb::blocked_range<size_t>(0, obj->layer_count()),
|
||
[&root_json, &obj](const tbb::blocked_range<size_t>& layer_range) {
|
||
for (size_t layer_index = layer_range.begin(); layer_index < layer_range.end(); ++ layer_index) {
|
||
const json& layer_json = root_json[JSON_LAYERS][layer_index];
|
||
Layer* layer = obj->get_layer(layer_index);
|
||
extract_layer(layer_json, *layer);
|
||
}
|
||
}
|
||
);
|
||
|
||
//support layers
|
||
Layer* previous_support_layer = NULL;
|
||
//create support_layers
|
||
for (int index = 0; index < support_layer_count; index++)
|
||
{
|
||
json& layer_json = root_json[JSON_SUPPORT_LAYERS][index];
|
||
SupportLayer* new_support_layer = obj->add_support_layer(layer_json[JSON_LAYER_ID], layer_json[JSON_SUPPORT_LAYER_INTERFACE_ID], layer_json[JSON_LAYER_HEIGHT], layer_json[JSON_LAYER_PRINT_Z]);
|
||
if (!new_support_layer) {
|
||
BOOST_LOG_TRIVIAL(error) <<__FUNCTION__<< boost::format(":add_support_layer failed, out of memory");
|
||
return CLI_OUT_OF_MEMORY;
|
||
}
|
||
if (previous_support_layer) {
|
||
previous_support_layer->upper_layer = new_support_layer;
|
||
new_support_layer->lower_layer = previous_support_layer;
|
||
}
|
||
previous_support_layer = new_support_layer;
|
||
}
|
||
|
||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(": finished load layers, start to load support_layers.");
|
||
tbb::parallel_for(
|
||
tbb::blocked_range<size_t>(0, obj->support_layer_count()),
|
||
[&root_json, &obj](const tbb::blocked_range<size_t>& support_layer_range) {
|
||
for (size_t layer_index = support_layer_range.begin(); layer_index < support_layer_range.end(); ++ layer_index) {
|
||
const json& layer_json = root_json[JSON_SUPPORT_LAYERS][layer_index];
|
||
SupportLayer* support_layer = obj->get_support_layer(layer_index);
|
||
extract_support_layer(layer_json, *support_layer);
|
||
}
|
||
}
|
||
);
|
||
|
||
//load first group volumes
|
||
std::vector<groupedVolumeSlices>& firstlayer_objgroups = obj->firstLayerObjGroupsMod();
|
||
for (int index = 0; index < firstlayer_group_count; index++)
|
||
{
|
||
json& firstlayer_group_json = root_json[JSON_FIRSTLAYER_GROUPS][index];
|
||
groupedVolumeSlices firstlayer_group = firstlayer_group_json;
|
||
//convert the id
|
||
for (ObjectID& obj_id : firstlayer_group.volume_ids)
|
||
{
|
||
ModelVolume* currentModelVolumePtr = nullptr;
|
||
ModelVolumePtrs& volumes_ptr = obj->model_object()->volumes;
|
||
size_t volume_count = volumes_ptr.size();
|
||
if (obj_id.id < volume_count) {
|
||
currentModelVolumePtr = volumes_ptr[obj_id.id];
|
||
obj_id = currentModelVolumePtr->id();
|
||
}
|
||
else {
|
||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< boost::format(": can not find volume_id %1% from object file %2% in firstlayer groups, volume_count %3%!")
|
||
%obj_id.id %object_filenames[obj_index].first %volume_count;
|
||
return CLI_IMPORT_CACHE_LOAD_FAILED;
|
||
}
|
||
}
|
||
firstlayer_objgroups.push_back(std::move(firstlayer_group));
|
||
}
|
||
|
||
count ++;
|
||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(": load object %1% from %2% successfully.")%count%object_filenames[obj_index].first;
|
||
}
|
||
catch(nlohmann::detail::parse_error &err) {
|
||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": parse "<<object_filenames[obj_index].first<<" got a nlohmann::detail::parse_error, reason = " << err.what();
|
||
return CLI_IMPORT_CACHE_LOAD_FAILED;
|
||
}
|
||
catch(std::exception &err) {
|
||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": load from "<<object_filenames[obj_index].first<<" got a generic exception, reason = " << err.what();
|
||
ret = CLI_IMPORT_CACHE_LOAD_FAILED;
|
||
}
|
||
}
|
||
|
||
object_jsons.clear();
|
||
object_filenames.clear();
|
||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__<< boost::format(": total printobject count %1%, loaded %2%, ret=%3%")%m_objects.size() %count %ret;
|
||
return ret;
|
||
}
|
||
|
||
BoundingBoxf3 PrintInstance::get_bounding_box() {
|
||
return print_object->model_object()->instance_bounding_box(*model_instance, false);
|
||
}
|
||
|
||
Polygon PrintInstance::get_convex_hull_2d() {
|
||
Polygon poly = print_object->model_object()->convex_hull_2d(model_instance->get_matrix());
|
||
poly.douglas_peucker(0.1);
|
||
return poly;
|
||
}
|
||
|
||
//BBS: instance_shift is too large because of multi-plate, apply without plate offset.
|
||
Point PrintInstance::shift_without_plate_offset() const
|
||
{
|
||
const Print* print = print_object->print();
|
||
const Vec3d plate_offset = print->get_plate_origin();
|
||
return shift - Point(scaled(plate_offset.x()), scaled(plate_offset.y()));
|
||
}
|
||
|
||
} // namespace Slic3r
|
