Files
OrcaSlicer/src/libslic3r/Print.cpp
SoftFever d8dd8fa634 Feature/bs1.8beta (#2844)
* 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>
2023-12-01 10:42:45 +00:00

4009 lines
190 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
///|/ 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 &region : 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 &region : 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 &lt = 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 &lt = 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