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>
This commit is contained in:
SoftFever 2023-12-01 18:42:45 +08:00 committed by GitHub
parent 2f3ec2ab7d
commit d8dd8fa634
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
420 changed files with 43962 additions and 14438 deletions

157
src/libslic3r/AStar.hpp Normal file
View file

@ -0,0 +1,157 @@
#ifndef ASTAR_HPP
#define ASTAR_HPP
#include <cmath> // std::isinf() is here
#include <unordered_map>
#include "libslic3r/MutablePriorityQueue.hpp"
namespace Slic3r { namespace astar {
// Borrowed from C++20
template<class T> using remove_cvref_t = std::remove_cv_t<std::remove_reference_t<T>>;
// Input interface for the Astar algorithm. Specialize this struct for a
// particular type and implement all the 4 methods and specify the Node type
// to register the new type for the astar implementation.
template<class T> struct TracerTraits_
{
// The type of a node used by this tracer. Usually a point in space.
using Node = typename T::Node;
// Call fn for every new node reachable from node 'src'. fn should have the
// candidate node as its only argument.
template<class Fn> static void foreach_reachable(const T &tracer, const Node &src, Fn &&fn) { tracer.foreach_reachable(src, fn); }
// Get the distance from node 'a' to node 'b'. This is sometimes referred
// to as the g value of a node in AStar context.
static float distance(const T &tracer, const Node &a, const Node &b) { return tracer.distance(a, b); }
// Get the estimated distance heuristic from node 'n' to the destination.
// This is referred to as the h value in AStar context.
// If node 'n' is the goal, this function should return a negative value.
// Note that this heuristic should be admissible (never bigger than the real
// cost) in order for Astar to work.
static float goal_heuristic(const T &tracer, const Node &n) { return tracer.goal_heuristic(n); }
// Return a unique identifier (hash) for node 'n'.
static size_t unique_id(const T &tracer, const Node &n) { return tracer.unique_id(n); }
};
// Helper definition to get the node type of a tracer
template<class T> using TracerNodeT = typename TracerTraits_<remove_cvref_t<T>>::Node;
constexpr auto Unassigned = std::numeric_limits<size_t>::max();
template<class Tracer> struct QNode // Queue node. Keeps track of scores g, and h
{
TracerNodeT<Tracer> node; // The actual node itself
size_t queue_id; // Position in the open queue or Unassigned if closed
size_t parent; // unique id of the parent or Unassigned
float g, h;
float f() const { return g + h; }
QNode(TracerNodeT<Tracer> n = {}, size_t p = Unassigned, float gval = std::numeric_limits<float>::infinity(), float hval = 0.f)
: node{std::move(n)}, parent{p}, queue_id{InvalidQueueID}, g{gval}, h{hval}
{}
};
// Run the AStar algorithm on a tracer implementation.
// The 'tracer' argument encapsulates the domain (grid, point cloud, etc...)
// The 'source' argument is the starting node.
// The 'out' argument is the output iterator into which the output nodes are
// written. For performance reasons, the order is reverse, from the destination
// to the source -- (destination included, source is not).
// The 'cached_nodes' argument is an optional associative container to hold a
// QNode entry for each visited node. Any compatible container can be used
// (like std::map or maps with different allocators, even a sufficiently large
// std::vector).
//
// Note that no destination node is given in the signature. The tracer's
// goal_heuristic() method should return a negative value if a node is a
// destination node.
template<class Tracer, class It, class NodeMap = std::unordered_map<size_t, QNode<Tracer>>>
bool search_route(const Tracer &tracer, const TracerNodeT<Tracer> &source, It out, NodeMap &&cached_nodes = {})
{
using Node = TracerNodeT<Tracer>;
using QNode = QNode<Tracer>;
using TracerTraits = TracerTraits_<remove_cvref_t<Tracer>>;
struct LessPred
{ // Comparison functor needed by the priority queue
NodeMap &m;
bool operator()(size_t node_a, size_t node_b) { return m[node_a].f() < m[node_b].f(); }
};
auto qopen = make_mutable_priority_queue<size_t, true>([&cached_nodes](size_t el, size_t qidx) { cached_nodes[el].queue_id = qidx; }, LessPred{cached_nodes});
QNode initial{source, /*parent = */ Unassigned, /*g = */ 0.f};
size_t source_id = TracerTraits::unique_id(tracer, source);
cached_nodes[source_id] = initial;
qopen.push(source_id);
size_t goal_id = TracerTraits::goal_heuristic(tracer, source) < 0.f ? source_id : Unassigned;
while (goal_id == Unassigned && !qopen.empty()) {
size_t q_id = qopen.top();
qopen.pop();
QNode &q = cached_nodes[q_id];
// This should absolutely be initialized in the cache already
assert(!std::isinf(q.g));
TracerTraits::foreach_reachable(tracer, q.node, [&](const Node &succ_nd) {
if (goal_id != Unassigned) return true;
float h = TracerTraits::goal_heuristic(tracer, succ_nd);
float dst = TracerTraits::distance(tracer, q.node, succ_nd);
size_t succ_id = TracerTraits::unique_id(tracer, succ_nd);
QNode qsucc_nd{succ_nd, q_id, q.g + dst, h};
if (h < 0.f) {
goal_id = succ_id;
cached_nodes[succ_id] = qsucc_nd;
} else {
// If succ_id is not in cache, it gets created with g = infinity
QNode &prev_nd = cached_nodes[succ_id];
if (qsucc_nd.g < prev_nd.g) {
// new route is better, apply it:
// Save the old queue id, it would be lost after the next line
size_t queue_id = prev_nd.queue_id;
// The cache needs to be updated either way
prev_nd = qsucc_nd;
if (queue_id == InvalidQueueID)
// was in closed or unqueued, rescheduling
qopen.push(succ_id);
else // was in open, updating
qopen.update(queue_id);
}
}
return goal_id != Unassigned;
});
}
// Write the output, do not reverse. Clients can do so if they need to.
if (goal_id != Unassigned) {
const QNode *q = &cached_nodes[goal_id];
while (q->parent != Unassigned) {
assert(!std::isinf(q->g)); // Uninitialized nodes are NOT allowed
*out = q->node;
++out;
q = &cached_nodes[q->parent];
}
}
return goal_id != Unassigned;
}
}} // namespace Slic3r::astar
#endif // ASTAR_HPP

View file

@ -255,6 +255,10 @@ void AppConfig::set_defaults()
if (get("show_daily_tips").empty()) {
set_bool("show_daily_tips", true);
}
//true is auto calculate
if (get("auto_calculate").empty()) {
set_bool("auto_calculate", true);
}
if (get("show_home_page").empty()) {
set_bool("show_home_page", true);
@ -998,7 +1002,7 @@ void AppConfig::set_vendors(const AppConfig &from)
m_dirty = true;
}
void AppConfig::save_printer_cali_infos(const PrinterCaliInfo &cali_info)
void AppConfig::save_printer_cali_infos(const PrinterCaliInfo &cali_info, bool need_change_status)
{
auto iter = std::find_if(m_printer_cali_infos.begin(), m_printer_cali_infos.end(), [&cali_info](const PrinterCaliInfo &cali_info_item) {
return cali_info_item.dev_id == cali_info.dev_id;
@ -1007,7 +1011,9 @@ void AppConfig::save_printer_cali_infos(const PrinterCaliInfo &cali_info)
if (iter == m_printer_cali_infos.end()) {
m_printer_cali_infos.emplace_back(cali_info);
} else {
(*iter).cali_finished = cali_info.cali_finished;
if (need_change_status) {
(*iter).cali_finished = cali_info.cali_finished;
}
(*iter).cache_flow_ratio = cali_info.cache_flow_ratio;
(*iter).selected_presets = cali_info.selected_presets;
}

View file

@ -209,7 +209,7 @@ public:
}
const std::vector<PrinterCaliInfo> &get_printer_cali_infos() const { return m_printer_cali_infos; }
void save_printer_cali_infos(const PrinterCaliInfo& cali_info);
void save_printer_cali_infos(const PrinterCaliInfo& cali_info, bool need_change_status = true);
// return recent/last_opened_folder or recent/settings_folder or empty string.
std::string get_last_dir() const;

View file

@ -228,16 +228,23 @@ void update_selected_items_axis_align(ArrangePolygons& selected, const DynamicPr
double c = m02 / m00 - cy * cy;
//if a and c are close, there is no dominant axis, then do not rotate
if (std::abs(a) < 1.5*std::abs(c) && std::abs(c) < 1.5*std::abs(a)) {
// ratio is always no more than 1
double ratio = std::abs(a) > std::abs(c) ? std::abs(c / a) :
std::abs(c) > 0 ? std::abs(a / c) : 0;
if (ratio>0.66) {
validResult = false;
}
else {
angle = std::atan2(2 * b, (a - c)) / 2;
angle = PI / 2 - angle;
// if the angle is close to PI or -PI, it means the object is vertical, then do not rotate
if (std::abs(std::abs(angle) - PI) < 0.01)
angle = 0;
validResult = true;
}
}
}
if (validResult) { ap.rotation += (PI / 2 - angle); }
if (validResult) { ap.rotation += angle; }
}
}
@ -406,6 +413,18 @@ protected:
return bindist;
}
double dist_to_bin(const Box& ibb, const ClipperLib::IntPoint& origin_pack, typename Packer::PlacementConfig::Alignment starting_point_alignment)
{
double bindist = 0;
if (starting_point_alignment == PConfig::Alignment::BOTTOM_LEFT)
bindist = norm(pl::distance(ibb.minCorner(), origin_pack));
else if (starting_point_alignment == PConfig::Alignment::TOP_RIGHT)
bindist = norm(pl::distance(ibb.maxCorner(), origin_pack));
else
bindist = norm(pl::distance(ibb.center(), origin_pack));
return bindist;
}
// This is "the" object function which is evaluated many times for each
// vertex (decimated with the accuracy parameter) of each object.
// Therefore it is upmost crucial for this function to be as efficient
@ -488,7 +507,7 @@ protected:
score = 0.2 * dist + 0.8 * bindist;
}
else {
double bindist = norm(pl::distance(ibb.center(), origin_pack));
double bindist = dist_to_bin(ibb, origin_pack, m_pconf.starting_point);
dist = 0.8 * dist + 0.2 * bindist;
@ -520,11 +539,13 @@ protected:
auto ascore = 1.0 - (item.area() + parea) / bbarea;
if (ascore < alignment_score) alignment_score = ascore;
}
}
}
density = std::sqrt(norm(fullbb.width()) * norm(fullbb.height()));
double R = double(m_remaining.size()) / m_item_count;
// alighment score is more important for rectangle items
double alignment_weight = std::max(0.3, 0.6 * item.area() / ibb.area());
// The final mix of the score is the balance between the
// distance from the full pile center, the pack density and
@ -533,8 +554,8 @@ protected:
score = 0.50 * dist + 0.50 * density;
else
// Let the density matter more when fewer objects remain
score = 0.50 * dist + (1.0 - R) * 0.20 * density +
0.30 * alignment_score;
score = (1 - 0.2 - alignment_weight) * dist + (1.0 - R) * 0.20 * density +
alignment_weight * alignment_score;
}
break;
}
@ -814,7 +835,8 @@ public:
template<> std::function<double(const Item&, const ItemGroup&)> AutoArranger<Box>::get_objfn()
{
auto origin_pack = m_pconf.starting_point == PConfig::Alignment::CENTER ? m_bin.center() : m_bin.minCorner();
auto origin_pack = m_pconf.starting_point == PConfig::Alignment::CENTER ? m_bin.center() :
m_pconf.starting_point == PConfig::Alignment::TOP_RIGHT ? m_bin.maxCorner() : m_bin.minCorner();
return [this, origin_pack](const Item &itm, const ItemGroup&) {
auto result = objfunc(itm, origin_pack);

View file

@ -419,4 +419,15 @@ std::string_view BuildVolume::type_name(BuildVolume_Type type)
return {};
}
indexed_triangle_set BuildVolume::bounding_mesh(bool scale) const
{
auto max_pt3 = m_bboxf.max;
if (scale) {
return its_make_cube(scale_(max_pt3.x()), scale_(max_pt3.y()), scale_(max_pt3.z()));
}
else {
return its_make_cube(max_pt3.x(), max_pt3.y(), max_pt3.z());
}
}
} // namespace Slic3r

View file

@ -52,6 +52,7 @@ public:
// Bounding volume of printable_area(), printable_height(), unscaled.
const BoundingBoxf3& bounding_volume() const { return m_bboxf; }
BoundingBoxf bounding_volume2d() const { return { to_2d(m_bboxf.min), to_2d(m_bboxf.max) }; }
indexed_triangle_set bounding_mesh(bool scale=true) const;
// Center of the print bed, unscaled.
Vec2d bed_center() const { return to_2d(m_bboxf.center()); }

View file

@ -304,7 +304,7 @@ set(lisbslic3r_sources
PrincipalComponents2D.hpp
SupportSpotsGenerator.cpp
SupportSpotsGenerator.hpp
TreeSupport.hpp
TreeSupport.hpp
TreeSupport.cpp
MinimumSpanningTree.hpp
MinimumSpanningTree.cpp

View file

@ -308,6 +308,7 @@ ConfigOption* ConfigOptionDef::create_default_option() const
opt->keys_map = this->enum_keys_map;
return opt;
}
delete dft;
}
return this->default_value->clone();
@ -782,6 +783,8 @@ int ConfigBase::load_from_json(const std::string &file, ConfigSubstitutionContex
json j;
std::list<std::string> different_settings_append;
std::string new_support_style;
std::string is_infill_first;
std::string get_wall_sequence;
bool is_project_settings = false;
CNumericLocalesSetter locales_setter;
@ -845,6 +848,16 @@ int ConfigBase::load_from_json(const std::string &file, ConfigSubstitutionContex
different_settings_append.push_back("support_style");
new_support_style = "tree_hybrid";
}
} else if (opt_key == "wall_infill_order") {
//BBS: check wall_infill order to decide if it be different and append to diff_setting_append
if (it.value() == "outer wall/inner wall/infill" || it.value() == "infill/outer wall/inner wall" || it.value() == "inner-outer-inner wall/infill") {
get_wall_sequence = "wall_seq_diff_to_system";
}
if (it.value() == "infill/outer wall/inner wall" || it.value() == "infill/inner wall/outer wall") {
different_settings_append.push_back("is_infill_first");
is_infill_first = "true";
}
}
}
else if (it.value().is_array()) {
@ -916,6 +929,12 @@ int ConfigBase::load_from_json(const std::string &file, ConfigSubstitutionContex
ConfigOptionEnum<SupportMaterialStyle>* opt = this->option<ConfigOptionEnum<SupportMaterialStyle>>("support_style", true);
opt->value = smsTreeHybrid;
}
if (!is_infill_first.empty()) {
ConfigOptionBool *opt = this->option<ConfigOptionBool>("is_infill_first", true);
opt->value = true;
}
if (is_project_settings) {
std::vector<std::string>& different_settings = this->option<ConfigOptionStrings>("different_settings_to_system", true)->values;
size_t size = different_settings.size();
@ -932,6 +951,25 @@ int ConfigBase::load_from_json(const std::string &file, ConfigSubstitutionContex
is_first[index] = true;
}
else {
// remove unneeded key
if (get_wall_sequence.empty()) {
std::string wall_sqe_string = "wall_sequence";
int pos=different_settings[index].find(wall_sqe_string);
if (pos != different_settings[index].npos) {
int erase_len = wall_sqe_string.size();
if (pos + erase_len < different_settings[index].size() && different_settings[index][pos + erase_len] == ';')
erase_len++;
different_settings[index].erase(pos, erase_len);
}
}
if (different_settings[index].empty()) {
is_first[index] = true;
continue;
}
Slic3r::unescape_strings_cstyle(different_settings[index], original_diffs[index]);
}
}
@ -945,6 +983,8 @@ int ConfigBase::load_from_json(const std::string &file, ConfigSubstitutionContex
index = 0;
else if (diff_key == "support_style")
index = 0;
else if (diff_key == "is_infill_first")
index = 0;
//check whether exist firstly
if (!original_diffs[index].empty()) {

View file

@ -445,6 +445,11 @@ const ModelObjectPtrs& Cut::perform_by_contour(std::vector<Part> parts, int dowe
ModelObject* lower{ nullptr };
if (m_attributes.has(ModelObjectCutAttribute::KeepLower)) cut_mo->clone_for_cut(&lower);
if (upper && lower) {
upper->name = upper->name + "_A";
lower->name = lower->name + "_B";
}
const size_t cut_parts_cnt = parts.size();
bool has_modifiers = false;
@ -527,6 +532,10 @@ const ModelObjectPtrs& Cut::perform_with_groove(const Groove& groove, const Tran
ModelObject* lower{ nullptr };
cut_mo->clone_for_cut(&lower);
if (upper && lower) {
upper->name = upper->name + "_A";
lower->name = lower->name + "_B";
}
const double groove_half_depth = 0.5 * double(groove.depth);
Model tmp_model_for_cut = Model();

View file

@ -26,6 +26,7 @@ ExtrusionEntityCollection::ExtrusionEntityCollection(const ExtrusionPaths &paths
ExtrusionEntityCollection& ExtrusionEntityCollection::operator=(const ExtrusionEntityCollection &other)
{
clear();
this->entities = other.entities;
for (size_t i = 0; i < this->entities.size(); ++i)
this->entities[i] = this->entities[i]->clone();

View file

@ -1095,7 +1095,7 @@ void Layer::make_ironing()
// Create the filler object.
f->spacing = ironing_params.line_spacing;
f->angle = float(ironing_params.angle + 0.25 * M_PI);
f->angle = float(ironing_params.angle);
f->link_max_length = (coord_t) scale_(3. * f->spacing);
double extrusion_height = ironing_params.height * f->spacing / nozzle_dmr;
float extrusion_width = Flow::rounded_rectangle_extrusion_width_from_spacing(float(nozzle_dmr), float(extrusion_height));

View file

@ -17,6 +17,8 @@ namespace Slic3r {
bool load_stl(const char *path, Model *model, const char *object_name_in, ImportstlProgressFn stlFn)
{
TriangleMesh mesh;
std::string design_id;
if (! mesh.ReadSTLFile(path, true, stlFn)) {
// die "Failed to open $file\n" if !-e $path;
return false;
@ -59,4 +61,4 @@ bool store_stl(const char *path, Model *model, bool binary)
return store_stl(path, &mesh, binary);
}
}; // namespace Slic3r
}; // namespace Slic3r

View file

@ -276,6 +276,7 @@ static constexpr const char* LOCK_ATTR = "locked";
static constexpr const char* BED_TYPE_ATTR = "bed_type";
static constexpr const char* PRINT_SEQUENCE_ATTR = "print_sequence";
static constexpr const char* FIRST_LAYER_PRINT_SEQUENCE_ATTR = "first_layer_print_sequence";
static constexpr const char* SPIRAL_VASE_MODE = "spiral_mode";
static constexpr const char* GCODE_FILE_ATTR = "gcode_file";
static constexpr const char* THUMBNAIL_FILE_ATTR = "thumbnail_file";
static constexpr const char* TOP_FILE_ATTR = "top_file";
@ -288,6 +289,8 @@ static constexpr const char* IDENTIFYID_ATTR = "identify_id";
static constexpr const char* PLATERID_ATTR = "plater_id";
static constexpr const char* PLATER_NAME_ATTR = "plater_name";
static constexpr const char* PLATE_IDX_ATTR = "index";
static constexpr const char* PRINTER_MODEL_ID_ATTR = "printer_model_id";
static constexpr const char* NOZZLE_DIAMETERS_ATTR = "nozzle_diameters";
static constexpr const char* SLICE_PREDICTION_ATTR = "prediction";
static constexpr const char* SLICE_WEIGHT_ATTR = "weight";
static constexpr const char* TIMELAPSE_TYPE_ATTR = "timelapse_type";
@ -1282,9 +1285,9 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
// BBS: load relationships
if (!_extract_xml_from_archive(archive, RELATIONSHIPS_FILE, _handle_start_relationships_element, _handle_end_relationships_element))
return false;
if (m_thumbnail_small.empty()) m_thumbnail_small = m_thumbnail_path;
if (!m_thumbnail_small.empty()) {
return _extract_from_archive(archive, m_thumbnail_small, [&data](auto &archive, auto const &stat) -> bool {
if (m_thumbnail_middle.empty()) m_thumbnail_middle = m_thumbnail_path;
if (!m_thumbnail_middle.empty()) {
return _extract_from_archive(archive, m_thumbnail_middle, [&data](auto &archive, auto const &stat) -> bool {
data.resize(stat.m_uncomp_size);
return mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, data.data(), data.size(), 0);
});
@ -1368,7 +1371,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
m_thumbnail_small.erase(m_thumbnail_small.begin());
if (!m_thumbnail_middle.empty() && m_thumbnail_middle.front() == '/')
m_thumbnail_middle.erase(m_thumbnail_middle.begin());
m_model->model_info->metadata_items.emplace("Thumbnail", m_thumbnail_small);
m_model->model_info->metadata_items.emplace("Thumbnail", m_thumbnail_middle);
m_model->model_info->metadata_items.emplace("Poster", m_thumbnail_middle);
// we then loop again the entries to read other files stored in the archive
@ -3900,6 +3903,11 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
};
m_curr_plater->config.set_key_value("first_layer_print_sequence", new ConfigOptionInts(get_vector_from_string(value)));
}
else if (key == SPIRAL_VASE_MODE) {
bool spiral_mode = false;
std::istringstream(value) >> std::boolalpha >> spiral_mode;
m_curr_plater->config.set_key_value("spiral_mode", new ConfigOptionBool(spiral_mode));
}
else if (key == GCODE_FILE_ATTR)
{
m_curr_plater->gcode_file = value;
@ -5375,7 +5383,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
~close_lock() {
if (filename) {
close_zip_writer(&archive);
boost::filesystem::remove(*filename);
boost::system::error_code ec;
boost::filesystem::remove(*filename, ec);
}
}
} lock{archive, &filepath_tmp};
@ -5439,8 +5448,10 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
std::string const * filename;
~close_lock() {
close_zip_writer(&archive);
if (filename)
boost::filesystem::remove(*filename);
if (filename) {
boost::system::error_code ec;
boost::filesystem::remove(*filename, ec);
}
}
} lock{ archive, &filename};
@ -5588,7 +5599,6 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
{
if (!_add_calibration_file_to_archive(archive, *calibration_data[index], index)) {
close_zip_writer(&archive);
boost::filesystem::remove(filename);
return false;
}
}
@ -5604,7 +5614,6 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
if (id_bboxes[index]->is_valid()) {
if (!_add_bbox_file_to_archive(archive, *id_bboxes[index], index)) {
close_zip_writer(&archive);
boost::filesystem::remove(filename);
return false;
}
}
@ -5631,7 +5640,6 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
// The index differes from the index of an object ID of an object instance of a 3MF file!
if (!_add_layer_height_profile_file_to_archive(archive, model)) {
close_zip_writer(&archive);
boost::filesystem::remove(filename);
return false;
}
@ -5648,7 +5656,6 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
// The index differes from the index of an object ID of an object instance of a 3MF file!
if (!_add_layer_config_ranges_file_to_archive(archive, model)) {
close_zip_writer(&archive);
boost::filesystem::remove(filename);
return false;
}
@ -6417,7 +6424,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
stream << " <" << COMPONENT_TAG << " objectid=\"" << volume_id;
else
stream << " <" << COMPONENT_TAG << " p:path=\"" << xml_escape(*ppath) << "\" objectid=\"" << volume_id; // << "\"/>\n";
stream << "\" " << PUUID_ATTR << "=\"" << hex_wrap<boost::uint32_t>{(boost::uint32_t) object_data.backup_id} << COMPONENT_UUID_SUFFIX;
if (m_production_ext)
stream << "\" " << PUUID_ATTR << "=\"" << hex_wrap<boost::uint32_t>{(boost::uint32_t) (index + (object_data.backup_id << 16))} << COMPONENT_UUID_SUFFIX;
const Transform3d &transf = volume->get_matrix();
stream << "\" " << TRANSFORM_ATTR << "=\"";
for (unsigned c = 0; c < 4; ++c) {
@ -6515,7 +6523,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
char buf[256];
unsigned int vertices_count = 0;
//unsigned int triangles_count = 0;
for (ModelVolume* volume : object.volumes) {
for (unsigned int index = 0; index < object.volumes.size(); index++) {
ModelVolume *volume = object.volumes[index];
if (volume == nullptr)
continue;
@ -6542,7 +6551,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
if (m_production_ext) {
std::stringstream stream;
reset_stream(stream);
stream << "\" " << PUUID_ATTR << "=\"" << hex_wrap<boost::uint32_t>{(boost::uint32_t) object_data.backup_id} << SUB_OBJECT_UUID_SUFFIX;
stream << "\" " << PUUID_ATTR << "=\"" << hex_wrap<boost::uint32_t>{(boost::uint32_t) (index + (object_data.backup_id << 16))} << SUB_OBJECT_UUID_SUFFIX;
//output_buffer += "\" ";
//output_buffer += PUUID_ATTR;
//output_buffer += "=\"";
@ -7218,6 +7227,10 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
stream << "\"/>\n";
}
ConfigOption* spiral_mode_opt = plate_data->config.option("spiral_mode");
if (spiral_mode_opt)
stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << SPIRAL_VASE_MODE << "\" " << VALUE_ATTR << "=\"" << spiral_mode_opt->getBool() << "\"/>\n";
if (save_gcode)
stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << GCODE_FILE_ATTR << "\" " << VALUE_ATTR << "=\"" << std::boolalpha << xml_escape(plate_data->gcode_file) << "\"/>\n";
if (!plate_data->gcode_file.empty()) {
@ -7371,6 +7384,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result)
break;
}
}
stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << PRINTER_MODEL_ID_ATTR << "\" " << VALUE_ATTR << "=\"" << plate_data->printer_model_id << "\"/>\n";
stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << NOZZLE_DIAMETERS_ATTR << "\" " << VALUE_ATTR << "=\"" << plate_data->nozzle_diameters << "\"/>\n";
stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << TIMELAPSE_TYPE_ATTR << "\" " << VALUE_ATTR << "=\"" << timelapse_type << "\"/>\n";
//stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << TIMELAPSE_ERROR_CODE_ATTR << "\" " << VALUE_ATTR << "=\"" << plate_data->timelapse_warning_code << "\"/>\n";
stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << SLICE_PREDICTION_ATTR << "\" " << VALUE_ATTR << "=\"" << plate_data->get_gcode_prediction_str() << "\"/>\n";
@ -7714,10 +7729,12 @@ public:
void set_interval(long n) {
boost::lock_guard lock(m_mutex);
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " entry, and last interval is: " << m_interval;
m_next_backup -= boost::posix_time::seconds(m_interval);
m_interval = n;
m_next_backup += boost::posix_time::seconds(m_interval);
m_cond.notify_all();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " exit, and new interval is: " << m_interval;
}
void put_other_changes()
@ -7795,6 +7812,7 @@ private:
};
private:
_BBS_Backup_Manager() {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " inital and interval = " << m_interval;
m_next_backup = boost::get_system_time() + boost::posix_time::seconds(m_interval);
boost::unique_lock lock(m_mutex);
m_thread = std::move(boost::thread(boost::ref(*this)));
@ -7823,7 +7841,7 @@ private:
}
void process_ui_task(Task& t, bool canceled = false) {
BOOST_LOG_TRIVIAL(info) << "process_ui_task" << t.to_string();
BOOST_LOG_TRIVIAL(info) << "process_ui_task" << t.to_string() << " and interval = " << m_interval;
switch (t.type) {
case Backup: {
if (canceled)
@ -7867,7 +7885,7 @@ private:
}
void process_task(Task& t) {
BOOST_LOG_TRIVIAL(info) << "process_task" << t.to_string();
BOOST_LOG_TRIVIAL(info) << "process_task" << t.to_string() << " and interval = " << m_interval;
switch (t.type) {
case Backup:
// do it in response
@ -7882,13 +7900,15 @@ private:
break;
}
case RemoveObject: {
boost::filesystem::remove(t.path + "/mesh_" + boost::lexical_cast<std::string>(t.id) + ".xml");
boost::system::error_code ec;
boost::filesystem::remove(t.path + "/mesh_" + boost::lexical_cast<std::string>(t.id) + ".xml", ec);
t.type = None;
break;
}
case RemoveBackup: {
try {
boost::filesystem::remove(t.path + "/.3mf");
boost::system::error_code ec;
boost::filesystem::remove(t.path + "/.3mf", ec);
// We Saved with SplitModel now, so we can safe delete these sub models.
boost::filesystem::remove_all(t.path + "/3D/Objects");
boost::filesystem::create_directory(t.path + "/3D/Objects");

View file

@ -69,6 +69,8 @@ struct PlateData
int plate_index;
std::vector<std::pair<int, int>> objects_and_instances;
std::map<int, std::pair<int, int>> obj_inst_map;
std::string printer_model_id;
std::string nozzle_diameters;
std::string gcode_file;
std::string gcode_file_md5;
std::string thumbnail_file;
@ -231,7 +233,7 @@ extern bool load_bbs_3mf(const char* path, DynamicPrintConfig* config, ConfigSub
extern std::string bbs_3mf_get_thumbnail(const char * path);
extern bool load_gcode_3mf_from_stream(std::istream & data, DynamicPrintConfig* config, Model* model, PlateDataPtrs* plate_data_list,
extern bool load_gcode_3mf_from_stream(std::istream & data, DynamicPrintConfig* config, Model* model, PlateDataPtrs* plate_data_list,
Semver* file_version);

View file

@ -1357,7 +1357,22 @@ namespace DoExport {
total_weight += weight;
total_cost += weight * extruder->filament_cost() * 0.001;
}
for (auto volume : result.print_statistics.wipe_tower_volumes_per_extruder) {
total_extruded_volume += volume.second;
size_t extruder_id = volume.first;
auto extruder = std::find_if(extruders.begin(), extruders.end(), [extruder_id](const Extruder& extr) {return extr.id() == extruder_id; });
if (extruder == extruders.end())
continue;
double s = PI * sqr(0.5* extruder->filament_diameter());
double weight = volume.second * extruder->filament_density() * 0.001;
total_used_filament += volume.second/s;
total_weight += weight;
total_cost += weight * extruder->filament_cost() * 0.001;
}
total_cost += config.time_cost.getFloat() * (normal_print_time/3600.0);
print_statistics.total_extruded_volume = total_extruded_volume;
@ -1398,8 +1413,7 @@ namespace DoExport {
if (ret.size() < MAX_TAGS_COUNT) check(_(L("Layer change G-code")), config.layer_change_gcode.value);
if (ret.size() < MAX_TAGS_COUNT) check(_(L("Time lapse G-code")), config.time_lapse_gcode.value);
if (ret.size() < MAX_TAGS_COUNT) check(_(L("Change filament G-code")), config.change_filament_gcode.value);
//BBS
//if (ret.size() < MAX_TAGS_COUNT) check(_(L("Printing by object G-code")), config.printing_by_object_gcode.value);
if (ret.size() < MAX_TAGS_COUNT) check(_(L("Printing by object G-code")), config.printing_by_object_gcode.value);
//if (ret.size() < MAX_TAGS_COUNT) check(_(L("Color Change G-code")), config.color_change_gcode.value);
//Orca
if (ret.size() < MAX_TAGS_COUNT) check(_(L("Change extrusion role G-code")), config.change_extrusion_role_gcode.value);
@ -1531,6 +1545,27 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
}
m_processor.result().timelapse_warning_code = m_timelapse_warning_code;
m_processor.result().support_traditional_timelapse = m_support_traditional_timelapse;
{ //BBS:check bed and filament compatible
const ConfigOptionDef *bed_type_def = print_config_def.get("curr_bed_type");
assert(bed_type_def != nullptr);
const t_config_enum_values *bed_type_keys_map = bed_type_def->enum_keys_map;
const ConfigOptionInts *bed_temp_opt = m_config.option<ConfigOptionInts>(get_bed_temp_key(m_config.curr_bed_type));
for(auto extruder_id : m_initial_layer_extruders){
int cur_bed_temp = bed_temp_opt->get_at(extruder_id);
if (cur_bed_temp == 0 && bed_type_keys_map != nullptr) {
for (auto item : *bed_type_keys_map) {
if (item.second == m_config.curr_bed_type) {
m_processor.result().bed_match_result = BedMatchResult(false, item.first, extruder_id);
break;
}
}
}
if (m_processor.result().bed_match_result.match == false)
break;
}
}
m_processor.finalize(true);
// DoExport::update_print_estimated_times_stats(m_processor, print->m_print_statistics);
DoExport::update_print_estimated_stats(m_processor, m_writer.extruders(), print->m_print_statistics, print->config());
@ -1919,6 +1954,24 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
}
}
{
std::string filament_density_list = "; filament_density: ";
(filament_density_list+=m_config.filament_density.serialize()) +='\n';
file.writeln(filament_density_list);
std::string filament_diameter_list = "; filament_diameter: ";
(filament_diameter_list += m_config.filament_diameter.serialize()) += '\n';
file.writeln(filament_diameter_list);
coordf_t max_height_z = -1;
for (const auto& object : print.objects())
max_height_z = std::max(object->layers().back()->print_z, max_height_z);
std::ostringstream max_height_z_tip;
max_height_z_tip<<"; max_z_height: " << std::fixed << std::setprecision(2) << max_height_z << '\n';
file.writeln(max_height_z_tip.str());
}
file.write_format("; HEADER_BLOCK_END\n\n");
@ -3545,6 +3598,27 @@ LayerResult GCode::process_layer(
Skirt::make_skirt_loops_per_extruder_1st_layer(print, layer_tools, m_skirt_done) :
Skirt::make_skirt_loops_per_extruder_other_layers(print, layer_tools, m_skirt_done);
// BBS: get next extruder according to flush and soluble
auto get_next_extruder = [&](int current_extruder,const std::vector<unsigned int>&extruders) {
std::vector<float> flush_matrix(cast<float>(m_config.flush_volumes_matrix.values));
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));
unsigned int next_extruder = current_extruder;
float min_flush = std::numeric_limits<float>::max();
for (auto extruder_id : extruders) {
if (print.config().filament_soluble.get_at(extruder_id) || extruder_id == current_extruder)
continue;
if (wipe_volumes[current_extruder][extruder_id] < min_flush) {
next_extruder = extruder_id;
min_flush = wipe_volumes[current_extruder][extruder_id];
}
}
return next_extruder;
};
if (m_config.enable_overhang_speed && !m_config.overhang_speed_classic) {
for (const auto &layer_to_print : layers) {
m_extrusion_quality_estimator.prepare_for_new_layer(layer_to_print.original_object,
@ -3597,12 +3671,21 @@ LayerResult GCode::process_layer(
if (print.config().filament_soluble.get_at(extruder_id))
continue;
//BBS: now we don't consider interface filament used in other object
if (extruder_id == interface_extruder)
continue;
dontcare_extruder = extruder_id;
break;
}
#if 0
//BBS: not found a suitable extruder in current layer ,dontcare_extruider==first_extruder_id==interface_extruder
if (dontcare_extruder == interface_extruder && (object.config().support_interface_not_for_body && object.config().support_interface_filament.value!=0)) {
// BBS : get a suitable extruder from other layer
auto all_extruders = print.extruders();
dontcare_extruder = get_next_extruder(dontcare_extruder, all_extruders);
}
#endif
if (support_dontcare)
support_extruder = dontcare_extruder;
@ -3964,16 +4047,56 @@ LayerResult GCode::process_layer(
m_last_obj_copy = this_object_copy;
this->set_origin(unscale(offset));
//FIXME the following code prints regions in the order they are defined, the path is not optimized in any way.
bool is_infill_first = print.default_region_config().wall_infill_order == WallInfillOrder::InfillInnerOuter ||
print.default_region_config().wall_infill_order == WallInfillOrder::InfillOuterInner;
bool is_infill_first =print.config().is_infill_first;
auto has_infill = [](const std::vector<ObjectByExtruder::Island::Region> &by_region) {
for (auto region : by_region) {
if (!region.infills.empty())
return true;
}
return false;
};
//BBS: for first layer, we always print wall firstly to get better bed adhesive force
//This behaviour is same with cura
if (is_infill_first && !first_layer) {
gcode += this->extrude_infill(print, by_region_specific, false);
if (!has_wipe_tower && need_insert_timelapse_gcode_for_traditional && !has_insert_timelapse_gcode && has_infill(by_region_specific)) {
gcode += this->retract(false, false, LiftType::NormalLift);
std::string timepals_gcode = insert_timelapse_gcode();
gcode += timepals_gcode;
m_writer.set_current_position_clear(false);
//BBS: check whether custom gcode changes the z position. Update if changed
double temp_z_after_timepals_gcode;
if (GCodeProcessor::get_last_z_from_gcode(timepals_gcode, temp_z_after_timepals_gcode)) {
Vec3d pos = m_writer.get_position();
pos(2) = temp_z_after_timepals_gcode;
m_writer.set_position(pos);
}
has_insert_timelapse_gcode = true;
}
gcode += this->extrude_infill(print, by_region_specific, false);
gcode += this->extrude_perimeters(print, by_region_specific);
} else {
gcode += this->extrude_perimeters(print, by_region_specific);
gcode += this->extrude_infill(print,by_region_specific, false);
if (!has_wipe_tower && need_insert_timelapse_gcode_for_traditional && !has_insert_timelapse_gcode && has_infill(by_region_specific)) {
gcode += this->retract(false, false, LiftType::NormalLift);
std::string timepals_gcode = insert_timelapse_gcode();
gcode += timepals_gcode;
m_writer.set_current_position_clear(false);
//BBS: check whether custom gcode changes the z position. Update if changed
double temp_z_after_timepals_gcode;
if (GCodeProcessor::get_last_z_from_gcode(timepals_gcode, temp_z_after_timepals_gcode)) {
Vec3d pos = m_writer.get_position();
pos(2) = temp_z_after_timepals_gcode;
m_writer.set_position(pos);
}
has_insert_timelapse_gcode = true;
}
gcode += this->extrude_infill(print,by_region_specific, false);
}
// ironing
gcode += this->extrude_infill(print,by_region_specific, true);
@ -4002,6 +4125,12 @@ LayerResult GCode::process_layer(
}
}
}
if (first_layer) {
for (auto iter = by_extruder.begin(); iter != by_extruder.end(); ++iter) {
if (!iter->second.empty())
m_initial_layer_extruders.insert(iter->first);
}
}
#if 0
// Apply spiral vase post-processing if this layer contains suitable geometry
@ -4197,9 +4326,7 @@ std::string GCode::extrude_loop(ExtrusionLoop loop, std::string description, dou
Point last_pos = this->last_pos();
if (!m_config.spiral_mode && description == "perimeter") {
assert(m_layer != nullptr);
bool is_outer_wall_first = m_config.wall_infill_order == WallInfillOrder::OuterInnerInfill
|| m_config.wall_infill_order == WallInfillOrder::InfillOuterInner
|| m_config.wall_infill_order == WallInfillOrder::InnerOuterInnerInfill;
bool is_outer_wall_first = m_config.wall_sequence == WallSequence::OuterInner;
m_seam_placer.place_seam(m_layer, loop, is_outer_wall_first, this->last_pos());
} else
loop.split_at(last_pos, false);
@ -5207,10 +5334,11 @@ bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role, LiftTyp
travel_bbox.inflated(1);
travel_bbox.defined = true;
const float protect_z_scaled = scale_(0.4);
// do not scale for z
const float protect_z = 0.4;
std::pair<float, float> z_range;
z_range.second = m_layer ? m_layer->print_z : 0.f;
z_range.first = std::max(0.f, z_range.second - protect_z_scaled);
z_range.first = std::max(0.f, z_range.second - protect_z);
std::vector<LayerPtrs> layers_of_objects;
std::vector<BoundingBox> boundingBox_for_objects;
std::vector<Points> objects_instances_shift;
@ -5261,7 +5389,7 @@ bool GCode::needs_retraction(const Polyline &travel, ExtrusionRole role, LiftTyp
float max_z_hop = 0.f;
for (int i = 0; i < m_config.z_hop.size(); i++)
max_z_hop = std::max(max_z_hop, (float)m_config.z_hop.get_at(i));
float travel_len_thresh = max_z_hop / tan(GCodeWriter::slope_threshold);
float travel_len_thresh = scale_(max_z_hop / tan(GCodeWriter::slope_threshold));
float accum_len = 0.f;
Polyline clipped_travel;
@ -5337,8 +5465,7 @@ std::string GCode::retract(bool toolchange, bool is_last_retraction, LiftType li
gcode += toolchange ? m_writer.retract_for_toolchange() : m_writer.retract();
gcode += m_writer.reset_e();
// check if should + can lift (roughly from SuperSlicer)
// Orca: check if should + can lift (roughly from SuperSlicer)
RetractLiftEnforceType retract_lift_type = RetractLiftEnforceType(EXTRUDER_CONFIG(retract_lift_enforce));
bool needs_lift = toolchange
@ -5371,7 +5498,7 @@ std::string GCode::retract(bool toolchange, bool is_last_retraction, LiftType li
return gcode;
}
std::string GCode::set_extruder(unsigned int extruder_id, double print_z)
std::string GCode::set_extruder(unsigned int extruder_id, double print_z, bool by_object)
{
if (!m_writer.need_toolchange(extruder_id))
return "";
@ -5405,6 +5532,10 @@ std::string GCode::set_extruder(unsigned int extruder_id, double print_z)
// Always reset the extrusion path, even if the tool change retract is set to zero.
m_wipe.reset_path();
// BBS: insert skip object label before change filament while by object
if (by_object)
m_writer.add_object_change_labels(gcode);
if (m_writer.extruder() != nullptr) {
// Process the custom filament_end_gcode. set_extruder() is only called if there is no wipe tower
// so it should not be injected twice.

View file

@ -216,7 +216,7 @@ public:
bool needs_retraction(const Polyline& travel, ExtrusionRole role, LiftType& lift_type);
std::string retract(bool toolchange = false, bool is_last_retraction = false, LiftType lift_type = LiftType::NormalLift);
std::string unretract() { return m_writer.unlift() + m_writer.unretract(); }
std::string set_extruder(unsigned int extruder_id, double print_z);
std::string set_extruder(unsigned int extruder_id, double print_z, bool by_object=false);
bool is_BBL_Printer();
// SoftFever
@ -549,6 +549,7 @@ private:
bool m_need_change_layer_lift_z = false;
int m_start_gcode_filament = -1;
std::set<unsigned int> m_initial_layer_extruders;
// BBS
int get_bed_temperature(const int extruder_id, const bool is_first_layer, const BedType bed_type) const;

View file

@ -91,34 +91,34 @@ inline Grids line_rasterization(const Line &line, int64_t xdist = RasteXDistance
void LinesBucketQueue::emplace_back_bucket(ExtrusionLayers &&els, const void *objPtr, Point offset)
{
auto oldSize = _buckets.capacity();
_buckets.emplace_back(std::move(els), objPtr, offset);
_pq.push(&_buckets.back());
auto newSize = _buckets.capacity();
auto oldSize = line_buckets.capacity();
line_buckets.emplace_back(std::move(els), objPtr, offset);
line_bucket_ptr_queue.push(&line_buckets.back());
auto newSize = line_buckets.capacity();
if (oldSize != newSize) { // pointers change
decltype(_pq) newQueue;
for (LinesBucket &bucket : _buckets) { newQueue.push(&bucket); }
std::swap(_pq, newQueue);
decltype(line_bucket_ptr_queue) newQueue;
for (LinesBucket &bucket : line_buckets) { newQueue.push(&bucket); }
std::swap(line_bucket_ptr_queue, newQueue);
}
}
// remove lowest and get the current bottom z
float LinesBucketQueue::getCurrBottomZ()
{
auto lowest = _pq.top();
_pq.pop();
auto lowest = line_bucket_ptr_queue.top();
line_bucket_ptr_queue.pop();
float layerBottomZ = lowest->curBottomZ();
std::vector<LinesBucket *> lowests;
lowests.push_back(lowest);
while (_pq.empty() == false && std::abs(_pq.top()->curBottomZ() - lowest->curBottomZ()) < EPSILON) {
lowests.push_back(_pq.top());
_pq.pop();
while (line_bucket_ptr_queue.empty() == false && std::abs(line_bucket_ptr_queue.top()->curBottomZ() - lowest->curBottomZ()) < EPSILON) {
lowests.push_back(line_bucket_ptr_queue.top());
line_bucket_ptr_queue.pop();
}
for (LinesBucket *bp : lowests) {
bp->raise();
if (bp->valid()) { _pq.push(bp); }
if (bp->valid()) { line_bucket_ptr_queue.push(bp); }
}
return layerBottomZ;
}
@ -126,7 +126,7 @@ float LinesBucketQueue::getCurrBottomZ()
LineWithIDs LinesBucketQueue::getCurLines() const
{
LineWithIDs lines;
for (const LinesBucket &bucket : _buckets) {
for (const LinesBucket &bucket : line_buckets) {
if (bucket.valid()) {
LineWithIDs tmpLines = bucket.curLines();
lines.insert(lines.end(), tmpLines.begin(), tmpLines.end());

View file

@ -109,12 +109,12 @@ struct LinesBucketPtrComp
class LinesBucketQueue
{
public:
std::vector<LinesBucket> _buckets;
std::priority_queue<LinesBucket *, std::vector<LinesBucket *>, LinesBucketPtrComp> _pq;
std::vector<LinesBucket> line_buckets;
std::priority_queue<LinesBucket *, std::vector<LinesBucket *>, LinesBucketPtrComp> line_bucket_ptr_queue;
public:
void emplace_back_bucket(ExtrusionLayers &&els, const void *objPtr, Point offset);
bool valid() const { return _pq.empty() == false; }
bool valid() const { return line_bucket_ptr_queue.empty() == false; }
float getCurrBottomZ();
LineWithIDs getCurLines() const;
};

View file

@ -44,6 +44,7 @@ static const size_t MIN_EXTRUDERS_COUNT = 5;
static const float DEFAULT_FILAMENT_DIAMETER = 1.75f;
static const int DEFAULT_FILAMENT_HRC = 0;
static const float DEFAULT_FILAMENT_DENSITY = 1.245f;
static const float DEFAULT_FILAMENT_COST = 29.99f;
static const int DEFAULT_FILAMENT_VITRIFICATION_TEMPERATURE = 0;
static const Slic3r::Vec3f DEFAULT_EXTRUDER_OFFSET = Slic3r::Vec3f::Zero();
@ -64,7 +65,9 @@ const std::vector<std::string> GCodeProcessor::Reserved_Tags = {
"_GP_ESTIMATED_PRINTING_TIME_PLACEHOLDER",
"_GP_TOTAL_LAYER_NUMBER_PLACEHOLDER",
" MANUAL_TOOL_CHANGE ",
"_DURING_PRINT_EXHAUST_FAN"
"_DURING_PRINT_EXHAUST_FAN",
" WIPE_TOWER_START",
" WIPE_TOWER_END"
};
const std::vector<std::string> GCodeProcessor::Reserved_Tags_compatible = {
@ -81,7 +84,10 @@ const std::vector<std::string> GCodeProcessor::Reserved_Tags_compatible = {
"_GP_LAST_LINE_M73_PLACEHOLDER",
"_GP_ESTIMATED_PRINTING_TIME_PLACEHOLDER",
"_GP_TOTAL_LAYER_NUMBER_PLACEHOLDER",
" MANUAL_TOOL_CHANGE "
" MANUAL_TOOL_CHANGE ",
"_DURING_PRINT_EXHAUST_FAN",
" WIPE_TOWER_START",
" WIPE_TOWER_END"
};
@ -732,22 +738,30 @@ void GCodeProcessor::UsedFilaments::reset()
color_change_cache = 0.0f;
volumes_per_color_change = std::vector<double>();
tool_change_cache = 0.0f;
model_extrude_cache = 0.0f;
volumes_per_extruder.clear();
flush_per_filament.clear();
role_cache = 0.0f;
filaments_per_role.clear();
wipe_tower_cache = 0.0f;
wipe_tower_volume_per_extruder.clear();
}
void GCodeProcessor::UsedFilaments::increase_caches(double extruded_volume)
void GCodeProcessor::UsedFilaments::increase_model_caches(double extruded_volume)
{
color_change_cache += extruded_volume;
tool_change_cache += extruded_volume;
model_extrude_cache += extruded_volume;
role_cache += extruded_volume;
}
void GCodeProcessor::UsedFilaments::increase_wipe_tower_caches(double extruded_volume)
{
wipe_tower_cache += extruded_volume;
}
void GCodeProcessor::UsedFilaments::process_color_change_cache()
{
if (color_change_cache != 0.0f) {
@ -756,15 +770,27 @@ void GCodeProcessor::UsedFilaments::process_color_change_cache()
}
}
void GCodeProcessor::UsedFilaments::process_extruder_cache(GCodeProcessor* processor)
void GCodeProcessor::UsedFilaments::process_model_cache(GCodeProcessor* processor)
{
size_t active_extruder_id = processor->m_extruder_id;
if (tool_change_cache != 0.0f) {
if (model_extrude_cache != 0.0f) {
if (volumes_per_extruder.find(active_extruder_id) != volumes_per_extruder.end())
volumes_per_extruder[active_extruder_id] += tool_change_cache;
volumes_per_extruder[active_extruder_id] += model_extrude_cache;
else
volumes_per_extruder[active_extruder_id] = tool_change_cache;
tool_change_cache = 0.0f;
volumes_per_extruder[active_extruder_id] = model_extrude_cache;
model_extrude_cache = 0.0f;
}
}
void GCodeProcessor::UsedFilaments::process_wipe_tower_cache(GCodeProcessor* processor)
{
size_t active_extruder_id = processor->m_extruder_id;
if (wipe_tower_cache != 0.0f) {
if (wipe_tower_volume_per_extruder.find(active_extruder_id) != wipe_tower_volume_per_extruder.end())
wipe_tower_volume_per_extruder[active_extruder_id] += wipe_tower_cache;
else
wipe_tower_volume_per_extruder[active_extruder_id] = wipe_tower_cache;
wipe_tower_cache = 0.0f;
}
}
@ -799,8 +825,9 @@ void GCodeProcessor::UsedFilaments::process_role_cache(GCodeProcessor* processor
void GCodeProcessor::UsedFilaments::process_caches(GCodeProcessor* processor)
{
process_color_change_cache();
process_extruder_cache(processor);
process_model_cache(processor);
process_role_cache(processor);
process_wipe_tower_cache(processor);
}
#if ENABLE_GCODE_VIEWER_STATISTICS
@ -852,8 +879,10 @@ void GCodeProcessorResult::reset() {
filament_diameters = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DIAMETER);
required_nozzle_HRC = std::vector<int>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_HRC);
filament_densities = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_DENSITY);
filament_costs = std::vector<float>(MIN_EXTRUDERS_COUNT, DEFAULT_FILAMENT_COST);
custom_gcode_per_print_z = std::vector<CustomGCode::Item>();
spiral_vase_layers = std::vector<std::pair<float, std::pair<size_t, size_t>>>();
bed_match_result = BedMatchResult(true);
warnings.clear();
//BBS: add mutex for protection of gcode result
@ -962,6 +991,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
m_result.required_nozzle_HRC.resize(extruders_count);
m_result.filament_densities.resize(extruders_count);
m_result.filament_vitrification_temperature.resize(extruders_count);
m_result.filament_costs.resize(extruders_count);
m_extruder_temps.resize(extruders_count);
m_result.nozzle_hrc = static_cast<int>(config.nozzle_hrc.getInt());
m_result.nozzle_type = config.nozzle_type;
@ -972,6 +1002,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
m_result.required_nozzle_HRC[i] = static_cast<int>(config.required_nozzle_HRC.get_at(i));
m_result.filament_densities[i] = static_cast<float>(config.filament_density.get_at(i));
m_result.filament_vitrification_temperature[i] = static_cast<float>(config.temperature_vitrification.get_at(i));
m_result.filament_costs[i] = static_cast<float>(config.filament_cost.get_at(i));
}
if (m_flavor == gcfMarlinLegacy || m_flavor == gcfMarlinFirmware || m_flavor == gcfKlipper || m_flavor == gcfRepRapFirmware) {
@ -1027,6 +1058,7 @@ void GCodeProcessor::apply_config(const PrintConfig& config)
const ConfigOptionFloat* z_offset = config.option<ConfigOptionFloat>("z_offset");
if (z_offset != nullptr)
m_z_offset = z_offset->value;
}
void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
@ -1114,6 +1146,19 @@ void GCodeProcessor::apply_config(const DynamicPrintConfig& config)
m_result.filament_densities.emplace_back(DEFAULT_FILAMENT_DENSITY);
}
}
//BBS
const ConfigOptionFloats* filament_costs = config.option<ConfigOptionFloats>("filament_cost");
if (filament_costs != nullptr) {
m_result.filament_costs.clear();
m_result.filament_costs.resize(filament_costs->values.size());
for (size_t i = 0; i < filament_costs->values.size(); ++i)
m_result.filament_costs[i]=static_cast<float>(filament_costs->values[i]);
}
for (size_t i = m_result.filament_costs.size(); i < m_result.extruders_count; ++i) {
m_result.filament_costs.emplace_back(DEFAULT_FILAMENT_COST);
}
//BBS
const ConfigOptionInts* filament_vitrification_temperature = config.option<ConfigOptionInts>("temperature_vitrification");
if (filament_vitrification_temperature != nullptr) {
@ -1330,6 +1375,7 @@ void GCodeProcessor::reset()
m_cached_position.reset();
m_wiping = false;
m_flushing = false;
m_wipe_tower = false;
m_remaining_volume = 0.f;
// BBS: arc move related data
m_move_path_type = EMovePathType::Noop_move;
@ -2106,6 +2152,17 @@ void GCodeProcessor::process_tags(const std::string_view comment, bool producers
return;
}
if (boost::starts_with(comment, reserved_tag(ETags::Wipe_Tower_Start))) {
m_wipe_tower = true;
return;
}
if (boost::starts_with(comment, reserved_tag(ETags::Wipe_Tower_End))) {
m_wipe_tower = false;
m_used_filaments.process_wipe_tower_cache(this);
return;
}
//BBS: flush start tag
if (boost::starts_with(comment, GCodeProcessor::Flush_Start_Tag)) {
m_flushing = true;
@ -2811,10 +2868,13 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line)
float delta_xyz = std::sqrt(sqr(delta_pos[X]) + sqr(delta_pos[Y]) + sqr(delta_pos[Z]));
float volume_extruded_filament = area_filament_cross_section * delta_pos[E];
float area_toolpath_cross_section = volume_extruded_filament / delta_xyz;
// save extruded volume to the cache
m_used_filaments.increase_caches(volume_extruded_filament);
if (m_wipe_tower) {
m_used_filaments.increase_wipe_tower_caches(volume_extruded_filament);
}
else {
// save extruded volume to the cache
m_used_filaments.increase_model_caches(volume_extruded_filament);
}
// volume extruded filament / tool displacement = area toolpath cross section
m_mm3_per_mm = area_toolpath_cross_section;
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
@ -3268,10 +3328,14 @@ void GCodeProcessor::process_G2_G3(const GCodeReader::GCodeLine& line)
if (type == EMoveType::Extrude) {
float volume_extruded_filament = area_filament_cross_section * delta_pos[E];
float area_toolpath_cross_section = volume_extruded_filament / delta_xyz;
//BBS: save extruded volume to the cache
m_used_filaments.increase_caches(volume_extruded_filament);
if (m_wipe_tower) {
//BBS: save wipe tower volume to the cache
m_used_filaments.increase_wipe_tower_caches(volume_extruded_filament);
}
else {
//BBS: save extruded volume to the cache
m_used_filaments.increase_model_caches(volume_extruded_filament);
}
//BBS: volume extruded filament / tool displacement = area toolpath cross section
m_mm3_per_mm = area_toolpath_cross_section;
#if ENABLE_GCODE_VIEWER_DATA_CHECKING
@ -4320,7 +4384,7 @@ void GCodeProcessor::process_filaments(CustomGCode::Type code)
m_used_filaments.process_color_change_cache();
if (code == CustomGCode::ToolChange) {
m_used_filaments.process_extruder_cache(this);
m_used_filaments.process_model_cache(this);
//BBS: reset remaining filament
m_remaining_volume = m_nozzle_volume;
}
@ -4353,6 +4417,7 @@ void GCodeProcessor::update_estimated_times_stats()
m_result.print_statistics.volumes_per_color_change = m_used_filaments.volumes_per_color_change;
m_result.print_statistics.volumes_per_extruder = m_used_filaments.volumes_per_extruder;
m_result.print_statistics.wipe_tower_volumes_per_extruder = m_used_filaments.wipe_tower_volume_per_extruder;
m_result.print_statistics.flush_per_filament = m_used_filaments.flush_per_filament;
m_result.print_statistics.used_filaments_per_role = m_used_filaments.filaments_per_role;
}

View file

@ -73,6 +73,7 @@ namespace Slic3r {
std::vector<double> volumes_per_color_change;
std::map<size_t, double> volumes_per_extruder;
std::map<size_t, double> wipe_tower_volumes_per_extruder;
//BBS: the flush amount of every filament
std::map<size_t, double> flush_per_filament;
std::map<ExtrusionRole, std::pair<double, double>> used_filaments_per_role;
@ -88,6 +89,7 @@ namespace Slic3r {
}
volumes_per_color_change.clear();
volumes_per_color_change.shrink_to_fit();
wipe_tower_volumes_per_extruder.clear();
volumes_per_extruder.clear();
flush_per_filament.clear();
used_filaments_per_role.clear();
@ -109,11 +111,23 @@ namespace Slic3r {
ConflictResult() = default;
};
struct BedMatchResult
{
bool match;
std::string bed_type_name;
int extruder_id;
BedMatchResult():match(true),bed_type_name(""),extruder_id(-1) {}
BedMatchResult(bool _match,const std::string& _bed_type_name="",int _extruder_id=-1)
:match(_match),bed_type_name(_bed_type_name),extruder_id(_extruder_id)
{}
};
using ConflictResultOpt = std::optional<ConflictResult>;
struct GCodeProcessorResult
{
ConflictResultOpt conflict_result;
BedMatchResult bed_match_result;
struct SettingsIds
{
@ -190,6 +204,7 @@ namespace Slic3r {
std::vector<float> filament_diameters;
std::vector<int> required_nozzle_HRC;
std::vector<float> filament_densities;
std::vector<float> filament_costs;
std::vector<int> filament_vitrification_temperature;
PrintEstimatedStatistics print_statistics;
std::vector<CustomGCode::Item> custom_gcode_per_print_z;
@ -223,11 +238,13 @@ namespace Slic3r {
extruder_colors = other.extruder_colors;
filament_diameters = other.filament_diameters;
filament_densities = other.filament_densities;
filament_costs = other.filament_costs;
print_statistics = other.print_statistics;
custom_gcode_per_print_z = other.custom_gcode_per_print_z;
spiral_vase_layers = other.spiral_vase_layers;
warnings = other.warnings;
bed_type = other.bed_type;
bed_match_result = other.bed_match_result;
#if ENABLE_GCODE_VIEWER_STATISTICS
time = other.time;
#endif
@ -261,7 +278,9 @@ namespace Slic3r {
Estimated_Printing_Time_Placeholder,
Total_Layer_Number_Placeholder,
Manual_Tool_Change,
During_Print_Exhaust_Fan
During_Print_Exhaust_Fan,
Wipe_Tower_Start,
Wipe_Tower_End,
};
static const std::string& reserved_tag(ETags tag) { return s_IsBBLPrinter ? Reserved_Tags[static_cast<unsigned char>(tag)] : Reserved_Tags_compatible[static_cast<unsigned char>(tag)]; }
@ -474,9 +493,12 @@ namespace Slic3r {
double color_change_cache;
std::vector<double> volumes_per_color_change;
double tool_change_cache;
double model_extrude_cache;
std::map<size_t, double> volumes_per_extruder;
double wipe_tower_cache;
std::map<size_t, double>wipe_tower_volume_per_extruder;
//BBS: the flush amount of every filament
std::map<size_t, double> flush_per_filament;
@ -485,10 +507,12 @@ namespace Slic3r {
void reset();
void increase_caches(double extruded_volume);
void increase_model_caches(double extruded_volume);
void increase_wipe_tower_caches(double extruded_volume);
void process_color_change_cache();
void process_extruder_cache(GCodeProcessor* processor);
void process_model_cache(GCodeProcessor* processor);
void process_wipe_tower_cache(GCodeProcessor* processor);
void update_flush_per_filament(size_t extrude_id, float flush_length);
void process_role_cache(GCodeProcessor* processor);
void process_caches(GCodeProcessor* processor);
@ -637,6 +661,7 @@ namespace Slic3r {
CachedPosition m_cached_position;
bool m_wiping;
bool m_flushing;
bool m_wipe_tower;
float m_remaining_volume;
bool m_manual_filament_change;

File diff suppressed because it is too large Load diff

View file

@ -27,136 +27,139 @@ class Grid;
namespace SeamPlacerImpl {
// ************ FOR BACKPORT COMPATIBILITY ONLY ***************
// Angle from v1 to v2, returning double atan2(y, x) normalized to <-PI, PI>.
template<typename Derived, typename Derived2> inline double angle(const Eigen::MatrixBase<Derived> &v1, const Eigen::MatrixBase<Derived2> &v2)
{
static_assert(Derived::IsVectorAtCompileTime && int(Derived::SizeAtCompileTime) == 2, "angle(): first parameter is not a 2D vector");
static_assert(Derived2::IsVectorAtCompileTime && int(Derived2::SizeAtCompileTime) == 2, "angle(): second parameter is not a 2D vector");
auto v1d = v1.template cast<double>();
auto v2d = v2.template cast<double>();
return atan2(cross2(v1d, v2d), v1d.dot(v2d));
}
// ***************************
struct GlobalModelInfo;
struct SeamComparator;
enum class EnforcedBlockedSeamPoint {
Blocked = 0,
Neutral = 1,
Enforced = 2,
Blocked = 0,
Neutral = 1,
Enforced = 2,
};
// struct representing single perimeter loop
struct Perimeter {
size_t start_index{};
size_t end_index{}; //inclusive!
size_t seam_index{};
float flow_width{};
struct Perimeter
{
size_t start_index{};
size_t end_index{}; // inclusive!
size_t seam_index{};
float flow_width{};
// During alignment, a final position may be stored here. In that case, finalized is set to true.
// Note that final seam position is not limited to points of the perimeter loop. In theory it can be any position
// Random position also uses this flexibility to set final seam point position
bool finalized = false;
Vec3f final_seam_position = Vec3f::Zero();
// During alignment, a final position may be stored here. In that case, finalized is set to true.
// Note that final seam position is not limited to points of the perimeter loop. In theory it can be any position
// Random position also uses this flexibility to set final seam point position
bool finalized = false;
Vec3f final_seam_position = Vec3f::Zero();
};
//Struct over which all processing of perimeters is done. For each perimeter point, its respective candidate is created,
// Struct over which all processing of perimeters is done. For each perimeter point, its respective candidate is created,
// then all the needed attributes are computed and finally, for each perimeter one point is chosen as seam.
// This seam position can be then further aligned
struct SeamCandidate {
SeamCandidate(const Vec3f &pos, Perimeter &perimeter,
float local_ccw_angle,
EnforcedBlockedSeamPoint type) :
position(pos), perimeter(perimeter), visibility(0.0f), overhang(0.0f), embedded_distance(0.0f), local_ccw_angle(
local_ccw_angle), type(type), central_enforcer(false) {
}
const Vec3f position;
// pointer to Perimeter loop of this point. It is shared across all points of the loop
Perimeter &perimeter;
float visibility;
float overhang;
// distance inside the merged layer regions, for detecting perimeter points which are hidden indside the print (e.g. multimaterial join)
// Negative sign means inside the print, comes from EdgeGrid structure
float embedded_distance;
float local_ccw_angle;
EnforcedBlockedSeamPoint type;
bool central_enforcer; //marks this candidate as central point of enforced segment on the perimeter - important for alignment
struct SeamCandidate
{
SeamCandidate(const Vec3f &pos, Perimeter &perimeter, float local_ccw_angle, EnforcedBlockedSeamPoint type)
: position(pos), perimeter(perimeter), visibility(0.0f), overhang(0.0f), embedded_distance(0.0f), local_ccw_angle(local_ccw_angle), type(type), central_enforcer(false)
{}
const Vec3f position;
// pointer to Perimeter loop of this point. It is shared across all points of the loop
Perimeter &perimeter;
float visibility;
float overhang;
// distance inside the merged layer regions, for detecting perimeter points which are hidden indside the print (e.g. multimaterial join)
// Negative sign means inside the print, comes from EdgeGrid structure
float embedded_distance;
float local_ccw_angle;
EnforcedBlockedSeamPoint type;
bool central_enforcer; // marks this candidate as central point of enforced segment on the perimeter - important for alignment
};
struct SeamCandidateCoordinateFunctor {
SeamCandidateCoordinateFunctor(const std::vector<SeamCandidate> &seam_candidates) :
seam_candidates(seam_candidates) {
}
const std::vector<SeamCandidate> &seam_candidates;
float operator()(size_t index, size_t dim) const {
return seam_candidates[index].position[dim];
}
struct SeamCandidateCoordinateFunctor
{
SeamCandidateCoordinateFunctor(const std::vector<SeamCandidate> &seam_candidates) : seam_candidates(seam_candidates) {}
const std::vector<SeamCandidate> &seam_candidates;
float operator()(size_t index, size_t dim) const { return seam_candidates[index].position[dim]; }
};
} // namespace SeamPlacerImpl
struct PrintObjectSeamData
{
using SeamCandidatesTree = KDTreeIndirect<3, float, SeamPlacerImpl::SeamCandidateCoordinateFunctor>;
using SeamCandidatesTree = KDTreeIndirect<3, float, SeamPlacerImpl::SeamCandidateCoordinateFunctor>;
struct LayerSeams
{
Slic3r::deque<SeamPlacerImpl::Perimeter> perimeters;
std::vector<SeamPlacerImpl::SeamCandidate> points;
std::unique_ptr<SeamCandidatesTree> points_tree;
};
// Map of PrintObjects (PO) -> vector of layers of PO -> vector of perimeter
std::vector<LayerSeams> layers;
// Map of PrintObjects (PO) -> vector of layers of PO -> unique_ptr to KD
// tree of all points of the given layer
struct LayerSeams
{
Slic3r::deque<SeamPlacerImpl::Perimeter> perimeters;
std::vector<SeamPlacerImpl::SeamCandidate> points;
std::unique_ptr<SeamCandidatesTree> points_tree;
};
// Map of PrintObjects (PO) -> vector of layers of PO -> vector of perimeter
std::vector<LayerSeams> layers;
// Map of PrintObjects (PO) -> vector of layers of PO -> unique_ptr to KD
// tree of all points of the given layer
void clear()
{
layers.clear();
}
void clear() { layers.clear(); }
};
class SeamPlacer {
class SeamPlacer
{
public:
// Number of samples generated on the mesh. There are sqr_rays_per_sample_point*sqr_rays_per_sample_point rays casted from each samples
static constexpr size_t raycasting_visibility_samples_count = 30000;
static constexpr size_t fast_decimation_triangle_count_target = 16000;
//square of number of rays per sample point
static constexpr size_t sqr_rays_per_sample_point = 5;
// Number of samples generated on the mesh. There are sqr_rays_per_sample_point*sqr_rays_per_sample_point rays casted from each samples
static constexpr size_t raycasting_visibility_samples_count = 30000;
static constexpr size_t fast_decimation_triangle_count_target = 16000;
//square of number of rays per sample point
static constexpr size_t sqr_rays_per_sample_point = 5;
// snapping angle - angles larger than this value will be snapped to during seam painting
static constexpr float sharp_angle_snapping_threshold = 55.0f * float(PI) / 180.0f;
// overhang angle for seam placement that still yields good results, in degrees, measured from vertical direction
static constexpr float overhang_angle_threshold = 50.0f * float(PI) / 180.0f;
// snapping angle - angles larger than this value will be snapped to during seam painting
static constexpr float sharp_angle_snapping_threshold = 55.0f * float(PI) / 180.0f;
// overhang angle for seam placement that still yields good results, in degrees, measured from vertical direction
//BBS
static constexpr float overhang_angle_threshold = 45.0f * float(PI) / 180.0f;
// determines angle importance compared to visibility ( neutral value is 1.0f. )
static constexpr float angle_importance_aligned = 0.6f;
static constexpr float angle_importance_nearest = 1.0f; // use much higher angle importance for nearest mode, to combat the visibility info noise
// determines angle importance compared to visibility ( neutral value is 1.0f. )
static constexpr float angle_importance_aligned = 0.6f;
static constexpr float angle_importance_nearest = 1.0f; // use much higher angle importance for nearest mode, to combat the visibility info noise
// For long polygon sides, if they are close to the custom seam drawings, they are oversampled with this step size
static constexpr float enforcer_oversampling_distance = 0.2f;
// For long polygon sides, if they are close to the custom seam drawings, they are oversampled with this step size
static constexpr float enforcer_oversampling_distance = 0.2f;
// When searching for seam clusters for alignment:
// following value describes, how much worse score can point have and still be picked into seam cluster instead of original seam point on the same layer
static constexpr float seam_align_score_tolerance = 0.3f;
// seam_align_tolerable_dist_factor - how far to search for seam from current position, final dist is seam_align_tolerable_dist_factor * flow_width
static constexpr float seam_align_tolerable_dist_factor = 4.0f;
// minimum number of seams needed in cluster to make alignment happen
static constexpr size_t seam_align_minimum_string_seams = 6;
// millimeters covered by spline; determines number of splines for the given string
static constexpr size_t seam_align_mm_per_segment = 4.0f;
// When searching for seam clusters for alignment:
// following value describes, how much worse score can point have and still be picked into seam cluster instead of original seam point on the same layer
static constexpr float seam_align_score_tolerance = 0.3f;
// seam_align_tolerable_dist_factor - how far to search for seam from current position, final dist is seam_align_tolerable_dist_factor * flow_width
static constexpr float seam_align_tolerable_dist_factor = 4.0f;
// minimum number of seams needed in cluster to make alignment happen
static constexpr size_t seam_align_minimum_string_seams = 6;
// millimeters covered by spline; determines number of splines for the given string
static constexpr size_t seam_align_mm_per_segment = 4.0f;
//The following data structures hold all perimeter points for all PrintObject.
std::unordered_map<const PrintObject*, PrintObjectSeamData> m_seam_per_object;
// The following data structures hold all perimeter points for all PrintObject.
std::unordered_map<const PrintObject *, PrintObjectSeamData> m_seam_per_object;
void init(const Print &print, std::function<void(void)> throw_if_canceled_func);
void init(const Print &print, std::function<void(void)> throw_if_canceled_func);
void place_seam(const Layer *layer, ExtrusionLoop &loop, bool external_first, const Point &last_pos) const;
void place_seam(const Layer *layer, ExtrusionLoop &loop, bool external_first, const Point &last_pos) const;
private:
void gather_seam_candidates(const PrintObject *po, const SeamPlacerImpl::GlobalModelInfo &global_model_info);
void calculate_candidates_visibility(const PrintObject *po,
const SeamPlacerImpl::GlobalModelInfo &global_model_info);
void calculate_overhangs_and_layer_embedding(const PrintObject *po);
void align_seam_points(const PrintObject *po, const SeamPlacerImpl::SeamComparator &comparator);
std::vector<std::pair<size_t, size_t>> find_seam_string(const PrintObject *po,
std::pair<size_t, size_t> start_seam,
const SeamPlacerImpl::SeamComparator &comparator) const;
std::optional<std::pair<size_t, size_t>> find_next_seam_in_layer(
const std::vector<PrintObjectSeamData::LayerSeams> &layers,
const Vec3f& projected_position,
const size_t layer_idx, const float max_distance,
const SeamPlacerImpl::SeamComparator &comparator) const;
void gather_seam_candidates(const PrintObject *po, const SeamPlacerImpl::GlobalModelInfo &global_model_info, const SeamPosition configured_seam_preference);
void calculate_candidates_visibility(const PrintObject *po, const SeamPlacerImpl::GlobalModelInfo &global_model_info);
void calculate_overhangs_and_layer_embedding(const PrintObject *po);
void align_seam_points(const PrintObject *po, const SeamPlacerImpl::SeamComparator &comparator);
std::vector<std::pair<size_t, size_t>> find_seam_string(const PrintObject *po, std::pair<size_t, size_t> start_seam, const SeamPlacerImpl::SeamComparator &comparator) const;
std::optional<std::pair<size_t, size_t>> find_next_seam_in_layer(const std::vector<PrintObjectSeamData::LayerSeams> &layers,
const Vec3f & projected_position,
const size_t layer_idx,
const float max_distance,
const SeamPlacerImpl::SeamComparator & comparator) const;
};
} // namespace Slic3r

View file

@ -388,6 +388,11 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
it_per_layer_extruder_override = per_layer_extruder_switches.begin();
unsigned int extruder_override = 0;
// BBS: collect first layer extruders of an object's wall, which will be used by brim generator
int layerCount = 0;
std::vector<int> firstLayerExtruders;
firstLayerExtruders.clear();
// Collect the object extruders.
for (auto layer : object.layers()) {
LayerTools &layer_tools = this->tools_for_layer(layer->print_z);
@ -413,8 +418,12 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
something_nonoverriddable = true;
}
if (something_nonoverriddable)
if (something_nonoverriddable){
layer_tools.extruders.emplace_back((extruder_override == 0) ? region.config().wall_filament.value : extruder_override);
if (layerCount == 0) {
firstLayerExtruders.emplace_back((extruder_override == 0) ? region.config().wall_filament.value : extruder_override);
}
}
layer_tools.has_object = true;
}
@ -449,8 +458,12 @@ void ToolOrdering::collect_extruders(const PrintObject &object, const std::vecto
if (has_solid_infill || has_infill)
layer_tools.has_object = true;
}
layerCount++;
}
sort_remove_duplicates(firstLayerExtruders);
const_cast<PrintObject&>(object).object_first_layer_wall_extruders = firstLayerExtruders;
for (auto& layer : m_layer_tools) {
// Sort and remove duplicates
sort_remove_duplicates(layer.extruders);
@ -1027,8 +1040,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
if (!object->config().flush_into_infill && !object->config().flush_into_objects && !object->config().flush_into_support)
continue;
bool wipe_into_infill_only = !object->config().flush_into_objects && object->config().flush_into_infill;
bool is_infill_first = print.default_region_config().wall_infill_order == WallInfillOrder::InfillInnerOuter ||
print.default_region_config().wall_infill_order == WallInfillOrder::InfillOuterInner;
bool is_infill_first = print.config().is_infill_first;
if (is_infill_first != perimeters_done || wipe_into_infill_only) {
for (const ExtrusionEntity* ee : layerm->fills.entities) { // iterate through all infill Collections
auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);
@ -1082,7 +1094,7 @@ float WipingExtrusions::mark_wiping_extrusions(const Print& print, unsigned int
break;
auto &entities = this_support_layer->support_fills.entities;
if (support_overriddable && !is_support_overridden(object)) {
if (support_overriddable && !is_support_overridden(object) && !(object_config.support_interface_not_for_body.value && !support_intf_overriddable &&(new_extruder==object_config.support_interface_filament-1||old_extruder==object_config.support_interface_filament-1))) {
set_support_extruder_override(object, copy, new_extruder, num_of_copies);
for (const ExtrusionEntity* ee : entities) {
if (ee->role() == erSupportMaterial || ee->role() == erSupportTransition)
@ -1141,8 +1153,7 @@ void WipingExtrusions::ensure_perimeters_infills_order(const Print& print)
if (!object->config().flush_into_infill && !object->config().flush_into_objects)
continue;
bool is_infill_first = print.default_region_config().wall_infill_order == WallInfillOrder::InfillInnerOuter ||
print.default_region_config().wall_infill_order == WallInfillOrder::InfillOuterInner;
bool is_infill_first = print.config().is_infill_first;
for (const ExtrusionEntity* ee : layerm->fills.entities) { // iterate through all infill Collections
auto* fill = dynamic_cast<const ExtrusionEntityCollection*>(ee);

View file

@ -770,6 +770,7 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool, bool extrude_per
"; CP TOOLCHANGE START\n")
.comment_with_value(" toolchange #", m_num_tool_changes + 1); // the number is zero-based
if (tool != (unsigned)(-1))
writer.append(std::string("; material : " + (m_current_tool < m_filpar.size() ? m_filpar[m_current_tool].material : "(NONE)") + " -> " + m_filpar[tool].material + "\n").c_str())
.append(";--------------------\n");
@ -787,6 +788,7 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool, bool extrude_per
// Ram the hot material out of the melt zone, retract the filament into the cooling tubes and let it cool.
if (tool != (unsigned int)-1){ // This is not the last change.
writer.append(";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_Tower_Start) + "\n");
toolchange_Unload(writer, cleaning_box, m_filpar[m_current_tool].material,
is_first_layer() ? m_filpar[tool].nozzle_temperature_initial_layer : m_filpar[tool].nozzle_temperature);
toolchange_Change(writer, tool, m_filpar[tool].material); // Change the tool, set a speed override for soluble and flex materials.
@ -806,8 +808,8 @@ WipeTower::ToolChangeResult WipeTower::tool_change(size_t tool, bool extrude_per
writer.travel(Vec2f(0, 0));
writer.travel(initial_position);
}
toolchange_Wipe(writer, cleaning_box, wipe_length); // Wipe the newly loaded filament until the end of the assigned wipe area.
writer.append(";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_Tower_End) + "\n");
++ m_num_tool_changes;
} else
toolchange_Unload(writer, cleaning_box, m_filpar[m_current_tool].material, m_filpar[m_current_tool].nozzle_temperature);
@ -1185,6 +1187,7 @@ WipeTower::ToolChangeResult WipeTower::finish_layer(bool extrude_perimeter, bool
.set_initial_tool(m_current_tool)
.set_y_shift(m_y_shift - (m_current_shape == SHAPE_REVERSED ? m_layer_info->toolchanges_depth() : 0.f));
writer.append(";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_Tower_Start) + "\n");
// Slow down on the 1st layer.
bool first_layer = is_first_layer();
@ -1324,6 +1327,7 @@ WipeTower::ToolChangeResult WipeTower::finish_layer(bool extrude_perimeter, bool
writer.add_wipe_point(writer.pos())
.add_wipe_point(target);
writer.append(";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_Tower_End) + "\n");
// Ask our writer about how much material was consumed.
// Skip this in case the layer is sparse and config option to not print sparse layers is enabled.
@ -1601,6 +1605,9 @@ void WipeTower::generate(std::vector<std::vector<WipeTower::ToolChangeResult>> &
// BBS
//m_internal_rotation += 180.f;
if (m_layer_info->depth < m_perimeter_width)
continue;
if (m_layer_info->depth < m_wipe_tower_depth - m_perimeter_width) {
// align y shift to perimeter width
float dy = m_extra_spacing * m_perimeter_width;
@ -1687,7 +1694,7 @@ WipeTower::ToolChangeResult WipeTower::only_generate_out_wall()
// BBS: Delete some unnecessary travel
//if (writer.x() > fill_box.ld.x() + EPSILON) writer.travel(fill_box.ld.x(), writer.y());
//if (writer.y() > fill_box.ld.y() + EPSILON) writer.travel(writer.x(), fill_box.ld.y());
writer.append(";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_Tower_Start) + "\n");
// outer perimeter (always):
// BBS
box_coordinates wt_box(Vec2f(0.f, (m_current_shape == SHAPE_REVERSED ? m_layer_info->toolchanges_depth() : 0.f)), m_wipe_tower_width, m_layer_info->depth + m_perimeter_width);
@ -1698,6 +1705,8 @@ WipeTower::ToolChangeResult WipeTower::only_generate_out_wall()
Vec2f target = (writer.pos() == wt_box.ld ? wt_box.rd : (writer.pos() == wt_box.rd ? wt_box.ru : (writer.pos() == wt_box.ru ? wt_box.lu : wt_box.ld)));
writer.add_wipe_point(writer.pos()).add_wipe_point(target);
writer.append(";" + GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Wipe_Tower_End) + "\n");
// Ask our writer about how much material was consumed.
// Skip this in case the layer is sparse and config option to not print sparse layers is enabled.
if (!m_no_sparse_layers || toolchanges_on_layer)

View file

@ -0,0 +1,349 @@
#include "JumpPointSearch.hpp"
#include "BoundingBox.hpp"
#include "ExPolygon.hpp"
#include "Point.hpp"
#include "libslic3r/AStar.hpp"
#include "libslic3r/KDTreeIndirect.hpp"
#include "libslic3r/Polygon.hpp"
#include "libslic3r/Polyline.hpp"
#include "libslic3r/libslic3r.h"
#include <algorithm>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <cstdlib>
#include <iterator>
#include <limits>
#include <optional>
#include <string>
#include <unordered_map>
#include <vector>
#include <oneapi/tbb/scalable_allocator.h>
//#define DEBUG_FILES
#ifdef DEBUG_FILES
#include "libslic3r/SVG.hpp"
#endif
namespace Slic3r {
// execute fn for each pixel on the line. If fn returns false, terminate the iteration
template<typename PointFn> void dda(coord_t x0, coord_t y0, coord_t x1, coord_t y1, const PointFn &fn)
{
coord_t dx = abs(x1 - x0);
coord_t dy = abs(y1 - y0);
coord_t x = x0;
coord_t y = y0;
coord_t n = 1 + dx + dy;
coord_t x_inc = (x1 > x0) ? 1 : -1;
coord_t y_inc = (y1 > y0) ? 1 : -1;
coord_t error = dx - dy;
dx *= 2;
dy *= 2;
for (; n > 0; --n) {
if (!fn(x, y)) return;
if (error > 0) {
x += x_inc;
error -= dy;
} else {
y += y_inc;
error += dx;
}
}
}
// will draw the line twice, second time with and offset of 1 in the direction of normal
// may call the fn on the same coordiantes multiple times!
template<typename PointFn> void double_dda_with_offset(coord_t x0, coord_t y0, coord_t x1, coord_t y1, const PointFn &fn)
{
Vec2d normal = Point{y1 - y0, x1 - x0}.cast<double>().normalized();
normal.x() = ceil(normal.x());
normal.y() = ceil(normal.y());
Point start_offset = Point(x0, y0) + (normal).cast<coord_t>();
Point end_offset = Point(x1, y1) + (normal).cast<coord_t>();
dda(x0, y0, x1, y1, fn);
dda(start_offset.x(), start_offset.y(), end_offset.x(), end_offset.y(), fn);
}
template<typename CellPositionType, typename CellQueryFn> class JPSTracer
{
public:
// Use incoming_dir [0,0] for starting points, so that all directions are checked from that point
struct Node
{
CellPositionType position;
CellPositionType incoming_dir;
};
JPSTracer(CellPositionType target, CellQueryFn is_passable) : target(target), is_passable(is_passable) {}
private:
CellPositionType target;
CellQueryFn is_passable; // should return boolean whether the cell is passable or not
CellPositionType find_jump_point(CellPositionType start, CellPositionType forward_dir) const
{
CellPositionType next = start + forward_dir;
while (next != target && is_passable(next) && !(is_jump_point(next, forward_dir))) { next = next + forward_dir; }
if (is_passable(next)) {
return next;
} else {
return start;
}
}
bool is_jump_point(CellPositionType pos, CellPositionType forward_dir) const
{
if (abs(forward_dir.x()) + abs(forward_dir.y()) == 2) {
// diagonal
CellPositionType horizontal_check_dir = CellPositionType{forward_dir.x(), 0};
CellPositionType vertical_check_dir = CellPositionType{0, forward_dir.y()};
if (!is_passable(pos - horizontal_check_dir) && is_passable(pos + forward_dir - 2 * horizontal_check_dir)) { return true; }
if (!is_passable(pos - vertical_check_dir) && is_passable(pos + forward_dir - 2 * vertical_check_dir)) { return true; }
if (find_jump_point(pos, horizontal_check_dir) != pos) { return true; }
if (find_jump_point(pos, vertical_check_dir) != pos) { return true; }
return false;
} else { // horizontal or vertical
CellPositionType side_dir = CellPositionType(forward_dir.y(), forward_dir.x());
if (!is_passable(pos + side_dir) && is_passable(pos + forward_dir + side_dir)) { return true; }
if (!is_passable(pos - side_dir) && is_passable(pos + forward_dir - side_dir)) { return true; }
return false;
}
}
public:
template<class Fn> void foreach_reachable(const Node &from, Fn &&fn) const
{
const CellPositionType & pos = from.position;
const CellPositionType & forward_dir = from.incoming_dir;
std::vector<CellPositionType> dirs_to_check{};
if (abs(forward_dir.x()) + abs(forward_dir.y()) == 0) { // special case for starting point
dirs_to_check = all_directions;
} else if (abs(forward_dir.x()) + abs(forward_dir.y()) == 2) {
// diagonal
CellPositionType horizontal_check_dir = CellPositionType{forward_dir.x(), 0};
CellPositionType vertical_check_dir = CellPositionType{0, forward_dir.y()};
if (!is_passable(pos - horizontal_check_dir) && is_passable(pos + forward_dir - 2 * horizontal_check_dir)) {
dirs_to_check.push_back(forward_dir - 2 * horizontal_check_dir);
}
if (!is_passable(pos - vertical_check_dir) && is_passable(pos + forward_dir - 2 * vertical_check_dir)) {
dirs_to_check.push_back(forward_dir - 2 * vertical_check_dir);
}
dirs_to_check.push_back(horizontal_check_dir);
dirs_to_check.push_back(vertical_check_dir);
dirs_to_check.push_back(forward_dir);
} else { // horizontal or vertical
CellPositionType side_dir = CellPositionType(forward_dir.y(), forward_dir.x());
if (!is_passable(pos + side_dir) && is_passable(pos + forward_dir + side_dir)) { dirs_to_check.push_back(forward_dir + side_dir); }
if (!is_passable(pos - side_dir) && is_passable(pos + forward_dir - side_dir)) { dirs_to_check.push_back(forward_dir - side_dir); }
dirs_to_check.push_back(forward_dir);
}
for (const CellPositionType &dir : dirs_to_check) {
CellPositionType jp = find_jump_point(pos, dir);
if (jp != pos) fn(Node{jp, dir});
}
}
float distance(Node a, Node b) const { return (a.position - b.position).template cast<double>().norm(); }
float goal_heuristic(Node n) const { return n.position == target ? -1.f : (target - n.position).template cast<double>().norm(); }
size_t unique_id(Node n) const { return (static_cast<size_t>(uint16_t(n.position.x())) << 16) + static_cast<size_t>(uint16_t(n.position.y())); }
const std::vector<CellPositionType> all_directions{{1, 0}, {1, 1}, {0, 1}, {-1, 1}, {-1, 0}, {-1, -1}, {0, -1}, {1, -1}};
};
void JPSPathFinder::clear()
{
inpassable.clear();
max_search_box.max = Pixel(std::numeric_limits<coord_t>::min(), std::numeric_limits<coord_t>::min());
max_search_box.min = Pixel(std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max());
add_obstacles(bed_shape);
}
void JPSPathFinder::add_obstacles(const Lines &obstacles)
{
auto store_obstacle = [&](coord_t x, coord_t y) {
max_search_box.max.x() = std::max(max_search_box.max.x(), x);
max_search_box.max.y() = std::max(max_search_box.max.y(), y);
max_search_box.min.x() = std::min(max_search_box.min.x(), x);
max_search_box.min.y() = std::min(max_search_box.min.y(), y);
inpassable.insert(Pixel{x, y});
return true;
};
for (const Line &l : obstacles) {
Pixel start = pixelize(l.a);
Pixel end = pixelize(l.b);
double_dda_with_offset(start.x(), start.y(), end.x(), end.y(), store_obstacle);
}
}
Polyline JPSPathFinder::find_path(const Point &p0, const Point &p1)
{
Pixel start = pixelize(p0);
Pixel end = pixelize(p1);
if (inpassable.empty() || (start - end).cast<float>().norm() < 3.0) { return Polyline{p0, p1}; }
if (inpassable.find(start) != inpassable.end()) {
dda(start.x(), start.y(), end.x(), end.y(), [&](coord_t x, coord_t y) {
if (inpassable.find(Pixel(x, y)) == inpassable.end() || start == end) { // new start not found yet, and xy passable
start = Pixel(x, y);
return false;
}
return true;
});
}
if (inpassable.find(end) != inpassable.end()) {
dda(end.x(), end.y(), start.x(), start.y(), [&](coord_t x, coord_t y) {
if (inpassable.find(Pixel(x, y)) == inpassable.end() || start == end) { // new start not found yet, and xy passable
end = Pixel(x, y);
return false;
}
return true;
});
}
BoundingBox search_box = max_search_box;
search_box.max -= Pixel(1, 1);
search_box.min += Pixel(1, 1);
BoundingBox bounding_square(Points{start, end});
bounding_square.max += Pixel(5, 5);
bounding_square.min -= Pixel(5, 5);
coord_t bounding_square_size = 2 * std::max(bounding_square.size().x(), bounding_square.size().y());
bounding_square.max.x() += (bounding_square_size - bounding_square.size().x()) / 2;
bounding_square.min.x() -= (bounding_square_size - bounding_square.size().x()) / 2;
bounding_square.max.y() += (bounding_square_size - bounding_square.size().y()) / 2;
bounding_square.min.y() -= (bounding_square_size - bounding_square.size().y()) / 2;
// Intersection - limit the search box to a square area around the start and end, to fasten the path searching
search_box.max = search_box.max.cwiseMin(bounding_square.max);
search_box.min = search_box.min.cwiseMax(bounding_square.min);
auto cell_query = [&](Pixel pixel) { return search_box.contains(pixel) && (pixel == start || pixel == end || inpassable.find(pixel) == inpassable.end()); };
JPSTracer<Pixel, decltype(cell_query)> tracer(end, cell_query);
using QNode = astar::QNode<JPSTracer<Pixel, decltype(cell_query)>>;
std::unordered_map<size_t, QNode> astar_cache{};
std::vector<Pixel> out_path;
std::vector<decltype(tracer)::Node> out_nodes;
if (!astar::search_route(tracer, {start, {0, 0}}, std::back_inserter(out_nodes), astar_cache)) {
// path not found - just reconstruct the best path from astar cache.
// Note that astar_cache is NOT empty - at least the starting point should always be there
auto coordiante_func = [&astar_cache](size_t idx, size_t dim) { return float(astar_cache[idx].node.position[dim]); };
std::vector<size_t> keys;
keys.reserve(astar_cache.size());
for (const auto &pair : astar_cache) { keys.push_back(pair.first); }
KDTreeIndirect<2, float, decltype(coordiante_func)> kd_tree(coordiante_func, keys);
size_t closest_qnode = find_closest_point(kd_tree, end.cast<float>());
out_path.push_back(end);
while (closest_qnode != astar::Unassigned) {
out_path.push_back(astar_cache[closest_qnode].node.position);
closest_qnode = astar_cache[closest_qnode].parent;
}
} else {
for (const auto &node : out_nodes) { out_path.push_back(node.position); }
out_path.push_back(start);
}
#ifdef DEBUG_FILES
auto scaled_points = [](const Points &ps) {
Points r;
for (const Point &p : ps) { r.push_back(Point::new_scale(p.x(), p.y())); }
return r;
};
auto scaled_point = [](const Point &p) { return Point::new_scale(p.x(), p.y()); };
::Slic3r::SVG svg(debug_out_path(("path_jps" + std::to_string(print_z) + "_" + std::to_string(rand() % 1000)).c_str()).c_str(),
BoundingBox(scaled_point(search_box.min), scaled_point(search_box.max)));
for (const auto &p : inpassable) { svg.draw(scaled_point(p), "black", scale_(0.4)); }
for (const auto &qn : astar_cache) { svg.draw(scaled_point(qn.second.node.position), "blue", scale_(0.3)); }
svg.draw(Polyline(scaled_points(out_path)), "yellow", scale_(0.25));
svg.draw(scaled_point(end), "purple", scale_(0.4));
svg.draw(scaled_point(start), "green", scale_(0.4));
#endif
std::vector<Pixel> tmp_path;
tmp_path.reserve(out_path.size());
// Some path found, reverse and remove points that do not change direction
std::reverse(out_path.begin(), out_path.end());
{
tmp_path.push_back(out_path.front()); // first point
for (size_t i = 1; i < out_path.size() - 1; i++) {
if ((out_path[i] - out_path[i - 1]).cast<float>().normalized() != (out_path[i + 1] - out_path[i]).cast<float>().normalized()) { tmp_path.push_back(out_path[i]); }
}
tmp_path.push_back(out_path.back()); // last_point
out_path = tmp_path;
}
#ifdef DEBUG_FILES
svg.draw(Polyline(scaled_points(out_path)), "orange", scale_(0.20));
#endif
tmp_path.clear();
// remove redundant jump points - there are points that change direction but are not needed - this inefficiency arises from the
// usage of grid search The removal alg tries to find the longest Px Px+k path without obstacles. If Px Px+k+1 is blocked, it will
// insert the Px+k point to result and continue search from Px+k
{
tmp_path.push_back(out_path.front()); // first point
size_t index_of_last_stored_point = 0;
for (size_t i = 1; i < out_path.size(); i++) {
if (i - index_of_last_stored_point < 2) continue;
bool passable = true;
auto store_obstacle = [&](coord_t x, coord_t y) {
if (Pixel(x, y) != start && Pixel(x, y) != end && inpassable.find(Pixel(x, y)) != inpassable.end()) {
passable = false;
return false;
}
return true;
};
dda(tmp_path.back().x(), tmp_path.back().y(), out_path[i].x(), out_path[i].y(), store_obstacle);
if (!passable) {
tmp_path.push_back(out_path[i - 1]);
index_of_last_stored_point = i - 1;
}
}
tmp_path.push_back(out_path.back()); // last_point
out_path = tmp_path;
}
#ifdef DEBUG_FILES
svg.draw(Polyline(scaled_points(out_path)), "red", scale_(0.15));
svg.Close();
#endif
// before returing the path, transform it from pixels back to points.
// Also replace the first and last pixel by input points so that result path patches input params exactly.
for (Pixel &p : out_path) { p = unpixelize(p); }
out_path.front() = p0;
out_path.back() = p1;
return Polyline(out_path);
}
} // namespace Slic3r

View file

@ -0,0 +1,38 @@
#pragma once
#ifndef SRC_LIBSLIC3R_JUMPPOINTSEARCH_HPP_
#define SRC_LIBSLIC3R_JUMPPOINTSEARCH_HPP_
#include "BoundingBox.hpp"
#include "Polygon.hpp"
#include "libslic3r/Layer.hpp"
#include "libslic3r/Point.hpp"
#include "libslic3r/Polyline.hpp"
#include "libslic3r/libslic3r.h"
#include <unordered_map>
#include <unordered_set>
namespace Slic3r {
class JPSPathFinder
{
using Pixel = Point;
std::unordered_set<Pixel, PointHash> inpassable;
coordf_t print_z;
BoundingBox max_search_box;
Lines bed_shape;
const coord_t resolution = scaled(1.5);
Pixel pixelize(const Point &p) { return p / resolution; }
Point unpixelize(const Pixel &p) { return p * resolution; }
public:
JPSPathFinder() = default;
void init_bed_shape(const Points &bed_shape) { this->bed_shape = (to_lines(Polygon{bed_shape})); };
void clear();
void add_obstacles(const Lines &obstacles);
Polyline find_path(const Point &start, const Point &end);
};
} // namespace Slic3r
#endif /* SRC_LIBSLIC3R_JUMPPOINTSEARCH_HPP_ */

View file

@ -179,7 +179,6 @@ void Layer::make_perimeters()
&& config.opt_serialize("inner_wall_line_width") == other_config.opt_serialize("inner_wall_line_width")
&& config.opt_serialize("outer_wall_line_width") == other_config.opt_serialize("outer_wall_line_width")
&& config.detect_thin_wall == other_config.detect_thin_wall
//&& config.wall_infill_order == other_config.wall_infill_order
&& config.infill_wall_overlap == other_config.infill_wall_overlap
&& config.fuzzy_skin == other_config.fuzzy_skin
&& config.fuzzy_skin_thickness == other_config.fuzzy_skin_thickness

View file

@ -277,6 +277,7 @@ public:
// for tree supports
ExPolygons base_areas;
ExPolygons overhang_areas;
// Is there any valid extrusion assigned to this LayerRegion?
@ -300,7 +301,6 @@ protected:
size_t m_interface_id;
// for tree support
ExPolygons overhang_areas;
ExPolygons roof_areas;
ExPolygons roof_1st_layer; // the layer just below roof. When working with PolySupport, this layer should be printed with regular material
ExPolygons floor_areas;
@ -312,6 +312,7 @@ protected:
int type;
coordf_t dist_to_top; // mm dist to top
bool need_infill = false;
bool need_extra_wall = false;
AreaGroup(ExPolygon *a, int t, coordf_t d) : area(a), type(t), dist_to_top(d) {}
};
enum OverhangType { Detected = 0, Enforced };

View file

@ -223,6 +223,12 @@ indexed_triangle_set cgal_to_indexed_triangle_set(const _Mesh &cgalmesh)
return its;
}
template<class _Mesh> TriangleMesh cgal_to_triangle_mesh(const _Mesh &cgalmesh)
{
indexed_triangle_set its = cgal_to_indexed_triangle_set(cgalmesh);
return TriangleMesh(std::move(its));
}
std::unique_ptr<CGALMesh, CGALMeshDeleter>
triangle_mesh_to_cgal(const std::vector<stl_vertex> &V,
const std::vector<stl_triangle_vertex_indices> &F)

View file

@ -97,9 +97,14 @@ Model& Model::assign_copy(const Model &rhs)
);
}
if (rhs.calib_pa_pattern) {
this->calib_pa_pattern = std::make_unique<CalibPressureAdvancePattern>(CalibPressureAdvancePattern(*rhs.calib_pa_pattern));
}
// BBS: for design info
this->design_info = rhs.design_info;
this->model_info = rhs.model_info;
this->stl_design_id = rhs.stl_design_id;
this->profile_info = rhs.profile_info;
return *this;
@ -130,6 +135,7 @@ Model& Model::assign_copy(Model &&rhs)
//BBS: add auxiliary path logic
// BBS: backup, all in one temp dir
this->stl_design_id = rhs.stl_design_id;
this->backup_path = std::move(rhs.backup_path);
this->object_backup_id_map = std::move(rhs.object_backup_id_map);
this->next_object_backup_id = rhs.next_object_backup_id;
@ -927,6 +933,7 @@ void Model::load_from(Model& model)
object_backup_id_map = model.object_backup_id_map;
next_object_backup_id = model.next_object_backup_id;
design_info = model.design_info;
stl_design_id = model.stl_design_id;
model_info = model.model_info;
profile_info = model.profile_info;
model.design_info.reset();

View file

@ -245,6 +245,11 @@ private:
friend class ModelObject;
};
enum class CutMode : int {
cutPlanar,
cutTongueAndGroove
};
enum class CutConnectorType : int {
Plug
, Dowel
@ -267,6 +272,11 @@ enum class CutConnectorShape : int {
, Undef
//,D-shape
};
struct CutConnectorParas
{
float snap_space_proportion{0.3};
float snap_bulge_proportion{0.15};
};
struct CutConnectorAttributes
{
@ -904,8 +914,8 @@ public:
bool is_support_blocker() const { return m_type == ModelVolumeType::SUPPORT_BLOCKER; }
bool is_support_modifier() const { return m_type == ModelVolumeType::SUPPORT_BLOCKER || m_type == ModelVolumeType::SUPPORT_ENFORCER; }
t_model_material_id material_id() const { return m_material_id; }
void reset_extra_facets();
void set_material_id(t_model_material_id material_id);
void reset_extra_facets();
ModelMaterial* material() const;
void set_material(t_model_material_id material_id, const ModelMaterial &material);
// Extract the current extruder ID based on this ModelVolume's config and the parent ModelObject's config.
@ -1488,6 +1498,7 @@ public:
static GlobalSpeedMap printSpeedMap;
// DesignInfo of Model
std::string stl_design_id;
std::shared_ptr<ModelDesignInfo> design_info = nullptr;
std::shared_ptr<ModelInfo> model_info = nullptr;
std::shared_ptr<ModelProfileInfo> profile_info = nullptr;

View file

@ -1372,18 +1372,23 @@ static void remove_multiple_edges_in_vertices(MMU_Graph &graph, const std::vecto
static void cut_segmented_layers(const std::vector<ExPolygons> &input_expolygons,
std::vector<std::vector<ExPolygons>> &segmented_regions,
const float cut_width,
const float interlocking_depth,
const std::function<void()> &throw_on_cancel_callback)
{
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - cutting segmented layers in parallel - begin";
tbb::parallel_for(tbb::blocked_range<size_t>(0, segmented_regions.size()),[&segmented_regions, &input_expolygons, &cut_width, &throw_on_cancel_callback](const tbb::blocked_range<size_t>& range) {
tbb::parallel_for(tbb::blocked_range<size_t>(0, segmented_regions.size()),
[&segmented_regions, &input_expolygons, &cut_width, &interlocking_depth, &throw_on_cancel_callback](const tbb::blocked_range<size_t> &range) {
for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) {
throw_on_cancel_callback();
const size_t num_extruders_plus_one = segmented_regions[layer_idx].size();
std::vector<ExPolygons> segmented_regions_cuts(num_extruders_plus_one); // Indexed by extruder_id
for (size_t extruder_idx = 0; extruder_idx < num_extruders_plus_one; ++extruder_idx)
if (const ExPolygons &ex_polygons = segmented_regions[layer_idx][extruder_idx]; !ex_polygons.empty())
segmented_regions_cuts[extruder_idx] = diff_ex(ex_polygons, offset_ex(input_expolygons[layer_idx], cut_width));
segmented_regions[layer_idx] = std::move(segmented_regions_cuts);
const float region_cut_width = ((layer_idx % 2 == 0) && (interlocking_depth != 0.f)) ? interlocking_depth : cut_width;
const size_t num_extruders_plus_one = segmented_regions[layer_idx].size();
if (region_cut_width > 0.f) {
std::vector<ExPolygons> segmented_regions_cuts(num_extruders_plus_one); // Indexed by extruder_id
for (size_t extruder_idx = 0; extruder_idx < num_extruders_plus_one; ++extruder_idx)
if (const ExPolygons &ex_polygons = segmented_regions[layer_idx][extruder_idx]; !ex_polygons.empty())
segmented_regions_cuts[extruder_idx] = diff_ex(ex_polygons, offset_ex(input_expolygons[layer_idx], -region_cut_width));
segmented_regions[layer_idx] = std::move(segmented_regions_cuts);
}
}
}); // end of parallel_for
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - cutting segmented layers in parallel - end";
@ -1664,9 +1669,12 @@ static inline std::vector<std::vector<ExPolygons>> mmu_segmentation_top_and_bott
self = union_ex(self);
}
// Trim one region by the other if some of the regions overlap.
for (size_t color_idx = 1; color_idx < triangles_by_color_merged.size(); ++ color_idx)
triangles_by_color_merged[color_idx][layer_idx] = diff_ex(triangles_by_color_merged[color_idx][layer_idx],
triangles_by_color_merged[color_idx - 1][layer_idx]);
ExPolygons painted_regions;
for (size_t color_idx = 1; color_idx < triangles_by_color_merged.size(); ++color_idx) {
triangles_by_color_merged[color_idx][layer_idx] = diff_ex(triangles_by_color_merged[color_idx][layer_idx], painted_regions);
append(painted_regions, triangles_by_color_merged[color_idx][layer_idx]);
}
triangles_by_color_merged[0][layer_idx] = diff_ex(triangles_by_color_merged[0][layer_idx], painted_regions);
}
});
@ -2039,10 +2047,10 @@ std::vector<std::vector<ExPolygons>> multi_material_segmentation_by_painting(con
BOOST_LOG_TRIVIAL(debug) << "MMU segmentation - layers segmentation in parallel - end";
throw_on_cancel_callback();
//if (auto w = print_object.config().mmu_segmented_region_max_width; w > 0.f) {
// cut_segmented_layers(input_expolygons, segmented_regions, float(-scale_(w)), throw_on_cancel_callback);
// throw_on_cancel_callback();
//}
if (auto max_width = print_object.config().mmu_segmented_region_max_width, interlocking_depth = print_object.config().mmu_segmented_region_interlocking_depth; max_width > 0.f || interlocking_depth > 0.f) {
cut_segmented_layers(input_expolygons, segmented_regions, float(scale_(max_width)), float(scale_(interlocking_depth)), throw_on_cancel_callback);
throw_on_cancel_callback();
}
// The first index is extruder number (includes default extruder), and the second one is layer number
std::vector<std::vector<ExPolygons>> top_and_bottom_layers = mmu_segmentation_top_and_bottom_layers(print_object, input_expolygons, throw_on_cancel_callback);

View file

@ -3,7 +3,7 @@
#include <assert.h>
#include <type_traits>
constexpr auto InvalidQueueID = std::numeric_limits<size_t>::max();
template<typename T, typename IndexSetter, typename LessPredicate, const bool ResetIndexWhenRemoved = false>
class MutablePriorityQueue
{

View file

@ -11,6 +11,7 @@
#include "ClipperUtils.hpp"
#include "ExtrusionEntity.hpp"
#include "ExtrusionEntityCollection.hpp"
#include "PrintConfig.hpp"
#include "ShortestPath.hpp"
#include "VariableWidth.hpp"
#include "CurveAnalyzer.hpp"
@ -1495,12 +1496,18 @@ void PerimeterGenerator::process_classic()
// BBS: don't simplify too much which influence arc fitting when export gcode if arc_fitting is enabled
double surface_simplify_resolution = (print_config->enable_arc_fitting && this->config->fuzzy_skin == FuzzySkinType::None) ? 0.2 * m_scaled_resolution : m_scaled_resolution;
for (const Surface &surface : this->slices->surfaces) {
//BBS: reorder the surface to reduce the travel time
ExPolygons surface_exp;
for (const Surface &surface : this->slices->surfaces)
surface_exp.push_back(surface.expolygon);
std::vector<size_t> surface_order = chain_expolygons(surface_exp);
for (size_t order_idx = 0; order_idx < surface_order.size(); order_idx++) {
const Surface &surface = this->slices->surfaces[surface_order[order_idx]];
// detect how many perimeters must be generated for this island
int loop_number = this->config->wall_loops + surface.extra_perimeters - 1; // 0-indexed loops
if (this->layer_id == 0 && this->config->only_one_wall_first_layer)
loop_number = 0;
//BBS: set the topmost layer to be one wall
// Set the topmost layer to be one wall
if (loop_number > 0 && config->only_one_wall_top && this->upper_slices == nullptr)
loop_number = 0;
@ -1723,8 +1730,8 @@ void PerimeterGenerator::process_classic()
// we continue inwards after having finished the brim
// TODO: add test for perimeter order
bool is_outer_wall_first =
this->config->wall_infill_order == WallInfillOrder::OuterInnerInfill ||
this->config->wall_infill_order == WallInfillOrder::InfillOuterInner;
this->object_config->wall_sequence == WallSequence::OuterInner ||
this->object_config->wall_sequence == WallSequence::InnerOuterInner;
if (is_outer_wall_first ||
//BBS: always print outer wall first when there indeed has brim.
(this->layer_id == 0 &&
@ -1732,7 +1739,7 @@ void PerimeterGenerator::process_classic()
this->object_config->brim_width.value > 0))
entities.reverse();
// SoftFever: sandwich mode
else if (this->config->wall_infill_order == WallInfillOrder::InnerOuterInnerInfill)
else if (this->object_config->wall_sequence == WallSequence::InnerOuterInner)
if (entities.entities.size() > 1){
int last_outer=0;
int outer = 0;
@ -1862,6 +1869,39 @@ void PerimeterGenerator::process_classic()
} // for each island
}
//BBS:
void PerimeterGenerator::add_infill_contour_for_arachne( ExPolygons infill_contour,
int loops,
coord_t ext_perimeter_spacing,
coord_t perimeter_spacing,
coord_t min_perimeter_infill_spacing,
coord_t spacing,
bool is_inner_part)
{
if( offset_ex(infill_contour, -float(spacing / 2.)).empty() )
{
infill_contour.clear(); // Infill region is too small, so let's filter it out.
}
// create one more offset to be used as boundary for fill
// we offset by half the perimeter spacing (to get to the actual infill boundary)
// and then we offset back and forth by half the infill spacing to only consider the
// non-collapsing regions
coord_t insert = (loops < 0) ? 0: ext_perimeter_spacing;
if (is_inner_part || loops > 0)
insert = perimeter_spacing;
insert = coord_t(scale_(this->config->infill_wall_overlap.get_abs_value(unscale<double>(insert))));
Polygons inner_pp;
for (ExPolygon &ex : infill_contour)
ex.simplify_p(m_scaled_resolution, &inner_pp);
this->fill_surfaces->append(offset2_ex(union_ex(inner_pp), float(-min_perimeter_infill_spacing / 2.), float(insert + min_perimeter_infill_spacing / 2.)), stInternal);
append(*this->fill_no_overlap, offset2_ex(union_ex(inner_pp), float(-min_perimeter_infill_spacing / 2.), float(+min_perimeter_infill_spacing / 2.)));
}
// Thanks, Cura developers, for implementing an algorithm for generating perimeters with variable width (Arachne) that is based on the paper
// "A framework for adaptive width control of dense contour-parallel toolpaths in fused deposition modeling"
void PerimeterGenerator::process_arachne()
@ -1973,7 +2013,7 @@ void PerimeterGenerator::process_arachne()
}
loop_number = int(perimeters.size()) - 1;
#ifdef ARACHNE_DEBUG
#ifdef ARACHNE_DEBUG
{
static int iRun = 0;
export_perimeters_to_svg(debug_out_path("arachne-perimeters-%d-%d.svg", layer_id, iRun++), to_polygons(last), perimeters, union_ex(wallToolPaths.getInnerContour()));
@ -1996,14 +2036,12 @@ void PerimeterGenerator::process_arachne()
int direction = -1;
bool is_outer_wall_first =
this->config->wall_infill_order == WallInfillOrder::OuterInnerInfill ||
this->config->wall_infill_order == WallInfillOrder::InfillOuterInner ||
this->config->wall_infill_order == WallInfillOrder::InnerOuterInnerInfill;
this->object_config->wall_sequence == WallSequence::OuterInner ||
this->object_config->wall_sequence == WallSequence::InnerOuterInner;
if (layer_id == 0){ // disable inner outer inner algorithm after the first layer
is_outer_wall_first =
this->config->wall_infill_order == WallInfillOrder::OuterInnerInfill ||
this->config->wall_infill_order == WallInfillOrder::InfillOuterInner;
this->object_config->wall_sequence == WallSequence::OuterInner;
}
if (is_outer_wall_first) {
start_perimeter = 0;
@ -2242,8 +2280,9 @@ void PerimeterGenerator::process_arachne()
this->loops->append(extrusion_coll);
}
ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour());
ExPolygons infill_contour = union_ex(wallToolPaths.getInnerContour());
const coord_t spacing = (perimeters.size() == 1) ? ext_perimeter_spacing2 : perimeter_spacing;
if (offset_ex(infill_contour, -float(spacing / 2.)).empty())
infill_contour.clear(); // Infill region is too small, so let's filter it out.

View file

@ -69,6 +69,8 @@ public:
void process_classic();
void process_arachne();
void add_infill_contour_for_arachne( ExPolygons infill_contour, int loops, coord_t ext_perimeter_spacing, coord_t perimeter_spacing, coord_t min_perimeter_infill_spacing, coord_t spacing, bool is_inner_part );
double ext_mm3_per_mm() const { return m_ext_mm3_per_mm; }
double mm3_per_mm() const { return m_mm3_per_mm; }
double mm3_per_mm_overhang() const { return m_mm3_per_mm_overhang; }

View file

@ -83,6 +83,7 @@ using Transform2d = Eigen::Transform<double, 2, Eigen::Affine, Eigen::DontAli
using Transform3f = Eigen::Transform<float, 3, Eigen::Affine, Eigen::DontAlign>;
using Transform3d = Eigen::Transform<double, 3, Eigen::Affine, Eigen::DontAlign>;
// using ColorRGBA = std::array<float, 4>;
// I don't know why Eigen::Transform::Identity() return a const object...
template<int N, class T> Transform<N, T> identity() { return Transform<N, T>::Identity(); }
inline const auto &identity3f = identity<3, float>;

View file

@ -683,5 +683,4 @@ Polygon make_circle_num_segments(double radius, size_t num_segments)
}
return out;
}
}

View file

@ -518,6 +518,8 @@ void Preset::save(DynamicPrintConfig* parent_config)
else
from_str = std::string("Default");
boost::filesystem::create_directories(fs::path(this->file).parent_path());
//BBS: only save difference if it has parent
if (parent_config) {
DynamicPrintConfig temp_config;
@ -530,15 +532,36 @@ void Preset::save(DynamicPrintConfig* parent_config)
opt_dst->set(opt_src);
}
temp_config.save_to_json(this->file, this->name, from_str, this->version.to_string(), this->custom_defined);
}
else
} else if (!filament_id.empty() && inherits().empty()) {
DynamicPrintConfig temp_config = config;
temp_config.set_key_value(BBL_JSON_KEY_FILAMENT_ID, new ConfigOptionString(filament_id));
temp_config.save_to_json(this->file, this->name, from_str, this->version.to_string(), this->custom_defined);
} else {
this->config.save_to_json(this->file, this->name, from_str, this->version.to_string(), this->custom_defined);
}
fs::path idx_file(this->file);
idx_file.replace_extension(".info");
this->save_info(idx_file.string());
}
void Preset::reload(Preset const &parent)
{
DynamicPrintConfig config;
// BBS: change to json format
// ConfigSubstitutions config_substitutions = config.load_from_ini(preset.file, substitution_rule);
std::map<std::string, std::string> key_values;
std::string reason;
ForwardCompatibilitySubstitutionRule substitution_rule = ForwardCompatibilitySubstitutionRule::Disable;
try {
ConfigSubstitutions config_substitutions = config.load_from_json(file, substitution_rule, key_values, reason);
this->config = parent.config;
this->config.apply(std::move(config));
} catch (const std::exception &err) {
BOOST_LOG_TRIVIAL(error) << boost::format("Failed loading the user-config file: %1%. Reason: %2%") % file % err.what();
}
}
// Return a label of this preset, consisting of a name and a "(modified)" suffix, if this preset is dirty.
std::string Preset::label(bool no_alias) const
{
@ -665,7 +688,7 @@ std::string Preset::get_printer_type(PresetBundle *preset_bundle)
vendor_name = vendor_profile.first;
return vendor_model.model_id;
}
}
}
}
return "";
}
@ -686,6 +709,25 @@ std::string Preset::get_current_printer_type(PresetBundle *preset_bundle)
return "";
}
bool Preset::has_lidar(PresetBundle *preset_bundle)
{
bool has_lidar = false;
if (preset_bundle) {
auto config = &preset_bundle->printers.get_edited_preset().config;
std::string vendor_name;
for (auto vendor_profile : preset_bundle->vendors) {
for (auto vendor_model : vendor_profile.second.models)
if (vendor_model.name == config->opt_string("printer_model")) {
vendor_name = vendor_profile.first;
break;
}
}
if (!vendor_name.empty())
has_lidar = vendor_name.compare("BBL") == 0 ? true : false;
}
return has_lidar;
}
bool Preset::is_custom_defined()
{
if (custom_defined == "1")
@ -706,7 +748,7 @@ BedType Preset::get_default_bed_type(PresetBundle* preset_bundle)
}
std::string model_id = this->get_printer_type(preset_bundle);
if (model_id == "BL-P001" || model_id == "BL-P002") {
if (model_id == "BL-P001" || model_id == "BL-P002" || model_id == "C13") {
return BedType::btPC;
} else if (model_id == "C11") {
return BedType::btPEI;
@ -717,7 +759,7 @@ BedType Preset::get_default_bed_type(PresetBundle* preset_bundle)
bool Preset::has_cali_lines(PresetBundle* preset_bundle)
{
std::string model_id = this->get_printer_type(preset_bundle);
if (model_id == "BL-P001" || model_id == "BL-P002") {
if (model_id == "BL-P001" || model_id == "BL-P002" || model_id == "C13") {
return true;
}
return false;
@ -727,7 +769,7 @@ static std::vector<std::string> s_Preset_print_options {
"layer_height", "initial_layer_print_height", "wall_loops", "slice_closing_radius", "spiral_mode", "slicing_mode",
"top_shell_layers", "top_shell_thickness", "bottom_shell_layers", "bottom_shell_thickness",
"extra_perimeters_on_overhangs", "ensure_vertical_shell_thickness", "reduce_crossing_wall", "detect_thin_wall", "detect_overhang_wall", "overhang_reverse", "overhang_reverse_threshold","overhang_reverse_internal_only",
"seam_position", "staggered_inner_seams", "wall_infill_order", "sparse_infill_density", "sparse_infill_pattern", "top_surface_pattern", "bottom_surface_pattern",
"seam_position", "staggered_inner_seams", "wall_sequence", "is_infill_first", "sparse_infill_density", "sparse_infill_pattern", "top_surface_pattern", "bottom_surface_pattern",
"infill_direction",
"minimum_sparse_infill_area", "reduce_infill_retraction","internal_solid_infill_pattern",
"ironing_type", "ironing_pattern", "ironing_flow", "ironing_speed", "ironing_spacing", "ironing_angle",
@ -746,7 +788,7 @@ static std::vector<std::string> s_Preset_print_options {
"support_interface_pattern", "support_interface_spacing", "support_interface_loop_pattern",
"support_top_z_distance", "support_on_build_plate_only","support_critical_regions_only", "bridge_no_support", "thick_bridges", "max_bridge_length", "print_sequence", "support_remove_small_overhang",
"filename_format", "wall_filament", "support_bottom_z_distance",
"sparse_infill_filament", "solid_infill_filament", "support_filament", "support_interface_filament",
"sparse_infill_filament", "solid_infill_filament", "support_filament", "support_interface_filament","support_interface_not_for_body",
"ooze_prevention", "standby_temperature_delta", "interface_shells", "line_width", "initial_layer_line_width",
"inner_wall_line_width", "outer_wall_line_width", "sparse_infill_line_width", "internal_solid_infill_line_width",
"top_surface_line_width", "support_line_width", "infill_wall_overlap", "bridge_flow", "internal_bridge_flow",
@ -774,7 +816,7 @@ static std::vector<std::string> s_Preset_print_options {
"make_overhang_printable", "make_overhang_printable_angle", "make_overhang_printable_hole_size" ,"notes",
"wipe_tower_cone_angle", "wipe_tower_extra_spacing", "wipe_tower_extruder", "wiping_volumes_extruders","wipe_tower_bridging", "single_extruder_multi_material_priming",
"wipe_tower_rotation_angle", "tree_support_branch_distance_organic", "tree_support_branch_diameter_organic", "tree_support_branch_angle_organic",
"hole_to_polyhole", "hole_to_polyhole_threshold", "hole_to_polyhole_twisted"
"hole_to_polyhole", "hole_to_polyhole_threshold", "hole_to_polyhole_twisted", "mmu_segmented_region_max_width", "mmu_segmented_region_interlocking_depth",
};
static std::vector<std::string> s_Preset_filament_options {
@ -819,7 +861,7 @@ static std::vector<std::string> s_Preset_printer_options {
"printer_technology",
"printable_area", "bed_exclude_area","bed_custom_texture", "bed_custom_model", "gcode_flavor",
"fan_kickstart", "fan_speedup_time", "fan_speedup_overhangs",
"single_extruder_multi_material", "manual_filament_change", "machine_start_gcode", "machine_end_gcode", "before_layer_change_gcode", "layer_change_gcode", "time_lapse_gcode", "change_filament_gcode", "change_extrusion_role_gcode",
"single_extruder_multi_material", "manual_filament_change", "machine_start_gcode", "machine_end_gcode", "before_layer_change_gcode", "printing_by_object_gcode", "layer_change_gcode", "time_lapse_gcode", "change_filament_gcode", "change_extrusion_role_gcode",
"printer_model", "printer_variant", "printable_height", "extruder_clearance_radius", "extruder_clearance_height_to_lid", "extruder_clearance_height_to_rod",
"default_print_profile", "inherits",
"silent_mode",
@ -1018,8 +1060,13 @@ void PresetCollection::load_presets(
// see https://github.com/prusa3d/PrusaSlicer/issues/732
boost::filesystem::path dir = boost::filesystem::absolute(boost::filesystem::path(dir_path) / subdir).make_preferred();
// Load custom roots first
if (fs::exists(dir / "base")) {
load_presets(dir.string(), "base", substitutions, substitution_rule);
}
//BBS: add config related logs
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" enter, load presets from %1%, current type %2%")%dir %Preset::get_type_string(m_type);
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" enter, load presets from %1%, current type %2%")%dir %Preset::get_type_string(m_type);
//BBS do not parse folder if not exists
m_dir_path = dir.string();
if (!fs::exists(dir)) {
@ -1084,8 +1131,12 @@ void PresetCollection::load_presets(
}
preset.version = *version;
if (key_values.find(BBL_JSON_KEY_FILAMENT_ID) != key_values.end())
preset.filament_id = key_values[BBL_JSON_KEY_FILAMENT_ID];
if (key_values.find(BBL_JSON_KEY_IS_CUSTOM) != key_values.end())
preset.custom_defined = key_values[BBL_JSON_KEY_IS_CUSTOM];
if (key_values.find("instantiation") != key_values.end())
preset.is_visible = key_values["instantiation"] != "false";
//BBS: use inherit config as the base
Preset* inherit_preset = nullptr;
@ -1105,12 +1156,12 @@ void PresetCollection::load_presets(
preset.filament_id = inherit_preset->filament_id;
}
else {
if (!preset.is_custom_defined()) {
// We support custom root preset now
auto inherits_config2 = dynamic_cast<ConfigOptionString *>(inherits_config);
if ((inherits_config2 && !inherits_config2->value.empty()) && !preset.is_custom_defined()) {
BOOST_LOG_TRIVIAL(error) << boost::format("can not find parent for config %1%!")%preset.file;
continue;
}
//should not happen
//BOOST_LOG_TRIVIAL(error) << boost::format("can not find parent for config %1%!")%preset.file;
// Find a default preset for the config. The PrintPresetCollection provides different default preset based on the "printer_technology" field.
preset.config = default_preset.config;
}
@ -1147,6 +1198,7 @@ void PresetCollection::load_presets(
fs::remove(file_path);
}
presets_loaded.emplace_back(preset);
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << __LINE__ << " load config successful and preset name is:" << preset.name;
} catch (const std::runtime_error &err) {
errors_cummulative += err.what();
errors_cummulative += "\n";
@ -1219,16 +1271,26 @@ int PresetCollection::get_differed_values_to_update(Preset& preset, std::map<std
if (opt_src)
key_values[option] = opt_src->serialize();
}
//add other values
key_values[BBL_JSON_KEY_VERSION] = preset.version.to_string();
key_values[BBL_JSON_KEY_BASE_ID] = preset.base_id;
key_values[BBL_JSON_KEY_UPDATE_TIME] = std::to_string(preset.updated_time);
key_values[BBL_JSON_KEY_TYPE] = Preset::get_iot_type_string(preset.type);
}
else {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(" Error: can not find the parent! Should not happen, name %1%") %preset.name;
return -1;
for (auto iter = preset.config.cbegin(); iter != preset.config.cend(); ++iter)
{
key_values[iter->first] = iter->second->serialize();
}
}
//add other values
key_values[BBL_JSON_KEY_VERSION] = preset.version.to_string();
if (!preset.base_id.empty()) {
key_values[BBL_JSON_KEY_BASE_ID] = preset.base_id;
} else {
key_values.erase(BBL_JSON_KEY_BASE_ID);
if (get_preset_base(preset) == &preset && !preset.filament_id.empty()) {
key_values[BBL_JSON_KEY_FILAMENT_ID] = preset.filament_id;
}
}
key_values[BBL_JSON_KEY_UPDATE_TIME] = std::to_string(preset.updated_time);
key_values[BBL_JSON_KEY_TYPE] = Preset::get_iot_type_string(preset.type);
return 0;
}
@ -1363,7 +1425,7 @@ bool PresetCollection::reset_project_embedded_presets()
return re_select;
}
void PresetCollection::set_sync_info_and_save(std::string name, std::string setting_id, std::string syncinfo)
void PresetCollection::set_sync_info_and_save(std::string name, std::string setting_id, std::string syncinfo, long long update_time)
{
lock();
for (auto it = m_presets.begin(); it != m_presets.end(); it++) {
@ -1373,16 +1435,34 @@ void PresetCollection::set_sync_info_and_save(std::string name, std::string sett
preset->sync_info.clear();
else
preset->sync_info = syncinfo;
if (get_preset_base(*preset) == preset) {
for (auto & preset2 : m_presets)
if (preset2.inherits() == preset->name) {
preset2.base_id = setting_id;
preset2.save_info();
}
}
preset->setting_id = setting_id;
preset->save_info();
if (update_time > 0)
preset->updated_time = update_time;
preset->sync_info == "update" ? preset->save(nullptr) : preset->save_info();
break;
}
}
unlock();
}
bool PresetCollection::need_sync(std::string name, std::string setting_id, long long update_time)
{
lock();
auto preset = find_preset(name, false, true);
bool need = preset == nullptr || preset->setting_id != setting_id || preset->updated_time < update_time;
unlock();
return need;
}
//BBS: get user presets
int PresetCollection::get_user_presets(std::vector<Preset>& result_presets)
int PresetCollection::get_user_presets(PresetBundle *preset_bundle, std::vector<Preset> &result_presets)
{
int count = 0;
result_presets.clear();
@ -1390,6 +1470,10 @@ int PresetCollection::get_user_presets(std::vector<Preset>& result_presets)
lock();
for (Preset &preset : m_presets) {
if (!preset.is_user()) continue;
if (get_preset_base(preset) != &preset && preset.base_id.empty()) continue;
if (!preset.setting_id.empty() && preset.sync_info.empty()) continue;
//if (!preset.is_bbl_vendor_preset(preset_bundle)) continue;
if (preset.sync_info == "hold") continue;
result_presets.push_back(preset);
count++;
@ -1426,7 +1510,9 @@ void PresetCollection::save_user_presets(const std::string& dir_path, const std:
for (auto it = m_presets.begin(); it != m_presets.end(); it++) {
Preset* preset = &m_presets[it - m_presets.begin()];
if (!preset->is_user()) continue;
preset->file = path_from_name(preset->name);
if (preset->sync_info != "save") continue;
preset->sync_info.clear();
preset->file = path_for_preset(*preset);
if (preset->is_custom_defined()) {
preset->save(nullptr);
@ -1434,11 +1520,13 @@ void PresetCollection::save_user_presets(const std::string& dir_path, const std:
//BBS: only save difference for user preset
std::string inherits = Preset::inherits(preset->config);
if (inherits.empty()) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(" can not find inherits for %1% , should not happen")%preset->name;
// BBS add sync info
preset->sync_info = "delete";
need_to_delete_list.push_back(preset->setting_id);
delete_name_list.push_back(preset->name);
// We support custom root preset now
//BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(" can not find inherits for %1% , should not happen")%preset->name;
//// BBS add sync info
//preset->sync_info = "delete";
//need_to_delete_list.push_back(preset->setting_id);
//delete_name_list.push_back(preset->name);
preset->save(nullptr);
continue;
}
Preset* parent_preset = this->find_preset(inherits, false, true);
@ -1471,7 +1559,7 @@ bool PresetCollection::load_user_preset(std::string name, std::map<std::string,
//std::deque<Preset> presets_loaded;
int count = 0;
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" enter, name %1% , total value counts %2%")%name %preset_values.size();
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" enter, name %1% , total value counts %2%")%name %preset_values.size();
//if the version is not matching, skip it
if (preset_values.find(BBL_JSON_KEY_VERSION) == preset_values.end()) {
@ -1497,13 +1585,6 @@ bool PresetCollection::load_user_preset(std::string name, std::map<std::string,
}
std::string cloud_setting_id = preset_values[BBL_JSON_KEY_SETTING_ID];
//base_id
if (preset_values.find(BBL_JSON_KEY_BASE_ID) == preset_values.end()) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format("can not find base_id, not loading for user preset %1%")%name;
return false;
}
std::string cloud_base_id = preset_values[BBL_JSON_KEY_BASE_ID];
//update_time
long long cloud_update_time = 0;
if (preset_values.find(BBL_JSON_KEY_UPDATE_TIME) != preset_values.end()) {
@ -1517,12 +1598,6 @@ bool PresetCollection::load_user_preset(std::string name, std::map<std::string,
}
std::string cloud_user_id = preset_values[BBL_JSON_KEY_USER_ID];
//filament_id
std::string cloud_filament_id;
if ((m_type == Preset::TYPE_FILAMENT) && preset_values.find(BBL_JSON_KEY_FILAMENT_ID) != preset_values.end()) {
cloud_filament_id = preset_values[BBL_JSON_KEY_FILAMENT_ID];
}
lock();
//std::string name = preset->name;
auto iter = this->find_preset_internal(name);
@ -1535,6 +1610,11 @@ bool PresetCollection::load_user_preset(std::string name, std::map<std::string,
iter->sync_info = "update";
else
iter->sync_info.clear();
// Fixup possible data lost
iter->setting_id = cloud_setting_id;
fs::path idx_file(iter->file);
idx_file.replace_extension(".info");
iter->save_info(idx_file.string());
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format("preset %1%'s update_time is eqaul or newer, cloud update_time %2%, local update_time %3%")%name %cloud_update_time %iter->updated_time;
unlock();
return false;
@ -1542,11 +1622,24 @@ bool PresetCollection::load_user_preset(std::string name, std::map<std::string,
else {
//update the one from cloud which is newer
need_update = true;
iter->sync_info.clear();
}
}
DynamicPrintConfig new_config, cloud_config;
// base_id
if (preset_values.find(BBL_JSON_KEY_BASE_ID) == preset_values.end()) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format("can not find base_id, not loading for user preset %1%") % name;
unlock();
return false;
}
std::string cloud_base_id = preset_values[BBL_JSON_KEY_BASE_ID];
//filament_id
std::string cloud_filament_id;
if ((m_type == Preset::TYPE_FILAMENT) && preset_values.find(BBL_JSON_KEY_FILAMENT_ID) != preset_values.end()) {
cloud_filament_id = preset_values[BBL_JSON_KEY_FILAMENT_ID];
}
DynamicPrintConfig new_config, cloud_config;
try {
ConfigSubstitutions config_substitutions = cloud_config.load_string_map(preset_values, rule);
if (! config_substitutions.empty())
@ -1570,12 +1663,16 @@ bool PresetCollection::load_user_preset(std::string name, std::map<std::string,
new_config = inherit_preset->config;
}
else {
// We support custom root preset now
auto inherits_config2 = dynamic_cast<ConfigOptionString *>(inherits_config);
if (inherits_config2 && !inherits_config2->value.empty()) {
//we should skip this preset here
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", can not find inherit preset for user preset %1%, just skip")%name;
unlock();
return false;
}
// Find a default preset for the config. The PrintPresetCollection provides different default preset based on the "printer_technology" field.
//new_config = default_preset.config;
//we should skip this preset here
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", can not find inherit preset for user preset %1%, just skip")%name;
unlock();
return false;
new_config = default_preset.config;
}
new_config.apply(std::move(cloud_config));
Preset::normalize(new_config);
@ -1591,7 +1688,8 @@ bool PresetCollection::load_user_preset(std::string name, std::map<std::string,
}
iter->config = new_config;
iter->updated_time = cloud_update_time;
iter->version = cloud_version.value();
iter->sync_info = "save";
iter->version = cloud_version.value();
iter->user_id = cloud_user_id;
iter->setting_id = cloud_setting_id;
iter->base_id = cloud_base_id;
@ -1607,7 +1705,8 @@ bool PresetCollection::load_user_preset(std::string name, std::map<std::string,
preset.loaded = true;
preset.config = new_config;
preset.updated_time = cloud_update_time;
preset.version = cloud_version.value();
preset.sync_info = "save";
preset.version = cloud_version.value();
preset.user_id = cloud_user_id;
preset.setting_id = cloud_setting_id;
preset.base_id = cloud_base_id;
@ -1651,28 +1750,33 @@ void PresetCollection::update_after_user_presets_loaded()
return;
}
//BBS: validate_printers
bool PresetCollection::validate_printers(const std::string &name, DynamicPrintConfig& config, std::string &inherit)
//BBS: validate_preset
bool PresetCollection::validate_preset(const std::string &preset_name, std::string &inherit_name)
{
std::string& original_name = config.opt_string("printer_settings_id", true);
std::deque<Preset>::iterator it = this->find_preset_internal(original_name);
bool found = it != m_presets.end() && it->name == original_name && (it->is_system || it->is_default);
std::deque<Preset>::iterator it = this->find_preset_internal(preset_name);
bool found = (it != m_presets.end()) && (it->name == preset_name) && (it->is_system || it->is_default);
if (!found) {
it = this->find_preset_renamed(original_name);
it = this->find_preset_renamed(preset_name);
found = it != m_presets.end() && (it->is_system || it->is_default);
}
if (!found) {
if (!inherit.empty()) {
it = this->find_preset_internal(inherit);
found = it != m_presets.end() && it->name == inherit && (it->is_system || it->is_default);
if (!inherit_name.empty()) {
it = this->find_preset_internal(inherit_name);
found = it != m_presets.end() && it->name == inherit_name && (it->is_system || it->is_default);
if (found)
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": preset_name %1%, inherit_name %2%, found inherit in list")%preset_name %inherit_name;
else
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": preset_name %1%, inherit_name %2%, can not found preset and inherit in list")%preset_name %inherit_name;
}
else {
//inherit is null , should not happen , just consider it as valid
found = false;
BOOST_LOG_TRIVIAL(warning) << boost::format(": name %1%, printer_settings %2%, no inherit, set to not found")%name %original_name;
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": preset_name %1%, no inherit, set to not found")%preset_name;
}
}
BOOST_LOG_TRIVIAL(warning) << boost::format(": name %1%, printer_settings %2%, inherit %3%, found result %4%")%name %original_name % inherit % found;
else {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": preset_name %1%, found in list")%preset_name;
}
return found;
}
@ -1945,7 +2049,7 @@ std::pair<Preset*, bool> PresetCollection::load_external_preset(
}
else {
//external config
preset.file = path_from_name(preset.name);
preset.file = path_for_preset(preset);
//BBS: save full config here for external
//we can not reach here
preset.save(nullptr);
@ -1985,6 +2089,113 @@ Preset& PresetCollection::load_preset(const std::string &path, const std::string
return preset;
}
bool PresetCollection::clone_presets(std::vector<Preset const *> const &presets, std::vector<std::string> &failures, std::function<void(Preset &, Preset::Type &)> modifier, bool force_rewritten)
{
std::vector<Preset> new_presets;
for (auto curr_preset : presets) {
new_presets.push_back(*curr_preset);
auto &preset = new_presets.back();
preset.vendor = nullptr;
preset.renamed_from.clear();
preset.setting_id.clear();
preset.inherits().clear();
preset.is_default = false;
preset.is_system = false;
preset.is_external = false;
preset.is_visible = true;
preset.is_project_embedded = false;
modifier(preset, m_type);
if (find_preset(preset.name) && !force_rewritten) {
failures.push_back(preset.name);
}
preset.file = this->path_for_preset(preset);
if (m_type == Preset::TYPE_PRINT)
preset.config.option<ConfigOptionString>("print_settings_id", true)->value = preset.name;
else if (m_type == Preset::TYPE_FILAMENT)
preset.config.option<ConfigOptionStrings>("filament_settings_id", true)->values[0] = preset.name;
else if (m_type == Preset::TYPE_PRINTER)
preset.config.option<ConfigOptionString>("printer_settings_id", true)->value = preset.name;
}
if (!failures.empty() && !force_rewritten)
return false;
lock();
auto old_name = this->get_edited_preset().name;
for (auto preset : new_presets) {
preset.alias.clear();
auto it = this->find_preset_internal(preset.name);
assert((it == m_presets.end() || it->name != preset.name) || force_rewritten);
if (it == m_presets.end() || it->name != preset.name) {
Preset &new_preset = *m_presets.insert(it, preset);
new_preset.save(nullptr);
} else if (force_rewritten) {
*it = preset;
(*it).save(nullptr);
}
}
this->select_preset_by_name(old_name, true);
unlock();
return true;
}
bool PresetCollection::clone_presets_for_printer(std::vector<Preset const *> const &presets, std::vector<std::string> &failures, std::string const &printer, bool force_rewritten)
{
return clone_presets(presets, failures, [printer](Preset &preset, Preset::Type &type) {
std::string prefix = preset.name.substr(0, preset.name.find(" @"));
std::replace(prefix.begin(), prefix.end(), '/', '-');
preset.name = prefix + " @" + printer;
//preset.alias = "";
auto *compatible_printers = dynamic_cast<ConfigOptionStrings *>(preset.config.option("compatible_printers"));
compatible_printers->values = std::vector<std::string>{ printer };
}, force_rewritten);
}
bool PresetCollection::create_presets_from_template_for_printer(std::vector<Preset const *> const & templates,
std::vector<std::string> & failures,
std::string const & printer,
std::function<std::string(std::string)> create_filament_id,
bool force_rewritten)
{
return clone_presets(templates, failures, [printer, create_filament_id](Preset &preset, Preset::Type &type) {
std::string prefix = preset.name.substr(0, preset.name.find(" @"));
std::replace(prefix.begin(), prefix.end(), '/', '-');
preset.name = prefix + " @" + printer;
auto *compatible_printers = dynamic_cast<ConfigOptionStrings *>(preset.config.option("compatible_printers"));
compatible_printers->values = std::vector<std::string>{printer};
preset.is_visible = true;
if (type == Preset::TYPE_FILAMENT)
preset.filament_id = create_filament_id(prefix);
}, force_rewritten);
}
bool PresetCollection::clone_presets_for_filament(Preset const *const & preset,
std::vector<std::string> &failures,
std::string const & filament_name,
std::string const & filament_id,
const DynamicConfig & dynamic_config,
const std::string & compatible_printers,
bool force_rewritten)
{
std::vector<Preset const *> const presets = {preset};
return clone_presets(presets, failures, [&filament_name, &filament_id, &dynamic_config, &compatible_printers](Preset &preset, Preset::Type &type) {
preset.name = filament_name + " @" + compatible_printers;
if (type == Preset::TYPE_FILAMENT) {
preset.config.apply_only(dynamic_config, {"filament_vendor", "compatible_printers", "filament_type"},true);
preset.filament_id = filament_id;
}
},
force_rewritten);
}
std::map<std::string, std::vector<Preset const *>> PresetCollection::get_filament_presets() const
{
std::map<std::string, std::vector<Preset const *>> filament_presets;
for (auto &preset : m_presets) {
if (get_preset_base(preset) == &preset) { filament_presets[preset.filament_id].push_back(&preset); }
}
return filament_presets;
}
//BBS: add project embedded preset logic
void PresetCollection::save_current_preset(const std::string &new_name, bool detach, bool save_to_project, Preset* _curr_preset)
{
@ -2020,15 +2231,27 @@ void PresetCollection::save_current_preset(const std::string &new_name, bool det
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": save preset %1% , with detach")%new_name;
}
//BBS: add lock logic for sync preset in background
if (m_type == Preset::TYPE_PRINT)
preset.config.option<ConfigOptionString>("print_settings_id", true)->value = new_name;
else if (m_type == Preset::TYPE_FILAMENT)
preset.config.option<ConfigOptionStrings>("filament_settings_id", true)->values[0] = new_name;
else if (m_type == Preset::TYPE_PRINTER)
preset.config.option<ConfigOptionString>("printer_settings_id", true)->value = new_name;
final_inherits = preset.inherits();
unlock();
// TODO: apply change from custom root to devided presets.
if (preset.inherits().empty()) {
for (auto &preset2 : m_presets)
if (preset2.inherits() == preset.name)
preset2.reload(preset);
}
} else {
// Creating a new preset.
Preset &preset = *m_presets.insert(it, curr_preset);
std::string &inherits = preset.inherits();
std::string old_name = preset.name;
preset.name = new_name;
preset.file = this->path_from_name(new_name);
preset.vendor = nullptr;
preset.alias.clear();
preset.renamed_from.clear();
@ -2037,19 +2260,16 @@ void PresetCollection::save_current_preset(const std::string &new_name, bool det
// Clear the link to the parent profile.
inherits.clear();
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": save preset %1% , with detach")%new_name;
} else if (preset.is_system) {
// Inheriting from a system preset.
inherits = /* preset.vendor->name + "/" + */ old_name;
} else if (inherits.empty()) {
// Inheriting from a user preset. Link the new preset to the old preset.
// inherits = old_name;
} else {
// Inherited from a user preset. Just maintain the "inherited" flag,
// meaning it will inherit from either the system preset, or the inherited user preset.
auto base = get_preset_base(curr_preset);
inherits = base ? base->name : "";
}
preset.is_default = false;
preset.is_system = false;
preset.is_external = false;
preset.file = this->path_for_preset(preset);
// The newly saved preset will be activated -> make it visible.
preset.is_visible = true;
// Just system presets have aliases
@ -2061,11 +2281,11 @@ void PresetCollection::save_current_preset(const std::string &new_name, bool det
else
preset.is_project_embedded = false;
if (m_type == Preset::TYPE_PRINT)
preset.config.option<ConfigOptionString >("print_settings_id", true)->value = preset.name;
preset.config.option<ConfigOptionString>("print_settings_id", true)->value = new_name;
else if (m_type == Preset::TYPE_FILAMENT)
preset.config.option<ConfigOptionStrings>("filament_settings_id", true)->values[0] = preset.name;
preset.config.option<ConfigOptionStrings>("filament_settings_id", true)->values[0] = new_name;
else if (m_type == Preset::TYPE_PRINTER)
preset.config.option<ConfigOptionString>("printer_settings_id", true)->value = preset.name;
preset.config.option<ConfigOptionString>("printer_settings_id", true)->value = new_name;
//BBS: add lock logic for sync preset in background
final_inherits = inherits;
unlock();
@ -2081,7 +2301,6 @@ void PresetCollection::save_current_preset(const std::string &new_name, bool det
this->get_selected_preset().base_id = parent_preset->setting_id;
}
}
this->get_selected_preset().updated_time = (long long)Slic3r::Utils::get_current_time_utc();
if (parent_preset)
this->get_selected_preset().save(&(parent_preset->config));
else
@ -2093,6 +2312,13 @@ bool PresetCollection::delete_current_preset()
Preset &selected = this->get_selected_preset();
if (selected.is_default)
return false;
if (get_preset_base(selected) == &selected) {
for (auto &preset2 : m_presets)
if (preset2.inherits() == selected.name)
return false;
}
//BBS: add project embedded preset logic and refine is_external
//if (! selected.is_external && ! selected.is_system) {
if (! selected.is_system) {
@ -2142,7 +2368,7 @@ const Preset* PresetCollection::get_selected_preset_parent() const
return nullptr;
const Preset &selected_preset = this->get_selected_preset();
if (selected_preset.is_system || selected_preset.is_default)
if (get_preset_base(selected_preset) == &selected_preset)
return &selected_preset;
const Preset &edited_preset = this->get_edited_preset();
@ -2190,6 +2416,17 @@ const Preset* PresetCollection::get_preset_parent(const Preset& child) const
preset;
}
const Preset *PresetCollection::get_preset_base(const Preset &child) const
{
if (child.is_system || child.is_default)
return &child;
// Handle user preset
if (child.inherits().empty())
return &child; // this is user root
auto inherits = find_preset(child.inherits());
return inherits ? get_preset_base(*inherits) : nullptr;
}
// Return vendor of the first parent profile, for which the vendor is defined, or null if such profile does not exist.
PresetWithVendorProfile PresetCollection::get_preset_with_vendor_profile(const Preset &preset) const
{
@ -2614,12 +2851,20 @@ std::vector<std::string> PresetCollection::system_preset_names() const
}
// Generate a file path from a profile name. Add the ".ini" suffix if it is missing.
std::string PresetCollection::path_from_name(const std::string &new_name) const
std::string PresetCollection::path_from_name(const std::string &new_name, bool detach) const
{
//BBS: change to json format
//std::string file_name = boost::iends_with(new_name, ".ini") ? new_name : (new_name + ".ini");
std::string file_name = boost::iends_with(new_name, ".json") ? new_name : (new_name + ".json");
return (boost::filesystem::path(m_dir_path) / file_name).make_preferred().string();
if (detach)
return (boost::filesystem::path(m_dir_path) / "base" / file_name).make_preferred().string();
else
return (boost::filesystem::path(m_dir_path) / file_name).make_preferred().string();
}
std::string PresetCollection::path_for_preset(const Preset &preset) const
{
return path_from_name(preset.name, get_preset_base(preset) == &preset);
}
const Preset& PrinterPresetCollection::default_preset_for(const DynamicPrintConfig &config) const
@ -2643,6 +2888,21 @@ const Preset* PrinterPresetCollection::find_system_preset_by_model_and_variant(c
return it != cend() ? &*it : nullptr;
}
const Preset *PrinterPresetCollection::find_custom_preset_by_model_and_variant(const std::string &model_id, const std::string &variant) const
{
if (model_id.empty()) { return nullptr; }
const auto it = std::find_if(cbegin(), cend(), [&](const Preset &preset) {
if (preset.config.opt_string("printer_model") != model_id)
return false;
if (variant.empty())
return true;
return preset.config.opt_string("printer_variant") == variant;
});
return it != cend() ? &*it : nullptr;
}
bool PrinterPresetCollection::only_default_printers() const
{
for (const auto& printer : get_presets()) {

View file

@ -20,6 +20,10 @@
#define PRESET_PRINTER_NAME "machine"
#define PRESET_SLA_PRINT_NAME "sla_print"
#define PRESET_SLA_MATERIALS_NAME "sla_materials"
#define PRESET_PROFILES_DIR "profiles"
#define PRESET_PROFILES_TEMOLATE_DIR "profiles_template"
#define PRESET_TEMPLATE_DIR "Template"
#define PRESET_CUSTOM_VENDOR "Custom"
//BBS: iot preset type strings
#define PRESET_IOT_PRINTER_TYPE "printer"
@ -57,6 +61,8 @@
#define BBL_JSON_KEY_DEFAULT_MATERIALS "default_materials"
#define BBL_JSON_KEY_MODEL_ID "model_id"
//BBL: json path
namespace Slic3r {
@ -171,6 +177,8 @@ public:
// This type is here to support PresetConfigSubstitutions for physical printers, however it does not belong to the Preset class,
// PhysicalPrinter class is used instead.
TYPE_PHYSICAL_PRINTER,
// BBS: plate config
TYPE_PLATE,
// BBS: model config
TYPE_MODEL,
};
@ -245,6 +253,7 @@ public:
//BBS: add logic for only difference save
//if parent_config is null, save all keys, otherwise, only save difference
void save(DynamicPrintConfig* parent_config);
void reload(Preset const & parent);
// Return a label of this preset, consisting of a name and a "(modified)" suffix, if this preset is dirty.
std::string label(bool no_alias) const;
@ -300,6 +309,8 @@ public:
std::string get_filament_type(std::string &display_filament_type);
std::string get_printer_type(PresetBundle *preset_bundle); // get edited preset type
std::string get_current_printer_type(PresetBundle *preset_bundle); // get current preset type
bool has_lidar(PresetBundle *preset_bundle);
bool is_custom_defined();
BedType get_default_bed_type(PresetBundle *preset_bundle);
@ -386,8 +397,8 @@ public:
typedef std::function<void(Preset* preset, std::string sync_info)> SyncFunc;
//BBS get m_presets begin
Iterator lbegin() { return m_presets.begin(); }
//BBS: validate_printers
bool validate_printers(const std::string &name, DynamicPrintConfig& config, std::string &inherit);
//BBS: validate_preset
bool validate_preset(const std::string &name, std::string &inherit);
Iterator begin() { return m_presets.begin() + m_num_default_presets; }
ConstIterator begin() const { return m_presets.cbegin() + m_num_default_presets; }
@ -425,8 +436,9 @@ public:
bool load_user_preset(std::string name, std::map<std::string, std::string> preset_values, PresetsConfigSubstitutions& substitutions, ForwardCompatibilitySubstitutionRule rule);
void update_after_user_presets_loaded();
//BBS: get user presets
int get_user_presets(std::vector<Preset>& result_presets);
void set_sync_info_and_save(std::string name, std::string setting_id, std::string syncinfo);
int get_user_presets(PresetBundle *preset_bundle, std::vector<Preset> &result_presets);
void set_sync_info_and_save(std::string name, std::string setting_id, std::string syncinfo, long long update_time);
bool need_sync(std::string name, std::string setting_id, long long update_time);
//BBS: add function to generate differed preset for save
//the pointer should be freed by the caller
@ -444,6 +456,20 @@ public:
Preset& load_preset(const std::string &path, const std::string &name, const DynamicPrintConfig &config, bool select = true, Semver file_version = Semver(), bool is_custom_defined = false);
Preset& load_preset(const std::string &path, const std::string &name, DynamicPrintConfig &&config, bool select = true, Semver file_version = Semver(), bool is_custom_defined = false);
bool clone_presets(std::vector<Preset const *> const &presets, std::vector<std::string> &failures, std::function<void(Preset &, Preset::Type &)> modifier, bool force_rewritten = false);
bool clone_presets_for_printer(std::vector<Preset const *> const &presets, std::vector<std::string> &failures, std::string const &printer, bool force_rewritten = false);
bool create_presets_from_template_for_printer(
std::vector<Preset const *> const &templates, std::vector<std::string> &failures, std::string const &printer, std::function <std::string(std::string)> create_filament_id, bool force_rewritten = false);
bool clone_presets_for_filament(Preset const *const & preset,
std::vector<std::string> &failures,
std::string const & filament_name,
std::string const & filament_id,
const DynamicConfig & dynamic_config,
const std::string & compatible_printers,
bool force_rewritten = false);
std::map<std::string, std::vector<Preset const *>> get_filament_presets() const;
// Returns a loaded preset, returns true if an existing preset was selected AND modified from config.
// In that case the successive filament loaded for a multi material printer should not be modified, but
// an external preset should be created instead.
@ -514,6 +540,7 @@ public:
// Get parent preset for a child preset, based on the "inherits" field of a child,
// where the "inherits" profile name is searched for in both m_presets and m_map_system_profile_renamed.
const Preset* get_preset_parent(const Preset& child) const;
const Preset* get_preset_base(const Preset& child) const;
// Return the selected preset including the user modifications.
Preset& get_edited_preset() { return m_edited_preset; }
const Preset& get_edited_preset() const { return m_edited_preset; }
@ -648,7 +675,8 @@ public:
bool select_preset_by_name(const std::string &name, bool force);
// Generate a file path from a profile name. Add the ".ini" suffix if it is missing.
std::string path_from_name(const std::string &new_name) const;
std::string path_from_name(const std::string &new_name, bool detach = false) const;
std::string path_for_preset(const Preset & preset) const;
size_t num_default_presets() { return m_num_default_presets; }
@ -754,6 +782,7 @@ public:
const Preset& default_preset_for(const DynamicPrintConfig &config) const override;
const Preset* find_system_preset_by_model_and_variant(const std::string &model_id, const std::string &variant) const;
const Preset* find_custom_preset_by_model_and_variant(const std::string &model_id, const std::string &variant) const;
bool only_default_printers() const;
private:

View file

@ -21,6 +21,7 @@
#include <boost/property_tree/ptree.hpp>
#include <boost/locale.hpp>
#include <boost/log/trivial.hpp>
#include <miniz/miniz.h>
// Store the print/filament/printer presets into a "presets" subdirectory of the Slic3rPE config dir.
@ -540,6 +541,7 @@ std::string PresetBundle::get_hotend_model_for_printer_model(std::string model_n
PresetsConfigSubstitutions PresetBundle::load_user_presets(std::string user, ForwardCompatibilitySubstitutionRule substitution_rule)
{
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << __LINE__ << " entry and user is: " << user;
PresetsConfigSubstitutions substitutions;
std::string errors_cummulative;
@ -600,9 +602,14 @@ PresetsConfigSubstitutions PresetBundle::load_user_presets(AppConfig &
remove_users_preset(config, &my_presets);
std::map<std::string, std::map<std::string, std::string>>::iterator it;
for (int pass = 0; pass < 2; ++pass)
for (it = my_presets.begin(); it != my_presets.end(); it++) {
std::string name = it->first;
std::map<std::string, std::string>& value_map = it->second;
// Load user root presets at first pass
std::map<std::string, std::string>::iterator inherits_iter = value_map.find(BBL_JSON_KEY_INHERITS);
if ((pass == 1) == (inherits_iter == value_map.end() || inherits_iter->second.empty()))
continue;
//get the type first
std::map<std::string, std::string>::iterator type_iter = value_map.find(BBL_JSON_KEY_TYPE);
if (type_iter == value_map.end()) {
@ -659,101 +666,189 @@ PresetsConfigSubstitutions PresetBundle::import_presets(std::vector<std::string>
std::function<int(std::string const &)> override_confirm,
ForwardCompatibilitySubstitutionRule rule)
{
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " entry";
PresetsConfigSubstitutions substitutions;
int overwrite = 0;
std::vector<std::string> result;
for (auto &file : files) {
if (Slic3r::is_json_file(file)) {
try {
DynamicPrintConfig config;
// BBS: change to json format
// ConfigSubstitutions config_substitutions = config.load_from_ini(preset.file, substitution_rule);
std::map<std::string, std::string> key_values;
std::string reason;
ConfigSubstitutions config_substitutions = config.load_from_json(file, rule, key_values, reason);
std::string name = key_values[BBL_JSON_KEY_NAME];
std::string version_str = key_values[BBL_JSON_KEY_VERSION];
boost::optional<Semver> version = Semver::parse(version_str);
if (!version) continue;
Semver app_version = *(Semver::parse(SLIC3R_VERSION));
if (version->maj() != app_version.maj()) {
BOOST_LOG_TRIVIAL(warning) << "Preset incompatibla, not loading: " << name;
continue;
}
import_json_presets(substitutions, file, override_confirm, rule, overwrite, result);
}
// Determine if it is a preset bundle
if (boost::iends_with(file, ".bbscfg") || boost::iends_with(file, ".bbsflmt") || boost::iends_with(file, ".zip")) {
boost::system::error_code ec;
// create user folder
fs::path user_folder(data_dir() + "/" + PRESET_USER_DIR);
if (!fs::exists(user_folder)) fs::create_directory(user_folder, ec);
if (ec) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " create directory failed: " << ec.message();
// create default folder
fs::path default_folder(user_folder / DEFAULT_USER_FOLDER_NAME);
if (!fs::exists(default_folder)) fs::create_directory(default_folder, ec);
if (ec) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " create directory failed: " << ec.message();
//create temp folder
//std::string user_default_temp_dir = data_dir() + "/" + PRESET_USER_DIR + "/" + DEFAULT_USER_FOLDER_NAME + "/" + "temp";
fs::path temp_folder(default_folder / "temp");
std::string user_default_temp_dir = temp_folder.make_preferred().string();
if (fs::exists(temp_folder)) fs::remove_all(temp_folder);
fs::create_directory(temp_folder, ec);
if (ec) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " create directory failed: " << ec.message();
PresetCollection * collection = nullptr;
if (config.has("printer_settings_id"))
collection = &printers;
else if (config.has("print_settings_id"))
collection = &prints;
else if (config.has("filament_settings_id"))
collection = &filaments;
if (collection == nullptr) {
BOOST_LOG_TRIVIAL(warning) << "Preset type is unknown, not loading: " << name;
continue;
}
if (overwrite == 0) overwrite = 1;
if (auto p = collection->find_preset(name, false)) {
if (p->is_default || p->is_system) {
BOOST_LOG_TRIVIAL(warning) << "Preset already present and is system preset, not loading: " << name;
continue;
}
overwrite = override_confirm(name);
}
if (overwrite == 0 || overwrite == 2) {
BOOST_LOG_TRIVIAL(warning) << "Preset already present, not loading: " << name;
continue;
}
file = boost::filesystem::path(file).make_preferred().string();
mz_zip_archive zip_archive;
mz_zip_zero_struct(&zip_archive);
mz_bool status;
DynamicPrintConfig new_config;
Preset * inherit_preset = nullptr;
ConfigOption *inherits_config = config.option(BBL_JSON_KEY_INHERITS);
std::string inherits_value;
if (inherits_config) {
ConfigOptionString *option_str = dynamic_cast<ConfigOptionString *>(inherits_config);
inherits_value = option_str->value;
inherit_preset = collection->find_preset(inherits_value, false, true);
}
if (inherit_preset) {
new_config = inherit_preset->config;
} else {
// Find a default preset for the config. The PrintPresetCollection provides different default preset based on the "printer_technology" field.
// new_config = default_preset.config;
// we should skip this preset here
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", can not find inherit preset for user preset %1%, just skip") % name;
continue;
}
new_config.apply(std::move(config));
/*if (!open_zip_reader(&zip_archive, file)) {
BOOST_LOG_TRIVIAL(info) << "Failed to initialize reader ZIP archive";
return substitutions;
} else {
BOOST_LOG_TRIVIAL(info) << "Success to initialize reader ZIP archive";
}*/
Preset &preset = collection->load_preset(collection->path_from_name(name), name, std::move(new_config), false);
preset.is_external = true;
preset.version = *version;
inherit_preset = collection->find_preset(inherits_value, false, true); // pointer maybe wrong after insert, redo find
if (inherit_preset)
preset.base_id = inherit_preset->setting_id;
Preset::normalize(preset.config);
// Report configuration fields, which are misplaced into a wrong group.
const Preset &default_preset = collection->default_preset_for(new_config);
std::string incorrect_keys = Preset::remove_invalid_keys(preset.config, default_preset.config);
if (!incorrect_keys.empty())
BOOST_LOG_TRIVIAL(error) << "Error in a preset file: The preset \"" << preset.file << "\" contains the following incorrect keys: " << incorrect_keys
<< ", which were removed";
if (!config_substitutions.empty())
substitutions.push_back({name, collection->type(), PresetConfigSubstitutions::Source::UserFile, file, std::move(config_substitutions)});
preset.save(inherit_preset ? &inherit_preset->config : nullptr);
result.push_back(file);
} catch (const std::ifstream::failure &err) {
BOOST_LOG_TRIVIAL(error) << boost::format("The config cannot be loaded: %1%. Reason: %2%") % file % err.what();
} catch (const std::runtime_error &err) {
BOOST_LOG_TRIVIAL(error) << boost::format("Failed importing config file: %1%. Reason: %2%") % file % err.what();
FILE *zipFile = boost::nowide::fopen(file.c_str(), "rb");
status = mz_zip_reader_init_cfile(&zip_archive, zipFile, 0, MZ_ZIP_FLAG_CASE_SENSITIVE | MZ_ZIP_FLAG_IGNORE_PATH);
if (MZ_FALSE == status) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " Failed to initialize reader ZIP archive";
return substitutions;
} else {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " Success to initialize reader ZIP archive";
}
// Extract Files
int num_files = mz_zip_reader_get_num_files(&zip_archive);
for (int i = 0; i < num_files; i++) {
mz_zip_archive_file_stat file_stat;
status = mz_zip_reader_file_stat(&zip_archive, i, &file_stat);
if (status) {
std::string file_name = file_stat.m_filename;
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " Form zip file: " << file << ". Read file name: " << file_stat.m_filename;
size_t index = file_name.find_last_of('/');
if (std::string::npos != index) {
file_name = file_name.substr(index + 1);
}
if (BUNDLE_STRUCTURE_JSON_NAME == file_name) continue;
// create target file path
std::string target_file_path = boost::filesystem::path(temp_folder / file_name).make_preferred().string();
status = mz_zip_reader_extract_to_file(&zip_archive, i, encode_path(target_file_path.c_str()).c_str(), MZ_ZIP_FLAG_CASE_SENSITIVE);
// target file is opened
if (MZ_FALSE == status) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " Failed to open target file: " << target_file_path;
} else {
bool is_success = import_json_presets(substitutions, target_file_path, override_confirm, rule, overwrite, result);
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " import target file: " << target_file_path << " import result" << is_success;
}
}
}
fclose(zipFile);
if (fs::exists(temp_folder)) fs::remove_all(temp_folder, ec);
if (ec) BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " remove directory failed: " << ec.message();
}
}
files = result;
return substitutions;
}
bool PresetBundle::import_json_presets(PresetsConfigSubstitutions & substitutions,
std::string & file,
std::function<int(std::string const &)> override_confirm,
ForwardCompatibilitySubstitutionRule rule,
int & overwrite,
std::vector<std::string> & result)
{
try {
DynamicPrintConfig config;
// BBS: change to json format
// ConfigSubstitutions config_substitutions = config.load_from_ini(preset.file, substitution_rule);
std::map<std::string, std::string> key_values;
std::string reason;
ConfigSubstitutions config_substitutions = config.load_from_json(file, rule, key_values, reason);
std::string name = key_values[BBL_JSON_KEY_NAME];
std::string version_str = key_values[BBL_JSON_KEY_VERSION];
boost::optional<Semver> version = Semver::parse(version_str);
if (!version) return false;
Semver app_version = *(Semver::parse(SLIC3R_VERSION));
if (version->maj() != app_version.maj()) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << " Preset incompatibla, not loading: " << name;
return false;
}
PresetCollection *collection = nullptr;
if (config.has("printer_settings_id"))
collection = &printers;
else if (config.has("print_settings_id"))
collection = &prints;
else if (config.has("filament_settings_id"))
collection = &filaments;
if (collection == nullptr) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << " Preset type is unknown, not loading: " << name;
return false;
}
if (overwrite == 0) overwrite = 1;
if (auto p = collection->find_preset(name, false)) {
if (p->is_default || p->is_system) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << " Preset already present and is system preset, not loading: " << name;
return false;
}
if (overwrite != 2 && overwrite != 3) overwrite = override_confirm(name); //3: yes to all 2: no to all
}
if (overwrite == 0 || overwrite == 2) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << " Preset already present, not loading: " << name;
return false;
}
DynamicPrintConfig new_config;
Preset * inherit_preset = nullptr;
ConfigOption * inherits_config = config.option(BBL_JSON_KEY_INHERITS);
std::string inherits_value;
if (inherits_config) {
ConfigOptionString *option_str = dynamic_cast<ConfigOptionString *>(inherits_config);
inherits_value = option_str->value;
inherit_preset = collection->find_preset(inherits_value, false, true);
}
if (inherit_preset) {
new_config = inherit_preset->config;
} else {
// We support custom root preset now
auto inherits_config2 = dynamic_cast<ConfigOptionString *>(inherits_config);
if (inherits_config2 && !inherits_config2->value.empty()) {
// we should skip this preset here
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(", can not find inherit preset for user preset %1%, just skip") % name;
return false;
}
// Find a default preset for the config. The PrintPresetCollection provides different default preset based on the "printer_technology" field.
const Preset &default_preset = collection->default_preset_for(config);
new_config = default_preset.config;
}
new_config.apply(std::move(config));
Preset &preset = collection->load_preset(collection->path_from_name(name, inherit_preset == nullptr), name, std::move(new_config), false);
if (key_values.find(BBL_JSON_KEY_FILAMENT_ID) != key_values.end())
preset.filament_id = key_values[BBL_JSON_KEY_FILAMENT_ID];
preset.is_external = true;
preset.version = *version;
inherit_preset = collection->find_preset(inherits_value, false, true); // pointer maybe wrong after insert, redo find
if (inherit_preset) preset.base_id = inherit_preset->setting_id;
Preset::normalize(preset.config);
// Report configuration fields, which are misplaced into a wrong group.
const Preset &default_preset = collection->default_preset_for(new_config);
std::string incorrect_keys = Preset::remove_invalid_keys(preset.config, default_preset.config);
if (!incorrect_keys.empty())
BOOST_LOG_TRIVIAL(error) << "Error in a preset file: The preset \"" << preset.file << "\" contains the following incorrect keys: " << incorrect_keys
<< ", which were removed";
if (!config_substitutions.empty())
substitutions.push_back({name, collection->type(), PresetConfigSubstitutions::Source::UserFile, file, std::move(config_substitutions)});
preset.save(inherit_preset ? &inherit_preset->config : nullptr);
result.push_back(file);
} catch (const std::ifstream::failure &err) {
BOOST_LOG_TRIVIAL(error) << boost::format("The config cannot be loaded: %1%. Reason: %2%") % file % err.what();
} catch (const std::runtime_error &err) {
BOOST_LOG_TRIVIAL(error) << boost::format("Failed importing config file: %1%. Reason: %2%") % file % err.what();
}
return true;
}
//BBS save user preset to user_id preset folder
void PresetBundle::save_user_presets(AppConfig& config, std::vector<std::string>& need_to_delete_list)
{
@ -867,26 +962,80 @@ void PresetBundle::update_system_preset_setting_ids(std::map<std::string, std::m
}
//BBS: validate printers from previous project
bool PresetBundle::validate_printers(const std::string &name, DynamicPrintConfig& config)
static std::set<std::string> gcodes_key_set = {"filament_end_gcode", "filament_start_gcode", "change_filament_gcode", "layer_change_gcode", "machine_end_gcode", "machine_pause_gcode", "machine_start_gcode",
"template_custom_gcode", "printing_by_object_gcode", "before_layer_change_gcode", "time_lapse_gcode"};
int PresetBundle::validate_presets(const std::string &file_name, DynamicPrintConfig& config, std::set<std::string>& different_gcodes)
{
// BBS TODO:
#if 0
std::vector<std::string> inherits_values;
PrinterTechnology printer_technology = Preset::printer_technology(config);
size_t num_extruders = (printer_technology == ptFFF) ?
std::min(config.option<ConfigOptionFloats>("nozzle_diameter" )->values.size(),
config.option<ConfigOptionFloats>("filament_diameter")->values.size()) : 1;
inherits_values.resize(num_extruders + 2, std::string());
inherits_values = config.option<ConfigOptionStrings>("inherits_group", true)->values;
bool validated = false;
std::vector<std::string> inherits_values = config.option<ConfigOptionStrings>("inherits_group", true)->values;
std::vector<std::string> filament_preset_name = config.option<ConfigOptionStrings>("filament_settings_id", true)->values;
std::string printer_preset = config.option<ConfigOptionString>("printer_settings_id", true)->value;
bool has_different_settings_to_system = config.option("different_settings_to_system")?true:false;
std::vector<std::string> different_values;
int ret = VALIDATE_PRESETS_SUCCESS;
std::string inherits;
if (inherits_values.size() >= (num_extruders + 2))
inherits = inherits_values[num_extruders + 1];
if (has_different_settings_to_system)
different_values = config.option<ConfigOptionStrings>("different_settings_to_system", true)->values;
return this->printers.validate_printers(name, config, inherits);
#else
return true;
#endif
//PrinterTechnology printer_technology = Preset::printer_technology(config);
size_t filament_count = config.option<ConfigOptionFloats>("filament_diameter")->values.size();
inherits_values.resize(filament_count + 2, std::string());
different_values.resize(filament_count + 2, std::string());
filament_preset_name.resize(filament_count, std::string());
std::string printer_inherits = inherits_values[filament_count + 1];
validated = this->printers.validate_preset(printer_preset, printer_inherits);
if (!validated) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(":file_name %1%, found the printer preset not inherit from system") % file_name;
different_gcodes.emplace(printer_preset);
ret = VALIDATE_PRESETS_PRINTER_NOT_FOUND;
}
for(unsigned int index = 0; index < filament_count; index ++)
{
std::string filament_preset = filament_preset_name[index];
std::string filament_inherits = inherits_values[index+1];
validated = this->filaments.validate_preset(filament_preset, filament_inherits);
if (!validated) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(":file_name %1%, found the filament %2% preset not inherit from system") % file_name %(index+1);
different_gcodes.emplace(filament_preset);
ret = VALIDATE_PRESETS_FILAMENTS_NOT_FOUND;
}
}
//self defined presets, return directly
if (ret)
{
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(":file_name %1%, found self defined presets, count %2%") %file_name %different_gcodes.size();
return ret;
}
for(unsigned int index = 1; index < filament_count+2; index ++)
{
std::string different_settingss = different_values[index];
std::vector<std::string> different_keys;
Slic3r::unescape_strings_cstyle(different_settingss, different_keys);
for (unsigned int j = 0; j < different_keys.size(); j++) {
if (gcodes_key_set.find(different_keys[j]) != gcodes_key_set.end()) {
different_gcodes.emplace(different_keys[j]);
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(":preset index %1%, different key %2%") %index %different_keys[j];
}
}
}
if (!different_gcodes.empty())
{
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(":file_name %1%, found different gcodes count %2%") %file_name %different_gcodes.size();
return VALIDATE_PRESETS_MODIFIED_GCODES;
}
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(":file_name %1%, validate presets success!") % file_name;
return VALIDATE_PRESETS_SUCCESS;
}
void PresetBundle::remove_users_preset(AppConfig &config, std::map<std::string, std::map<std::string, std::string>> *my_presets)
@ -1113,6 +1262,116 @@ std::pair<PresetsConfigSubstitutions, std::string> PresetBundle::load_system_pre
return std::make_pair(std::move(substitutions), errors_cummulative);
}
std::pair<PresetsConfigSubstitutions, std::string> PresetBundle::load_system_models_from_json(ForwardCompatibilitySubstitutionRule compatibility_rule)
{
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" enter, compatibility_rule %1%") % compatibility_rule;
if (compatibility_rule == ForwardCompatibilitySubstitutionRule::EnableSystemSilent)
// Loading system presets, don't log substitutions.
compatibility_rule = ForwardCompatibilitySubstitutionRule::EnableSilent;
else if (compatibility_rule == ForwardCompatibilitySubstitutionRule::EnableSilentDisableSystem)
// Loading system presets, throw on unknown option value.
compatibility_rule = ForwardCompatibilitySubstitutionRule::Disable;
// Here the vendor specific read only Config Bundles are stored.
boost::filesystem::path dir = (boost::filesystem::path(resources_dir()) / "profiles").make_preferred();
PresetsConfigSubstitutions substitutions;
std::string errors_cummulative;
for (auto &dir_entry : boost::filesystem::directory_iterator(dir)) {
std::string vendor_file = dir_entry.path().string();
if (Slic3r::is_json_file(vendor_file)) {
std::string vendor_name = dir_entry.path().filename().string();
// Remove the .json suffix.
vendor_name.erase(vendor_name.size() - 5);
try {
// Load the config bundle, flatten it.
append(substitutions, load_vendor_configs_from_json(dir.string(), vendor_name, PresetBundle::LoadVendorOnly, compatibility_rule).first);
} catch (const std::runtime_error &err) {
errors_cummulative += err.what();
errors_cummulative += "\n";
}
}
}
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" finished, errors_cummulative %1%") % errors_cummulative;
return std::make_pair(std::move(substitutions), errors_cummulative);
}
std::pair<PresetsConfigSubstitutions, std::string> PresetBundle::load_system_filaments_json(ForwardCompatibilitySubstitutionRule compatibility_rule)
{
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" enter, compatibility_rule %1%") % compatibility_rule;
if (compatibility_rule == ForwardCompatibilitySubstitutionRule::EnableSystemSilent)
// Loading system presets, don't log substitutions.
compatibility_rule = ForwardCompatibilitySubstitutionRule::EnableSilent;
else if (compatibility_rule == ForwardCompatibilitySubstitutionRule::EnableSilentDisableSystem)
// Loading system presets, throw on unknown option value.
compatibility_rule = ForwardCompatibilitySubstitutionRule::Disable;
// Here the vendor specific read only Config Bundles are stored.
boost::filesystem::path dir = (boost::filesystem::path(resources_dir()) / "profiles").make_preferred();
PresetsConfigSubstitutions substitutions;
std::string errors_cummulative;
bool first = true;
for (auto &dir_entry : boost::filesystem::directory_iterator(dir)) {
std::string vendor_file = dir_entry.path().string();
if (Slic3r::is_json_file(vendor_file)) {
std::string vendor_name = dir_entry.path().filename().string();
// Remove the .json suffix.
vendor_name.erase(vendor_name.size() - 5);
try {
if (first) {
// Reset this PresetBundle and load the first vendor config.
append(substitutions, this->load_vendor_configs_from_json(dir.string(), vendor_name, PresetBundle::LoadSystem | PresetBundle::LoadFilamentOnly, compatibility_rule).first);
first = false;
} else {
// Load the other vendor configs, merge them with this PresetBundle.
// Report duplicate profiles.
PresetBundle other;
append(substitutions, other.load_vendor_configs_from_json(dir.string(), vendor_name, PresetBundle::LoadSystem | PresetBundle::LoadFilamentOnly, compatibility_rule).first);
std::vector<std::string> duplicates = this->merge_presets(std::move(other));
if (!duplicates.empty()) {
errors_cummulative += "Found duplicated settings in vendor " + vendor_name + "'s json file lists: ";
for (size_t i = 0; i < duplicates.size(); ++i) {
if (i > 0) errors_cummulative += ", ";
errors_cummulative += duplicates[i];
}
}
}
} catch (const std::runtime_error &err) {
errors_cummulative += err.what();
errors_cummulative += "\n";
}
}
}
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" finished, errors_cummulative %1%") % errors_cummulative;
return std::make_pair(std::move(substitutions), errors_cummulative);
}
VendorProfile PresetBundle::get_custom_vendor_models() const
{
VendorProfile vendor;
vendor.name = PRESET_CUSTOM_VENDOR;
vendor.id = PRESET_CUSTOM_VENDOR;
for (auto &preset : printers.get_presets()) {
if (preset.is_system) continue;
if (printers.get_preset_base(preset) != &preset) continue;
if (preset.is_default) continue;
auto model = preset.config.opt_string("printer_model");
auto variant = preset.config.opt_string("printer_variant");
auto iter_model = std::find_if(vendor.models.begin(), vendor.models.end(), [model](VendorProfile::PrinterModel &m) {
return m.name == model;
});
if (iter_model == vendor.models.end()) {
iter_model = vendor.models.emplace(vendor.models.end(), VendorProfile::PrinterModel{});
iter_model->id = model;
iter_model->name = model;
iter_model->variants = {VendorProfile::PrinterVariant(variant)};
} else {
iter_model->variants.push_back(VendorProfile::PrinterVariant(variant));
}
}
return vendor;
}
// Merge one vendor's presets with the other vendor's presets, report duplicates.
std::vector<std::string> PresetBundle::merge_presets(PresetBundle &&other)
@ -1181,6 +1440,28 @@ const std::string& PresetBundle::get_preset_name_by_alias( const Preset::Type& p
return presets.get_preset_name_by_alias(alias);
}
//BBS: get filament required hrc by filament type
const int PresetBundle::get_required_hrc_by_filament_type(const std::string& filament_type) const
{
static std::unordered_map<std::string, int>filament_type_to_hrc;
if (filament_type_to_hrc.empty()) {
for (auto iter = filaments.m_presets.begin(); iter != filaments.m_presets.end(); iter++) {
if (iter->vendor && iter->vendor->id == "BBL") {
if (iter->config.has("filament_type") && iter->config.has("required_nozzle_HRC")) {
auto type = iter->config.opt_string("filament_type", 0);
auto hrc = iter->config.opt_int("required_nozzle_HRC", 0);
filament_type_to_hrc[type] = hrc;
}
}
}
}
auto iter = filament_type_to_hrc.find(filament_type);
if (iter != filament_type_to_hrc.end())
return iter->second;
else
return 0;
}
//BBS: add project embedded preset logic
void PresetBundle::save_changes_for_preset(const std::string& new_name, Preset::Type type,
const std::vector<std::string>& unselected_options, bool save_to_project)
@ -1570,7 +1851,8 @@ unsigned int PresetBundle::sync_ams_list(unsigned int &unknowns)
filament_colors.push_back(filament_color);
continue;
}
auto iter = std::find_if(filaments.begin(), filaments.end(), [&filament_id](auto &f) { return f.is_compatible && f.is_system && f.filament_id == filament_id; });
auto iter = std::find_if(filaments.begin(), filaments.end(), [this, &filament_id](auto &f) {
return f.is_compatible && filaments.get_preset_base(f) == &f && f.filament_id == filament_id; });
if (iter == filaments.end()) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(": filament_id %1% not found or system or compatible") % filament_id;
auto filament_type = ams.opt_string("filament_type", 0u);
@ -1582,7 +1864,9 @@ unsigned int PresetBundle::sync_ams_list(unsigned int &unknowns)
});
}
if (iter == filaments.end())
iter = std::find_if(filaments.begin(), filaments.end(), [&filament_type](auto &f) { return f.is_compatible && f.is_system; });
iter = std::find_if(filaments.begin(), filaments.end(), [&filament_type](auto &f) {
return f.is_compatible && f.is_system;
});
if (iter == filaments.end())
continue;
++unknowns;
@ -2087,7 +2371,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
if (is_external)
presets.load_external_preset(name_or_path, name, config.opt_string(key, true), config, different_keys, PresetCollection::LoadAndSelect::Always, file_version, filament_id);
else
presets.load_preset(presets.path_from_name(name), name, config, selected, file_version, is_custom_defined).save(nullptr);
presets.load_preset(presets.path_from_name(name, inherits.empty()), name, config, selected, file_version, is_custom_defined).save(nullptr);
};
switch (Preset::printer_technology(config)) {
@ -2156,7 +2440,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
loaded = this->filaments.load_external_preset(name_or_path, name, old_filament_profile_names->values.front(), config, filament_different_keys_set, PresetCollection::LoadAndSelect::Always, file_version, filament_id).first;
else {
// called from Config Wizard.
loaded= &this->filaments.load_preset(this->filaments.path_from_name(name), name, config, true, file_version, is_custom_defined);
loaded= &this->filaments.load_preset(this->filaments.path_from_name(name, inherits.empty()), name, config, true, file_version, is_custom_defined);
loaded->save(nullptr);
}
this->filament_presets.clear();
@ -2831,10 +3115,9 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
// Enable substitutions for user config bundle, throw an exception when loading a system profile.
ConfigSubstitutionContext substitution_context { compatibility_rule };
PresetsConfigSubstitutions substitutions;
std::string vendor_system_path = data_dir() + "/" + PRESET_SYSTEM_DIR;
//BBS: add config related logs
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" enter, path %1%, compatibility_rule %2%")%path.c_str()%compatibility_rule;
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" enter, path %1%, compatibility_rule %2%")%path.c_str()%compatibility_rule;
if (flags.has(LoadConfigBundleAttribute::ResetUserProfile) || flags.has(LoadConfigBundleAttribute::LoadSystem))
// Reset this bundle, delete user profile files if SaveImported.
this->reset(flags.has(LoadConfigBundleAttribute::SaveImported));
@ -2885,7 +3168,7 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
auto config_version = Semver::parse(version_str);
if (! config_version) {
throw ConfigurationError((boost::format("vendor %1%'s config version: %2% invalid\nSuggest cleaning the directory %3% firstly")
% vendor_name % version_str %vendor_system_path).str());
% vendor_name % version_str % path).str());
} else {
vendor_profile.config_version = std::move(*config_version);
}
@ -2923,10 +3206,16 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
catch(nlohmann::detail::parse_error &err) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": parse "<<root_file<<" got a nlohmann::detail::parse_error, reason = " << err.what();
throw ConfigurationError((boost::format("Failed loading configuration file %1%: %2%\nSuggest cleaning the directory %3% firstly")
%root_file %err.what() %vendor_system_path).str());
%root_file %err.what() % path).str());
//goto __error_process;
}
if (flags.has(LoadConfigBundleAttribute::LoadFilamentOnly)) {
machine_model_subfiles.clear();
machine_subfiles.clear();
process_subfiles.clear();
}
//2) paste the machine model
for (auto& machine_model : machine_model_subfiles)
{
@ -3005,7 +3294,7 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
catch(nlohmann::detail::parse_error &err) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": parse "<< subfile <<" got a nlohmann::detail::parse_error, reason = " << err.what();
throw ConfigurationError((boost::format("Failed loading configuration file %1%: %2%\nSuggest cleaning the directory %3% firstly")
%subfile %err.what() %vendor_system_path).str());
%subfile %err.what() % path).str());
}
if (! model.id.empty() && ! model.variants.empty())
@ -3091,7 +3380,7 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
}
config = *default_config;
config.apply(config_src);
if (instantiation == "false") {
if (instantiation == "false" && "Template" != vendor_name) {
config_maps.emplace(preset_name, std::move(config));
if ((presets_collection->type() == Preset::TYPE_FILAMENT) && (!filament_id.empty()))
filament_id_maps.emplace(preset_name, filament_id);
@ -3169,7 +3458,7 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
loaded.setting_id = setting_id;
loaded.filament_id = filament_id;
if (presets_collection->type() == Preset::TYPE_FILAMENT) {
if (filament_id.empty()) {
if (filament_id.empty() && "Template" != vendor_name) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__<< ": can not find filament_id for " << preset_name;
//throw ConfigurationError(format("can not find inherits %1% for %2%", inherits, preset_name));
reason = "Can not find filament_id for " + preset_name;
@ -3221,7 +3510,7 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
//parse error
std::string subfile_path = path + "/" + vendor_name + "/" + subfile.second;
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", got error when parse process setting from %1%") % subfile_path;
throw ConfigurationError((boost::format("Failed loading configuration file %1%\nSuggest cleaning the directory %2% firstly") % subfile_path %vendor_system_path).str());
throw ConfigurationError((boost::format("Failed loading configuration file %1%\nSuggest cleaning the directory %2% firstly") % subfile_path % path).str());
}
}
@ -3236,7 +3525,7 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
//parse error
std::string subfile_path = path + "/" + vendor_name + "/" + subfile.second;
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", got error when parse filament setting from %1%") % subfile_path;
throw ConfigurationError((boost::format("Failed loading configuration file %1%\nSuggest cleaning the directory %2% firstly") % subfile_path %vendor_system_path).str());
throw ConfigurationError((boost::format("Failed loading configuration file %1%\nSuggest cleaning the directory %2% firstly") % subfile_path % path).str());
}
}
@ -3251,7 +3540,7 @@ std::pair<PresetsConfigSubstitutions, size_t> PresetBundle::load_vendor_configs_
//parse error
std::string subfile_path = path + "/" + vendor_name + "/" + subfile.second;
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(", got error when parse printer setting from %1%") % subfile_path;
throw ConfigurationError((boost::format("Failed loading configuration file %1%\nSuggest cleaning the directory %2% firstly") % subfile_path %vendor_system_path).str());
throw ConfigurationError((boost::format("Failed loading configuration file %1%\nSuggest cleaning the directory %2% firstly") % subfile_path % path).str());
}
}

View file

@ -9,7 +9,14 @@
#include <unordered_map>
#include <boost/filesystem/path.hpp>
#define DEFAULT_USER_FOLDER_NAME "default"
#define DEFAULT_USER_FOLDER_NAME "default"
#define BUNDLE_STRUCTURE_JSON_NAME "bundle_structure.json"
#define VALIDATE_PRESETS_SUCCESS 0
#define VALIDATE_PRESETS_PRINTER_NOT_FOUND 1
#define VALIDATE_PRESETS_FILAMENTS_NOT_FOUND 2
#define VALIDATE_PRESETS_MODIFIED_GCODES 3
// define an enum class of vendor type
enum class VendorType {
@ -57,6 +64,12 @@ public:
PresetsConfigSubstitutions load_user_presets(std::string user, ForwardCompatibilitySubstitutionRule rule);
PresetsConfigSubstitutions load_user_presets(AppConfig &config, std::map<std::string, std::map<std::string, std::string>>& my_presets, ForwardCompatibilitySubstitutionRule rule);
PresetsConfigSubstitutions import_presets(std::vector<std::string> &files, std::function<int(std::string const &)> override_confirm, ForwardCompatibilitySubstitutionRule rule);
bool import_json_presets(PresetsConfigSubstitutions & substitutions,
std::string & file,
std::function<int(std::string const &)> override_confirm,
ForwardCompatibilitySubstitutionRule rule,
int & overwrite,
std::vector<std::string> & result);
void save_user_presets(AppConfig& config, std::vector<std::string>& need_to_delete_list);
void remove_users_preset(AppConfig &config, std::map<std::string, std::map<std::string, std::string>> * my_presets = nullptr);
void update_user_presets_directory(const std::string preset_folder);
@ -64,7 +77,7 @@ public:
void update_system_preset_setting_ids(std::map<std::string, std::map<std::string, std::string>>& system_presets);
//BBS: add API to get previous machine
bool validate_printers(const std::string &name, DynamicPrintConfig& config);
int validate_presets(const std::string &file_name, DynamicPrintConfig& config, std::set<std::string>& different_gcodes);
//BBS: add function to generate differed preset for save
//the pointer should be freed by the caller
@ -173,6 +186,7 @@ public:
// Load a system config bundle.
LoadSystem,
LoadVendorOnly,
LoadFilamentOnly,
};
using LoadConfigBundleAttributes = enum_bitmask<LoadConfigBundleAttribute>;
// Load the config bundle based on the flags.
@ -189,7 +203,7 @@ public:
//void export_current_configbundle(const std::string &path);
//BBS: add a function to export system presets for cloud-slicer
//void export_system_configs(const std::string &path);
std::vector<std::string> export_current_configs(const std::string &path, std::function<int(std::string const &)> override_confirm,
std::vector<std::string> export_current_configs(const std::string &path, std::function<int(std::string const &)> override_confirm,
bool include_modify, bool export_system_settings = false);
// Enable / disable the "- default -" preset.
@ -218,11 +232,16 @@ public:
const std::string& get_preset_name_by_alias(const Preset::Type& preset_type, const std::string& alias) const;
const int get_required_hrc_by_filament_type(const std::string& filament_type) const;
// Save current preset of a provided type under a new name. If the name is different from the old one,
// Unselected option would be reverted to the beginning values
//BBS: add project embedded preset logic
void save_changes_for_preset(const std::string& new_name, Preset::Type type, const std::vector<std::string>& unselected_options, bool save_to_project = false);
std::pair<PresetsConfigSubstitutions, std::string> load_system_models_from_json(ForwardCompatibilitySubstitutionRule compatibility_rule);
std::pair<PresetsConfigSubstitutions, std::string> load_system_filaments_json(ForwardCompatibilitySubstitutionRule compatibility_rule);
VendorProfile get_custom_vendor_models() const;
//BBS: add BBL as default
static const char *BBL_BUNDLE;
static const char *BBL_DEFAULT_PRINTER_MODEL;

View file

@ -31,7 +31,7 @@
#include "Geometry/ConvexHull.hpp"
#include "I18N.hpp"
#include "ShortestPath.hpp"
#include "SupportMaterial.hpp"
#include "Support/SupportMaterial.hpp"
#include "Thread.hpp"
#include "Time.hpp"
#include "GCode.hpp"
@ -102,6 +102,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
"printable_area",
//BBS: add bed_exclude_area
"bed_exclude_area",
"thumbnail_size",
"before_layer_change_gcode",
"enable_pressure_advance",
"pressure_advance",
@ -113,6 +114,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
"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",
@ -183,6 +185,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
"nozzle_hrc",
"required_nozzle_HRC",
"upward_compatible_machine",
"is_infill_first",
// Orca
"chamber_temperature",
"thumbnails",
@ -310,7 +313,7 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
//|| opt_key == "resolution"
//BBS: when enable arc fitting, we must re-generate perimeter
|| opt_key == "enable_arc_fitting"
|| opt_key == "wall_infill_order") {
|| opt_key == "wall_sequence") {
osteps.emplace_back(posPerimeters);
osteps.emplace_back(posEstimateCurledExtrusions);
osteps.emplace_back(posInfill);
@ -1145,19 +1148,19 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
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 { ("Different nozzle diameters and different filament diameters is not allowed when prime tower is enabled.") };
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 { ("The Wipe Tower is currently only supported with the relative extruder addressing (use_relative_e_distances=1).") };
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 { ("Ooze prevention is currently not supported with the prime tower enabled.") };
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 {("The prime tower is currently only supported for the Marlin, RepRap/Sprinter, RepRapFirmware and Repetier G-code flavors.")};
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" };
@ -1339,13 +1342,6 @@ StringObjectException Print::validate(StringObjectException *warning, Polygons*
if (layer_height > min_nozzle_diameter)
return {L("Layer height cannot exceed nozzle diameter"), object, "layer_height"};
for (auto range : object->m_model_object->layer_config_ranges) {
double range_layer_height = range.second.opt_float("layer_height");
if (range_layer_height > object->m_slicing_params.max_layer_height ||
range_layer_height < object->m_slicing_params.min_layer_height)
return { L("Layer height cannot exceed nozzle diameter"), nullptr, "layer_height" };
}
// Validate extrusion widths.
std::string err_msg;
if (!validate_extrusion_width(object->config(), "line_width", layer_height, err_msg))
@ -1626,16 +1622,21 @@ std::map<ObjectID, unsigned int> getObjectExtruderMap(const Print& print) {
std::map<ObjectID, unsigned int> objectExtruderMap;
for (const PrintObject* object : print.objects()) {
// BBS
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));
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()));
}
objectExtruderMap.insert(std::make_pair(object->id(), objectFirstLayerFirstExtruder));
}
return objectExtruderMap;
}
@ -1841,6 +1842,7 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
obj->infill();
obj->ironing();
obj->generate_support_material();
obj->detect_overhangs_for_lift();
obj->estimate_curled_extrusions();
}
}
@ -1980,7 +1982,15 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
}
// BBS
if(!m_no_check)
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();

View file

@ -437,6 +437,9 @@ public:
// BBS: Boundingbox of the first layer
BoundingBox firstLayerObjectBrimBoundingBox;
// BBS: returns 1-based indices of extruders used to print the first layer wall of objects
std::vector<int> object_first_layer_wall_extruders;
// SoftFever
size_t get_id() const { return m_id; }
void set_id(size_t id) { m_id = id; }
@ -528,6 +531,7 @@ private:
std::vector < VolumeSlices > firstLayerObjSliceByVolume;
std::vector<groupedVolumeSlices> firstLayerObjSliceByGroups;
// BBS: per object skirt
ExtrusionEntityCollection m_skirt;
@ -590,8 +594,6 @@ struct FakeWipeTower
std::vector<ExtrusionPaths> getFakeExtrusionPathsFromWipeTower() const
{
float h = height;
float lh = layer_height;
int d = scale_(depth);
int w = scale_(width);
int bd = scale_(brim_width);
@ -599,13 +601,13 @@ struct FakeWipeTower
Point maxCorner = {minCorner.x() + w, minCorner.y() + d};
std::vector<ExtrusionPaths> paths;
for (float hh = 0.f; hh < h; hh += lh) {
ExtrusionPath path(ExtrusionRole::erWipeTower, 0.0, 0.0, lh);
for (float h = 0.f; h < height; h += layer_height) {
ExtrusionPath path(ExtrusionRole::erWipeTower, 0.0, 0.0, layer_height);
path.polyline = {minCorner, {maxCorner.x(), minCorner.y()}, maxCorner, {minCorner.x(), maxCorner.y()}, minCorner};
paths.push_back({path});
if (hh == 0.f) { // add brim
ExtrusionPath fakeBrim(ExtrusionRole::erBrim, 0.0, 0.0, lh);
if (h == 0.f) { // add brim
ExtrusionPath fakeBrim(ExtrusionRole::erBrim, 0.0, 0.0, layer_height);
Point wtbminCorner = {minCorner - Point{bd, bd}};
Point wtbmaxCorner = {maxCorner + Point{bd, bd}};
fakeBrim.polyline = {wtbminCorner, {wtbmaxCorner.x(), wtbminCorner.y()}, wtbmaxCorner, {wtbminCorner.x(), wtbmaxCorner.y()}, wtbminCorner};

View file

@ -22,6 +22,7 @@ enum StringExceptionType {
STRING_EXCEPT_FILAMENTS_DIFFERENT_TEMP = 2,
STRING_EXCEPT_OBJECT_COLLISION_IN_SEQ_PRINT = 3,
STRING_EXCEPT_OBJECT_COLLISION_IN_LAYER_PRINT = 4,
STRING_EXCEPT_LAYER_HEIGHT_EXCEEDS_LIMIT = 5,
STRING_EXCEPT_COUNT
};

View file

@ -179,6 +179,15 @@ static t_config_enum_values s_keys_map_WallInfillOrder {
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(WallInfillOrder)
//BBS
static t_config_enum_values s_keys_map_WallSequence {
{ "inner wall/outer wall", int(WallSequence::InnerOuter) },
{ "outer wall/inner wall", int(WallSequence::OuterInner) },
{ "inner-outer-inner wall", int(WallSequence::InnerOuterInner)}
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(WallSequence)
//BBS
static t_config_enum_values s_keys_map_PrintSequence {
{ "by layer", int(PrintSequence::ByLayer) },
@ -309,6 +318,13 @@ static const t_config_enum_values s_keys_map_BedType = {
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(BedType)
// BBS
static const t_config_enum_values s_keys_map_FirstLayerSeq = {
{ "Auto", flsAuto },
{ "Customize", flsCutomize },
};
CONFIG_OPTION_ENUM_DEFINE_STATIC_MAPS(FirstLayerSeq)
static t_config_enum_values s_keys_map_NozzleType {
{ "undefine", int(NozzleType::ntUndefine) },
{ "hardened_steel", int(NozzleType::ntHardenedSteel) },
@ -405,11 +421,13 @@ void PrintConfigDef::init_common_params()
def = this->add("bed_custom_texture", coString);
def->label = L("Bed custom texture");
def->mode = comAdvanced;
def->gui_type = ConfigOptionDef::GUIType::one_string;
def->set_default_value(new ConfigOptionString(""));
def = this->add("bed_custom_model", coString);
def->label = L("Bed custom model");
def->mode = comAdvanced;
def->gui_type = ConfigOptionDef::GUIType::one_string;
def->set_default_value(new ConfigOptionString(""));
def = this->add("elefant_foot_compensation", coFloat);
@ -677,6 +695,17 @@ void PrintConfigDef::init_fff_params()
def->max = 16;
def->set_default_value(new ConfigOptionInts{0});
def = this->add("first_layer_sequence_choice", coEnum);
def->category = L("Quality");
def->label = L("First layer filament sequence");
def->enum_keys_map = &ConfigOptionEnum<FirstLayerSeq>::get_enum_values();
def->enum_values.push_back("Auto");
def->enum_values.push_back("Customize");
def->enum_labels.push_back(L("Auto"));
def->enum_labels.push_back(L("Customize"));
def->mode = comSimple;
def->set_default_value(new ConfigOptionEnum<FirstLayerSeq>(flsAuto));
def = this->add("before_layer_change_gcode", coString);
def->label = L("Before layer change G-code");
def->tooltip = L("This G-code is inserted at every layer change before lifting z");
@ -1191,6 +1220,15 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionString("M104 S0 ; turn off temperature\nG28 X0 ; home X axis\nM84 ; disable motors\n"));
def = this->add("printing_by_object_gcode", coString);
def->label = L("Between Object Gcode");
def->tooltip = L("Insert Gcode between objects. This parameter will only come into effect when you print your models object by object");
def->multiline = true;
def->full_width = true;
def->height = 12;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionString(""));
def = this->add("filament_end_gcode", coStrings);
def->label = L("End G-code");
def->tooltip = L("End G-code when finish the printing of this filament");
@ -1292,23 +1330,26 @@ void PrintConfigDef::init_fff_params()
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0));
def = this->add("wall_infill_order", coEnum);
def->label = L("Order of inner wall/outer wall/infil");
def = this->add("wall_sequence", coEnum);
def->label = L("Order of walls");
def->category = L("Quality");
def->tooltip = L("Print sequence of inner wall, outer wall and infill. ");
def->enum_keys_map = &ConfigOptionEnum<WallInfillOrder>::get_enum_values();
def->enum_values.push_back("inner wall/outer wall/infill");
def->enum_values.push_back("outer wall/inner wall/infill");
def->enum_values.push_back("infill/inner wall/outer wall");
def->enum_values.push_back("infill/outer wall/inner wall");
def->enum_values.push_back("inner-outer-inner wall/infill");
def->enum_labels.push_back(L("inner/outer/infill"));
def->enum_labels.push_back(L("outer/inner/infill"));
def->enum_labels.push_back(L("infill/inner/outer"));
def->enum_labels.push_back(L("infill/outer/inner"));
def->enum_labels.push_back(L("inner-outer-inner/infill"));
def->tooltip = L("Print sequence of inner wall and outer wall. ");
def->enum_keys_map = &ConfigOptionEnum<WallSequence>::get_enum_values();
def->enum_values.push_back("inner wall/outer wall");
def->enum_values.push_back("outer wall/inner wall");
def->enum_values.push_back("inner-outer-inner wall");
def->enum_labels.push_back(L("inner/outer"));
def->enum_labels.push_back(L("outer/inner"));
def->enum_labels.push_back(L("inner wall/outer wall/inner wall"));
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum<WallInfillOrder>(WallInfillOrder::InnerOuterInfill));
def->set_default_value(new ConfigOptionEnum<WallSequence>(WallSequence::InnerOuter));
def = this->add("is_infill_first",coBool);
def->label = L("Print infill first");
def->tooltip = L("Order of wall/infill. false means print wall first. ");
def->category = L("Quality");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool{false});
def = this->add("extruder", coInt);
def->gui_type = ConfigOptionDef::GUIType::i_enum_open;
@ -1906,7 +1947,7 @@ def = this->add("filament_loading_speed", coFloats);
def = this->add("accel_to_decel_factor", coPercent);
def->label = L("accel_to_decel");
def->tooltip = L("Klipper's max_accel_to_decel will be adjusted to this %% of acceleration");
def->sidetext = L("%%");
def->sidetext = L("%");
def->min = 1;
def->max = 100;
def->mode = comAdvanced;
@ -2390,6 +2431,27 @@ def = this->add("filament_loading_speed", coFloats);
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionBool(false));
def = this->add("mmu_segmented_region_max_width", coFloat);
def->label = L("Maximum width of a segmented region");
def->tooltip = L("Maximum width of a segmented region. Zero disables this feature.");
def->sidetext = L("mm");
def->min = 0;
def->category = L("Advanced");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.));
def = this->add("mmu_segmented_region_interlocking_depth", coFloat);
def->label = L("Interlocking depth of a segmented region");
//def->tooltip = L("Interlocking depth of a segmented region. It will be ignored if "
// "\"mmu_segmented_region_max_width\" is zero or if \"mmu_segmented_region_interlocking_depth\""
// "is bigger then \"mmu_segmented_region_max_width\". Zero disables this feature.");
def->tooltip = L("Interlocking depth of a segmented region. Zero disables this feature.");
def->sidetext = L("mm"); //(zero to disable)
def->min = 0;
def->category = L("Advanced");
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(0.));
def = this->add("ironing_type", coEnum);
def->label = L("Ironing Type");
def->category = L("Quality");
@ -3072,8 +3134,27 @@ def = this->add("filament_loading_speed", coFloats);
"Using spiral line to lift z can prevent stringing");
def->sidetext = L("mm");
def->mode = comSimple;
def->min = 0;
def->max = 5;
def->set_default_value(new ConfigOptionFloats { 0.4 });
def = this->add("retract_lift_above", coFloats);
def->label = L("Z hop lower boundary");
def->tooltip = L("Z hop will only come into effect when Z is above this value and is below the parameter: \"Z hop upper boundary\"");
def->sidetext = L("mm");
def->mode = comAdvanced;
def->min = 0;
def->set_default_value(new ConfigOptionFloats{0.});
def = this->add("retract_lift_below", coFloats);
def->label = L("Z hop upper boundary");
def->tooltip = L("If this value is positive, Z hop will only come into effect when Z is above the parameter: \"Z hop lower boundary\" and is below this value");
def->sidetext = L("mm");
def->mode = comAdvanced;
def->min = 0;
def->set_default_value(new ConfigOptionFloats{0.});
def = this->add("z_hop_types", coEnums);
def->label = L("Z hop type");
def->tooltip = L("Z hop type");
@ -3223,7 +3304,7 @@ def = this->add("filament_loading_speed", coFloats);
def->tooltip = L("Distance from skirt to brim or object");
def->sidetext = L("mm");
def->min = 0;
def->max = 10;
def->max = 60;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionFloat(2));
@ -3570,7 +3651,14 @@ def = this->add("filament_loading_speed", coFloats);
def->tooltip = L("Filament to print support base and raft. \"Default\" means no specific filament for support and current filament is used");
def->min = 0;
def->mode = comSimple;
def->set_default_value(new ConfigOptionInt(1));
def->set_default_value(new ConfigOptionInt(0));
def = this->add("support_interface_not_for_body",coBool);
def->label = L("Reduce interface filament for base");
def->category = L("Support");
def->tooltip = L("Avoid using support interface filament to print support base");
def->mode = comSimple;
def->set_default_value(new ConfigOptionBool(true));
def = this->add("support_line_width", coFloatOrPercent);
def->label = L("Support");
@ -3599,7 +3687,7 @@ def = this->add("filament_loading_speed", coFloats);
def->min = 0;
// BBS
def->mode = comSimple;
def->set_default_value(new ConfigOptionInt(1));
def->set_default_value(new ConfigOptionInt(0));
auto support_interface_top_layers = def = this->add("support_interface_top_layers", coInt);
def->gui_type = ConfigOptionDef::GUIType::i_enum_open;
@ -3623,14 +3711,12 @@ def = this->add("filament_loading_speed", coFloats);
def->gui_type = ConfigOptionDef::GUIType::i_enum_open;
def->label = L("Bottom interface layers");
def->category = L("Support");
//def->tooltip = L("Number of bottom interface layers. "
// "-1 means same with use top interface layers");
def->tooltip = L("Number of bottom interface layers");
def->sidetext = L("layers");
def->min = -1;
def->enum_values.push_back("-1");
append(def->enum_values, support_interface_top_layers->enum_values);
//TRN To be shown in Print Settings "Bottom interface layers". Have to be as short as possible
def->enum_labels.push_back("-1");
def->enum_labels.push_back(L("Same as top"));
append(def->enum_labels, support_interface_top_layers->enum_labels);
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionInt(0));
@ -3681,7 +3767,7 @@ def = this->add("filament_loading_speed", coFloats);
def->enum_labels.push_back(L("Lightning"));
def->enum_labels.push_back(L("Hollow"));
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum<SupportMaterialPattern>(smpRectilinear));
def->set_default_value(new ConfigOptionEnum<SupportMaterialPattern>(smpDefault));
def = this->add("support_interface_pattern", coEnum);
def->label = L("Interface pattern");
@ -3701,7 +3787,7 @@ def = this->add("filament_loading_speed", coFloats);
def->enum_labels.push_back(L("Rectilinear Interlaced"));
def->enum_labels.push_back(L("Grid"));
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionEnum<SupportMaterialInterfacePattern>(smipRectilinear));
def->set_default_value(new ConfigOptionEnum<SupportMaterialInterfacePattern>(smipAuto));
def = this->add("support_base_pattern_spacing", coFloat);
def->label = L("Base pattern spacing");
@ -3919,12 +4005,12 @@ def = this->add("filament_loading_speed", coFloats);
def->set_default_value(new ConfigOptionFloat(3.));
def = this->add("tree_support_wall_count", coInt);
def->label = L("Tree support wall loops");
def->label = L("Support wall loops");
def->category = L("Support");
def->tooltip = L("This setting specify the count of walls around tree support");
def->tooltip = L("This setting specify the count of walls around support");
def->min = 0;
def->mode = comAdvanced;
def->set_default_value(new ConfigOptionInt(1));
def->set_default_value(new ConfigOptionInt(0));
def = this->add("tree_support_with_infill", coBool);
def->label = L("Tree support with infill");
@ -4126,7 +4212,7 @@ def = this->add("filament_loading_speed", coFloats);
//def->sidetext = L("mm");
def->mode = comDevelop;
// BBS: change data type to floats to add partplate logic
def->set_default_value(new ConfigOptionFloats{ 165.-10. });
def->set_default_value(new ConfigOptionFloats{ 15. });
def = this->add("wipe_tower_y", coFloats);
//def->label = L("Position Y");
@ -5207,6 +5293,19 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
}
} else if (opt_key == "overhang_fan_threshold" && value == "5%") {
value = "10%";
} else if( opt_key == "wall_infill_order" ) {
if (value == "inner wall/outer wall/infill" || value == "infill/inner wall/outer wall") {
opt_key = "wall_sequence";
value = "inner wall/outer wall";
} else if (value == "outer wall/inner wall/infill" || value == "infill/outer wall/inner wall") {
opt_key = "wall_sequence";
value = "outer wall/inner wall";
} else if (value == "inner-outer-inner wall/infill") {
opt_key = "wall_sequence";
value = "inner-outer-inner wall";
} else {
opt_key = "wall_sequence";
}
} else if(opt_key == "single_extruder_multi_material") {
value = "1";
}
@ -5229,6 +5328,9 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
else if (opt_key == "initial_layer_flow_ratio") {
opt_key = "bottom_solid_infill_flow_ratio";
}
else if(opt_key == "ironing_direction") {
opt_key = "ironing_angle";
}
// Ignore the following obsolete configuration keys:
static std::set<std::string> ignore = {
@ -5245,7 +5347,7 @@ void PrintConfigDef::handle_legacy(t_config_option_key &opt_key, std::string &va
"can_switch_nozzle_type", "can_add_auxiliary_fan", "extra_flush_volume", "spaghetti_detector", "adaptive_layer_height",
"z_hop_type", "z_lift_type", "bed_temperature_difference",
"extruder_type",
"internal_bridge_support_thickness","extruder_clearance_max_radius"
"internal_bridge_support_thickness","extruder_clearance_max_radius", "top_area_threshold"
};
if (ignore.find(opt_key) != ignore.end()) {
@ -5888,20 +5990,20 @@ CLIActionsConfigDef::CLIActionsConfigDef()
def->set_default_value(new ConfigOptionBool(false));*/
def = this->add("export_3mf", coString);
def->label = L("Export 3MF");
def->tooltip = L("Export project as 3MF.");
def->label = "Export 3MF";
def->tooltip = "Export project as 3MF.";
def->cli_params = "filename.3mf";
def->set_default_value(new ConfigOptionString("output.3mf"));
def = this->add("export_slicedata", coString);
def->label = L("Export slicing data");
def->tooltip = L("Export slicing data to a folder.");
def->label = "Export slicing data";
def->tooltip = "Export slicing data to a folder.";
def->cli_params = "slicing_data_directory";
def->set_default_value(new ConfigOptionString("cached_data"));
def = this->add("load_slicedata", coStrings);
def->label = L("Load slicing data");
def->tooltip = L("Load cached slicing data from directory");
def->label = "Load slicing data";
def->tooltip = "Load cached slicing data from directory";
def->cli_params = "slicing_data_directory";
def->set_default_value(new ConfigOptionString("cached_data"));
@ -5911,8 +6013,8 @@ CLIActionsConfigDef::CLIActionsConfigDef()
def->set_default_value(new ConfigOptionBool(false));*/
def = this->add("export_stl", coBool);
def->label = L("Export STL");
def->tooltip = L("Export the objects as multiple STL.");
def->label = "Export STL";
def->tooltip = "Export the objects as multiple STL.";
def->set_default_value(new ConfigOptionBool(false));
/*def = this->add("export_gcode", coBool);
@ -5929,27 +6031,33 @@ CLIActionsConfigDef::CLIActionsConfigDef()
def->set_default_value(new ConfigOptionBool(false));*/
def = this->add("slice", coInt);
def->label = L("Slice");
def->tooltip = L("Slice the plates: 0-all plates, i-plate i, others-invalid");
def->label = "Slice";
def->tooltip = "Slice the plates: 0-all plates, i-plate i, others-invalid";
def->cli = "slice";
def->cli_params = "option";
def->set_default_value(new ConfigOptionInt(0));
def = this->add("help", coBool);
def->label = L("Help");
def->tooltip = L("Show command help.");
def->label = "Help";
def->tooltip = "Show command help.";
def->cli = "help|h";
def->set_default_value(new ConfigOptionBool(false));
def = this->add("uptodate", coBool);
def->label = L("UpToDate");
def->tooltip = L("Update the configs values of 3mf to latest.");
def->label = "UpToDate";
def->tooltip = "Update the configs values of 3mf to latest.";
def->cli = "uptodate";
def->set_default_value(new ConfigOptionBool(false));
def = this->add("load_defaultfila", coBool);
def->label = L("Load default filaments");
def->tooltip = L("Load first filament as default for those not loaded");
def->label = "Load default filaments";
def->tooltip = "Load first filament as default for those not loaded";
def->cli_params = "option";
def->set_default_value(new ConfigOptionBool(false));
def = this->add("min_save", coBool);
def->label = "Minimum save";
def->tooltip = "export 3mf with minimum size.";
def->cli_params = "option";
def->set_default_value(new ConfigOptionBool(false));
@ -5960,15 +6068,15 @@ CLIActionsConfigDef::CLIActionsConfigDef()
def->set_default_value(new ConfigOptionBool(false));
def = this->add("mtcpp", coInt);
def->label = L("mtcpp");
def->tooltip = L("max triangle count per plate for slicing.");
def->label = "mtcpp";
def->tooltip = "max triangle count per plate for slicing.";
def->cli = "mtcpp";
def->cli_params = "count";
def->set_default_value(new ConfigOptionInt(1000000));
def = this->add("mstpp", coInt);
def->label = L("mstpp");
def->tooltip = L("max slicing time per plate in seconds.");
def->label = "mstpp";
def->tooltip = "max slicing time per plate in seconds.";
def->cli = "mstpp";
def->cli_params = "time";
def->set_default_value(new ConfigOptionInt(300));
@ -5977,12 +6085,11 @@ CLIActionsConfigDef::CLIActionsConfigDef()
def = this->add("no_check", coBool);
def->label = L("No check");
def->tooltip = L("Do not run any validity checks, such as gcode path conflicts check.");
def->cli_params = "option";
def->set_default_value(new ConfigOptionBool(false));
def = this->add("normative_check", coBool);
def->label = L("Normative check");
def->tooltip = L("Check the normative items.");
def->label = "Normative check";
def->tooltip = "Check the normative items.";
def->cli_params = "option";
def->set_default_value(new ConfigOptionBool(true));
@ -5997,19 +6104,19 @@ CLIActionsConfigDef::CLIActionsConfigDef()
def->set_default_value(new ConfigOptionBool(false));*/
def = this->add("info", coBool);
def->label = L("Output Model Info");
def->tooltip = L("Output the model's information.");
def->label = "Output Model Info";
def->tooltip = "Output the model's information.";
def->set_default_value(new ConfigOptionBool(false));
def = this->add("export_settings", coString);
def->label = L("Export Settings");
def->tooltip = L("Export settings to a file.");
def->label = "Export Settings";
def->tooltip = "Export settings to a file.";
def->cli_params = "settings.json";
def->set_default_value(new ConfigOptionString("output.json"));
def = this->add("pipe", coString);
def->label = L("Send progress to pipe");
def->tooltip = L("Send progress to pipe.");
def->label = "Send progress to pipe";
def->tooltip = "Send progress to pipe.";
def->cli_params = "pipename";
def->set_default_value(new ConfigOptionString(""));
}
@ -6053,15 +6160,15 @@ CLITransformConfigDef::CLITransformConfigDef()
def->set_default_value(new ConfigOptionPoint(Vec2d(100,100)));*/
def = this->add("arrange", coInt);
def->label = L("Arrange Options");
def->tooltip = L("Arrange options: 0-disable, 1-enable, others-auto");
def->label = "Arrange Options";
def->tooltip = "Arrange options: 0-disable, 1-enable, others-auto";
def->cli_params = "option";
//def->cli = "arrange|a";
def->set_default_value(new ConfigOptionInt(0));
def = this->add("repetitions", coInt);
def->label = L("Repetions count");
def->tooltip = L("Repetions count of the whole model");
def->label = "Repetions count";
def->tooltip = "Repetions count of the whole model";
def->cli_params = "count";
def->set_default_value(new ConfigOptionInt(1));
@ -6078,16 +6185,17 @@ CLITransformConfigDef::CLITransformConfigDef()
/*def = this->add("duplicate_grid", coPoint);
def->label = L("Duplicate by grid");
def->tooltip = L("Multiply copies by creating a grid.");
def->tooltip = L("Multiply copies by creating a grid.");*/
def = this->add("assemble", coBool);
def->label = L("Assemble");
def->tooltip = L("Arrange the supplied models in a plate and merge them in a single model in order to perform actions once.");
def->cli = "merge|m";*/
def->label = "Assemble";
def->tooltip = "Arrange the supplied models in a plate and merge them in a single model in order to perform actions once.";
//def->cli = "merge|m";
def->set_default_value(new ConfigOptionBool(false));
def = this->add("convert_unit", coBool);
def->label = L("Convert Unit");
def->tooltip = L("Convert the units of model");
def->label = "Convert Unit";
def->tooltip = "Convert the units of model";
def->set_default_value(new ConfigOptionBool(false));
def = this->add("orient", coInt);
@ -6107,8 +6215,8 @@ CLITransformConfigDef::CLITransformConfigDef()
def->set_default_value(new ConfigOptionFloat(0));
def = this->add("rotate_x", coFloat);
def->label = L("Rotate around X");
def->tooltip = L("Rotation angle around the X axis in degrees.");
def->label = "Rotate around X";
def->tooltip = "Rotation angle around the X axis in degrees.";
def->set_default_value(new ConfigOptionFloat(0));
def = this->add("rotate_y", coFloat);
@ -6117,8 +6225,8 @@ CLITransformConfigDef::CLITransformConfigDef()
def->set_default_value(new ConfigOptionFloat(0));
def = this->add("scale", coFloat);
def->label = L("Scale");
def->tooltip = L("Scale the model by a float factor");
def->label = "Scale";
def->tooltip = "Scale the model by a float factor";
def->cli_params = "factor";
def->set_default_value(new ConfigOptionFloat(1.f));
@ -6159,29 +6267,47 @@ CLIMiscConfigDef::CLIMiscConfigDef()
def->tooltip = L("Load configuration from the specified file. It can be used more than once to load options from multiple files.");*/
def = this->add("load_settings", coStrings);
def->label = L("Load General Settings");
def->tooltip = L("Load process/machine settings from the specified file");
def->label = "Load General Settings";
def->tooltip = "Load process/machine settings from the specified file";
def->cli_params = "\"setting1.json;setting2.json\"";
def->set_default_value(new ConfigOptionStrings());
def = this->add("load_filaments", coStrings);
def->label = L("Load Filament Settings");
def->tooltip = L("Load filament settings from the specified file list");
def->label = "Load Filament Settings";
def->tooltip = "Load filament settings from the specified file list";
def->cli_params = "\"filament1.json;filament2.json;...\"";
def->set_default_value(new ConfigOptionStrings());
def = this->add("skip_objects", coInts);
def->label = L("Skip Objects");
def->tooltip = L("Skip some objects in this print");
def->label = "Skip Objects";
def->tooltip = "Skip some objects in this print";
def->cli_params = "\"3,5,10,77\"";
def->set_default_value(new ConfigOptionInts());
def = this->add("clone_objects", coInts);
def->label = "Clone Objects";
def->tooltip = "Clone objects in the load list";
def->cli_params = "\"1,3,1,10\"";
def->set_default_value(new ConfigOptionInts());
def = this->add("uptodate_settings", coStrings);
def->label = L("load uptodate process/machine settings when using uptodate");
def->tooltip = L("load uptodate process/machine settings from the specified file when using uptodate");
def->label = "load uptodate process/machine settings when using uptodate";
def->tooltip = "load uptodate process/machine settings from the specified file when using uptodate";
def->cli_params = "\"setting1.json;setting2.json\"";
def->set_default_value(new ConfigOptionStrings());
def = this->add("uptodate_filaments", coStrings);
def->label = "load uptodate filament settings when using uptodate";
def->tooltip = "load uptodate filament settings from the specified file when using uptodate";
def->cli_params = "\"filament1.json;filament2.json;...\"";
def->set_default_value(new ConfigOptionStrings());
def = this->add("load_assemble_list", coString);
def->label = "Load assemble list";
def->tooltip = "Load assemble object list from config file";
def->cli_params = "assemble_list.json";
def->set_default_value(new ConfigOptionString());
/*def = this->add("output", coString);
def->label = L("Output File");
def->tooltip = L("The file where the output will be written (if not specified, it will be based on the input file).");
@ -6205,18 +6331,23 @@ CLIMiscConfigDef::CLIMiscConfigDef()
def = this->add("outputdir", coString);
def->label = L("Output directory");
def->tooltip = L("Output directory for the exported files.");
def->label = "Output directory";
def->tooltip = "Output directory for the exported files.";
def->cli_params = "dir";
def->set_default_value(new ConfigOptionString());
def = this->add("debug", coInt);
def->label = L("Debug level");
def->tooltip = L("Sets debug logging level. 0:fatal, 1:error, 2:warning, 3:info, 4:debug, 5:trace\n");
def->label = "Debug level";
def->tooltip = "Sets debug logging level. 0:fatal, 1:error, 2:warning, 3:info, 4:debug, 5:trace\n";
def->min = 0;
def->cli_params = "level";
def->set_default_value(new ConfigOptionInt(1));
def = this->add("enable_timelapse", coBool);
def->label = "Enable timeplapse for print";
def->tooltip = "If enabled, this slicing will be considered using timelapse";
def->set_default_value(new ConfigOptionBool(false));
#if (defined(_MSC_VER) || defined(__MINGW32__)) && defined(SLIC3R_GUI)
/*def = this->add("sw_renderer", coBool);
def->label = L("Render with a software renderer");
@ -6229,6 +6360,33 @@ CLIMiscConfigDef::CLIMiscConfigDef()
def->tooltip = L("Load custom gcode from json");
def->cli_params = "custom_gcode_toolchange.json";
def->set_default_value(new ConfigOptionString());
def = this->add("load_filament_ids", coInts);
def->label = "Load filament ids";
def->tooltip = "Load filament ids for each object";
def->cli_params = "\"1,2,3,1\"";
def->set_default_value(new ConfigOptionInts());
def = this->add("allow_multicolor_oneplate", coBool);
def->label = "Allow multiple color on one plate";
def->tooltip = "If enabled, the arrange will allow multiple color on one plate";
def->set_default_value(new ConfigOptionBool(true));
def = this->add("allow_rotations", coBool);
def->label = "Allow rotatations when arrange";
def->tooltip = "If enabled, the arrange will allow rotations when place object";
def->set_default_value(new ConfigOptionBool(true));
def = this->add("avoid_extrusion_cali_region", coBool);
def->label = "Avoid extrusion calibrate region when doing arrange";
def->tooltip = "If enabled, the arrange will avoid extrusion calibrate region when place object";
def->set_default_value(new ConfigOptionBool(false));
def = this->add("skip_modified_gcodes", coBool);
def->label = "Skip modified gcodes in 3mf";
def->tooltip = "Skip the modified gcodes in 3mf from Printer or filament Presets";
def->cli_params = "option";
def->set_default_value(new ConfigOptionBool(false));
}
const CLIActionsConfigDef cli_actions_config_def;

View file

@ -90,6 +90,14 @@ enum class WallInfillOrder {
InnerOuterInnerInfill,
Count,
};
// BBS
enum class WallSequence {
InnerOuter,
OuterInner,
InnerOuterInner,
Count,
};
//BBS
enum class PrintSequence {
ByLayer,
@ -216,6 +224,12 @@ enum BedType {
btCount
};
// BBS
enum FirstLayerSeq {
flsAuto,
flsCutomize
};
// BBS
enum NozzleType {
ntUndefine = 0,
@ -348,7 +362,6 @@ CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(GCodeThumbnailsFormat)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PrintHostType)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(AuthorizationType)
CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS(PerimeterGeneratorType)
#undef CONFIG_OPTION_ENUM_DECLARE_STATIC_MAPS
// Defines each and every confiuration option of Slic3r, including the properties of the GUI dialogs.
@ -687,6 +700,8 @@ PRINT_CONFIG_CLASS_DEFINE(
// Force the generation of solid shells between adjacent materials/volumes.
((ConfigOptionBool, interface_shells))
((ConfigOptionFloat, layer_height))
((ConfigOptionFloat, mmu_segmented_region_max_width))
((ConfigOptionFloat, mmu_segmented_region_interlocking_depth))
((ConfigOptionFloat, raft_contact_distance))
((ConfigOptionFloat, raft_expansion))
((ConfigOptionPercent, raft_first_layer_density))
@ -709,6 +724,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionInt, enforce_support_layers))
((ConfigOptionInt, support_filament))
((ConfigOptionFloatOrPercent, support_line_width))
((ConfigOptionBool, support_interface_not_for_body))
((ConfigOptionBool, support_interface_loop_pattern))
((ConfigOptionInt, support_interface_filament))
((ConfigOptionInt, support_interface_top_layers))
@ -735,6 +751,7 @@ PRINT_CONFIG_CLASS_DEFINE(
// BBS
((ConfigOptionBool, flush_into_infill))
((ConfigOptionBool, flush_into_support))
((ConfigOptionEnum<WallSequence>, wall_sequence))
// BBS
((ConfigOptionFloat, tree_support_branch_distance))
((ConfigOptionFloat, tree_support_tip_diameter))
@ -824,6 +841,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionEnum<InfillPattern>, ironing_pattern))
((ConfigOptionPercent, ironing_flow))
((ConfigOptionFloat, ironing_spacing))
((ConfigOptionFloat, ironing_direction))
((ConfigOptionFloat, ironing_speed))
((ConfigOptionFloat, ironing_angle))
// Detect bridging perimeters
@ -917,7 +935,8 @@ PRINT_CONFIG_CLASS_DEFINE(
PRINT_CONFIG_CLASS_DEFINE(
GCodeConfig,
((ConfigOptionString, before_layer_change_gcode))
((ConfigOptionString, before_layer_change_gcode))
((ConfigOptionString, printing_by_object_gcode))
((ConfigOptionFloats, deretraction_speed))
//BBS
((ConfigOptionBool, enable_arc_fitting))
@ -941,6 +960,7 @@ PRINT_CONFIG_CLASS_DEFINE(
((ConfigOptionInts, required_nozzle_HRC))
// BBS
((ConfigOptionBool, scan_first_layer))
((ConfigOptionPoints, thumbnail_size))
// ((ConfigOptionBool, spaghetti_detector))
((ConfigOptionBool, gcode_add_line_number))
((ConfigOptionBool, bbl_bed_temperature_gcode))
@ -1037,6 +1057,8 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
//BBS: add bed_exclude_area
((ConfigOptionPoints, bed_exclude_area))
// BBS
((ConfigOptionString, bed_custom_texture))
((ConfigOptionString, bed_custom_model))
((ConfigOptionEnum<BedType>, curr_bed_type))
((ConfigOptionInts, cool_plate_temp))
((ConfigOptionInts, eng_plate_temp))
@ -1141,12 +1163,13 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
((ConfigOptionInt, slow_down_layers))
((ConfigOptionInts, support_material_interface_fan_speed))
// Orca: notes for profiles from PrusaSlicer
((ConfigOptionStrings, filament_notes))
((ConfigOptionString, notes))
((ConfigOptionString, printer_notes))
((ConfigOptionStrings, filament_notes))
((ConfigOptionString, notes))
((ConfigOptionString, printer_notes))
((ConfigOptionBools, activate_chamber_temp_control))
((ConfigOptionInts , chamber_temperature))
((ConfigOptionBool, is_infill_first))

View file

@ -68,7 +68,6 @@ using namespace std::literals;
#endif
// #define SLIC3R_DEBUG
// Make assert active if SLIC3R_DEBUG
#ifdef SLIC3R_DEBUG
#undef NDEBUG
@ -912,7 +911,8 @@ bool PrintObject::invalidate_state_by_config_options(
}
} else if (
opt_key == "wall_loops"
|| opt_key == "only_one_wall_top"
|| opt_key == "top_one_wall_type"
|| opt_key == "min_width_top_surface"
|| opt_key == "only_one_wall_first_layer"
|| opt_key == "extra_perimeters_on_overhangs"
|| opt_key == "initial_layer_line_width"
@ -945,6 +945,8 @@ bool PrintObject::invalidate_state_by_config_options(
steps.emplace_back(posPerimeters);
} else if (
opt_key == "layer_height"
|| opt_key == "mmu_segmented_region_max_width"
|| opt_key == "mmu_segmented_region_interlocking_depth"
|| opt_key == "raft_layers"
|| opt_key == "raft_contact_distance"
|| opt_key == "slice_closing_radius"
@ -995,6 +997,7 @@ bool PrintObject::invalidate_state_by_config_options(
|| opt_key == "support_interface_pattern"
|| opt_key == "support_interface_loop_pattern"
|| opt_key == "support_interface_filament"
|| opt_key == "support_interface_not_for_body"
|| opt_key == "support_interface_spacing"
|| opt_key == "support_bottom_interface_spacing" //BBS
|| opt_key == "support_base_pattern"

View file

@ -398,14 +398,14 @@ void SLAPrint::Steps::drill_holes(SLAPrintObject &po)
auto bb = bounding_box(m);
Eigen::AlignedBox<float, 3> ebb{bb.min.cast<float>(),
bb.max.cast<float>()};
AABBTreeIndirect::traverse(
tree,
AABBTreeIndirect::intersecting(ebb),
[&part_to_drill, &hollowed_mesh](size_t faceid)
{
part_to_drill.indices.emplace_back(hollowed_mesh.its.indices[faceid]);
});
//BBS
//AABBTreeIndirect::traverse(
// tree,
// AABBTreeIndirect::intersecting(ebb),
// [&part_to_drill, &hollowed_mesh](size_t faceid)
//{
// part_to_drill.indices.emplace_back(hollowed_mesh.its.indices[faceid]);
//});
auto cgal_meshpart = MeshBoolean::cgal::triangle_mesh_to_cgal(
remove_unconnected_vertices(part_to_drill));

View file

@ -430,16 +430,16 @@ std::vector<double> smooth_height_profile(const std::vector<double>& profile, co
// return false;
//};
//int count = 0;
//std::vector<double> ret = profile;
//bool has_steep_change = has_steep_height_change(ret, LAYER_HEIGHT_CHANGE_STEP);
//while (has_steep_change && count < 6) {
// ret = gauss_blur(ret, smoothing_params);
// has_steep_change = has_steep_height_change(ret, LAYER_HEIGHT_CHANGE_STEP);
// count++;
//}
//return ret;
return gauss_blur(profile, smoothing_params);
int count = 0;
std::vector<double> ret = profile;
// bool has_steep_change = has_steep_height_change(ret, LAYER_HEIGHT_CHANGE_STEP);
while (/*has_steep_change &&*/ count < 6) {
ret = gauss_blur(ret, smoothing_params);
//has_steep_change = has_steep_height_change(ret, LAYER_HEIGHT_CHANGE_STEP);
count++;
}
return ret;
// return gauss_blur(profile, smoothing_params);
}
void adjust_layer_height_profile(

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,332 @@
// Tree supports by Thomas Rahm, losely based on Tree Supports by CuraEngine.
// Original source of Thomas Rahm's tree supports:
// https://github.com/ThomasRahm/CuraEngine
//
// Original CuraEngine copyright:
// Copyright (c) 2021 Ultimaker B.V.
// CuraEngine is released under the terms of the AGPLv3 or higher.
#ifndef slic3r_TreeSupport_hpp
#define slic3r_TreeSupport_hpp
#include <boost/container/small_vector.hpp>
#include "../Point.hpp"
#include "../BoundingBox.hpp"
#include "../Utils.hpp"
#include "TreeModelVolumes.hpp"
#include "TreeSupportCommon.hpp"
// #define TREE_SUPPORT_SHOW_ERRORS
#ifdef SLIC3R_TREESUPPORTS_PROGRESS
// The various stages of the process can be weighted differently in the progress bar.
// These weights are obtained experimentally using a small sample size. Sensible weights can differ drastically based on the assumed default settings and model.
#define TREE_PROGRESS_TOTAL 10000
#define TREE_PROGRESS_PRECALC_COLL TREE_PROGRESS_TOTAL * 0.1
#define TREE_PROGRESS_PRECALC_AVO TREE_PROGRESS_TOTAL * 0.4
#define TREE_PROGRESS_GENERATE_NODES TREE_PROGRESS_TOTAL * 0.1
#define TREE_PROGRESS_AREA_CALC TREE_PROGRESS_TOTAL * 0.3
#define TREE_PROGRESS_DRAW_AREAS TREE_PROGRESS_TOTAL * 0.1
#define TREE_PROGRESS_GENERATE_BRANCH_AREAS TREE_PROGRESS_DRAW_AREAS / 3
#define TREE_PROGRESS_SMOOTH_BRANCH_AREAS TREE_PROGRESS_DRAW_AREAS / 3
#define TREE_PROGRESS_FINALIZE_BRANCH_AREAS TREE_PROGRESS_DRAW_AREAS / 3
#endif // SLIC3R_TREESUPPORTS_PROGRESS
namespace Slic3r
{
// Forward declarations
class TreeSupport;
class Print;
class PrintObject;
class SupportGeneratorLayer;
using SupportGeneratorLayersPtr = std::vector<SupportGeneratorLayer*>;
namespace TreeSupport3D
{
// The number of vertices in each circle.
static constexpr const size_t SUPPORT_TREE_CIRCLE_RESOLUTION = 25;
struct AreaIncreaseSettings
{
AreaIncreaseSettings(
TreeModelVolumes::AvoidanceType type = TreeModelVolumes::AvoidanceType::Fast, coord_t increase_speed = 0,
bool increase_radius = false, bool no_error = false, bool use_min_distance = false, bool move = false) :
increase_speed{ increase_speed }, type{ type }, increase_radius{ increase_radius }, no_error{ no_error }, use_min_distance{ use_min_distance }, move{ move } {}
coord_t increase_speed;
// Packing for smaller memory footprint of SupportElementState && SupportElementMerging
TreeModelVolumes::AvoidanceType type;
bool increase_radius : 1;
bool no_error : 1;
bool use_min_distance : 1;
bool move : 1;
bool operator==(const AreaIncreaseSettings& other) const
{
return type == other.type &&
increase_speed == other.increase_speed &&
increase_radius == other.increase_radius &&
no_error == other.no_error &&
use_min_distance == other.use_min_distance &&
move == other.move;
}
};
#define TREE_SUPPORTS_TRACK_LOST
// C++17 does not support in place initializers of bit values, thus a constructor zeroing the bits is provided.
struct SupportElementStateBits {
SupportElementStateBits() :
to_buildplate(false),
to_model_gracious(false),
use_min_xy_dist(false),
supports_roof(false),
can_use_safe_radius(false),
skip_ovalisation(false),
#ifdef TREE_SUPPORTS_TRACK_LOST
lost(false),
verylost(false),
#endif // TREE_SUPPORTS_TRACK_LOST
deleted(false),
marked(false)
{}
/*!
* \brief The element trys to reach the buildplate
*/
bool to_buildplate : 1;
/*!
* \brief Will the branch be able to rest completely on a flat surface, be it buildplate or model ?
*/
bool to_model_gracious : 1;
/*!
* \brief Whether the min_xy_distance can be used to get avoidance or similar. Will only be true if support_xy_overrides_z=Z overrides X/Y.
*/
bool use_min_xy_dist : 1;
/*!
* \brief True if this Element or any parent provides support to a support roof.
*/
bool supports_roof : 1;
/*!
* \brief An influence area is considered safe when it can use the holefree avoidance <=> It will not have to encounter holes on its way downward.
*/
bool can_use_safe_radius : 1;
/*!
* \brief Skip the ovalisation to parent and children when generating the final circles.
*/
bool skip_ovalisation : 1;
#ifdef TREE_SUPPORTS_TRACK_LOST
// Likely a lost branch, debugging information.
bool lost : 1;
bool verylost : 1;
#endif // TREE_SUPPORTS_TRACK_LOST
// Not valid anymore, to be deleted.
bool deleted : 1;
// General purpose flag marking a visited element.
bool marked : 1;
};
struct SupportElementState : public SupportElementStateBits
{
int type = 0;
coordf_t radius = 0;
float print_z = 0;
/*!
* \brief The layer this support elements wants reach
*/
LayerIndex target_height;
/*!
* \brief The position this support elements wants to support on layer=target_height
*/
Point target_position;
/*!
* \brief The next position this support elements wants to reach. NOTE: This is mainly a suggestion regarding direction inside the influence area.
*/
Point next_position;
/*!
* \brief The next height this support elements wants to reach
*/
LayerIndex layer_idx;
/*!
* \brief The Effective distance to top of this element regarding radius increases and collision calculations.
*/
uint32_t effective_radius_height;
/*!
* \brief The amount of layers this element is below the topmost layer of this branch.
*/
uint32_t distance_to_top;
/*!
* \brief The resulting center point around which a circle will be drawn later.
* Will be set by setPointsOnAreas
*/
Point result_on_layer{ std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max() };
bool result_on_layer_is_set() const { return this->result_on_layer != Point{ std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max() }; }
void result_on_layer_reset() { this->result_on_layer = Point{ std::numeric_limits<coord_t>::max(), std::numeric_limits<coord_t>::max() }; }
/*!
* \brief The amount of extra radius we got from merging branches that could have reached the buildplate, but merged with ones that can not.
*/
coord_t increased_to_model_radius; // how much to model we increased only relevant for merging
/*!
* \brief Counter about the times the elephant foot was increased. Can be fractions for merge reasons.
*/
double elephant_foot_increases;
/*!
* \brief The element trys not to move until this dtt is reached, is set to 0 if the element had to move.
*/
uint32_t dont_move_until;
/*!
* \brief Settings used to increase the influence area to its current state.
*/
AreaIncreaseSettings last_area_increase;
/*!
* \brief Amount of roof layers that were not yet added, because the branch needed to move.
*/
uint32_t missing_roof_layers;
// called by increase_single_area() and increaseAreas()
[[nodiscard]] static SupportElementState propagate_down(const SupportElementState& src)
{
SupportElementState dst{ src };
++dst.distance_to_top;
--dst.layer_idx;
// set to invalid as we are a new node on a new layer
dst.result_on_layer_reset();
dst.skip_ovalisation = false;
return dst;
}
};
/*!
* \brief Get the Distance to top regarding the real radius this part will have. This is different from distance_to_top, which is can be used to calculate the top most layer of the branch.
* \param elem[in] The SupportElement one wants to know the effectiveDTT
* \return The Effective DTT.
*/
[[nodiscard]] inline size_t getEffectiveDTT(const TreeSupportSettings &settings, const SupportElementState &elem)
{
return elem.effective_radius_height < settings.increase_radius_until_layer ?
(elem.distance_to_top < settings.increase_radius_until_layer ? elem.distance_to_top : settings.increase_radius_until_layer) :
elem.effective_radius_height;
}
/*!
* \brief Get the Radius, that this element will have.
* \param elem[in] The Element.
* \return The radius the element has.
*/
[[nodiscard]] inline coord_t support_element_radius(const TreeSupportSettings &settings, const SupportElementState &elem)
{
return settings.getRadius(getEffectiveDTT(settings, elem), elem.elephant_foot_increases);
}
/*!
* \brief Get the collision Radius of this Element. This can be smaller then the actual radius, as the drawAreas will cut off areas that may collide with the model.
* \param elem[in] The Element.
* \return The collision radius the element has.
*/
[[nodiscard]] inline coord_t support_element_collision_radius(const TreeSupportSettings &settings, const SupportElementState &elem)
{
return settings.getRadius(elem.effective_radius_height, elem.elephant_foot_increases);
}
struct SupportElement
{
using ParentIndices =
#ifdef NDEBUG
// To reduce memory allocation in release mode.
boost::container::small_vector<int32_t, 4>;
#else // NDEBUG
// To ease debugging.
std::vector<int32_t>;
#endif // NDEBUG
// SupportElement(const SupportElementState &state) : SupportElementState(state) {}
SupportElement(const SupportElementState &state, Polygons &&influence_area) : state(state), influence_area(std::move(influence_area)) {}
SupportElement(const SupportElementState &state, ParentIndices &&parents, Polygons &&influence_area) :
state(state), parents(std::move(parents)), influence_area(std::move(influence_area)) {}
SupportElementState state;
/*!
* \brief All elements in the layer above the current one that are supported by this element
*/
ParentIndices parents;
/*!
* \brief The resulting influence area.
* Will only be set in the results of createLayerPathing, and will be nullptr inside!
*/
Polygons influence_area;
};
void tree_supports_show_error(std::string_view message, bool critical);
using SupportElements = std::deque<SupportElement>;
[[nodiscard]] inline coord_t support_element_radius(const TreeSupportSettings &settings, const SupportElement &elem)
{
return support_element_radius(settings, elem.state);
}
[[nodiscard]] inline coord_t support_element_collision_radius(const TreeSupportSettings &settings, const SupportElement &elem)
{
return support_element_collision_radius(settings, elem.state);
}
void create_layer_pathing(const TreeModelVolumes& volumes, const TreeSupportSettings& config, std::vector<SupportElements>& move_bounds, std::function<void()> throw_on_cancel);
void create_nodes_from_area(const TreeModelVolumes& volumes, const TreeSupportSettings& config, std::vector<SupportElements>& move_bounds, std::function<void()> throw_on_cancel);
void organic_smooth_branches_avoid_collisions(const PrintObject& print_object, const TreeModelVolumes& volumes, const TreeSupportSettings& config, const std::vector<std::pair<SupportElement*, int>>& elements_with_link_down, const std::vector<size_t>& linear_data_layers, std::function<void()> throw_on_cancel);
indexed_triangle_set draw_branches(PrintObject& print_object, const TreeModelVolumes& volumes, const TreeSupportSettings& config, std::vector<SupportElements>& move_bounds, std::function<void()> throw_on_cancel);
void slice_branches(PrintObject& print_object, const TreeModelVolumes& volumes, const TreeSupportSettings& config, const std::vector<Polygons>& overhangs, std::vector<SupportElements>& move_bounds, const indexed_triangle_set& cummulative_mesh, SupportGeneratorLayersPtr& bottom_contacts, SupportGeneratorLayersPtr& top_contacts, SupportGeneratorLayersPtr& intermediate_layers, SupportGeneratorLayerStorage& layer_storage, std::function<void()> throw_on_cancel);
void generate_initial_areas(const PrintObject& print_object, const TreeModelVolumes& volumes, const TreeSupportSettings& config, const std::vector<Polygons>& overhangs, std::vector<SupportElements>& move_bounds, InterfacePlacer& interface_placer, std::function<void()> throw_on_cancel);
// Organic specific: Smooth branches and produce one cummulative mesh to be sliced.
void organic_draw_branches(
PrintObject& print_object,
TreeModelVolumes& volumes,
const TreeSupportSettings& config,
std::vector<SupportElements>& move_bounds,
// I/O:
SupportGeneratorLayersPtr& bottom_contacts,
SupportGeneratorLayersPtr& top_contacts,
InterfacePlacer& interface_placer,
// Output:
SupportGeneratorLayersPtr& intermediate_layers,
SupportGeneratorLayerStorage& layer_storage,
std::function<void()> throw_on_cancel);
} // namespace TreeSupport3D
void generate_tree_support_3D(PrintObject &print_object, TreeSupport* tree_support, std::function<void()> throw_on_cancel = []{});
} // namespace Slic3r
#endif /* slic3r_TreeSupport_hpp */

View file

@ -23,6 +23,7 @@
#include "Point.hpp"
#include "Execution/ExecutionTBB.hpp"
#include "Execution/ExecutionSeq.hpp"
#include "CutUtils.hpp"
#include "Utils.hpp"
#include "Format/STL.hpp"
#include <libqhullcpp/Qhull.h>

View file

@ -26,7 +26,7 @@ namespace Slic3r {
class TriangleMesh;
class TriangleMeshSlicer;
struct Groove;
struct RepairedMeshErrors {
// How many edges were united by merging their end points with some other end points in epsilon neighborhood?
int edges_fixed = 0;
@ -353,6 +353,7 @@ indexed_triangle_set its_make_frustum_dowel(double r, double h, int sectorCou
indexed_triangle_set its_make_pyramid(float base, float height);
indexed_triangle_set its_make_sphere(double radius, double fa);
indexed_triangle_set its_make_snap(double r, double h, float space_proportion = 0.25f, float bulge_proportion = 0.125f);
indexed_triangle_set its_make_groove_plane(const Groove &cur_groove, float rotate_radius, std::vector<Vec3d> &cur_groove_vertices);
indexed_triangle_set its_convex_hull(const std::vector<Vec3f> &pts);
inline indexed_triangle_set its_convex_hull(const indexed_triangle_set &its) { return its_convex_hull(its.vertices); }

View file

@ -51,9 +51,12 @@ bool is_equal(float lh, float rh)
return abs(lh - rh) <= epson;
}
bool is_less(float lh, float rh)
{
return lh + epson < rh;
bool is_equal_for_sort(float lh, float rh) {
return abs(lh - rh) <= 1e-8;
}
bool is_equal(const Vec3f &lh, const Vec3f &rh) {
return is_equal(lh[0], rh[0]) && is_equal(lh[1], rh[1]) && is_equal(lh[2], rh[2]);
}
class IntersectionReference
@ -174,7 +177,7 @@ static FacetSliceType slice_facet(
// (external on the right of the line)
for (int j = 0; j < 3; ++ j) { // loop through facet edges
int edge_id;
const stl_vertex *a, *b;
const stl_vertex *a, *b, *c;
int a_id, b_id;
{
int k = (idx_vertex_lowest + j) % 3;
@ -184,6 +187,7 @@ static FacetSliceType slice_facet(
a = vertices + k;
b_id = indices[l];
b = vertices + l;
c = vertices + (k + 2) % 3;
}
// Is edge or face aligned with the cutting plane?
@ -319,6 +323,159 @@ static FacetSliceType slice_facet(
return FacetSliceType::NoSlice;
}
// Return true, if the facet has been sliced and line_out has been filled.
static FacetSliceType slice_facet_for_cut_mesh(
// Z height of the slice in XY plane. Scaled or unscaled (same as vertices[].z()).
float slice_z,
// 3 vertices of the triangle, XY scaled. Z scaled or unscaled (same as slice_z).
const stl_vertex * vertices,
const stl_triangle_vertex_indices &indices,
const Vec3i & edge_ids,
const int idx_vertex_lowest,
const bool horizontal,
IntersectionLine & line_out)
{
IntersectionPoint points[3];
size_t num_points = 0;
auto point_on_layer = size_t(-1);
// Reorder vertices so that the first one is the one with lowest Z.
// This is needed to get all intersection lines in a consistent order
// (external on the right of the line)
for (int j = 0; j < 3; ++j) { // loop through facet edges
int edge_id;
const stl_vertex *a, *b, *c;
int a_id, b_id;
{
int k = (idx_vertex_lowest + j) % 3;
int l = (k + 1) % 3;
edge_id = edge_ids(k);
a_id = indices[k];
a = vertices + k;
b_id = indices[l];
b = vertices + l;
c = vertices + (k + 2) % 3;
}
// Is edge or face aligned with the cutting plane?
if (is_equal(a->z(), slice_z) && is_equal(b->z(), slice_z)) {
// Edge is horizontal and belongs to the current layer.
// The following rotation of the three vertices may not be efficient, but this branch happens rarely.
const stl_vertex &v0 = vertices[0];
const stl_vertex &v1 = vertices[1];
const stl_vertex &v2 = vertices[2];
// We may ignore this edge for slicing purposes, but we may still use it for object cutting.
FacetSliceType result = FacetSliceType::Slicing;
if (horizontal) {
// All three vertices are aligned with slice_z.
line_out.edge_type = IntersectionLine::FacetEdgeType::Horizontal;
result = FacetSliceType::Cutting;
double normal = (v1.x() - v0.x()) * (v2.y() - v1.y()) - (v1.y() - v0.y()) * (v2.x() - v1.x());
if (normal < 0) {
// If normal points downwards this is a bottom horizontal facet so we reverse its point order.
std::swap(a, b);
std::swap(a_id, b_id);
}
} else {
// Two vertices are aligned with the cutting plane, the third vertex is below or above the cutting plane.
// Is the third vertex below the cutting plane?
bool third_below = c->z() < slice_z;
// Two vertices on the cutting plane, the third vertex is below the plane. Consider the edge to be part of the slice
// only if it is the upper edge.
// (the bottom most edge resp. vertex of a triangle is not owned by the triangle, but the top most edge resp. vertex is part of the triangle
// in respect to the cutting plane).
result = third_below ? FacetSliceType::Slicing : FacetSliceType::Cutting;
if (third_below) {
line_out.edge_type = IntersectionLine::FacetEdgeType::Top;
std::swap(a, b);
std::swap(a_id, b_id);
} else
line_out.edge_type = IntersectionLine::FacetEdgeType::Bottom;
}
line_out.a.x() = a->x();
line_out.a.y() = a->y();
line_out.b.x() = b->x();
line_out.b.y() = b->y();
line_out.a_id = a_id;
line_out.b_id = b_id;
assert(line_out.a != line_out.b);
return result;
}
if (is_equal(a->z(), slice_z)) {
// Only point a alings with the cutting plane.
if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != a_id) {
point_on_layer = num_points;
IntersectionPoint &point = points[num_points++];
point.x() = a->x();
point.y() = a->y();
point.point_id = a_id;
}
} else if (is_equal(b->z(), slice_z)) {
// Only point b alings with the cutting plane.
if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != b_id) {
point_on_layer = num_points;
IntersectionPoint &point = points[num_points++];
point.x() = b->x();
point.y() = b->y();
point.point_id = b_id;
}
} else if ((a->z() < slice_z && b->z() > slice_z) || (b->z() < slice_z && a->z() > slice_z)) {
// A general case. The face edge intersects the cutting plane. Calculate the intersection point.
assert(a_id != b_id);
// Sort the edge to give a consistent answer.
if (a_id > b_id) {
std::swap(a_id, b_id);
std::swap(a, b);
}
IntersectionPoint &point = points[num_points];
double t = (double(slice_z) - double(b->z())) / (double(a->z()) - double(b->z()));
if (t <= 0.) {
if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != a_id) {
point.x() = a->x();
point.y() = a->y();
point_on_layer = num_points++;
point.point_id = a_id;
}
} else if (t >= 1.) {
if (point_on_layer == size_t(-1) || points[point_on_layer].point_id != b_id) {
point.x() = b->x();
point.y() = b->y();
point_on_layer = num_points++;
point.point_id = b_id;
}
} else {
point.x() = coord_t(floor(double(b->x()) + (double(a->x()) - double(b->x())) * t + 0.5));
point.y() = coord_t(floor(double(b->y()) + (double(a->y()) - double(b->y())) * t + 0.5));
point.edge_id = edge_id;
++num_points;
}
}
}
// Facets must intersect each plane 0 or 2 times, or it may touch the plane at a single vertex only.
assert(num_points < 3);
if (num_points == 2) {
line_out.edge_type = IntersectionLine::FacetEdgeType::General;
line_out.a = static_cast<const Point &>(points[1]);
line_out.b = static_cast<const Point &>(points[0]);
line_out.a_id = points[1].point_id;
line_out.b_id = points[0].point_id;
line_out.edge_a_id = points[1].edge_id;
line_out.edge_b_id = points[0].edge_id;
// Not a zero lenght edge.
// FIXME slice_facet() may create zero length edges due to rounding of doubles into coord_t.
// assert(line_out.a != line_out.b);
// The plane cuts at least one edge in a general position.
assert(line_out.a_id == -1 || line_out.b_id == -1);
assert(line_out.edge_a_id != -1 || line_out.edge_b_id != -1);
// General slicing position, use the segment for both slicing and object cutting.
return FacetSliceType::Slicing;
}
return FacetSliceType::NoSlice;
}
template<typename TransformVertex>
void slice_facet_at_zs(
// Scaled or unscaled vertices. transform_vertex_fn may scale zs.
@ -336,12 +493,12 @@ void slice_facet_at_zs(
// find facet extents
const float min_z = fminf(vertices[0].z(), fminf(vertices[1].z(), vertices[2].z()));
const float max_z = fmaxf(vertices[0].z(), fmaxf(vertices[1].z(), vertices[2].z()));
// find layer extents
auto min_layer = std::lower_bound(zs.begin(), zs.end(), min_z); // first layer whose slice_z is >= min_z
auto max_layer = std::upper_bound(min_layer, zs.end(), max_z); // first layer whose slice_z is > max_z
int idx_vertex_lowest = (vertices[1].z() == min_z) ? 1 : ((vertices[2].z() == min_z) ? 2 : 0);
for (auto it = min_layer; it != max_layer; ++ it) {
IntersectionLine il;
// Ignore horizontal triangles. Any valid horizontal triangle must have a vertical triangle connected, otherwise the part has zero volume.
@ -396,7 +553,7 @@ static inline IntersectionLines slice_make_lines(
const float min_z = fminf(vertices[0].z(), fminf(vertices[1].z(), vertices[2].z()));
const float max_z = fmaxf(vertices[0].z(), fmaxf(vertices[1].z(), vertices[2].z()));
assert(min_z <= plane_z && max_z >= plane_z);
int idx_vertex_lowest = (vertices[1].z() == min_z) ? 1 : ((vertices[2].z() == min_z) ? 2 : 0);
int idx_vertex_lowest = (vertices[1].z() == min_z) ? 1 : ((vertices[2].z() == min_z) ? 2 : 0);
IntersectionLine il;
// Ignore horizontal triangles. Any valid horizontal triangle must have a vertical triangle connected, otherwise the part has zero volume.
if (min_z != max_z && slice_facet(plane_z, vertices, indices, face_edge_ids[face_idx], idx_vertex_lowest, false, il) == FacetSliceType::Slicing) {
@ -452,7 +609,7 @@ void slice_facet_with_slabs(
const float min_z = fminf(vertices[0].z(), fminf(vertices[1].z(), vertices[2].z()));
const float max_z = fmaxf(vertices[0].z(), fmaxf(vertices[1].z(), vertices[2].z()));
const bool horizontal = min_z == max_z;
// find layer extents
auto min_layer = std::lower_bound(zs.begin(), zs.end(), min_z); // first layer whose slice_z is >= min_z
auto max_layer = std::upper_bound(min_layer, zs.end(), max_z); // first layer whose slice_z is > max_z
@ -2001,15 +2158,16 @@ void slice_mesh_slabs(
// Remove duplicates of slice_vertices, optionally triangulate the cut.
static void triangulate_slice(
indexed_triangle_set &its,
IntersectionLines &lines,
indexed_triangle_set &its,
IntersectionLines &lines,
std::vector<int> &slice_vertices,
// Vertices of the original (unsliced) mesh. Newly added vertices are those on the slice.
int num_original_vertices,
// Z height of the slice.
float z,
bool triangulate,
bool normals_down)
float z,
bool triangulate,
bool normals_down,
const std::map<int, Vec3f*> &section_vertices_map)
{
sort_remove_duplicates(slice_vertices);
@ -2019,11 +2177,12 @@ static void triangulate_slice(
map_vertex_to_index.reserve(slice_vertices.size());
for (int i : slice_vertices)
map_vertex_to_index.emplace_back(to_2d(its.vertices[i]), i);
std::sort(map_vertex_to_index.begin(), map_vertex_to_index.end(),
[](const std::pair<Vec2f, int> &l, const std::pair<Vec2f, int> &r) {
return is_less(l.first.x(), r.first.x()) ||
(is_equal(l.first.x(), r.first.x()) && (is_less(l.first.y(), r.first.y()) ||
(is_equal(l.first.y(), r.first.y()) && l.second < r.second))); });
std::sort(map_vertex_to_index.begin(), map_vertex_to_index.end(),
[](const std::pair<Vec2f, int> &l, const std::pair<Vec2f, int> &r) {
return l.first.x() < r.first.x() ||
(is_equal_for_sort(l.first.x(), r.first.x()) && (l.first.y()< r.first.y() ||
(is_equal_for_sort(l.first.y(), r.first.y()) && l.second < r.second)));
});
// 2) Discover duplicate points on the slice. Remap duplicate vertices to a vertex with a lowest index.
// Remove denegerate triangles, if they happen to be created by merging duplicate vertices.
@ -2072,14 +2231,40 @@ static void triangulate_slice(
stl_triangle_vertex_indices facet;
for (size_t j = 0; j < 3; ++ j) {
Vec3f v = triangles[i ++].cast<float>();
auto it = lower_bound_by_predicate(map_vertex_to_index.begin(), map_vertex_to_index.end(),
auto it = lower_bound_by_predicate(map_vertex_to_index.begin(), map_vertex_to_index.end(),
[&v](const std::pair<Vec2f, int> &l) {
return is_less(l.first.x(), v.x()) || (is_equal(l.first.x(), v.x()) && is_less(l.first.y(), v.y()));
return l.first.x() < v.x() || (is_equal_for_sort(l.first.x(), v.x()) && l.first.y() < v.y());
});
int idx = -1;
if (it != map_vertex_to_index.end() && is_equal(it->first.x(), v.x()) && is_equal(it->first.y(), v.y()))
idx = it->second;
else {
bool exist = false;
for (auto iter = section_vertices_map.begin(); iter != section_vertices_map.end(); iter++) {
if (is_equal(v, *iter->second)) {
idx = iter->first;
exist = true;
break;
}
}
// go on finding
if (!exist) {
for (; it != map_vertex_to_index.end(); it++) {
if (is_equal(it->first.x(), v.x()) && is_equal(it->first.y(), v.y())) {
idx = it->second;
exist = true;
break;
}
}
}
// go on finding
if (!exist) {
for (; it != map_vertex_to_index.begin(); it--) {
if (is_equal(it->first.x(), v.x()) && is_equal(it->first.y(), v.y())) {
idx = it->second;
exist = true;
break;
}
}
}
if (!exist){
// Try to find the vertex in the list of newly added vertices. Those vertices are not matched on the cut and they shall be rare.
for (size_t k = idx_vertex_new_first; k < its.vertices.size(); ++ k)
if (its.vertices[k] == v) {
@ -2160,16 +2345,22 @@ void cut_mesh(const indexed_triangle_set& mesh, float z, indexed_triangle_set* u
IntersectionLines upper_lines, lower_lines;
std::vector<int> upper_slice_vertices, lower_slice_vertices;
std::vector<Vec3i> facets_edge_ids = its_face_edge_ids(mesh);
std::map<int, Vec3f *> section_vertices_map;
for (int facet_idx = 0; facet_idx < int(mesh.indices.size()); ++ facet_idx) {
const stl_triangle_vertex_indices &facet = mesh.indices[facet_idx];
Vec3f vertices[3] { mesh.vertices[facet(0)], mesh.vertices[facet(1)], mesh.vertices[facet(2)] };
float min_z = std::min(vertices[0].z(), std::min(vertices[1].z(), vertices[2].z()));
float max_z = std::max(vertices[0].z(), std::max(vertices[1].z(), vertices[2].z()));
for (size_t i = 0; i < 3; i++) {
if (is_equal(z, vertices[i].z()) && section_vertices_map[facet(i)] == nullptr) {
section_vertices_map[facet(i)] = new Vec3f(vertices[i].x(), vertices[i].y(), vertices[i].z());
}
}
// intersect facet with cutting plane
IntersectionLine line;
int idx_vertex_lowest = (vertices[1].z() == min_z) ? 1 : ((vertices[2].z() == min_z) ? 2 : 0);
int idx_vertex_lowest = is_equal(vertices[1].z(), min_z) ? 1 : (is_equal(vertices[2].z() , min_z) ? 2 : 0);
FacetSliceType slice_type = FacetSliceType::NoSlice;
if (z > min_z - EPSILON && z < max_z + EPSILON) {
Vec3f vertices_scaled[3];
@ -2180,7 +2371,7 @@ void cut_mesh(const indexed_triangle_set& mesh, float z, indexed_triangle_set* u
dst.y() = scale_(src.y());
dst.z() = src.z();
}
slice_type = slice_facet(z, vertices_scaled, mesh.indices[facet_idx], facets_edge_ids[facet_idx], idx_vertex_lowest, min_z == max_z, line);
slice_type = slice_facet_for_cut_mesh(z, vertices_scaled, mesh.indices[facet_idx], facets_edge_ids[facet_idx], idx_vertex_lowest, is_equal(min_z, max_z), line);
}
if (slice_type != FacetSliceType::NoSlice) {
@ -2198,12 +2389,12 @@ void cut_mesh(const indexed_triangle_set& mesh, float z, indexed_triangle_set* u
upper_lines.emplace_back(line);
}
}
if (min_z > z || (min_z == z && max_z > z)) {
if (min_z > z || (is_equal(min_z , z) && max_z > z)) {
// facet is above the cut plane and does not belong to it
if (upper != nullptr)
upper->indices.emplace_back(facet);
} else if (max_z < z || (max_z == z && min_z < z)) {
} else if (max_z < z || (is_equal(max_z, z) && min_z < z)) {
// facet is below the cut plane and does not belong to it
if (lower != nullptr)
lower->indices.emplace_back(facet);
@ -2215,21 +2406,56 @@ void cut_mesh(const indexed_triangle_set& mesh, float z, indexed_triangle_set* u
assert(line.edge_b_id != -1);
// look for the vertex on whose side of the slicing plane there are no other vertices
int isolated_vertex =
(vertices[0].z() > z) == (vertices[1].z() > z) ? 2 :
(vertices[1].z() > z) == (vertices[2].z() > z) ? 0 : 1;
// get vertices starting from the isolated one
int iv = isolated_vertex;
stl_vertex v0v1, v2v0;
assert(facets_edge_ids[facet_idx](iv) == line.edge_a_id || facets_edge_ids[facet_idx](iv) == line.edge_b_id);
if (facets_edge_ids[facet_idx](iv) == line.edge_a_id) {
// Unscale to doubles first, then to floats to reach the same accuracy as triangulate_expolygons_2d().
v0v1 = to_3d(unscaled<double>(line.a).cast<float>().eval(), z);
v2v0 = to_3d(unscaled<double>(line.b).cast<float>().eval(), z);
int isolated_vertex, isolated_vertex_option = -1;
std::vector<int> list{0, 1, 2};
auto get_third = [&list](int isolated_vertex, int temp) {// not use vertex data
for (size_t i = 0; i < list.size(); i++) {
if (list[i] != isolated_vertex && list[i] != temp) {
return list[i];
}
}
return -1;
};
if (is_equal(vertices[0].z(), z)) {
isolated_vertex = vertices[1].z() > z ? 1 : 2;
isolated_vertex_option = get_third(isolated_vertex, 0);
} else if (is_equal(vertices[1].z(), z)) {
isolated_vertex = vertices[2].z() > z ? 2 : 0;
isolated_vertex_option = get_third(isolated_vertex, 1);
} else if (is_equal(vertices[2].z(), z)) {
isolated_vertex = vertices[0].z() > z ? 0 : 1;
isolated_vertex_option = get_third(isolated_vertex, 2);
} else {
v0v1 = to_3d(unscaled<double>(line.b).cast<float>().eval(), z);
v2v0 = to_3d(unscaled<double>(line.a).cast<float>().eval(), z);
isolated_vertex = (vertices[0].z() > z) == (vertices[1].z() > z) ? 2 : (vertices[1].z() > z) == (vertices[2].z() > z) ? 0 : 1;
}
// get vertices starting from the isolated one
stl_vertex v0v1, v2v0;
auto calc_isolated_vertex = [&v0v1, &v2v0, &line, &facet_idx, &facets_edge_ids, &z](int iv, bool &is_find) {
assert(facets_edge_ids[facet_idx](iv) == line.edge_a_id || facets_edge_ids[facet_idx](iv) == line.edge_b_id);
is_find = true;
if (facets_edge_ids[facet_idx](iv) == line.edge_a_id) {
// Unscale to doubles first, then to floats to reach the same accuracy as triangulate_expolygons_2d().
v0v1 = to_3d(unscaled<double>(line.a).cast<float>().eval(), z);
v2v0 = to_3d(unscaled<double>(line.b).cast<float>().eval(), z);
} else if (facets_edge_ids[facet_idx](iv) == line.edge_b_id) {
v0v1 = to_3d(unscaled<double>(line.b).cast<float>().eval(), z);
v2v0 = to_3d(unscaled<double>(line.a).cast<float>().eval(), z);
} else {
is_find = false;
}
};
bool find_isolated_vertex;
int iv;
calc_isolated_vertex(isolated_vertex, find_isolated_vertex);
if (!find_isolated_vertex && isolated_vertex_option != -1) {
calc_isolated_vertex(isolated_vertex_option, find_isolated_vertex);
if (!find_isolated_vertex) {
BOOST_LOG_TRIVIAL(trace) << "cut_mesh:error:could not find isolated_vertex";
continue;
}
iv = isolated_vertex_option;
} else {
iv = isolated_vertex;
}
const stl_vertex &v0 = vertices[iv];
const int iv0 = facet[iv];
@ -2243,48 +2469,71 @@ void cut_mesh(const indexed_triangle_set& mesh, float z, indexed_triangle_set* u
const int iv2 = facet[iv];
// intersect v0-v1 and v2-v0 with cutting plane and make new vertices
auto new_vertex = [upper, lower, &upper_slice_vertices, &lower_slice_vertices](const Vec3f &a, const int ia, const Vec3f &b, const int ib, const Vec3f &c) {
auto new_vertex = [upper, lower, &upper_slice_vertices, &lower_slice_vertices](const Vec3f &a, const int ia, const Vec3f &b, const int ib, const Vec3f &c,
const int ic, const Vec3f &new_pt, bool &is_new_vertex) {
int iupper, ilower;
if (c == a)
is_new_vertex = false;
if (is_equal(new_pt, a))
iupper = ilower = ia;
else if (c == b)
else if (is_equal(new_pt, b))
iupper = ilower = ib;
else if (is_equal(new_pt, c))
iupper = ilower = ic;
else {
// Insert a new vertex into upper / lower.
is_new_vertex = true;
if (upper) {
iupper = int(upper->vertices.size());
upper->vertices.emplace_back(c);
upper->vertices.emplace_back(new_pt);
upper_slice_vertices.emplace_back(iupper);
}
if (lower) {
ilower = int(lower->vertices.size());
lower->vertices.emplace_back(c);
lower->vertices.emplace_back(new_pt);
lower_slice_vertices.emplace_back(ilower);
}
}
return std::make_pair(iupper, ilower);
};
auto [iv0v1_upper, iv0v1_lower] = new_vertex(v1, iv1, v0, iv0, v0v1);
auto [iv2v0_upper, iv2v0_lower] = new_vertex(v2, iv2, v0, iv0, v2v0);
auto new_face = [](indexed_triangle_set *its, int i, int j, int k) {
if (its != nullptr && i != j && i != k && j != k)
its->indices.emplace_back(i, j, k);
bool is_new_vertex_v0v1;
bool is_new_vertex_v2v0;
auto [iv0v1_upper, iv0v1_lower] = new_vertex(v1, iv1, v0, iv0, v2, iv2, v0v1, is_new_vertex_v0v1);
auto [iv2v0_upper, iv2v0_lower] = new_vertex(v2, iv2, v0, iv0, v1, iv1, v2v0, is_new_vertex_v2v0);
auto new_face = [](indexed_triangle_set *its, int i, int j, int k) {
if (its != nullptr && i != j && i != k && j != k) its->indices.emplace_back(i, j, k);
};
if (v0.z() > z) {
new_face(upper, iv0, iv0v1_upper, iv2v0_upper);
new_face(lower, iv1, iv2, iv0v1_lower);
new_face(lower, iv2, iv2v0_lower, iv0v1_lower);
} else {
new_face(upper, iv1, iv2, iv0v1_upper);
new_face(upper, iv2, iv2v0_upper, iv0v1_upper);
new_face(lower, iv0, iv0v1_lower, iv2v0_lower);
if (is_new_vertex_v0v1 && is_new_vertex_v2v0) {
if (v0.z() > z) {
new_face(upper, iv0, iv0v1_upper, iv2v0_upper);
new_face(lower, iv1, iv2, iv0v1_lower);
new_face(lower, iv2, iv2v0_lower, iv0v1_lower);
} else {
new_face(upper, iv1, iv2, iv0v1_upper);
new_face(upper, iv2, iv2v0_upper, iv0v1_upper);
new_face(lower, iv0, iv0v1_lower, iv2v0_lower);
}
} else if (is_new_vertex_v0v1) {
if (v0.z() > z) {
new_face(upper, iv0, iv0v1_upper, iv2);
new_face(lower, iv1, iv2, iv0v1_lower);
} else {
new_face(lower, iv0, iv0v1_lower, iv2);
new_face(upper, iv1, iv2, iv0v1_upper);
}
} else if (is_new_vertex_v2v0) {
if (v0.z() > z) {
new_face(upper, iv0, iv1, iv2v0_upper);
new_face(lower, iv1, iv2, iv2v0_lower);
} else {
new_face(lower, iv0, iv1, iv2v0_lower);
new_face(upper, iv1, iv2, iv2v0_upper);
}
}
}
}
if (upper != nullptr) {
triangulate_slice(*upper, upper_lines, upper_slice_vertices, int(mesh.vertices.size()), z, triangulate_caps, NORMALS_DOWN);
triangulate_slice(*upper, upper_lines, upper_slice_vertices, int(mesh.vertices.size()), z, triangulate_caps, NORMALS_DOWN, section_vertices_map);
#ifndef NDEBUG
if (triangulate_caps) {
size_t num_open_edges_new = its_num_open_edges(*upper);
@ -2294,7 +2543,7 @@ void cut_mesh(const indexed_triangle_set& mesh, float z, indexed_triangle_set* u
}
if (lower != nullptr) {
triangulate_slice(*lower, lower_lines, lower_slice_vertices, int(mesh.vertices.size()), z, triangulate_caps, NORMALS_UP);
triangulate_slice(*lower, lower_lines, lower_slice_vertices, int(mesh.vertices.size()), z, triangulate_caps, NORMALS_UP, section_vertices_map);
#ifndef NDEBUG
if (triangulate_caps) {
size_t num_open_edges_new = its_num_open_edges(*lower);
@ -2302,6 +2551,7 @@ void cut_mesh(const indexed_triangle_set& mesh, float z, indexed_triangle_set* u
}
#endif // NDEBUG
}
std::map<int, Vec3f*>().swap(section_vertices_map);
}
} // namespace Slic3r

View file

@ -1703,7 +1703,12 @@ void TriangleSelector::deserialize(const std::pair<std::vector<std::pair<int, in
{
if (needs_reset)
reset(); // dump any current state
for (auto [triangle_id, ibit] : data.first) {
if (triangle_id >= int(m_triangles.size())) {
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "array bound:error:triangle_id >= int(m_triangles.size())";
return;
}
}
// Reserve number of triangles as if each triangle was saved with 4 bits.
// With MMU painting this estimate may be somehow low, but better than nothing.
m_triangles.reserve(std::max(m_mesh.its.indices.size(), data.second.size() / 4));

View file

@ -48,6 +48,7 @@
#define CLI_PRINTABLE_SIZE_REDUCED -20
#define CLI_OBJECT_ARRANGE_FAILED -21
#define CLI_OBJECT_ORIENT_FAILED -22
#define CLI_MODIFIED_PARAMS_TO_PRINTER -23
#define CLI_NO_SUITABLE_OBJECTS -50
@ -124,8 +125,11 @@ inline std::string convert_to_full_version(std::string short_version)
}
return result;
}
template<typename DataType>
inline DataType round_divide(DataType dividend, DataType divisor) //!< Return dividend divided by divisor rounded to the nearest integer
{
return (dividend + divisor / 2) / divisor;
}
// Set a path with GUI localization files.
void set_local_dir(const std::string &path);

View file

@ -67,7 +67,7 @@ static constexpr double RESOLUTION = 0.0125;
static constexpr double SPARSE_INFILL_RESOLUTION = 0.04;
#define SCALED_SPARSE_INFILL_RESOLUTION (SPARSE_INFILL_RESOLUTION / SCALING_FACTOR)
static constexpr double SUPPORT_RESOLUTION = 0.05;
static constexpr double SUPPORT_RESOLUTION = 0.1;
#define SCALED_SUPPORT_RESOLUTION (SUPPORT_RESOLUTION / SCALING_FACTOR)
// Maximum perimeter length for the loop to apply the small perimeter speed.
#define SMALL_PERIMETER_LENGTH(LENGTH) (((LENGTH) / SCALING_FACTOR) * 2 * PI)
@ -76,6 +76,7 @@ static constexpr double INSET_OVERLAP_TOLERANCE = 0.4;
//FIXME This is quite a lot.
static constexpr double EXTERNAL_INFILL_MARGIN = 3;
static constexpr double BRIDGE_INFILL_MARGIN = 1;
static constexpr double WIPE_TOWER_MARGIN = 15.;
//FIXME Better to use an inline function with an explicit return type.
//inline coord_t scale_(coordf_t v) { return coord_t(floor(v / SCALING_FACTOR + 0.5f)); }
#define scale_(val) ((val) / SCALING_FACTOR)

View file

@ -208,6 +208,11 @@ const std::string& var_dir()
std::string var(const std::string &file_name)
{
boost::system::error_code ec;
if (boost::filesystem::exists(file_name, ec)) {
return file_name;
}
auto file = (boost::filesystem::path(g_var_dir) / file_name).make_preferred();
return file.string();
}