mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-05-15 01:22:07 +00:00
* ENH: Show Recent File Image Keep Scale Change-Id: Ib8a6cf916eaee8e353bf858bc4f2ea503705809e * FIX: wipetower position problem jira: STUDIO-4914 Change-Id: I7b05d3c53931ed8ce3d4603ff21ee6ef675611e5 * FIX: dailytips adapts screen scale jira: STUDIO-5019 STUDIO-5026 STUDIO-5027 STUDIO-5028 STUDIO-5025 Change-Id: I63d3af1870218ba8e0f048a6ef03fb29fabe27cb * FIX: generate process preset based on template Jira: XXXX Change-Id: I50adf0790dc239307d236a4cebece860ef6beb16 Signed-off-by: maosheng.wei <maosheng.wei@bambulab.com> * FIX: object list plate name edit Change-Id: I61d3dcd7d9598d759a3a0b44cc77d2af2adca25a Jira: STUDIO-4937 * ENH:no longer checking nozzle type jira:[for nozzle type check] Change-Id: I0e88445a264f21b0c11519e9a22a165d05611d14 * ENH: improve first layer tree support First layer support can't be top interface, and min brim width of auto mode should be larger than 0. Jira: STUDIO-5010 Change-Id: I02f8b017b535f8a47965387e8679f692b1966e04 (cherry picked from commit 3e7d54abe352e8ab5f9d6492b5a86a96f9067f94) * ENH: version: bumped to 1.8 JIRA: no jira Change-Id: I50903098b59f1dd9a6b6cf7656cec7d388f3ff17 * ENH:try again after subscription failure jira:[Try again after subscription failure] Change-Id: Ibfb1e8e26eb166d786a372632a86ef98030db034 * ENH:display msg dialog once jira:[for http error msg] Change-Id: I12e9c155fdb567cac99c35b6feeef650269ba75d * ENH:remove config.json file Change-Id: Idfcf3a63fefe968e88153c26fb691fd05cd83dc4 * ENH:add protection in threads jira:[for random crash] Change-Id: I6286012dd77abccba461f7cd72a6fc531a84c95f * FIX: add protection for get_model_task_thread thread Jira: XXXX Change-Id: I3cbc17d181a0e13c658f31eaeb6a4df878e6df41 Signed-off-by: maosheng.wei <maosheng.wei@bambulab.com> * FIX: delete all compatible presets when delete third printer Jira: XXXX Change-Id: I1a294402627e7ab7a8c6701f20679b3d04aff059 Signed-off-by: maosheng.wei <maosheng.wei@bambulab.com> * ci: update build version to 01.08.00.51 Change-Id: I20a01adacbdb5fe69c106b9efd029f7308136e10 * ENH: default open support_interface_not_for_body jira:[NEW] Signed-off-by: xun.zhang <xun.zhang@bambulab.com> Change-Id: I48e084deb18633f9ec47a8ec4ec643163bf66318 * ENH:modified text with too low version jira:[for low version] Change-Id: I862a0defda976a35f326a8805e002330f2ed7fdf * NEW:update printer config file version Change-Id: I9a46b29b03beb67a3da0b8f76d8b5c4b3c482928 * FIX:The plane should rotate around the world coordinate system Jira: STUDIO-5054 Change-Id: I16e484b38d79cabd9473acf1abf3c5c6b0adc4c6 * ENH:translate for limit file size and so on Jira: STUDIO-5007 Change-Id: I2c279eb690841aa51cd8128f8028266cbc17e977 * ENH:use on_render_rotate_gizmos() replace GLGizmoRotate3D::on_render() Jira: STUDIO-4227 Change-Id: If9b9ea5596e59472d5fa87ac56aeb7f6ecc65643 * FIX: some mistakes in filament profiles jira:[NEW] Signed-off-by: xun.zhang <xun.zhang@bambulab.com> Change-Id: Ibe7f3650f2d9cf47561dd5f2ec591a5f6c553503 * FIX: fix shard_ptr is null Change-Id: I0187cf64ffbb08a2265a11900b5c865e9ac9678f * FIX:N1 printer image in dark mode JIRA:STUDIO-4057 Change-Id: I22c001d96839daf213d5096f6ff6e3d6398fa8c4 * FIX: create printer issue Jira: 5034 5059 5053 5034 create printer but filament is repeat 5039 create successful dialog remove to center 5053 create existing printer copywriting adjustments and preset updates Delete printer secondary confirmation dialog Change-Id: Ifb3822d1e168459d2af11e02b31ecaf3719d338a Signed-off-by: maosheng.wei <maosheng.wei@bambulab.com> * ENH:just don't check the nozzle diameter jira:[for nozzle check] Change-Id: I678e7d62832eaa14b9be47d6dce70f29ebd601f6 * NEW:p1 and x1 series added motor noise calibration JIRA: 5085 Change-Id: Id73cc2d34b6130f215d81ffcdc39ba6b241445bf * ci: update build version to 01.08.00.52 Change-Id: I93d800b413f2751d132fac53fbd9b191603d0352 * FIX: ObjectSetting changed when search plate JIRA: STUDIO-5095 Signed-off-by: Kunlong Ma <kunlong.ma@bambulab.com> Change-Id: I39b1ad997d51ac4224ff5ad2b3555f56da4bd911 * FIX: invalid support params in 3rd party profiles Many params are not right.Just use default jira:[NEW] Signed-off-by: xun.zhang <xun.zhang@bambulab.com> Change-Id: I5c4a1e8b046940e174f5681a79031b5f20fcafc5 * ENH: update A1 mini start gcode Change x-axis freq sweep amp 5->10 jira:[NEW] Signed-off-by: xun.zhang <xun.zhang@bambulab.com> Change-Id: I2e731cc6392c0204d5e4467bf4b933ab233bc157 * FIX: [STUDIO-4946] use utf8 path to create sub process Change-Id: I5873c114e8cd36978a7d50bf13c3aa7bf8b740ca Jira: STUDIO-4946 * FIX: fix a plate state not correct issue JIRA: no-jira the object and instance states lost after undo Change-Id: I527df9a7d426d994501a4ed5bbb198c7bbac810b * FIX: some translation Jira: 5096 5089 5036 5004 Change-Id: I4f1bd6e352b11451f5caf02cbc4eeb31dfa03eee Signed-off-by: maosheng.wei <maosheng.wei@bambulab.com> * FIX: [STUDIO-4935] plate name edit in object list Change-Id: I271fa217281d0c7ceca61166497136628a66681e Jira: STUDIO-4935 * FIX: take custom root as AMS sync candicate Change-Id: I9c71babcd74238d1533b15d77a978b19997c70c0 Jira: none * FIX: modify some default support params in code 1. Modify default values of some supports params, so 3rd party profiles are easier to setup. 3. Fix a bug that organic support may cause crash. Jira: none Change-Id: Icae348d8fe5985f4287404e96089198a499283f2 (cherry picked from commit 8889cfc703b72e142f288e525b89c87619f2213c) * FIX: do not generate sheath for normal support Jira: none Change-Id: I8f3f3e39171055f8d18c06ceee8e245594273238 (cherry picked from commit 93bc7ecf4346f179f502bebc3cf47b0030b56e2c) * FIX: push_notification on GUI thread Change-Id: Iaec347f5684fe0f65d6418759518189b67033c42 Jira: STUDIO-5106 * ENH: CLI: add some params to support more functions 1. uptodate_filaments to support update the original filaments to newest config 2. allow_rotations/avoid_extrusion_cali_region for auto-arrange 3. skip_modified_gcodes to support skip modified gcodes JIRA: STUDIO-5112 Change-Id: I95c09af1b5462cce3bf27aea32228d6d1d1d201d * FIX: missed manually entered values for secondary processing Jira: STUDIO-4964 Change-Id: I5cf0da1ae77cccd34de05b4a0318a751ac9f6753 * FIX: Z hop is still enabled when upper boundary is zero. Jira: STUDIO-4893 Signed-off-by: wenjie.guo <wenjie.guo@bambulab.com> Change-Id: I5f46a02e1fbb15ff43e253e3a184aa6cc38e7598 * ENH: update default filaments for Bambu printers jira:[NEW] Signed-off-by: xun.zhang <xun.zhang@bambulab.com> Change-Id: Ic6380f39e546854ad0b7dc36929a8605c9ab3acc * ENH: dailytips modification 1. modify closing behavior 2. dailytips can adjust self size according to the canvas size. And also adjust GodeViewer legend window size 3. fix a button text encoding bug 4. support vertical/horizontal layout(horizontal layout currently not used) jira: new Change-Id: I8e0b6e85c455d0608d7388fb441829c1991ad01f * FIX: [4857 5097] export list and del preset two confirm issue Jira: 4857 5097 Change-Id: If7cc4967a663f575527a227e9c4ac31e0491930c * FIX: UUID conflict issue when referencing volume Jira: XXXX 3mf file standard Change-Id: I953a87294684ea85d03a95e7d2843c096904aeae Signed-off-by: maosheng.wei <maosheng.wei@bambulab.com> * FIX: [4483 5003 5109] create printer and edit filament issue Jira: 4483 5003 5109 4483 dialog blink 5003 preset list too long 5109 encode Change-Id: I190e12272ca09f36b841f2f85a6cf60f2c2614bd Signed-off-by: maosheng.wei <maosheng.wei@bambulab.com> * FIX: cloud use presets limit notify Change-Id: I6cc7b4e560cb83db0fc30921633b10531957128e Jira: STUDIO-5091, STUDIO-5104 * FIX: do user preset sync later on startup Change-Id: I0653a0438477b1c803ce1cddc66ef47f95616dae Jira: STUDIO-5106 * FIX: linux: pressing enter in height range will crash jira: STUDIO-4391 Change-Id: I6bf990951d1456f5b2605b8d62a05bceb3cc4c10 * FIX: failed to limit the max width of DropDown Jira: STUDIO-4503 Signed-off-by: wenjie.guo <wenjie.guo@bambulab.com> Change-Id: Id9352d16f4bc016daade72a9c8d3d90164a1cb3d * FIX: not jump to preview after first wizard Change-Id: I8c94d66a91aa15a7874441a300b40438638bd33b Jira: STUDIO-5018 * ENH: CLI: clear custom gcodes when skip_modified_gcodes JIRA: STUDIO-5112 Change-Id: I2e7346d2ac57519029a4e80e5492c34d3d91ed77 * FIX: [4492 4851 4883 5121] create printer issue Jira: 4492 4851 4883 5121 Change-Id: If252b5f30be0403f79410aa8c00fc14b066d5bbd Signed-off-by: maosheng.wei <maosheng.wei@bambulab.com> * ENH: add 'edit preset' and 'delete preset' btn for each preset Jira: 5200 5113 Change-Id: I208ad63eb4b895306fa76db424da2e1df10a582e Signed-off-by: maosheng.wei <maosheng.wei@bambulab.com> * FIX: add skip label before tool change Jira: 5074 github: 2776 Signed-off-by: qing.zhang <qing.zhang@bambulab.com> Change-Id: Icaafd3b45da1e78c1a82e7d17d7505d9439b9100 * FIX:Network test dark mode adaptation JIRA:STUDIO-2468 Change-Id: I20cb7f1fd8eca3ce852acb563c1cc87978e216dc * FIX:n1 external feed prompt pop-up without retry button JIRA: STUDIO-4696 Change-Id: I31069c72e29d3398469d71cdbc2a344a5430fc2c * FIX: not show device page when switch printer preset Change-Id: I00d8524625a4682b6a39876ddb66bf8bd928dbef Jira: none * ENH: Check the nozzle diameter when sending calibration Jira: 4977 Change-Id: Iabbba44583bbd9fbaaa889ca546ee0ccbb2aa77f * FIX: Generate UUID from objectID and volumeIndex Jira: XXXX Change-Id: I65147ef9b695f8af8de260d722e604b0e0bab563 Signed-off-by: maosheng.wei <maosheng.wei@bambulab.com> * FIX: disable filament_typep Jira: XXXX Change-Id: Ib605b33e4474525fbe49e70596fc09aa356f478a Signed-off-by: maosheng.wei <maosheng.wei@bambulab.com> * ci: update build version to 01.08.00.53 Change-Id: I1d574fa2cf2a4d0eb63a38eb8ced7587d06a4272 * ENH: refine display logic of param 1. Refine the display logic of "support_interface_not_for_body".Only toggle if support_filament is default and support_interface_filament is specified jira:[NEW] Signed-off-by: xun.zhang <xun.zhang@bambulab.com> Change-Id: Ia2af030c4531ad6b04a198bfe8a1677b3d20a800 * FIX: user preset sync token Change-Id: Id2aa865b778ee9ac4cfddb68ceef0374507b519b Jira: none * FIX: Bitmap cache not take effect Change-Id: I972098fdbda0b4542c6c759a8f5e1f0f2a30852b Jira: STUDIO-4991 * NEW: Open HotModel Link With GetParam-From bambustudio JIRA: NO JIRA Change-Id: I4ac49bac5ee0c50988c76a38b00b7ba7dc3201f5 * NEW:AmsMaterialsSetting Support for user-preset JIRA: STUDIO-5135 Change-Id: If848047cd5dbd059d440de30989c505c361305a7 * FIX: upload custom root preset fail Change-Id: I621c8d542dd604b07cc5df63d97d7a31558d3aba Jira: none * FIX: show custom filament in AMS filament list Change-Id: I79b9f8f2f08db8c52bbed76f1ea133baff383c00 Jira: none * FIX: dailytips window and gcodeviwer legend window size issue reset to original logic of dailytips and legend window size jira: new Change-Id: Iacb016bb222ba3f87317cfbe1f2b003802d773a5 * ENH: add text translation jira: new Change-Id: I780cfb8a0a64d806b5e0a414b6598e3b7bdf52dc * FIX: Delete and search object outside the plate JIRA: 1. STUDIO-5132 Deleting object outside the plate will crash 2. STUDIO-5146 The search function cannot search for object outside the plate Signed-off-by: Kunlong Ma <kunlong.ma@bambulab.com> Change-Id: I84cb3fe990a9c2a182e7434c262466a70545280e * FIX: [5149 5142 5141 5140 5136] create printer and filament issue Jira: 5149 5142 5141 5140 5136 5149 process preset name can not show all 5142 improt configs combobox not update 5141 disable modify filament_vendor 5140 disable input Bambu and Generic vendor 5136 preset list window adjust Change-Id: I111a23996146cc16cc7f533c8616d50223d34c40 Signed-off-by: maosheng.wei <maosheng.wei@bambulab.com> * ci: update build version to 01.08.00.54 Change-Id: Ifd69c01a82f627a9c6cf4fe0d48a759563ee90e7 * FIX: print model from sdcard with p1p Change-Id: If85383ba762022ead3dd754ae02a08817b891114 Jira: none * FIX: dailytips text translation jira: STUDIO-2556 Change-Id: If44e503615b09ee1692f42ba1f998918ec5bd691 * FIX: clone shortcut key conflict with quit in macos jira: STUDIO-5166 Change-Id: I548f275bb68d3b0e6bb3cfad6fe93df09d507da3 * FIX:User preset material settings dependent on firmware JIRA: 5167 Change-Id: I82cf26848594b01155883ad0aa2e9ee77d371fb2 * ENH:update the description of nozzle detection Change-Id: Id27b25c69dc11fcf66fc82053af705906ae8c370 * FIX: [5159 5165 5171 5172] create printer and filament issue Jira: 5159 5165 5171 5172 5159 create printer dialog no refresh 5165 create printer 2 step dialog no refersh 5171 change font 5172 edit filament dialog darkUI issue input special character is prohibited '/' in preset name translate to '-' update printer combobox Change-Id: I5fa27836dab7f604f1a065c65efa099c7a2f0f96 Signed-off-by: maosheng.wei <maosheng.wei@bambulab.com> * ci: update build version to 01.08.00.55 Change-Id: If1865d561cf274719204662314de163497759e89 * FIX:fix GLmodel deconstruction causing section not to be rendered Jira: STUDIO-5156 Change-Id: Ibb2f459920989ee54f7b827352dc8893424b4de6 * FIX: missing unlock cause device or resource busy Change-Id: I87563312ea9c6ce4e4e471da7ce7a02b53b64762 * FIX: some translation Change-Id: I9758cbc758030b5a3945697a50ca4898af9fcb1b * ci: update build version to 01.08.00.56 Change-Id: Id5ee53dd2ebb0b37b6927dc58b3cca94a1f66a83 * ENH: remove PLA GLOW in A1 mini jira:[NEW] Signed-off-by: xun.zhang <xun.zhang@bambulab.com> Change-Id: Id99c1bbd4248e28df9150a85eecec831f6f32856 * ci: update build version to 01.08.00.57 Change-Id: Ib4dfa60f097128b76b95bb14ca04978619021b56 * Allow line width of nozzle diameter * 2.5 As it were, 1 mm would be disallowed but 0.99 would be allowed for 0.4 nozzle. 1 mm is the sane maximum and 0.99 is unnecessary tedious to write. * Russian translation update Russian translation Bambu Studio_v1.8.0 Beta * FIX: scale problem in needs_retraction jira:[NEW] Signed-off-by: xun.zhang <xun.zhang@bambulab.com> Change-Id: Idfbe954b22fa6aa5769c55e46874fa6a80ecbf45 (cherry picked from commit 4e853f50b748e3af11e2d64862b6ee557fda361b) * ENH: CLI: support load_assemble_list JIRA: STUDIO-4848 Change-Id: Ife11533740988331ea71eac86c370e625970cb8b * FIX: align to Y not working This is a bug introduced in 7fbb650 when solving jira STUDIO-4695. Now we use a more decent way to solve it. Change-Id: I92deffcb9fe53e8a24c93fe973446ae37df07375 (cherry picked from commit bd98430dbd15eb6c9bb4b447990e0dcf8a50eef0) * ENH: Add buried points for cut and meshboolean JIRA: NONE Signed-off-by: Kunlong Ma <kunlong.ma@bambulab.com> Change-Id: I67ce498d0c335dd7a8582f56b880c2c8314f8541 * FIX: 5092 edit filament add scrolled window Jira: 5092 Change-Id: I53ae996b04e4e2f1b1ddce6a858d505001b11615 Signed-off-by: maosheng.wei <maosheng.wei@bambulab.com> * FIX: can not select user preset when create filament Jira: XXXX github: 1936 and fix add preset for printer dialog can not show selected printer Change-Id: Id4308c6bdca17d52d4aa321db359941aa87e0e45 Signed-off-by: maosheng.wei <maosheng.wei@bambulab.com> * ENH: template filament don't be show in filament list and sort Jira: 5160 5179 Change-Id: I56a7e1897e1ef3c061dc66d318896413ca25b76b Signed-off-by: maosheng.wei <maosheng.wei@bambulab.com> * FIX: [5174] export configs dialog issue filament name too long to can not show all Jira: 5174 Change-Id: I92018c9d7f86009b78b533592d899b4b5d78c3c8 Signed-off-by: maosheng.wei <maosheng.wei@bambulab.com> * ENH: add filament Bambu TPU 95A HF 1.As title jira:[NEW] Signed-off-by: xun.zhang <xun.zhang@bambulab.com> Change-Id: I752ec43da6297a6c172679997ce68f2318a7b8fb * ENH: modify some params with filaments 1.Modify the PEI bed temperature of PLA Basic, Matte, and Tough to 65 in A1 mini. Set the bed temperature for the first layer of Bambu PETG-CF to 65 and 70 for the other layers jira:[NEW] Signed-off-by: xun.zhang <xun.zhang@bambulab.com> Change-Id: Ia902bbb7f824082d5346709d781cac64296f47a8 * ENH: add more status during printing JIRA: STUDIO-5195 Change-Id: I85b3107839c6e2fdecbc10d90a876463e284468c Signed-off-by: Stone Li <stone.li@bambulab.com> * FIX:cut imgui has overlapping rendering on Apple Jira: STUDIO-5150 Change-Id: I7969e19dc189cd617026a183067dad628208955c * FIX:not TakeSnapshot for m_start_dragging_m Jira: STUDIO-5176 Change-Id: Ia03e3e2c2664dbdcffa19ec8d0fa97dfd95e6d35 * FIX: rendered color changes Jira: STUDIO-4956 during the drag processin connectors editing state Change-Id: I3027176ea9f93a9ba9d6a2052f41aaa4adef79f1 * FIX: merge the patch from Prusa Thanks for PrusaSlicer and YuSanka Jira:STUDIO-5175 commit 510d59687b3b19c4a0f4e6540620d0694dd1b7ac Author: YuSanka <yusanka@gmail.com> Date: Thu Oct 5 14:13:14 2023 +0200 Follow-up 1b451cdf: Fixed #11415 - Connectors disappear when slicing => only when using multiple cut planes AND excluding parts Change-Id: I9ccd5b85f482d723d21fccf5e104c9e0a9cc4849 * FIX:Press ESC directly to exit after entering the profile rendering rendering is not normal,Code from PrusaSlicer,thanks for PrusaSlicer and enricoturri1966 commit a078627552f54497ed0518dc7bc349d243576d19 Author: enricoturri1966 <enricoturri@seznam.cz> Date: Mon Jan 30 14:00:02 2023 +0100 Follow-up of 1218103fd620b319c56fd08116f81b581c537188 - Fixed gizmo missbehavior when closing a gizmo by resetting the selection clicking on the scene Jira: STUDIO-5164 Change-Id: I261da9dba2a5ac37f3e263c175fbccd80d8045bd * FIX: correct the strings and move create printer dialog center Jira: 5221 5183 Change-Id: Ida4eba63f0e962ffcc8000fcc04cf20849577217 Signed-off-by: maosheng.wei <maosheng.wei@bambulab.com> * ENH: CLI: skip layer height limit validate when slicing for existing models JIRA: no jira Change-Id: I1444a28b500ca7d08ed2606eecfa5cfaf261105e * ENH:update the translation of auto refill jira:[for translation] Change-Id: Iaa7b4f3d7cd88c8b4f69a3db721ebd8ca8986eea * FIX: icon issue for copying Jira: STUDIO-4168 Icon issue when filling bed with copies Change-Id: I61a03ecae02b75602c236ed2810e9c9cfe5a19f9 (cherry picked from commit b5079f8a2e79f19f65803f23ef4fd83aff17c84a) * ENH: update some filament params 1. Modify texture bed temp to 65 2. Modify max-v-speed for ABS 3. Modify some params in Generic PA 4. Modify PLA,PVA params jira:[NEW] Signed-off-by: xun.zhang <xun.zhang@bambulab.com> Change-Id: I42584a6015b8526f6bbb93024316968198bd76ce * FIX: 3770 printable checkbox incorrect display in darkUI Jira: 3770 Change-Id: I97f67d7a0ffc41f6ee625abeecc52ee4e73cf318 * FIX:Display garbled code in AmsMaterialsSetting pop-up Change-Id: I50531e939afa7715ae376bac47172ccf7b248114 * ENH:Modifying the Line Color of Transparent Materials JIRA: STUDIO-4311,5088,4983 Change-Id: I9e63413dc9cd7d523f0f7f1a2e32c4537a84467a * FIX: crash when async delete printer file Change-Id: I92c5e812d04da263338fb0eea2fd7583cf9ecde0 Jira: STUDIO-5222 * FIX: 3733 backup time not effective Jira: 3733 Change-Id: I50c2ce156fcbd0a17aa8a6777bce04aa6093c830 Signed-off-by: maosheng.wei <maosheng.wei@bambulab.com> * FIX: enable edit and delete preset btn and fix issue Jira: XXXX Change-Id: I724d7236b28fcc4746698f094531948a8fbb5d93 Signed-off-by: maosheng.wei <maosheng.wei@bambulab.com> * FIX:send print job,file name displays error JIRA:3137 Change-Id: I1c113025d274a13fba1b845a58aada14058fadd4 * FIX: skip hold user preset from sync Change-Id: I2252246e19bd80903ad82170782ea49535d30d05 Jira: STUDIO-5185 * FIX: 5115 translations Jira: 5115 Change-Id: I21b03bdd4d28c0bb097226143177e763cf8c777f Signed-off-by: maosheng.wei <maosheng.wei@bambulab.com> * FIX: add link for ironing parameter Change-Id: I451f5549db3ac2205aa5703a2e5edc831e946af8 * FIX: scale problem in lift type decide 1. Scale the travel threshhold jira:[NEW] Signed-off-by: xun.zhang <xun.zhang@bambulab.com> Change-Id: Ib594d640fe63b0919bc9318af88577513f7dbf30 * ENH: add small perimeter speed and threshold The original param is added by Prusa. Thanks orca for adding threshold. 1. Re add small perimeter speed and threhold. github: #2221 Change-Id: I35b269b26f085d80f0edca28650bb21fc04898d7 * FIX: modify the picture of pa manual cali Jira: STUDIO-5102 Change-Id: Id87898959ad4461b7bd2505b159271f2aa589c36 * FIX: Filament preset is the same with the first one Jira: STUDIO-4519 Filament preset is the same wit the first one, it should align with the last one. Signed-off-by: wenjie.guo <wenjie.guo@bambulab.com> Change-Id: I304d0ff0fbc1c8948d410ea552e4d42b6a4e8fd9 * FIX: scoreDailog dark mode issue Jira: 4570 Change-Id: I8be97b306a1494f73e3bba678ecc864e7ff88ca3 * FIX: CLI: fix the slicing issue while only one object with multicolor using seq-print JIRA: no-jira Change-Id: Iea2d23ff8e484bf2fd58aa2f596a8e4e4292fe39 * ENH: open support wall count for normal support 1. open support wall count for normal support Enabling this option makes normal support stronger and gives better overhang quality, but also more difficult to removal. Jira: STUDIO-5192 2. fix a bug where tree support (hybrid style) may get overlapped extrusions near the walls. 3. fix a bug where raft layers can't be 1 in tree support Jira: STUDIO-5261 Change-Id: Iadc0c67a9b50b5b221c8e83d5aa22ed282018cf8 (cherry picked from commit c0bb0084e386cb70ed6e16edf93190e4b38f5b90) * FIX: compiling error on linux jira: none Change-Id: I1a4563503b5ddf74a1979cc0cee7a15b8aced904 (cherry picked from commit de52c6ca62c9f3a6314ddf5a856c1d8534329886) * ENH: add translation for small perimeter jira:[NEW] Signed-off-by: xun.zhang <xun.zhang@bambulab.com> Change-Id: I95013649e4e0c07c0f04b89a91488814c8d228cc * FIX: clone shortcut key issue on macos jira: STUDIO-5166 Change-Id: I1967da1d443ed43bd750dad8e11560688d7bd533 * FIX: custom gcode window cannot paste/ navigate jira: STUDIO-5208、STUDIO-5070 Change-Id: I4ecb9d06cf5db0ae53a4678181aae9298bac106b * ENH: modify dailytips collapse & expand interaction jira: STUDIO-5209、STUDIO-5210 Change-Id: Ifb0b998e5004d4b49390ba5a250eaf4743bf3471 * ENH:Add shortcut keys and lists for objects search JIRA: STUDIO-5157 STUDIO-5158 STUDIO-5240 Signed-off-by: Kunlong Ma <kunlong.ma@bambulab.com> Change-Id: Ic7cfaaa9b4bb8a85208bafab7fe3bafdb78f0045 * FIX:Re-calculate button with White Box displayed in dark mode JIRA: STUDIO-5098 Signed-off-by: Kunlong Ma <kunlong.ma@bambulab.com> Change-Id: I07cc6c72d5dbd03b72573cd27dd5938bb0e6a29a * NEW: display plate index when printing a task JIRA: STUDIO-2689 display on the thumbnail of the current task Change-Id: I5f1f46c56e9d1e9120a66d491551908dfad099d6 Signed-off-by: Stone Li <stone.li@bambulab.com> * ENH:fixed incorrect path prefix jira:[for file path prefix] Change-Id: Ie9e3999f02b78272e528ceceb479e746d46a7e6c * FIX: thumbnail is not clear in dark mode JIRA: STUDIO-5087 Change-Id: Ie86493ed71b5554095927f061509a1f551758b61 Signed-off-by: wenjie.guo <wenjie.guo@bambulab.com> * FIX: translation Jira: XXXX Change-Id: Id03f0d704aa852632a907ea628d1277400112062 Signed-off-by: maosheng.wei <maosheng.wei@bambulab.com> * ENH: first nozzle change to 0.4 and nozzle change to mm Jira: XXXX Change-Id: I14defd36442dbd7008e46782b08415b6244224f1 Signed-off-by: maosheng.wei <maosheng.wei@bambulab.com> * ENH:editing_window_width's value is small on the laptop Jira: STUDIO-5238 STUDIO-5265 apply_selected_connectors should check_and_update_connectors_state Change-Id: I8c2c1c920cc4d197d1908815a3e62f4962335451 * FIX: fix new_bed_shape's calculation process Jira: STUDIO-5122 Change-Id: I5f3e6a301a297123af28692c90bef6759f425b06 * ENH:update some translations jira:[STUDIO-5262] Change-Id: Idb1d3586888043ac325f272bc7a2b788adb3e9e5 * FIX: edit text command resets object settings Jira: STUDIO-4655 Signed-off-by: wenjie.guo <wenjie.guo@bambulab.com> Change-Id: Iac25c4e40f1d0d32e6d1f40e62226cc22bc96042 * ci: update build version to 01.08.00.58 Change-Id: Iacfec02aa50c617e4c9fe566319b07b30d47dce1 * FIX: remove GetUserAgent Change-Id: I92886e1f0dcb091109231a10da8c19d51178e13b Jira: STUDIO-5205 * FIX: nozzle_diameter_map data structure adjustment Change-Id: Ifb724afc0fcf974e6d331e73ecac723107a102cf * ENH:add _A and _B for perform_with_groove Jira: STUDIO-5267 Change-Id: Iee3310dfa1cd8e6680310f0af0eff5c817490813 * ENH:is_equal for min_z and max_z Jira: STUDIO-5267 Change-Id: I9493883d8be9d44e26ddc9afe62b7e9eb09c5052 * ci: update build version to 01.08.00.59 Change-Id: Ie8ed29ccf5d6c94594eb2ab8b717416fbeace3bd * FIX:Image display unclear in light mode JIRA:5161 Change-Id: I134cc64a2af0dfff60c47d0ff09d78d9c0f86b3f * FIX:fix bugs of non manifold edge Jira: STUDIO-5267 Change-Id: I8ac9a2cf96da0bc07ee00b309e65611b92fd174d * ENH:nozzle type detection jira:[STUDIO-5246] Change-Id: Ic41a2161a0e41d23f56af93ad8ec34cc83ada0e3 * ENH: upadte P1S start gcode 1.turn on MC board fan by default on P1S jira:[NEW] Signed-off-by: xun.zhang <xun.zhang@bambulab.com> Change-Id: I5b2f7868e350942fb8b7baf7d429e22a0987184a (cherry picked from commit e866a575b6b7d9552f7412f84272f4b48dfc3961) * ENH: improve support style's tooltip jira: none Change-Id: I8ee858d7052f04ce7ea6b226a500c7d1bf8a482f (cherry picked from commit 665f31c4fcde22bd894cbb4a5fb160635947f2a4) * ENH: set layer range error to warning 1. If layer range exceeds maximum/minimum layer range in printer settings,pop up a window to warn jira:[NEW] Signed-off-by: xun.zhang <xun.zhang@bambulab.com> Change-Id: I0304ee790e557ecf967f355c171993d1f51b5057 * ENH: CLI: remove the warning of layer height JIRA: no jira Change-Id: Idaceee4f52611479fc3f4238d016d891b4dc8cd1 * FIX: the word search is not translated Jira: STUDIO-5224 The world search in the device panel is not translated. Signed-off-by: wenjie.guo <wenjie.guo@bambulab.com> Change-Id: Ia3d651c2159a3aad94e10cd0a6da98848f53ee2a (cherry picked from commit 4a46a0a4750d82d49c9523f4b668a1a00c41ed83) * FIX: Bitmap will flash when sending printing task Jira: STUDIO-5278 Signed-off-by: wenjie.guo <wenjie.guo@bambulab.com> Change-Id: Ib0c8710b8d5d6b98fad043c950f054aa35bea965 * ENH:display the euler angle of rotation plane Jira: STUDIO-5268 Change-Id: I6b7b431931d60f1a9a832908400417781798c472 * ci: update build version to 01.08.00.60 Change-Id: I1c15b5c6437554c43327cd6b537f7a5860dba5a0 * ENH:cancel EnterReturnsTrue for imgui in cut Jira: STUDIO-5269 Change-Id: I2832e1dccaf9755448debe7b2bd56426f90dfe0d * ci: update build version to 01.08.00.61 Change-Id: Ib03e664a20990322c788686550c491d0139d8237 * FIX: some translation problems jira:[NEW] Signed-off-by: xun.zhang <xun.zhang@bambulab.com> Change-Id: If9f2af53b4f0bfa9469e84bcba68cc182df4a473 * add: Ukrainian lang support for 1.8 * fix linux * fix some string and colors * fix linux build error 2 * fix .gitignore * FIX: calibration selected preset is null in some case jira: STUDIO-5258 Change-Id: Iee63593c5f833c5a43e3b1d1c9ddb82f8c69569a * FIX: create filament issue Jira: 5296 5297 5295 5302 5311 5276 5296 create filament: list has same printer 5297 create filament: filament combobox has blank options 5298 edit filament: delete last preset prompt users 5302 create filament: filament combox has cili preset 5311 create filament: printer name too long to can not show all 5276 edit filament: PLA Aero filament type filter issue add prusa vendor Revised copy Change-Id: I5dcc615ce0951b1a9953fa12283f6090f5069045 * FIX: some translation Change-Id: Icb8614a0af18f96d15f3b97c17e0f6f708296847 * FIX:cancel is_equal for slicing function Jira: STUDIO-5267 Change-Id: I66d759aa2c968f8a28a6a5d8378929754f2db689 * FIX:UI stuck due to pop-up window with wrong chamber temperature JIRA: 5304 Change-Id: I1a49a7219b7a6f5700243704c348724e7930ce1a * FIX: allow input '+' and hide edit preset btn Change-Id: I97aec7c3ac4cc8b9d6c535f0126aaa1926553d86 * ENH: handle printer direct close and not retry Change-Id: I5dd55f8085cf6383a8420ab41e614ea6ae210c78 Jira: STUDIO-5305 * ci: update build version to 01.08.00.62 Change-Id: I09716bf79354b503197c751272fd3171e0abc8fd * add: new translation to ukr for AirFlow and Prusa * add: Texture Plate name fix * add new feature to localization .de, fix .it (#2876) * FIX:add slice_facet_for_cut_mesh api for cut tool and modify section_vertices_map 's traverse Jira: STUDIO-5267 Change-Id: Ifc4b183a4e4c4fdb4f47742f14f70a1ed93fa056 Change-Id: I52bfaef8926ef967b78a6cb712a1731a1b528a24 * FIX: Make the front smaller for Czech in device panel Jira: STUDIO-5151 Signed-off-by: wenjie.guo <wenjie.guo@bambulab.com> Change-Id: I315174b55f923da069854fb4fed8cf3937b82074 * FIX: there is no object can be jumped to in notification jira: new Change-Id: Ib81bf49236952ede24a2de126051572d63916e01 * FIX: add height range, modifier in Preview pane will crash jira: STUDIO-5340 1. fix crash at add height range, modifiers in Preview from objectList 2. fix an assert hit when slicing 3. fix an assert hit when enter AssembleView 4. forbidden popup menu by right-click objectList in Preview Change-Id: I444bc76b1a4307999b387e4f60386b2d272bd308 * FIX: Black spot in the sending printing task page Jira: STUDIO-5307 Signed-off-by: wenjie.guo <wenjie.guo@bambulab.com> Change-Id: I3bd97c063ec5f0faf7d12047da39f60ce55cae4b * FIX: reset_cut_by_contours should update_buffer_data Jira: STUDIO-5376 Change-Id: I5aacb1f7b65822031d7138abd61a45b09c743531 * ENH:editing_window_width's value is small on the laptop Jira: STUDIO-5238 STUDIO-5265 Change-Id: Ia958772bcb081817da621115f99328bb62770cd5 * ENH: bumped version to 1.8.1 Change-Id: I9d25403daa5b7b8ca415c0b364670da9e0f932b0 * FIX: create filament dialog: create btn can not show all Jira: 5310 5331 Change-Id: I185272c90d9ff1c3d6b47abbefbf488d0d965cca * FIX:update custom_texture when new_shape=false Jira: STUDIO-5287 Change-Id: I3add95f9f9345c14a48cc7467513d1b3ce95f4c9 * ENH:editing_window_width's value is small on the laptop Jira: STUDIO-5238 Change-Id: I9044129f4e0c8ca7469db05b3e547fca4754342a * FIX:add slash_to_back_slash for saving file path Jira: STUDIO-5287 Change-Id: I9f3c176cd0831c793958f08601c63efac98176a4 * FIX: a button color didn't response to dark mode change jira: STUDIO-5315 Change-Id: I95489f01ccd1f77b9e95b0d0f69e5398d2e88487 * FIX: height range layers displayed in wrong position jira: STUDIO-5341 Change-Id: I83918b4624f367efa54321f1898e1176cdb04ea9 * FIX: auto arranging issues with locked plates 1. global auto arranging may put items overlap with wipe tower if some plates are locked jira: STUDIO-5329 2. items outside bed may overlap with plate boundary if it's rotated jira: STUDIO-5329 3. plate-wise auto arranging uses wrong min_obj_distance if the plate is by-layer printing but global setting is by-object printing jira: STUDIO-5330 Change-Id: I5dba2f1317e183c9aeec1cb2bd227fbddf4316e6 (cherry picked from commit db1eac41efff5f1e8d5ac0af74c6fc7ab59fc253) * FIX: a mistake in upward machine jira:[NEW] Signed-off-by: xun.zhang <xun.zhang@bambulab.com> Change-Id: Ibdb26c3d904634f322aef0dc0c7b8867d9fb5854 * FIX:a blank pop-up appears JIRA:XXXX Change-Id: Ice92b55204e4897fec024a6d99412eb810bddd4a * FIX:fixed failure in updating nozzle type jira:[STUDIO-5248] Change-Id: Iad37b5d5b76d37cb1723ef21d7c39b1e3fcaf8d7 * FIX:fixed issue with AI monitoring settings jira:[STUDIO-5082] Change-Id: I967fe3c1e9da61a55bcbfaa2a8e067dd5af18f72 * FIX:fixed issue with lan mode jira:[STUDIO-5189] Change-Id: I1d0a05f19dcea154cf3ef2b61ed0546d3581905e * FIX:update text for loading or unloading filaments jira:[STUDIO-5231] Change-Id: Ic7729c3ec012485b3d87e3d01f11e87502c67895 * FIX: Revert "ENH: do not leave a gap for top... Revert "ENH: do not leave a gap for top interface if the top z distance is 0" This reverts commit 79ea32c7cbbdb7e689637980af7c36caf42284c9. Revert reason: the supports are impossible to remove in some cases. jira: STUDIO-5385 Change-Id: I376a6f4dfd78da6dfea68b9ac3d552cddd0b4272 (cherry picked from commit 34e38b705fde7f5d7f9a3a89c96a3627ce0c998e) * ENH: improve normal support's quality 1. Add a base_interface_layer when using Supp.W 2. Fix a bug where base_angle and interface_angle are wong jira: STUDIO-5386 Change-Id: I52ab32c63b3cd1e6e2ba6463b01ae26699cf13d3 (cherry picked from commit 92ddd4a10b793572a1fa009da5b9e44fcdf81de2) * NEW:tracking stl model files jira:[STUDIO-5372] Change-Id: Idb1275b07441f0cd06c24588d5f7c20f81f1556c * FIX: edit filament dialog: preset name too long to del btn nan't show Jira: 5336 5174 and verify string normalization Change-Id: I380c3bed2bf43d01094b68979a8b67f4187c0b93 * FIX: some translation Jira: 5232 5300 5334 Change-Id: Ie474ca823011e81aab82a9809af3d6e42980496b * FIX: some translation Change-Id: Iaabe6087bed3b7d47d911cf4fb51c770804e72fb * ENH: change default tree_support_wall_count to 0 Now normal support also uses this option, so we can't default it to 1, otherwise normal supports will be too hard to remove. jira: none Change-Id: Ic5700af5c17e3a7b265c8915f28b0db35c6e06e6 (cherry picked from commit 6b84a9826da108b76569e686bd9def0b23ae29fd) * FIX:The name of the material in the error prompt is empty JIRA:STUDIO-4907 Change-Id: I3cf44f099256a51f21a44a89c89c000f734d1f36 * ci: update build version to 01.08.01.51 Change-Id: Ib20f5a12b65472102befec0a2adf82744fc29c46 * FIX: imgui textinput cannot paste on macos jira: STUDIO-5070、STUDIO-5365 Change-Id: Iea8f41e12744ecda0fbb95c1a8f2e014a7cdc384 * FIX: not cache printer file list on error Change-Id: I99843aedbf14d3d1d553ccac9b0bd26403274a82 Jira: none * FIX: thread of close BBLUserPresetExceedLimit notify Change-Id: I9698134ba1cc91fc83eac441f900d68c4c4b556a * ENH: Resolve non manifold edges by fixing model interfaces Jira: STUDIO-5124 Change-Id: I7ea86be44acb80b6c4762a76208b4a031acd0b27 * FIX:nozzle type sync jira:[STUDIO-5248] Change-Id: I63d48628832473d8d371ed643dc8528b00382531 * FIX: array bound happen in TriangleSelector::deserialize Jira: STUDIO-5170 Change-Id: I92b72a887845b462fad208f13607293b44d3d333 * FIX:cropping rendering without considering assembly views Jira: STUDIO-5260 Change-Id: Ia56cf80b305ae05f25f06ec277f85b3c5430a6df * FIX: PA for custom filament not available in BL Studio github: 2971 Change-Id: I6ccd36a183e7367d69557300f7242f5403f4bb33 * FIX: Bitmap is way too small on Mac Jira: STUDIO-5393 Signed-off-by: wenjie.guo <wenjie.guo@bambulab.com> Change-Id: I6b550669fa8cd5fc9bfa6ed96d64d19a949f01b2 * FIX: move shutdown wait to OnExit Change-Id: I70d9a2bb686525ae5273aa9d63e25691da4ab65c Jira: STUDIO-2884 * FIX: calibration manage result dialog issue on macos jira: STUDIO-4949 STUDIO-5378 Change-Id: I00abefd45a0d274a4b68bb1ab18debe8f91d169e * FIX: adjust bed shape dialog button UI style fix that button text is hard to see in dark mode jira: STUDIO-5247 Change-Id: I2cf5b3cdd2eff9b821bdf5525bec4f329fc58dd1 * FIX: 5331 rescale btn Jira: STUDIO-5331 Change-Id: If153424b8480e64b166018e3cd98c17db557d0a8 Signed-off-by: maosheng.wei <maosheng.wei@bambulab.com> * FIX: support do not generate jira:[NEW] Signed-off-by: xun.zhang <xun.zhang@bambulab.com> Change-Id: Ide9709d95203185538e280517f7aa6136beeda44 * FIX: remove not match printer config ota cache Change-Id: Ib73fc2ea31fa2186061cfcb5a170bc59b9db84ca Jira: none * FIX:cancel the variable of "checkbox_size" as a fixed value Jira: STUDIO-5150 Change-Id: I30d876d141b8b35ab4a3fee4889993d87b7c1741 * ENH:add reset_cut_by_contours in on_load function Jira:STUDIO-5269 m_connector_size_tolerance default value is 0.1f Change-Id: I6c67fff3cb0c1190e9141ed6f68fbfa848679f35 * ENH:cancel EnterReturnsTrue for imgui in cut Jira: STUDIO-5269 Change-Id: Ifc4b183a4e4c4fdb4f47742f14f70a1ed93fa056 Signed-off-by: zhou.xu <zhou.xu@bambulab.com> * FIX: dailytips should not change content frequently when slicing all jira: STUDIO-5234 Change-Id: Icb7e9c28404d9db8ebed58d937e13f89c5403b5c * FIX: objectList clone shortcut key issue jira: new Change-Id: Ia75bf58a7d53f962e1af6c2fd97497270b7eea84 * ENH:handling cases without msgs jira:[STUDIO-5401 STUDIO-5399] Change-Id: Iae651d5a19a45b0138a6aa621326a8b4a9649824 * ENH: optimize param description jira:[NEW] Signed-off-by: xun.zhang <xun.zhang@bambulab.com> Change-Id: Id0ca9224227a716b21fc0b8430722264dc319344 * ENH: add translation jira:[NEW] Signed-off-by: xun.zhang <xun.zhang@bambulab.com> Change-Id: I3b1f04fee3cd6322793794ad8b8707859f6c7d26 * FIX: close edit preset paramsDialog, mac unresponsive Jira: 5298 Change-Id: I021e00567354cfb1f2f5f1f2bf6ba1fc35b164c5 * ENH:disable AI monitoring on the p1p series Change-Id: I514bb1fb1ced6c03dd619230a9adac3be63f2de2 * ci: update build version to 01.08.01.52 Change-Id: I9f5e30d3fc4b7ef9321c522d3c18fce98f03742f * FIX: close edit preset paramsDialog, mac unresponsive Change-Id: Ic816754a20b7f6a5cdb46475750eb301fec3ad3a * FIX: organic support not work with raft only There is no raft generated when only raft enabled but no support needed. jira: none Change-Id: Ic0c9269e2f98038d85c9bc54e4a85f892dc5d764 * FIX: CLI: add object config when assemble JIRA: no jira Change-Id: I945f820fb58f2f643170b4b0b66742f6bbbdfd29 * FIX: delete preset prompt Jira: XXXX Change-Id: I6511c806c56393d4f6bd72d1c506da59675d49ff * FIX:Reorganize the assignment of variables of "m_editing_window_width" Jira: STUDIO-5238 Change-Id: If369916f3f5c21510f5f297bfd05c1230bdda7a4 * ENH: CLI: re-compute flush_volumes_matrix when it is missed Change-Id: Ie8f53c6bef003b1434de02ea14de5787b376484f * FIX: some translation for delete filament Change-Id: Ib46a8eba33f2e21016476aaab4a57a740e86b1b8 * FIX: scrolled window / del preset / edit filament issue Jira: 5092 GitHub: 1936 edit filament: just one preset, the scroll bar obscures the preset name edit filament: delete selected preset, click no, but preset be deleted from UI edit filament: serial sometimes displays incorrectly Change-Id: Ibc91609e252179de0c05ca065099756da6631165 * ci: update build version to 01.08.01.53 Change-Id: I5563a2c0812ab9a0d7727df27e17e681066ffa08 --------- Signed-off-by: maosheng.wei <maosheng.wei@bambulab.com> Signed-off-by: xun.zhang <xun.zhang@bambulab.com> Signed-off-by: Kunlong Ma <kunlong.ma@bambulab.com> Signed-off-by: wenjie.guo <wenjie.guo@bambulab.com> Signed-off-by: qing.zhang <qing.zhang@bambulab.com> Signed-off-by: Stone Li <stone.li@bambulab.com> Signed-off-by: zhou.xu <zhou.xu@bambulab.com> Co-authored-by: zorro.zhang <zorro.zhang@bambulab.com> Co-authored-by: liz.li <liz.li@bambulab.com> Co-authored-by: maosheng.wei <maosheng.wei@bambulab.com> Co-authored-by: chunmao.guo <chunmao.guo@bambulab.com> Co-authored-by: tao wang <tao.wang@bambulab.com> Co-authored-by: Arthur <arthur.tang@bambulab.com> Co-authored-by: lane.wei <lane.wei@bambulab.com> Co-authored-by: gerrit <gerrit@bambulab.com> Co-authored-by: xun.zhang <xun.zhang@bambulab.com> Co-authored-by: zhou.xu <zhou.xu@bambulab.com> Co-authored-by: hu.wang <hu.wang@bambulab.com> Co-authored-by: Kunlong Ma <kunlong.ma@bambulab.com> Co-authored-by: wenjie.guo <wenjie.guo@bambulab.com> Co-authored-by: qing.zhang <qing.zhang@bambulab.com> Co-authored-by: zhimin.zeng <zhimin.zeng@bambulab.com> Co-authored-by: the Raz <rasmus@abc.se> Co-authored-by: Andy <andylg@yandex.ru> Co-authored-by: Stone Li <stone.li@bambulab.com> Co-authored-by: enricoturri1966 <enricoturri@seznam.cz> Co-authored-by: Dmytro Chystiakov <dlchistyakov@gmail.com> Co-authored-by: Heiko Liebscher <hliebscher@idn.de>
4009 lines
190 KiB
C++
4009 lines
190 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 <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>
|
||
|
||
//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",
|
||
"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"
|
||
|
||
};
|
||
|
||
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"
|
||
// 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 == "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"
|
||
) {
|
||
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 == "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();
|
||
for (auto plate_data : m_model.plates_custom_gcodes) {
|
||
for (auto item : plate_data.second.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;
|
||
|
||
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,
|
||
// 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(scale_(0.5 * print.config().extruder_clearance_radius.value - 0.1)),
|
||
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->height())
|
||
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();
|
||
|
||
if (m_objects.empty())
|
||
return {std::string()};
|
||
|
||
if (extruders.empty())
|
||
return { L("No extrusions under current settings.") };
|
||
|
||
if (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));
|
||
! 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 ?
|
||
format(_u8L("The object %1% exceeds the maximum build volume height."), print_object.model_object()->name) :
|
||
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) {
|
||
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 = 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;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
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";
|
||
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
|