From 4e507fffb2952598fd5a092ff9d82f6608b994bb Mon Sep 17 00:00:00 2001 From: Kiss Lorand <50251547+kisslorand@users.noreply.github.com> Date: Sun, 10 May 2026 10:12:56 +0300 Subject: [PATCH] Fix support interface semantics, gap handling, behavior; fix bottom interface generation for organic trees; fix non organic tree interfaces (#11812) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fix support interface semantics and gap handling Fix zero-gap interface detection and gap initialization for supports and raft. Ensures correct top/bottom contact semantics and avoids relying on default zero gaps. * Additional fixes and robustness improvements Fix incorrect coupling between top and bottom support interface spacing and density, ensuring bottom interfaces use their own parameters for smoothing and toolpath generation. Restore correct bottom interface generation for organic (tree) supports when a non-zero bottom Z gap is used, and preserve contacts even when base polygons are empty. Improve robustness of organic support slicing by fixing layer index drift and guarding against degenerate polygon boolean operations. * Typo and semantics fix differnt_support_interface_filament -> different_support_interface_filament soluble -> zero_top_z_gap * Fix non organic tree bottom support interface generation Slim tree bottom interface layer numbers were capped by the object's layer number beneath it. Fixed by refactoring the generation algorithm. * Fix non organic tree interlaced support generation Deterministic local interlaced support layers generation for non-organic tree support * Enable support interface multimaterial for non organic tree Enables mixed-material support interface behavior for non organic tree support type. * Fix tree support interface layer counts and contact handling - Correct non‑organic tree top interface layer budgeting so gaps don’t consume a layer (N stays N). - Remove the extra roof interface pass that was duplicating the 2nd layer. - Organic tree: use only the lowest contact footprint and avoid extra bottom‑contact extrusion so interface count matches the user setting. * Bugfixes (a lot) Many, many bugs fixed, majority edge-case but still not playing by the rule. Some of them: - on multilevel base, on very small level variations the support was capped to the topmost level height - non-organic tree had gaps for supports in a multilevel base situiation - independent support layer height had issues with support interfaces (base support beneath bottom interface, top contact layer sometimes missing) - organic tree had issues with small variation multi-level base - organic tree support with zero top Z distance could overlap support-material and interface-material paths when separate materials were used - many, many others (I lost track of them) --- localization/i18n/OrcaSlicer.pot | 12 +- localization/i18n/ca/OrcaSlicer_ca.po | 24 +- localization/i18n/cs/OrcaSlicer_cs.po | 21 +- localization/i18n/de/OrcaSlicer_de.po | 23 +- localization/i18n/en/OrcaSlicer_en.po | 21 +- localization/i18n/es/OrcaSlicer_es.po | 22 +- localization/i18n/fr/OrcaSlicer_fr.po | 24 +- localization/i18n/hu/OrcaSlicer_hu.po | 29 +- localization/i18n/it/OrcaSlicer_it.po | 23 +- localization/i18n/ja/OrcaSlicer_ja.po | 24 +- localization/i18n/ko/OrcaSlicer_ko.po | 24 +- localization/i18n/lt/OrcaSlicer_lt.po | 24 +- localization/i18n/nl/OrcaSlicer_nl.po | 24 +- localization/i18n/pl/OrcaSlicer_pl.po | 24 +- localization/i18n/pt_BR/OrcaSlicer_pt_BR.po | 24 +- localization/i18n/ru/OrcaSlicer_ru.po | 24 +- localization/i18n/sv/OrcaSlicer_sv.po | 23 +- localization/i18n/tr/OrcaSlicer_tr.po | 24 +- localization/i18n/uk/OrcaSlicer_uk.po | 23 +- localization/i18n/vi/OrcaSlicer_vi.po | 25 +- localization/i18n/zh_CN/OrcaSlicer_zh_CN.po | 20 +- localization/i18n/zh_TW/OrcaSlicer_zh_TW.po | 22 +- src/libslic3r/Layer.hpp | 1 + src/libslic3r/PrintConfig.cpp | 10 +- src/libslic3r/Slicing.cpp | 117 +++++-- src/libslic3r/Slicing.hpp | 13 +- src/libslic3r/Support/SupportCommon.cpp | 80 ++++- src/libslic3r/Support/SupportMaterial.cpp | 16 +- src/libslic3r/Support/SupportMaterial.hpp | 2 +- src/libslic3r/Support/SupportParameters.hpp | 92 ++--- src/libslic3r/Support/TreeSupport.cpp | 367 ++++++++++++++++---- src/libslic3r/Support/TreeSupport3D.cpp | 331 +++++++++++------- src/libslic3r/Support/TreeSupportCommon.hpp | 12 +- 33 files changed, 1098 insertions(+), 447 deletions(-) diff --git a/localization/i18n/OrcaSlicer.pot b/localization/i18n/OrcaSlicer.pot index 33d9dbb407..13c581ab18 100644 --- a/localization/i18n/OrcaSlicer.pot +++ b/localization/i18n/OrcaSlicer.pot @@ -14212,7 +14212,10 @@ msgstr "" msgid "Raft contact Z distance" msgstr "" -msgid "Z gap between object and raft. Ignored for soluble interface." +msgid "" +"Z gap between raft and object. " +"If Support Top Z Distance is 0, this value is ignored and " +"the object is printed in direct contact with the raft (no gap)." msgstr "" msgid "Raft expansion" @@ -14983,13 +14986,16 @@ msgstr "" msgid "Top Z distance" msgstr "" -msgid "The Z gap between the top support interface and object." +msgid "Z gap between the support's top and object." msgstr "" msgid "Bottom Z distance" msgstr "" -msgid "The Z gap between the bottom support interface and object." +msgid "" +"Z gap between the object and the support bottom. " +"If Support Top Z Distance is 0 and the bottom has interface layers, this value " +"is ignored and the support is printed in direct contact with the object (no gap)." msgstr "" msgid "Support/raft base" diff --git a/localization/i18n/ca/OrcaSlicer_ca.po b/localization/i18n/ca/OrcaSlicer_ca.po index 768d46bd79..c8a44d1039 100644 --- a/localization/i18n/ca/OrcaSlicer_ca.po +++ b/localization/i18n/ca/OrcaSlicer_ca.po @@ -16647,10 +16647,14 @@ msgstr "Model d'impressora" msgid "Raft contact Z distance" msgstr "Distància Z de contacte de la Vora d'Adherència" -msgid "Z gap between object and raft. Ignored for soluble interface." +msgid "" +"Z gap between raft and object. " +"If Support Top Z Distance is 0, this value is ignored and " +"the object is printed in direct contact with the raft (no gap)." msgstr "" -"Distància Z entre l'objecte i la Vora d'Adherència. S'ignorarà per a " -"interfície soluble" +"Espai Z entre la vora d'Adherència i l'objecte. " +"Si la distància Z superior del suport és 0, aquest valor s'ignora i l'objecte " +"s'imprimeix en contacte directe amb la vora d'Adherència (sense espai)." msgid "Raft expansion" msgstr "Expansió de la Vora d'Adherència" @@ -17631,14 +17635,20 @@ msgstr "Ignora els voladissos petits que possiblement no requereixen suport." msgid "Top Z distance" msgstr "Distància Z superior" -msgid "The Z gap between the top support interface and object." -msgstr "La distància z entre la interfície de suport superior i l'objecte" +msgid "Z gap between the support's top and object." +msgstr "Espai Z entre la part superior del suport i l'objecte." msgid "Bottom Z distance" msgstr "Distància Z inferior" -msgid "The Z gap between the bottom support interface and object." -msgstr "La distància z entre la interfície de suport inferior i l'objecte" +msgid "" +"Z gap between the object and the support bottom. " +"If Support Top Z Distance is 0 and the bottom has interface layers, this value " +"is ignored and the support is printed in direct contact with the object (no gap)." +msgstr "" +"Espai Z entre l'objecte i la part inferior del suport. " +"Si la distància Z superior del suport és 0 i la part inferior té capes d'interfície, aquest " +"valor s'ignora i el suport s'imprimeix en contacte directe amb l'objecte (sense espai)." msgid "Support/raft base" msgstr "Base del Suport/Vora d'Adherència" diff --git a/localization/i18n/cs/OrcaSlicer_cs.po b/localization/i18n/cs/OrcaSlicer_cs.po index 6755d2502f..46366b4f15 100644 --- a/localization/i18n/cs/OrcaSlicer_cs.po +++ b/localization/i18n/cs/OrcaSlicer_cs.po @@ -16181,9 +16181,14 @@ msgstr "Varianta tiskárny" msgid "Raft contact Z distance" msgstr "Z vzdálenost kontaktu raftu" -msgid "Z gap between object and raft. Ignored for soluble interface." +msgid "" +"Z gap between raft and object. " +"If Support Top Z Distance is 0, this value is ignored and " +"the object is printed in direct contact with the raft (no gap)." msgstr "" -"Z mezera mezi objektem a raftem. Ignorováno pro rozpustitelné rozhraní." +"Z mezera mezi raftem a objektem. " +"Pokud je Horní Z vzdálenost podpory 0, tato hodnota se ignoruje " +"a objekt se tiskne v přímém kontaktu s raftem (bez mezery)." msgid "Raft expansion" msgstr "Rozšíření raftu" @@ -17134,14 +17139,20 @@ msgstr "Ignore small overhangs that possibly don't require support." msgid "Top Z distance" msgstr "Horní vzdálenost v ose Z" -msgid "The Z gap between the top support interface and object." +msgid "Z gap between the support's top and object." msgstr "Z mezera mezi horním rozhraním podpory a objektem." msgid "Bottom Z distance" msgstr "Spodní vzdálenost v ose Z" -msgid "The Z gap between the bottom support interface and object." -msgstr "Z mezera mezi spodním rozhraním podpory a objektem." +msgid "" +"Z gap between the object and the support bottom. " +"If Support Top Z Distance is 0 and the bottom has interface layers, this value " +"is ignored and the support is printed in direct contact with the object (no gap)." +msgstr "" +"Z mezera mezi spodním rozhraním podpory a objektem. " +"Pokud je Horní Z vzdálenost podpory 0 a spodek má vrstvy rozhraní, tato hodnota " +"se ignoruje a podpora se tiskne v přímém kontaktu s objektem (bez mezery)." msgid "Support/raft base" msgstr "Podpora/základ raftu" diff --git a/localization/i18n/de/OrcaSlicer_de.po b/localization/i18n/de/OrcaSlicer_de.po index 26a85806e3..e2b74f5eb3 100644 --- a/localization/i18n/de/OrcaSlicer_de.po +++ b/localization/i18n/de/OrcaSlicer_de.po @@ -16767,9 +16767,14 @@ msgstr "Druckervariante" msgid "Raft contact Z distance" msgstr "Z Abstand Objekt Druckbasis " -msgid "Z gap between object and raft. Ignored for soluble interface." +msgid "" +"Z gap between raft and object. " +"If Support Top Z Distance is 0, this value is ignored and " +"the object is printed in direct contact with the raft (no gap)." msgstr "" -"Z-Abstand zwischen Objekt und Druckbasis. Bei löslicher Oberfläche ignoriert" +"Z-Abstand zwischen Druckbasis (Raft) und Objekt. " +"Wenn der obere Z-Abstand der Stützen ist 0, wird dieser Wert ignoriert und das " +"Objekt wird in direktem Kontakt mit der Druckbasis (Raft) gedruckt (kein Abstand)." msgid "Raft expansion" msgstr "Druckbasis Erweiterung" @@ -17744,14 +17749,20 @@ msgstr "" msgid "Top Z distance" msgstr "Oberer Z-Abstand" -msgid "The Z gap between the top support interface and object." -msgstr "Der Z-Abstand zwischen der oberen Stütz-Schnittstelle und dem Objekt" +msgid "Z gap between the support's top and object." +msgstr "Z-Abstand zwischen der Oberseite der Stützen und dem Objekt." msgid "Bottom Z distance" msgstr "Unterer Z-Abstand" -msgid "The Z gap between the bottom support interface and object." -msgstr "Der Z-Abstand zwischen der unteren Stütz-Schnittstelle und dem Objekt" +msgid "" +"Z gap between the object and the support bottom. " +"If Support Top Z Distance is 0 and the bottom has interface layers, this value " +"is ignored and the support is printed in direct contact with the object (no gap)." +msgstr "" +"Z-Abstand zwischen dem Objekt und der Unterseite der Stützen. " +"Wenn der obere Z-Abstand der Stützen ist 0 und die Unterseite Schnittstellenschichten hat, wird dieser " +"Wert ignoriert und die Stützen werden in direktem Kontakt mit dem Objekt gedruckt (kein Abstand)." msgid "Support/raft base" msgstr "Stütz-/Basis-Objekt" diff --git a/localization/i18n/en/OrcaSlicer_en.po b/localization/i18n/en/OrcaSlicer_en.po index 1103146ce0..74c57e579c 100644 --- a/localization/i18n/en/OrcaSlicer_en.po +++ b/localization/i18n/en/OrcaSlicer_en.po @@ -14527,10 +14527,13 @@ msgstr "" msgid "Raft contact Z distance" msgstr "" -msgid "Z gap between object and raft. Ignored for soluble interface." +msgid "" +"Z gap between raft and object. " +"If Support Top Z Distance is 0, this value is ignored and " +"the object is printed in direct contact with the raft (no gap)." msgstr "" -"This is the Z gap between an object and a raft. It is ignored for soluble " -"interfaces." +"Z gap between raft and object. If Support Top Z Distance is 0, this value " +"is ignored and the object is printed in direct contact with the raft (no gap)." msgid "Raft expansion" msgstr "" @@ -15325,14 +15328,20 @@ msgstr "" msgid "Top Z distance" msgstr "" -msgid "The Z gap between the top support interface and object." -msgstr "This determines the Z gap between top support interfaces and objects." +msgid "Z gap between the support's top and object." +msgstr "Z gap between the support's top and object." msgid "Bottom Z distance" msgstr "" -msgid "The Z gap between the bottom support interface and object." +msgid "" +"Z gap between the object and the support bottom. " +"If Support Top Z Distance is 0 and the bottom has interface layers, this value " +"is ignored and the support is printed in direct contact with the object (no gap)." msgstr "" +"Z gap between the object and the support bottom. " +"If Support Top Z Distance is 0 and the bottom has interface layers, this value " +"is ignored and the support is printed in direct contact with the object (no gap)." msgid "Support/raft base" msgstr "" diff --git a/localization/i18n/es/OrcaSlicer_es.po b/localization/i18n/es/OrcaSlicer_es.po index c52a64f72a..4951eadd34 100644 --- a/localization/i18n/es/OrcaSlicer_es.po +++ b/localization/i18n/es/OrcaSlicer_es.po @@ -16713,10 +16713,14 @@ msgstr "Variante de la impresora" msgid "Raft contact Z distance" msgstr "Distancia Z de contacto de la balsa (base de impresión)" -msgid "Z gap between object and raft. Ignored for soluble interface." +msgid "" +"Z gap between raft and object. " +"If Support Top Z Distance is 0, this value is ignored and " +"the object is printed in direct contact with the raft (no gap)." msgstr "" -"Espacio Z entre el objeto y la balsa (base de impresión). Se ignora con una " -"interfaz soluble." +"Espacio Z entre el objeto y la balsa (base de impresión). " +"Si la distancia Z superior del soporte es 0, este valor se ignora y el " +"objeto se imprime en contacto directo con la balsa (sin separación)." msgid "Raft expansion" msgstr "Expansión de la balsa (base de impresión)" @@ -17704,14 +17708,20 @@ msgstr "Ignorar pequeños voladizos que posiblemente no requieran soporte." msgid "Top Z distance" msgstr "Distancia Z superior" -msgid "The Z gap between the top support interface and object." +msgid "Z gap between the support's top and object." msgstr "La distancia z entre la interfaz de soporte superior y el objeto." msgid "Bottom Z distance" msgstr "Distancia Z inferior" -msgid "The Z gap between the bottom support interface and object." -msgstr "La distancia z entre la interfaz de apoyo inferior y el objeto." +msgid "" +"Z gap between the object and the support bottom. " +"If Support Top Z Distance is 0 and the bottom has interface layers, this value " +"is ignored and the support is printed in direct contact with the object (no gap)." +msgstr "" +"La distancia z entre la interfaz de apoyo inferior y el objeto. " +"Si la distancia Z superior del soporte es 0 y la base tiene capas de interfaz, este valor " +"se ignora y el soporte se imprime en contacto directo con el objeto (sin separación)." msgid "Support/raft base" msgstr "Capa base/balsa" diff --git a/localization/i18n/fr/OrcaSlicer_fr.po b/localization/i18n/fr/OrcaSlicer_fr.po index c7e9d3f354..42336db6a9 100644 --- a/localization/i18n/fr/OrcaSlicer_fr.po +++ b/localization/i18n/fr/OrcaSlicer_fr.po @@ -16872,8 +16872,14 @@ msgstr "Variante de l’imprimante" msgid "Raft contact Z distance" msgstr "Distance Z de contact du radeau" -msgid "Z gap between object and raft. Ignored for soluble interface." -msgstr "Écart en Z entre l'objet et le radeau. Ignoré pour l'interface soluble" +msgid "" +"Z gap between raft and object. " +"If Support Top Z Distance is 0, this value is ignored and " +"the object is printed in direct contact with the raft (no gap)." +msgstr "" +"Écart Z entre le radeau et l'objet. " +"Si la distance Z supérieure du support est 0, cette valeur est ignorée " +"et l'objet est imprimé en contact direct avec le radeau (sans écart)." msgid "Raft expansion" msgstr "Agrandissement du radeau" @@ -17870,14 +17876,20 @@ msgstr "" msgid "Top Z distance" msgstr "Distance Z supérieure" -msgid "The Z gap between the top support interface and object." -msgstr "L'écart z entre l'interface de support supérieure et l'objet" +msgid "Z gap between the support's top and object." +msgstr "Écart Z entre le haut du support et l'objet." msgid "Bottom Z distance" msgstr "Distance Z inférieure" -msgid "The Z gap between the bottom support interface and object." -msgstr "L'écart Z entre l'interface du support inférieur et l'objet" +msgid "" +"Z gap between the object and the support bottom. " +"If Support Top Z Distance is 0 and the bottom has interface layers, this value " +"is ignored and the support is printed in direct contact with the object (no gap)." +msgstr "" +"Écart Z entre l'objet et le bas du support. " +"Si la distance Z supérieure du support est 0 et que le bas a des couches d'interface, cette " +"valeur est ignorée et le support est imprimé en contact direct avec l'objet (sans écart)." msgid "Support/raft base" msgstr "Support/base du radeau" diff --git a/localization/i18n/hu/OrcaSlicer_hu.po b/localization/i18n/hu/OrcaSlicer_hu.po index d4660b0ec6..dffa28be51 100644 --- a/localization/i18n/hu/OrcaSlicer_hu.po +++ b/localization/i18n/hu/OrcaSlicer_hu.po @@ -16520,10 +16520,14 @@ msgstr "Nyomtató változat" msgid "Raft contact Z distance" msgstr "Tutaj érintkezési Z-távolság" -msgid "Z gap between object and raft. Ignored for soluble interface." +msgid "" +"Z gap between raft and object. " +"If Support Top Z Distance is 0, this value is ignored and " +"the object is printed in direct contact with the raft (no gap)." msgstr "" -"This is the Z gap between an object and a raft. It is ignored for soluble " -"interfaces." +"Z rés a tutaj és a tárgy között. " +"Ha a támasz felső Z-távolsága 0, ez az érték figyelmen kívül lesz " +"hagyva és a tárgy közvetlenül a tutajon kerül nyomtatásra (rés nélkül)." msgid "Raft expansion" msgstr "Tutaj kibővítése" @@ -17510,19 +17514,24 @@ msgstr "" msgid "Top Z distance" msgstr "Z távolság" -msgid "The Z gap between the top support interface and object." +msgid "Z gap between the support's top and object." msgstr "" -"Meghatározza a Z távolságot a felső támasz érintkező rétege és az objektum " -"között." +"Z rés a támasz teteje és a tárgy között." msgid "Bottom Z distance" msgstr "Alsó Z távolság" -msgid "The Z gap between the bottom support interface and object." -msgstr "A Z távolság az alsó támasz érintkező rétege és az objektum között." +msgid "" +"Z gap between the object and the support bottom. " +"If Support Top Z Distance is 0 and the bottom has interface layers, this value " +"is ignored and the support is printed in direct contact with the object (no gap)." +msgstr "" +"Z rés a tárgy és a támasz alja között. " +"Ha a támasz felső Z-távolsága 0 és az alján vannak interfész rétegek, ez az érték " +"figyelmen kívül lesz hagyva, és a támasz közvetlenül a tárgyhoz lesz nyomtatva (rés nélkül)." msgid "Support/raft base" -msgstr "Támaszték/tutaj alap" +msgstr "Támasz/tutaj alap" msgid "" "Filament to print support base and raft. \"Default\" means no specific " @@ -17558,7 +17567,7 @@ msgstr "" "letiltva." msgid "Support/raft interface" -msgstr "Támaszték/tutaj csatlakozófelület" +msgstr "Támasz/tutaj interfész" msgid "" "Filament to print support interface. \"Default\" means no specific filament " diff --git a/localization/i18n/it/OrcaSlicer_it.po b/localization/i18n/it/OrcaSlicer_it.po index f49ac5711a..ae9e26387a 100644 --- a/localization/i18n/it/OrcaSlicer_it.po +++ b/localization/i18n/it/OrcaSlicer_it.po @@ -16748,10 +16748,14 @@ msgstr "Variante stampante" msgid "Raft contact Z distance" msgstr "Distanza Z di contatto zattera" -msgid "Z gap between object and raft. Ignored for soluble interface." +msgid "" +"Z gap between raft and object. " +"If Support Top Z Distance is 0, this value is ignored and " +"the object is printed in direct contact with the raft (no gap)." msgstr "" -"Indica lo spazio Z tra oggetto e zattera. Viene ignorato per le interfacce " -"di supporto solubili." +"Spazio Z tra zattera e oggetto. " +"Se la distanza Z superiore del supporto è 0, questo valore viene ignorato e " +"l'oggetto viene stampato a contatto diretto con la zattera (senza spazio)." msgid "Raft expansion" msgstr "Espansione della zattera" @@ -17745,16 +17749,21 @@ msgstr "Ignora i piccoli sbalzi che potrebbero non richiedere supporto." msgid "Top Z distance" msgstr "Distanza Z superiore" -msgid "The Z gap between the top support interface and object." +msgid "Z gap between the support's top and object." msgstr "" -"Determina lo spazio Z tra l'interfaccia di supporto superiore e l'oggetto." +"Spazio Z tra la parte superiore del supporto e l'oggetto." msgid "Bottom Z distance" msgstr "Distanza Z inferiore" -msgid "The Z gap between the bottom support interface and object." +msgid "" +"Z gap between the object and the support bottom. " +"If Support Top Z Distance is 0 and the bottom has interface layers, this value " +"is ignored and the support is printed in direct contact with the object (no gap)." msgstr "" -"Determina lo spazio Z tra l'interfaccia di supporto inferiore e l'oggetto." +"Spazio Z tra l'oggetto e la base del supporto. " +"Se la distanza Z superiore del supporto è 0 e la base ha strati di interfaccia, questo valore " +"viene ignorato e il supporto viene stampato a contatto diretto con l'oggetto (senza spazio)." msgid "Support/raft base" msgstr "Base supporto/zattera" diff --git a/localization/i18n/ja/OrcaSlicer_ja.po b/localization/i18n/ja/OrcaSlicer_ja.po index 0b1d6d4f4a..ba6af3b17a 100644 --- a/localization/i18n/ja/OrcaSlicer_ja.po +++ b/localization/i18n/ja/OrcaSlicer_ja.po @@ -15234,10 +15234,14 @@ msgstr "プリンターバリエーション" msgid "Raft contact Z distance" msgstr "ラフト接触面Z間隔" -msgid "Z gap between object and raft. Ignored for soluble interface." +msgid "" +"Z gap between raft and object. " +"If Support Top Z Distance is 0, this value is ignored and " +"the object is printed in direct contact with the raft (no gap)." msgstr "" -"ラフトとオブジェクトのZ方向の間隔。可溶性材料を使用する場合この設定が無効で" -"す。" +"ラフトとオブジェクトの間のZ隙間。" +"サポート上面Z距離が0の場合、この値は無視され、" +"オブジェクトはラフトに直接接触して印刷されます(隙間なし)。" msgid "Raft expansion" msgstr "ラフト拡張" @@ -16046,14 +16050,20 @@ msgstr "" msgid "Top Z distance" msgstr "トップ面とのZ間隔" -msgid "The Z gap between the top support interface and object." -msgstr "サポート接触面とオブジェクトのZ方向の間隔" +msgid "Z gap between the support's top and object." +msgstr "サポート上面とオブジェクトの間のZ隙間。" msgid "Bottom Z distance" msgstr "底面とのZ間隔" -msgid "The Z gap between the bottom support interface and object." -msgstr "サポート底面とオブジェクトのZ方向間隔" +msgid "" +"Z gap between the object and the support bottom. " +"If Support Top Z Distance is 0 and the bottom has interface layers, this value " +"is ignored and the support is printed in direct contact with the object (no gap)." +msgstr "" +"オブジェクトとサポート下面の間のZ隙間。" +"サポート上面Z距離が0で、下面にインターフェース層がある場合、" +"この値は無視され、サポートはオブジェクトに直接接触して印刷されます(隙間なし)。" msgid "Support/raft base" msgstr "" diff --git a/localization/i18n/ko/OrcaSlicer_ko.po b/localization/i18n/ko/OrcaSlicer_ko.po index 85d1925bf6..d8f1a9a476 100644 --- a/localization/i18n/ko/OrcaSlicer_ko.po +++ b/localization/i18n/ko/OrcaSlicer_ko.po @@ -15509,8 +15509,14 @@ msgstr "프린터 변형" msgid "Raft contact Z distance" msgstr "라프트 접점 Z 거리" -msgid "Z gap between object and raft. Ignored for soluble interface." -msgstr "객체와 라프트 사이의 Z 거리. 가용성 재료의 접점은 무시됨" +msgid "" +"Z gap between raft and object. " +"If Support Top Z Distance is 0, this value is ignored and " +"the object is printed in direct contact with the raft (no gap)." +msgstr "" +"래프트와 객체 사이의 Z 간격. " +"서포트 상단 Z 거리가 0이면 이 값은 무시되고 " +"객체는 래프트에 직접 접촉하여 출력됩니다(간격 없음)." msgid "Raft expansion" msgstr "라프트 확장" @@ -16426,14 +16432,20 @@ msgstr "" msgid "Top Z distance" msgstr "상단 Z 거리" -msgid "The Z gap between the top support interface and object." -msgstr "서포트 상단과 객체 접점의 간격" +msgid "Z gap between the support's top and object." +msgstr "서포트 상단과 객체 사이의 Z 간격." msgid "Bottom Z distance" msgstr "하단 Z 거리" -msgid "The Z gap between the bottom support interface and object." -msgstr "서포트 하단과 객체 접점의 간격" +msgid "" +"Z gap between the object and the support bottom. " +"If Support Top Z Distance is 0 and the bottom has interface layers, this value " +"is ignored and the support is printed in direct contact with the object (no gap)." +msgstr "" +"객체와 서포트 하단 사이의 Z 간격. " +"서포트 상단 Z 거리가 0이고 하단에 인터페이스 레이어가 있으" +"면 이 값은 무시되고 서포트가 객체에 직접 접촉하여 출력됩니다(간격 없음)." msgid "Support/raft base" msgstr "서포트/라프트 기본" diff --git a/localization/i18n/lt/OrcaSlicer_lt.po b/localization/i18n/lt/OrcaSlicer_lt.po index 0245eb9b3d..e0ae114829 100644 --- a/localization/i18n/lt/OrcaSlicer_lt.po +++ b/localization/i18n/lt/OrcaSlicer_lt.po @@ -16435,10 +16435,14 @@ msgstr "Spausdintuvo variantas" msgid "Raft contact Z distance" msgstr "Platformos kontakto Z atstumas" -msgid "Z gap between object and raft. Ignored for soluble interface." +msgid "" +"Z gap between raft and object. " +"If Support Top Z Distance is 0, this value is ignored and " +"the object is printed in direct contact with the raft (no gap)." msgstr "" -"Z tarpas tarp objekto ir platformos. Į jį neatsižvelgiama tirpių sąsajų " -"atveju." +"Z tarpas tarp platformos ir objekto. " +"Jei viršutinis atramos Z atstumas yra 0, ši reikšmė ignoruojama ir " +"objektas spausdinamas tiesiogiai kontaktuojant su platforma (be tarpo)." msgid "Raft expansion" msgstr "Platformos išplėtimas" @@ -17408,14 +17412,20 @@ msgstr "Ignore small overhangs that possibly don't require support." msgid "Top Z distance" msgstr "Viršutinis Z atstumas" -msgid "The Z gap between the top support interface and object." -msgstr "Taip nustatomas Z tarpas tarp viršutinių atramų sąsajų ir objektų." +msgid "Z gap between the support's top and object." +msgstr "Z tarpas tarp atramos viršaus ir objekto." msgid "Bottom Z distance" msgstr "Apatinis Z atstumas" -msgid "The Z gap between the bottom support interface and object." -msgstr "Z tarpas tarp apatinės atramos sąsajos ir objekto." +msgid "" +"Z gap between the object and the support bottom. " +"If Support Top Z Distance is 0 and the bottom has interface layers, this value " +"is ignored and the support is printed in direct contact with the object (no gap)." +msgstr "" +"Z tarpas tarp objekto ir atramos apačios. " +"Jei viršutinis atramos Z atstumas yra 0 ir apačioje yra sąsajos sluoksniai, ši reikšmė " +"ignoruojama ir atrama spausdinama tiesiogiai kontaktuojant su objektu (be tarpo)." msgid "Support/raft base" msgstr "Atraminis ir (arba) platformos pagrindas" diff --git a/localization/i18n/nl/OrcaSlicer_nl.po b/localization/i18n/nl/OrcaSlicer_nl.po index 1dec100c62..758d0bf066 100644 --- a/localization/i18n/nl/OrcaSlicer_nl.po +++ b/localization/i18n/nl/OrcaSlicer_nl.po @@ -16429,10 +16429,14 @@ msgstr "Printervariant" msgid "Raft contact Z distance" msgstr "Vlot (raft) contact Z afstand:" -msgid "Z gap between object and raft. Ignored for soluble interface." +msgid "" +"Z gap between raft and object. " +"If Support Top Z Distance is 0, this value is ignored and " +"the object is printed in direct contact with the raft (no gap)." msgstr "" -"Dit is de Z-afstand tussen een object en een raft. Het wordt genegeerd voor " -"oplosbare materialen." +"Z-gap tussen het vlot en het object. " +"Als de bovenste Z-afstand van de ondersteuning 0 is, wordt deze waarde " +"genegeerd en wordt het object direct in contact met het vlot geprint (geen gap)." msgid "Raft expansion" msgstr "Vlot (raft) expansie" @@ -17407,15 +17411,21 @@ msgstr "Ignore small overhangs that possibly don't require support." msgid "Top Z distance" msgstr "Top Z afstand" -msgid "The Z gap between the top support interface and object." +msgid "Z gap between the support's top and object." msgstr "" -"Dit bepaald de Z-afstand tussen de bovenste support interfaces en het object." +"Z-gap tussen de bovenkant van de ondersteuning en het object." msgid "Bottom Z distance" msgstr "Onderste Z-afstand" -msgid "The Z gap between the bottom support interface and object." -msgstr "De z-opening tussen de onderste ondersteuningsinterface en het object" +msgid "" +"Z gap between the object and the support bottom. " +"If Support Top Z Distance is 0 and the bottom has interface layers, this value " +"is ignored and the support is printed in direct contact with the object (no gap)." +msgstr "" +"Z-gap tussen het object en de onderkant van de ondersteuning. " +"Als de bovenste Z-afstand van de ondersteuning 0 is en de onderkant interfacerlagen heeft, wordt " +"deze waarde genegeerd en wordt de ondersteuning direct in contact met het object geprint (geen gap)." msgid "Support/raft base" msgstr "Support/raft base" diff --git a/localization/i18n/pl/OrcaSlicer_pl.po b/localization/i18n/pl/OrcaSlicer_pl.po index fefb91e288..8de15512c2 100644 --- a/localization/i18n/pl/OrcaSlicer_pl.po +++ b/localization/i18n/pl/OrcaSlicer_pl.po @@ -15876,10 +15876,14 @@ msgstr "Wariant drukarki" msgid "Raft contact Z distance" msgstr "Odległość Z kontaktu z tratwą" -msgid "Z gap between object and raft. Ignored for soluble interface." +msgid "" +"Z gap between raft and object. " +"If Support Top Z Distance is 0, this value is ignored and " +"the object is printed in direct contact with the raft (no gap)." msgstr "" -"Dystans między modelem, a raftem, mierzony w poziomie. Ignorowany dla " -"rozpuszczalnych warstw łączących" +"Odstęp Z między tratwą a obiektem. " +"Jeśli górny odstęp Z podpór wynosi 0, ta wartość jest ignorowana i " +"obiekt jest drukowany w bezpośrednim kontakcie z tratwą (bez odstępu)." msgid "Raft expansion" msgstr "Rozszerzenie tratwy" @@ -16846,14 +16850,20 @@ msgstr "" msgid "Top Z distance" msgstr "Odstęp góry w osi Z" -msgid "The Z gap between the top support interface and object." -msgstr "Odstęp osi Z między górną warstwą łączącą podporę z obiektem" +msgid "Z gap between the support's top and object." +msgstr "Odstęp Z między górą podpór a obiektem." msgid "Bottom Z distance" msgstr "Odstęp spodu w osi Z" -msgid "The Z gap between the bottom support interface and object." -msgstr "Odległość w osi Z między dolną warstwą łączącyą a obiektem" +msgid "" +"Z gap between the object and the support bottom. " +"If Support Top Z Distance is 0 and the bottom has interface layers, this value " +"is ignored and the support is printed in direct contact with the object (no gap)." +msgstr "" +"Odstęp Z między obiektem a dołem podpór. " +"Jeśli górny odstęp Z podpór wynosi 0 i dół ma warstwy interfejsu, ta wartość jest " +"ignorowana, a podpory są drukowane w bezpośrednim kontakcie z obiektem (bez odstępu)." msgid "Support/raft base" msgstr "Podstawa podpory/tratwy" diff --git a/localization/i18n/pt_BR/OrcaSlicer_pt_BR.po b/localization/i18n/pt_BR/OrcaSlicer_pt_BR.po index 47c06c471c..221a4e0e77 100644 --- a/localization/i18n/pt_BR/OrcaSlicer_pt_BR.po +++ b/localization/i18n/pt_BR/OrcaSlicer_pt_BR.po @@ -16596,8 +16596,14 @@ msgstr "Variante da impressora" msgid "Raft contact Z distance" msgstr "Distância Z de contato da jangada" -msgid "Z gap between object and raft. Ignored for soluble interface." -msgstr "Vão Z entre o objeto e a jangada. Ignorado para interface solúvel." +msgid "" +"Z gap between raft and object. " +"If Support Top Z Distance is 0, this value is ignored and " +"the object is printed in direct contact with the raft (no gap)." +msgstr "" +"Espaço Z entre a balsa e o objeto. " +"Se a Distância Z Superior do Suporte for 0, este valor é ignorado " +"e o objeto é impresso em contato direto com a balsa (sem espaço)." msgid "Raft expansion" msgstr "Expansão da jangada" @@ -17575,14 +17581,20 @@ msgstr "Ignorar pequenas saliências que possivelmente não requerem suporte." msgid "Top Z distance" msgstr "Distância Z superior" -msgid "The Z gap between the top support interface and object." -msgstr "O vão Z entre a interface superior de suporte e o objeto." +msgid "Z gap between the support's top and object." +msgstr "Espaço Z entre o topo do suporte e o objeto." msgid "Bottom Z distance" msgstr "Distância Z inferior" -msgid "The Z gap between the bottom support interface and object." -msgstr "O vão Z entre a interface inferior de suporte e o objeto." +msgid "" +"Z gap between the object and the support bottom. " +"If Support Top Z Distance is 0 and the bottom has interface layers, this value " +"is ignored and the support is printed in direct contact with the object (no gap)." +msgstr "" +"Espaço Z entre o objeto e a base do suporte. " +"Se a Distância Z Superior do Suporte for 0 e a base tiver camadas de interface, este " +"valor é ignorado e o suporte é impresso em contato direto com o objeto (sem espaço)." msgid "Support/raft base" msgstr "Base de suporte/jangada" diff --git a/localization/i18n/ru/OrcaSlicer_ru.po b/localization/i18n/ru/OrcaSlicer_ru.po index 4998ad9034..43b04bf565 100644 --- a/localization/i18n/ru/OrcaSlicer_ru.po +++ b/localization/i18n/ru/OrcaSlicer_ru.po @@ -16957,10 +16957,14 @@ msgstr "Модификация принтера" msgid "Raft contact Z distance" msgstr "Зазор под моделью" -msgid "Z gap between object and raft. Ignored for soluble interface." +msgid "" +"Z gap between raft and object. " +"If Support Top Z Distance is 0, this value is ignored and " +"the object is printed in direct contact with the raft (no gap)." msgstr "" -"Вертикальное расстояние между подложкой и моделью. Значение игнорируется при " -"выборе растворимого материала." +"Вертикальное расстояние между подложкой и моделью. " +"Если зазор поддержки сверху равен 0, это значение игнорируется, " +"и модель печатается в прямом контакте с подложкой (без зазора)." msgid "Raft expansion" msgstr "Расширение подложки" @@ -18050,16 +18054,20 @@ msgstr "" msgid "Top Z distance" msgstr "Зазор поддержки сверху" -msgid "The Z gap between the top support interface and object." -msgstr "" -"Вертикальное расстояние между связующим слоем поддержки сверху и моделью." +msgid "Z gap between the support's top and object." +msgstr "Вертикальное расстояние между связующим слоем поддержки сверху и моделью." msgid "Bottom Z distance" msgstr "Зазор поддержки снизу" -msgid "The Z gap between the bottom support interface and object." +msgid "" +"Z gap between the object and the support bottom. " +"If Support Top Z Distance is 0 and the bottom has interface layers, this value " +"is ignored and the support is printed in direct contact with the object (no gap)." msgstr "" -"Вертикальное расстояние между связующим слоем поддержки снизу и моделью." +"Вертикальное расстояние между связующим слоем поддержки снизу и моделью. " +"Если зазор поддержки сверху равен 0 и снизу есть интерфейсные слои, это значение " +"игнорируется, и поддержка печатается в прямом контакте с моделью (без зазора)." msgid "Support/raft base" msgstr "Поддержка/подложка" diff --git a/localization/i18n/sv/OrcaSlicer_sv.po b/localization/i18n/sv/OrcaSlicer_sv.po index d3665b412b..e4afd775dc 100644 --- a/localization/i18n/sv/OrcaSlicer_sv.po +++ b/localization/i18n/sv/OrcaSlicer_sv.po @@ -16258,10 +16258,14 @@ msgstr "Printer variant" msgid "Raft contact Z distance" msgstr "Raft kontakt Z avstånd" -msgid "Z gap between object and raft. Ignored for soluble interface." +msgid "" +"Z gap between raft and object. " +"If Support Top Z Distance is 0, this value is ignored and " +"the object is printed in direct contact with the raft (no gap)." msgstr "" -"Där finns Z mellanrum mellan ett objekt och en raft. Detta ignoreras för " -"lösliga gränssnitt" +"Z-gapet mellan raften och objektet. " +"Om Topp Z-distans är 0 ignoreras detta värde och objektet " +"skrivs ut i direkt kontakt med raften (utan mellanrum)." msgid "Raft expansion" msgstr "Raft expansion" @@ -17222,15 +17226,20 @@ msgstr "Ignore small overhangs that possibly don't require support." msgid "Top Z distance" msgstr "Topp Z-distans" -msgid "The Z gap between the top support interface and object." +msgid "Z gap between the support's top and object." msgstr "" -"Detta bestämmer Z-avståndet mellan det övre support gränssnittet och objektet" +"Z-gapet mellan supportens topp och objektet." msgid "Bottom Z distance" msgstr "Nedre Z-avstånd" -msgid "The Z gap between the bottom support interface and object." -msgstr "Z-gapet mellan botten support och objektets anläggningsyta" +msgid "" +"Z gap between the object and the support bottom. " +"If Support Top Z Distance is 0 and the bottom has interface layers, this value " +"is ignored and the support is printed in direct contact with the object (no gap)." +msgstr "Z-gapet mellan objektet och supportens botten. " +"Om Topp Z-distans är 0 och botten har gränssnittslager ignoreras detta " +"värde och supporten skrivs ut i direkt kontakt med objektet (utan mellanrum)." msgid "Support/raft base" msgstr "Support/raft bas" diff --git a/localization/i18n/tr/OrcaSlicer_tr.po b/localization/i18n/tr/OrcaSlicer_tr.po index 1739550e07..7213477c1f 100644 --- a/localization/i18n/tr/OrcaSlicer_tr.po +++ b/localization/i18n/tr/OrcaSlicer_tr.po @@ -14490,8 +14490,14 @@ msgstr "Yazıcı çeşidi" msgid "Raft contact Z distance" msgstr "Raft kontak Z mesafesi" -msgid "Z gap between object and raft. Ignored for soluble interface." -msgstr "Nesne ve raft arasındaki Z boşluğu. Çözünür arayüz için göz ardı edildi." +msgid "" +"Z gap between raft and object. " +"If Support Top Z Distance is 0, this value is ignored and " +"the object is printed in direct contact with the raft (no gap)." +msgstr "" +"Sal ile nesne arasındaki Z boşluğu. " +"Destek üst Z mesafesi 0 ise bu değer yok sayılır ve nesne " +"sal ile doğrudan temas halinde basılır (boşluk yok)." msgid "Raft expansion" msgstr "Raft genişletme" @@ -15274,14 +15280,20 @@ msgstr "Muhtemelen destek gerektirmeyen küçük çıkıntıları göz ardı edi msgid "Top Z distance" msgstr "Üst z mesafesi" -msgid "The Z gap between the top support interface and object." -msgstr "Üst destek arayüzü ile nesne arasındaki z boşluğu." +msgid "Z gap between the support's top and object." +msgstr "Desteğin üstü ile nesne arasındaki Z boşluğu." msgid "Bottom Z distance" msgstr "Alt z mesafesi" -msgid "The Z gap between the bottom support interface and object." -msgstr "Alt destek arayüzü ile nesne arasındaki z boşluğu." +msgid "" +"Z gap between the object and the support bottom. " +"If Support Top Z Distance is 0 and the bottom has interface layers, this value " +"is ignored and the support is printed in direct contact with the object (no gap)." +msgstr "" +"Nesne ile destek altı arasındaki Z boşluğu. " +"Destek üst Z mesafesi 0 ise ve altta arayüz katmanları varsa bu değer yok " +"sayılır ve destek nesneyle doğrudan temas halinde basılır (boşluk yok)." msgid "Support/raft base" msgstr "Destek/raft tabanı" diff --git a/localization/i18n/uk/OrcaSlicer_uk.po b/localization/i18n/uk/OrcaSlicer_uk.po index 99466c46dc..23737951fa 100644 --- a/localization/i18n/uk/OrcaSlicer_uk.po +++ b/localization/i18n/uk/OrcaSlicer_uk.po @@ -15868,9 +15868,14 @@ msgstr "Варіант принтера" msgid "Raft contact Z distance" msgstr "Відстань Z контакту підкладки" -msgid "Z gap between object and raft. Ignored for soluble interface." +msgid "" +"Z gap between raft and object. " +"If Support Top Z Distance is 0, this value is ignored and " +"the object is printed in direct contact with the raft (no gap)." msgstr "" -"Зазор Z між об'єктом та підкладкою. Ігнорується для розчинного інтерфейсу" +"Z-зазор між підкладкою та моделлю. " +"Якщо верхній Z-зазор підтримки дорівнює 0, це значення ігнорується, " +"і модель друкується в прямому контакті з підкладкою (без зазору)." msgid "Raft expansion" msgstr "Розширення підкладки" @@ -16823,14 +16828,20 @@ msgstr "" msgid "Top Z distance" msgstr "Верхня відстань Z" -msgid "The Z gap between the top support interface and object." -msgstr "Зазор осі z між верхом підтримки та об'єктом" +msgid "Z gap between the support's top and object." +msgstr "Z-зазор між верхом підтримки та моделлю." msgid "Bottom Z distance" msgstr "Нижня відстань Z" -msgid "The Z gap between the bottom support interface and object." -msgstr "Зазор осі z між низом підтримки та об'єктом" +msgid "" +"Z gap between the object and the support bottom. " +"If Support Top Z Distance is 0 and the bottom has interface layers, this value " +"is ignored and the support is printed in direct contact with the object (no gap)." +msgstr "" +"Z-зазор між моделлю та нижньою частиною підтримки. " +"Якщо верхній Z-зазор підтримки дорівнює 0 і знизу є інтерфейсні шари, це значення " +"ігнорується, і підтримка друкується в прямому контакті з моделлю (без зазору)." msgid "Support/raft base" msgstr "База підтримки/підкладки" diff --git a/localization/i18n/vi/OrcaSlicer_vi.po b/localization/i18n/vi/OrcaSlicer_vi.po index c48d08f9cb..11c089e45c 100644 --- a/localization/i18n/vi/OrcaSlicer_vi.po +++ b/localization/i18n/vi/OrcaSlicer_vi.po @@ -16082,8 +16082,14 @@ msgstr "Biến thể máy in" msgid "Raft contact Z distance" msgstr "Khoảng cách Z tiếp xúc raft" -msgid "Z gap between object and raft. Ignored for soluble interface." -msgstr "Khe Z giữa đối tượng và raft. Bị bỏ qua cho giao diện hòa tan." +msgid "" +"Z gap between raft and object. " +"If Support Top Z Distance is 0, this value is ignored and " +"the object is printed in direct contact with the raft (no gap)." +msgstr "" +"Khoảng cách Z giữa bè và vật thể. " +"Nếu khoảng cách Z trên của hỗ trợ bằng 0, giá trị này sẽ bị bỏ qua " +"và vật thể được in tiếp xúc trực tiếp với bè (không có khe hở)." msgid "Raft expansion" msgstr "Mở rộng raft" @@ -17024,14 +17030,20 @@ msgstr "Ignore small overhangs that possibly don't require support." msgid "Top Z distance" msgstr "Khoảng cách Z trên" -msgid "The Z gap between the top support interface and object." -msgstr "Khe Z giữa giao diện support trên và đối tượng." +msgid "Z gap between the support's top and object." +msgstr "Khoảng cách Z giữa mặt trên của hỗ trợ và vật thể." msgid "Bottom Z distance" msgstr "Khoảng cách Z dưới" -msgid "The Z gap between the bottom support interface and object." -msgstr "Khe Z giữa giao diện support dưới và đối tượng." +msgid "" +"Z gap between the object and the support bottom. " +"If Support Top Z Distance is 0 and the bottom has interface layers, this value " +"is ignored and the support is printed in direct contact with the object (no gap)." +msgstr "" +"Khoảng cách Z giữa vật thể và phần đáy của hỗ trợ. " +"Nếu khoảng cách Z trên của hỗ trợ bằng 0 và phần đáy có các lớp giao diện, giá trị " +"này sẽ bị bỏ qua và hỗ trợ được in tiếp xúc trực tiếp với vật thể (không có khe hở)." msgid "Support/raft base" msgstr "Đế support/raft" @@ -22875,3 +22887,4 @@ msgstr "" #, c-format, boost-format #~ msgid "*Printing %s material with %s may cause nozzle damage" #~ msgstr "*In vật liệu %s với %s có thể gây hư hỏng đầu phun" + diff --git a/localization/i18n/zh_CN/OrcaSlicer_zh_CN.po b/localization/i18n/zh_CN/OrcaSlicer_zh_CN.po index 85e2f49a05..9d662abb3c 100644 --- a/localization/i18n/zh_CN/OrcaSlicer_zh_CN.po +++ b/localization/i18n/zh_CN/OrcaSlicer_zh_CN.po @@ -15131,8 +15131,11 @@ msgstr "打印机变种" msgid "Raft contact Z distance" msgstr "筏层Z间距" -msgid "Z gap between object and raft. Ignored for soluble interface." -msgstr "模型和筏层之间的Z间隙" +msgid "" +"Z gap between raft and object. " +"If Support Top Z Distance is 0, this value is ignored and " +"the object is printed in direct contact with the raft (no gap)." +msgstr "筏层与模型之间的Z间隙。如果支撑顶部Z距离为0,则忽略该值,模型与筏层直接接触打印(无间隙)。" msgid "Raft expansion" msgstr "筏层扩展" @@ -15991,14 +15994,19 @@ msgstr "将几乎不需要支撑的微小悬垂忽略掉。" msgid "Top Z distance" msgstr "顶部Z距离" -msgid "The Z gap between the top support interface and object." -msgstr "支撑顶部和模型之间的z间隙" +msgid "Z gap between the support's top and object." +msgstr "支撑顶部与模型之间的Z间隙。" msgid "Bottom Z distance" msgstr "底部Z距离" -msgid "The Z gap between the bottom support interface and object." -msgstr "支撑生成于模型表面时,支撑面底部和模型之间的z间隙" +msgid "" +"Z gap between the object and the support bottom. " +"If Support Top Z Distance is 0 and the bottom has interface layers, this value " +"is ignored and the support is printed in direct contact with the object (no gap)." +msgstr "" +"模型与支撑底部之间的Z间隙。" +"如果支撑顶部Z距离为0且底部有界面层,则忽略该值,支撑与模型直接接触打印(无间隙)。" msgid "Support/raft base" msgstr "支撑/筏层主体" diff --git a/localization/i18n/zh_TW/OrcaSlicer_zh_TW.po b/localization/i18n/zh_TW/OrcaSlicer_zh_TW.po index 12fc980c8f..98997731cb 100644 --- a/localization/i18n/zh_TW/OrcaSlicer_zh_TW.po +++ b/localization/i18n/zh_TW/OrcaSlicer_zh_TW.po @@ -4152,7 +4152,7 @@ msgstr "" "重設為 0。seam_slope_start_height 必須小於 layer_height。\n" "重設為 0。" -#, no-c-format, no-boost-format +#, fuzzy, c-format, boost-format msgid "" "Lock depth should smaller than skin depth.\n" "Reset to 50% of skin depth." @@ -15263,8 +15263,11 @@ msgstr "列印設備型號" msgid "Raft contact Z distance" msgstr "筏層 Z 間距" -msgid "Z gap between object and raft. Ignored for soluble interface." -msgstr "模型和筏層之間的Z間隙" +msgid "" +"Z gap between raft and object. " +"If Support Top Z Distance is 0, this value is ignored and " +"the object is printed in direct contact with the raft (no gap)." +msgstr "筏層與模型之間的Z間隙。若支撐頂部Z距離為0,則忽略此值,模型與筏層直接接觸列印(無間隙)。" msgid "Raft expansion" msgstr "筏層擴展" @@ -16127,14 +16130,19 @@ msgstr "將幾乎不需要支撐的微小懸空忽略掉。" msgid "Top Z distance" msgstr "頂部 Z 間距" -msgid "The Z gap between the top support interface and object." -msgstr "支撐頂部和模型之間的 Z 間隙" +msgid "Z gap between the support's top and object." +msgstr "支撐頂部與模型之間的Z間隙。" msgid "Bottom Z distance" msgstr "底部 Z 間距" -msgid "The Z gap between the bottom support interface and object." -msgstr "支撐產生於模型表面時,支撐面底部和模型之間的 Z 間隙" +msgid "" +"Z gap between the object and the support bottom. " +"If Support Top Z Distance is 0 and the bottom has interface layers, this value " +"is ignored and the support is printed in direct contact with the object (no gap)." +msgstr "" +"模型與支撐底部之間的Z間隙。" +"若支撐頂部Z距離為0且底部有介面層,則忽略此值,支撐與模型直接接觸列印(無間隙)。" msgid "Support/raft base" msgstr "支撐/筏層主體" diff --git a/src/libslic3r/Layer.hpp b/src/libslic3r/Layer.hpp index f1acad9ba7..cb2e6c7c1a 100644 --- a/src/libslic3r/Layer.hpp +++ b/src/libslic3r/Layer.hpp @@ -322,6 +322,7 @@ protected: ExPolygon *area; int type; int interface_id = 0; + bool interface_as_base = false; coordf_t dist_to_top; // mm dist to top bool need_infill = false; bool need_extra_wall = false; diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index 0115ae54aa..07cfb8863a 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -4854,7 +4854,9 @@ void PrintConfigDef::init_fff_params() def = this->add("raft_contact_distance", coFloat); def->label = L("Raft contact Z distance"); def->category = L("Support"); - def->tooltip = L("Z gap between object and raft. Ignored for soluble interface."); + def->tooltip = L("Z gap between raft and object. " + "If Support Top Z Distance is 0, this value is ignored and " + "the object is printed in direct contact with the raft (no gap)."); def->sidetext = L("mm"); // millimeters, CIS languages need translation def->min = 0; def->mode = comAdvanced; @@ -5839,7 +5841,7 @@ void PrintConfigDef::init_fff_params() def->label = L("Top Z distance"); def->min = 0; def->category = L("Support"); - def->tooltip = L("The Z gap between the top support interface and object."); + def->tooltip = L("Z gap between the support's top and object."); def->sidetext = L("mm"); // millimeters, CIS languages need translation // def->min = 0; #if 0 @@ -5856,7 +5858,9 @@ void PrintConfigDef::init_fff_params() def = this->add("support_bottom_z_distance", coFloat); def->label = L("Bottom Z distance"); def->category = L("Support"); - def->tooltip = L("The Z gap between the bottom support interface and object."); + def->tooltip = L("Z gap between the object and the support bottom. " + "If Support Top Z Distance is 0 and the bottom has interface layers, this value " + "is ignored and the support is printed in direct contact with the object (no gap)."); def->sidetext = L("mm"); // millimeters, CIS languages need translation def->min = 0; def->mode = comAdvanced; diff --git a/src/libslic3r/Slicing.cpp b/src/libslic3r/Slicing.cpp index aa24b1074d..c8c37a5a79 100644 --- a/src/libslic3r/Slicing.cpp +++ b/src/libslic3r/Slicing.cpp @@ -60,14 +60,15 @@ coordf_t Slicing::max_layer_height_from_nozzle(const DynamicPrintConfig &print_c } SlicingParameters SlicingParameters::create_from_config( - const PrintConfig &print_config, - const PrintObjectConfig &object_config, - coordf_t object_height, - const std::vector &object_extruders, - const Vec3d &object_shrinkage_compensation) + const PrintConfig &print_config, + const PrintObjectConfig &object_config, + coordf_t object_height, + const std::vector &object_extruders, + const Vec3d &object_shrinkage_compensation) { coordf_t initial_layer_print_height = (print_config.initial_layer_print_height.value <= 0) ? object_config.layer_height.value : print_config.initial_layer_print_height.value; + // If object_config.support_filament == 0 resp. object_config.support_interface_filament == 0, // print_config.nozzle_diameter.get_at(size_t(-1)) returns the 0th nozzle diameter, // which is consistent with the requirement that if support_filament == 0 resp. support_interface_filament == 0, @@ -75,19 +76,47 @@ SlicingParameters SlicingParameters::create_from_config( // In that case all the nozzles have to be of the same diameter. coordf_t support_material_extruder_dmr = print_config.nozzle_diameter.get_at(object_config.support_filament.value - 1); coordf_t support_material_interface_extruder_dmr = print_config.nozzle_diameter.get_at(object_config.support_interface_filament.value - 1); - bool soluble_interface = object_config.support_top_z_distance.value == 0.; + + // ORCA: store Z distance + const coordf_t support_top_z_gap = object_config.support_top_z_distance.value; + const coordf_t support_bottom_z_gap = object_config.support_bottom_z_distance.value; + const coordf_t raft_z_gap = object_config.raft_contact_distance.value; + + + /* -------------------------------------------------- */ + /* ORCA: Zero-gap interface detection (asymmetric) */ + /* -------------------------------------------------- */ + + const bool zero_topZ_contact = + support_top_z_gap == 0.0; + + const bool zero_gap_interface_top = + object_config.support_interface_top_layers.value > 0 && // Has some top interface layers + zero_topZ_contact; + + const bool zero_gap_interface_bottom = + (object_config.support_interface_bottom_layers.value < 0 // Negative value means "use same as top" + ? object_config.support_interface_top_layers.value + : object_config.support_interface_bottom_layers.value) > 0 && // Has some bottom interface layers + (support_bottom_z_gap == 0.0 || zero_topZ_contact); + + const bool zero_gap_interface_raft = + raft_z_gap == 0.0 || zero_topZ_contact; SlicingParameters params; - params.layer_height = object_config.layer_height.value; - params.first_print_layer_height = initial_layer_print_height; - params.first_object_layer_height = initial_layer_print_height; - params.object_print_z_min = 0.; + + params.layer_height = object_config.layer_height.value; + params.first_print_layer_height = initial_layer_print_height; + params.first_object_layer_height = initial_layer_print_height; + params.object_print_z_min = 0.0; // Orca: XYZ filament compensation - params.object_print_z_max = object_height * object_shrinkage_compensation.z(); + params.object_print_z_max = object_height * object_shrinkage_compensation.z(); params.object_print_z_uncompensated_max = object_height; - params.object_shrinkage_compensation_z = object_shrinkage_compensation.z(); - params.base_raft_layers = object_config.raft_layers.value; - params.soluble_interface = soluble_interface; + params.object_shrinkage_compensation_z = object_shrinkage_compensation.z(); + params.base_raft_layers = object_config.raft_layers.value; + params.zero_gap_interface_top = zero_gap_interface_top; + params.zero_gap_interface_bottom = zero_gap_interface_bottom; + params.zero_gap_interface_raft = zero_gap_interface_raft; // Miniumum/maximum of the minimum layer height over all extruders. params.min_layer_height = MIN_LAYER_HEIGHT; @@ -102,6 +131,7 @@ SlicingParameters SlicingParameters::create_from_config( max_layer_height_from_nozzle(print_config, object_config.support_interface_filament)); params.max_suport_layer_height = params.max_layer_height; } + if (object_extruders.empty()) { params.min_layer_height = std::max(params.min_layer_height, min_layer_height_from_nozzle(print_config, 0)); params.max_layer_height = std::min(params.max_layer_height, max_layer_height_from_nozzle(print_config, 0)); @@ -111,24 +141,58 @@ SlicingParameters SlicingParameters::create_from_config( params.max_layer_height = std::min(params.max_layer_height, max_layer_height_from_nozzle(print_config, extruder_id)); } } + params.min_layer_height = std::min(params.min_layer_height, params.layer_height); params.max_layer_height = std::max(params.max_layer_height, params.layer_height); - if (! soluble_interface) { - params.gap_raft_object = object_config.raft_contact_distance.value; - //BBS - params.gap_object_support = object_config.support_bottom_z_distance.value; - params.gap_support_object = object_config.support_top_z_distance.value; + /* -------------------------------------------------- */ + /* ORCA: Gap assignment */ + /* -------------------------------------------------- */ + // ORCA: Raft contact (raft -> object) + if (zero_gap_interface_raft) { + params.gap_raft_object = 0.0; + } else { + params.gap_raft_object = raft_z_gap; if (!print_config.independent_support_layer_height) { - params.gap_raft_object = std::round(params.gap_raft_object / object_config.layer_height + EPSILON) * object_config.layer_height; - params.gap_object_support = std::round(params.gap_object_support / object_config.layer_height + EPSILON) * object_config.layer_height; - params.gap_support_object = std::round(params.gap_support_object / object_config.layer_height + EPSILON) * object_config.layer_height; + params.gap_raft_object = + std::round(params.gap_raft_object / object_config.layer_height + EPSILON) + * object_config.layer_height; } } + // ORCA: BOTTOM contact (object -> support) + if (zero_gap_interface_bottom) { + params.gap_object_support = 0.0; + } else { + params.gap_object_support = support_bottom_z_gap; + + if (!print_config.independent_support_layer_height) { + params.gap_object_support = + std::round(params.gap_object_support / object_config.layer_height + EPSILON) + * object_config.layer_height; + } + } + + // ORCA: TOP contact (support -> object) + if (zero_gap_interface_top) { + params.gap_support_object = 0.0; + } else { + params.gap_support_object = support_top_z_gap; + + if (!print_config.independent_support_layer_height) { + params.gap_support_object = + std::round(params.gap_support_object / object_config.layer_height + EPSILON) + * object_config.layer_height; + } + } + + /* -------------------------------------------------- */ + /* Raft logic */ + /* -------------------------------------------------- */ + if (params.base_raft_layers > 0) { - params.interface_raft_layers = (params.base_raft_layers + 1) / 2; + params.interface_raft_layers = (params.base_raft_layers + 1) / 2; params.base_raft_layers -= params.interface_raft_layers; // Use as large as possible layer height for the intermediate raft layers. params.base_raft_layer_height = std::max(params.layer_height, 0.75 * support_material_extruder_dmr); @@ -141,11 +205,11 @@ SlicingParameters SlicingParameters::create_from_config( if (params.has_raft()) { // Raise first object layer Z by the thickness of the raft itself plus the extra distance required by the support material logic. //FIXME The last raft layer is the contact layer, which shall be printed with a bridging flow for ease of separation. Currently it is not the case. - if (params.raft_layers() == 1) { + if (params.raft_layers() == 1) { // There is only the contact layer. params.contact_raft_layer_height = initial_layer_print_height; params.raft_contact_top_z = initial_layer_print_height; - } else { + } else { assert(params.base_raft_layers > 0); assert(params.interface_raft_layers > 0); // Number of the base raft layers is decreased by the first layer. @@ -153,7 +217,8 @@ SlicingParameters SlicingParameters::create_from_config( // Number of the interface raft layers is decreased by the contact layer. params.raft_interface_top_z = params.raft_base_top_z + coordf_t(params.interface_raft_layers - 1) * params.interface_raft_layer_height; params.raft_contact_top_z = params.raft_interface_top_z + params.contact_raft_layer_height; - } + } + coordf_t print_z = params.raft_contact_top_z + params.gap_raft_object; params.object_print_z_min = print_z; params.object_print_z_max += print_z; diff --git a/src/libslic3r/Slicing.hpp b/src/libslic3r/Slicing.hpp index a7b21a140d..f735ec5518 100644 --- a/src/libslic3r/Slicing.hpp +++ b/src/libslic3r/Slicing.hpp @@ -84,9 +84,10 @@ struct SlicingParameters // If the object is printed over a non-soluble raft, the first layer may be printed with a briding flow. bool first_object_layer_bridging { false }; - // Soluble interface? (PLA soluble in water, HIPS soluble in lemonen) - // otherwise the interface must be broken off. - bool soluble_interface { false }; + // Zero-gap interface flags for top / bottom / raft contact. + bool zero_gap_interface_top { false }; + bool zero_gap_interface_bottom { false }; + bool zero_gap_interface_raft { false }; // Gap when placing object over raft. coordf_t gap_raft_object { 0 }; // Gap when placing support over object. @@ -100,7 +101,7 @@ struct SlicingParameters coordf_t raft_base_top_z { 0 }; coordf_t raft_interface_top_z { 0 }; coordf_t raft_contact_top_z { 0 }; - // In case of a soluble interface, object_print_z_min == raft_contact_top_z, otherwise there is a gap between the raft and the 1st object layer. + // In case of a zero-gap raft interface, object_print_z_min == raft_contact_top_z, otherwise there is a gap between the raft and the 1st object layer. coordf_t object_print_z_min { 0 }; // This value of maximum print Z is scaled by shrinkage compensation in the Z-axis. coordf_t object_print_z_max { 0 }; @@ -133,7 +134,9 @@ inline bool equal_layering(const SlicingParameters &sp1, const SlicingParameters // BBS: following are not required for equal layer height. // Since the z-gap diff may be multiple of layer height. #if 0 - sp1.soluble_interface == sp2.soluble_interface && + sp1.zero_gap_interface_top == sp2.zero_gap_interface_top && + sp1.zero_gap_interface_bottom == sp2.zero_gap_interface_bottom && + sp1.zero_gap_interface_raft == sp2.zero_gap_interface_raft && sp1.gap_raft_object == sp2.gap_raft_object && sp1.gap_object_support == sp2.gap_object_support && sp1.gap_support_object == sp2.gap_support_object && diff --git a/src/libslic3r/Support/SupportCommon.cpp b/src/libslic3r/Support/SupportCommon.cpp index 75e04ad4aa..67809df075 100644 --- a/src/libslic3r/Support/SupportCommon.cpp +++ b/src/libslic3r/Support/SupportCommon.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include "SupportCommon.hpp" @@ -69,22 +70,30 @@ std::pair generate_interfa if (support_params.has_base_interfaces()) base_interface_layers.assign(intermediate_layers.size(), nullptr); const auto smoothing_distance = support_params.support_material_interface_flow.scaled_spacing() * 1.5; - const auto minimum_island_radius = support_params.support_material_interface_flow.scaled_spacing() / support_params.interface_density; + // ORCA: use top/bottom interface densities for smoothing. + const auto minimum_island_radius_top = support_params.support_material_interface_flow.scaled_spacing() / support_params.top_interface_density; + const auto minimum_island_radius_bottom = support_params.support_material_interface_flow.scaled_spacing() / support_params.bottom_interface_density; const auto closing_distance = smoothing_distance; // scaled(config.support_material_closing_radius.value); // Insert a new layer into base_interface_layers, if intersection with base exists. - auto insert_layer = [&layer_storage, smooth_supports, closing_distance, smoothing_distance, minimum_island_radius]( + // ORCA: regularize top and bottom interfaces with separate minimum island radii. + auto insert_layer = [&layer_storage, smooth_supports, closing_distance, smoothing_distance, minimum_island_radius_top, minimum_island_radius_bottom]( SupportGeneratorLayer &intermediate_layer, Polygons &bottom, Polygons &&top, SupportGeneratorLayer *top_interface_layer, const Polygons *subtract, SupporLayerType type) -> SupportGeneratorLayer* { bool has_top_interface = top_interface_layer && ! top_interface_layer->polygons.empty(); assert(! bottom.empty() || ! top.empty() || has_top_interface); - // Merge top into bottom, unite them with a safety offset. - append(bottom, std::move(top)); - // Merge top / bottom interfaces. For snug supports, merge using closing distance and regularize (close concave corners). - bottom = intersection( - smooth_supports ? - smooth_outward(closing(std::move(bottom), closing_distance + minimum_island_radius, closing_distance, SUPPORT_SURFACES_OFFSET_PARAMETERS), smoothing_distance) : - union_safety_offset(std::move(bottom)), - intermediate_layer.polygons); + // ORCA: regularize interfaces using the top/bottom radii. + auto regularize = [&](Polygons polys, coordf_t minimum_island_radius) -> Polygons { + if (polys.empty()) + return polys; + return smooth_supports ? + smooth_outward(closing(std::move(polys), closing_distance + minimum_island_radius, closing_distance, SUPPORT_SURFACES_OFFSET_PARAMETERS), smoothing_distance) : + union_safety_offset(std::move(polys)); + }; + // ORCA: apply independent smoothing to bottom vs top. + Polygons bottom_polys = regularize(std::move(bottom), minimum_island_radius_bottom); + Polygons top_polys = regularize(std::move(top), minimum_island_radius_top); + append(bottom_polys, std::move(top_polys)); + bottom = intersection(std::move(bottom_polys), intermediate_layer.polygons); if (has_top_interface) { // Don't trim the precomputed Organic supports top interface with base layer // as the precomputed top interface likely expands over multiple tree tips. @@ -1365,7 +1374,8 @@ SupportGeneratorLayersPtr generate_support_layers( SupportGeneratorLayer &layer = *layers_sorted[u]; if (! layer.polygons.empty()) { empty = false; - num_interfaces += one_of(layer.layer_type, support_types_interface); + const bool is_base_interface = std::find(base_interface_layers.begin(), base_interface_layers.end(), &layer) != base_interface_layers.end(); + num_interfaces += one_of(layer.layer_type, support_types_interface) || is_base_interface; if (layer.layer_type == SupporLayerType::TopContact) { ++ num_top_contacts; assert(num_top_contacts <= 1); @@ -1562,7 +1572,7 @@ void generate_support_toolpaths( auto filler_raft_contact = filler_raft_contact_ptr ? filler_raft_contact_ptr.get() : filler_interface.get(); // Filler for the base interface (to be used for soluble interface / non soluble base, to produce non soluble interface layer below soluble interface layer). auto filler_base_interface = std::unique_ptr(base_interface_layers.empty() ? nullptr : - Fill::new_from_type(support_params.interface_density > 0.95 || support_params.with_sheath ? ipRectilinear : ipSupportBase)); + Fill::new_from_type(support_params.top_interface_density > 0.95 || support_params.with_sheath ? ipRectilinear : ipSupportBase)); auto filler_support = std::unique_ptr(Fill::new_from_type(support_params.base_fill_pattern)); filler_interface->set_bounding_box(bbox_object); if (filler_first_layer_ptr) @@ -1610,6 +1620,8 @@ void generate_support_toolpaths( // This layer is a raft contact layer. Any contact polygons at this layer are raft contacts. bool raft_layer = slicing_params.interface_raft_layers && top_contact_layer.layer && is_approx(top_contact_layer.layer->print_z, slicing_params.raft_contact_top_z); + // ORCA: Organic tree uses projected contacts to build the interface stack; avoid extra bottom-contact extrusion. + const bool organic_tree = support_params.support_style == SupportMaterialStyle::smsTreeOrganic; if (config.support_interface_top_layers == 0) { // If no top interface layers were requested, we treat the contact layer exactly as a generic base layer. // Don't merge the raft contact layer though. @@ -1638,10 +1650,34 @@ void generate_support_toolpaths( base_layer.merge(std::move(bottom_contact_layer)); else if (base_layer.empty() && ! bottom_contact_layer.empty() && ! bottom_contact_layer.layer->bridging) base_layer = std::move(bottom_contact_layer); - } else if (bottom_contact_layer.could_merge(top_contact_layer) && ! raft_layer) + } else if (bottom_contact_layer.could_merge(top_contact_layer) && ! raft_layer) { top_contact_layer.merge(std::move(bottom_contact_layer)); - else if (bottom_contact_layer.could_merge(interface_layer)) + } else if (bottom_contact_layer.could_merge(interface_layer) && ! organic_tree) { bottom_contact_layer.merge(std::move(interface_layer)); + } + + // Orca: For organic trees the support-material regions are generated from + // expanded wall polygons. With zero top Z gap and separate interface material, + // that expansion can overlap same-layer interface-material regions, so trim + // the support-material regions from those interface footprints here. + if (organic_tree && support_params.zero_gap_interface_top && !support_params.can_merge_support_regions && + (!base_layer.empty() || !base_interface_layer.empty())) { + Polygons interface_polygons; + if (!top_contact_layer.empty()) + polygons_append(interface_polygons, top_contact_layer.polygons_to_extrude()); + if (!interface_layer.empty()) + polygons_append(interface_polygons, interface_layer.polygons_to_extrude()); + if (!interface_polygons.empty()) { + const coord_t trim_margin = std::max( + support_params.support_material_flow.scaled_width(), + support_params.support_material_interface_flow.scaled_width()); + Polygons interface_keepout = offset(interface_polygons, trim_margin); + if (!base_layer.empty()) + base_layer.set_polygons_to_extrude(diff(base_layer.polygons_to_extrude(), interface_keepout)); + if (!base_interface_layer.empty()) + base_interface_layer.set_polygons_to_extrude(diff(base_interface_layer.polygons_to_extrude(), interface_keepout)); + } + } #if 0 if ( ! interface_layer.empty() && ! base_layer.empty()) { @@ -1661,6 +1697,9 @@ void generate_support_toolpaths( if (! layer_ex.empty() && ! layer_ex.polygons_to_extrude().empty()) { bool interface_as_base = interface_layer_type == InterfaceLayerType::InterfaceAsBase; bool raft_contact = interface_layer_type == InterfaceLayerType::RaftContact; + // ORCA: detect bottom interface layers for density selection. + bool bottom_interface = interface_layer_type == InterfaceLayerType::BottomContact || + (interface_layer_type == InterfaceLayerType::Interface && layer_ex.layer->layer_type == SupporLayerType::BottomInterface); //FIXME Bottom interfaces are extruded with the briding flow. Some bridging layers have its height slightly reduced, therefore // the bridging flow does not quite apply. Reduce the flow to area of an ellipse? (A = pi * a * b) auto *filler = raft_contact ? filler_raft_contact : filler_interface.get(); @@ -1676,7 +1715,10 @@ void generate_support_toolpaths( raft_contact ? support_params.raft_interface_angle(support_layer.interface_id()) : support_interface_angle; - double density = raft_contact ? support_params.raft_interface_density : interface_as_base ? support_params.support_density : support_params.interface_density; + // ORCA: pick density based on interface type. + double density = raft_contact ? support_params.raft_interface_density : + interface_as_base ? support_params.support_density : + bottom_interface ? support_params.bottom_interface_density : support_params.top_interface_density; filler->spacing = raft_contact ? support_params.raft_interface_flow.spacing() : interface_as_base ? support_params.support_material_flow.spacing() : support_params.support_material_interface_flow.spacing(); filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / density)); @@ -1694,9 +1736,9 @@ void generate_support_toolpaths( const bool top_interfaces = config.support_interface_top_layers.value != 0; const bool bottom_interfaces = top_interfaces && config.support_interface_bottom_layers != 0; extrude_interface(top_contact_layer, raft_layer ? InterfaceLayerType::RaftContact : top_interfaces ? InterfaceLayerType::TopContact : InterfaceLayerType::InterfaceAsBase); - extrude_interface(bottom_contact_layer, bottom_interfaces ? InterfaceLayerType::BottomContact : InterfaceLayerType::InterfaceAsBase); + if (!organic_tree) + extrude_interface(bottom_contact_layer, bottom_interfaces ? InterfaceLayerType::BottomContact : InterfaceLayerType::InterfaceAsBase); extrude_interface(interface_layer, top_interfaces ? InterfaceLayerType::Interface : InterfaceLayerType::InterfaceAsBase); - // Base interface layers under soluble interfaces if ( ! base_interface_layer.empty() && ! base_interface_layer.polygons_to_extrude().empty()) { Fill *filler = filler_base_interface.get(); @@ -1706,7 +1748,7 @@ void generate_support_toolpaths( Flow interface_flow = support_params.support_material_flow.with_height(float(base_interface_layer.layer->height)); filler->angle = support_interface_angle; filler->spacing = support_params.support_material_interface_flow.spacing(); - filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / support_params.interface_density)); + filler->link_max_length = coord_t(scale_(filler->spacing * link_max_length_factor / support_params.top_interface_density)); fill_expolygons_generate_paths( // Destination base_interface_layer.extrusions, @@ -1714,7 +1756,7 @@ void generate_support_toolpaths( // Regions to fill union_safety_offset_ex(base_interface_layer.polygons_to_extrude()), // Filler and its parameters - filler, float(support_params.interface_density), + filler, float(support_params.top_interface_density), // Extrusion parameters ExtrusionRole::erSupportMaterial, interface_flow); } diff --git a/src/libslic3r/Support/SupportMaterial.cpp b/src/libslic3r/Support/SupportMaterial.cpp index 61484730ea..da26be6b84 100644 --- a/src/libslic3r/Support/SupportMaterial.cpp +++ b/src/libslic3r/Support/SupportMaterial.cpp @@ -1739,7 +1739,7 @@ static inline std::pair new_cont print_z = slicing_params.raft_contact_top_z; bottom_z = slicing_params.raft_interface_top_z; height = slicing_params.contact_raft_layer_height; - } else if (slicing_params.soluble_interface) { + } else if (slicing_params.zero_gap_interface_top) { // Align the contact surface height with a layer immediately below the supported layer. // Interface layer will be synchronized with the object. print_z = layer.bottom_z(); @@ -1862,7 +1862,7 @@ static inline void fill_contact_layer( #endif // SLIC3R_DEBUG )); // 2) infill polygons, expand them by half the extrusion width + a tiny bit of extra. - bool reduce_interfaces = object_config.support_style.value != smsSnug && layer_id > 0 && !slicing_params.soluble_interface; + bool reduce_interfaces = object_config.support_style.value != smsSnug && layer_id > 0 && !slicing_params.zero_gap_interface_top; if (reduce_interfaces) { // Reduce the amount of dense interfaces: Do not generate dense interfaces below overhangs with 60% overhang of the extrusions. Polygons dense_interface_polygons = diff(overhang_polygons, lower_layer_polygons_for_dense_interface()); @@ -2421,12 +2421,12 @@ static inline SupportGeneratorLayer* detect_bottom_contacts( Layer* upper_layer = layer.upper_layer; if (object.print()->config().independent_support_layer_height) { // If the layer is extruded with no bridging flow, support just the normal extrusions. - layer_new.height = slicing_params.soluble_interface ? + layer_new.height = slicing_params.zero_gap_interface_bottom ? // Align the interface layer with the object's layer height. upper_layer->height : // Place a bridge flow interface layer or the normal flow interface layer over the top surface. support_params.support_material_bottom_interface_flow.height(); - layer_new.print_z = slicing_params.soluble_interface ? upper_layer->print_z : + layer_new.print_z = slicing_params.zero_gap_interface_bottom ? upper_layer->print_z : layer.print_z + layer_new.height + slicing_params.gap_object_support; } else { @@ -2436,11 +2436,11 @@ static inline SupportGeneratorLayer* detect_bottom_contacts( } layer_new.bottom_z = layer.print_z; layer_new.idx_object_layer_below = layer_id; - layer_new.bridging = !slicing_params.soluble_interface && object.config().thick_bridges; + layer_new.bridging = !slicing_params.zero_gap_interface_bottom && object.config().thick_bridges; //FIXME how much to inflate the bottom surface, as it is being extruded with a bridging flow? The following line uses a normal flow. layer_new.polygons = expand(touching, float(support_params.support_material_flow.scaled_width()), SUPPORT_SURFACES_OFFSET_PARAMETERS); - if (! slicing_params.soluble_interface) { + if (!slicing_params.zero_gap_interface_bottom) { // Walk the top surfaces, snap the top of the new bottom surface to the closest top of the top surface, // so there will be no support surfaces generated with thickness lower than m_support_layer_height_min. for (size_t top_idx = size_t(std::max(0, contact_idx)); @@ -2909,7 +2909,7 @@ SupportGeneratorLayersPtr PrintObjectSupportMaterial::raft_and_intermediate_supp // Continue printing the other layers up to extr2z. step = dist / coordf_t(n_layers_extra); } - if (! m_slicing_params.soluble_interface && extr2->layer_type == SupporLayerType::TopContact) { + if (!m_slicing_params.zero_gap_interface_top && extr2->layer_type == SupporLayerType::TopContact) { // This is a top interface layer, which does not have a height assigned yet. Do it now. assert(extr2->height == 0.); assert(extr1z > m_slicing_params.first_print_layer_height - EPSILON); @@ -3170,7 +3170,7 @@ void PrintObjectSupportMaterial::trim_support_layers_by_object( polygons_append(polygons_trimming, offset({ expoly }, trimming_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS)); } } - if (! m_slicing_params.soluble_interface && m_object_config->thick_bridges) { + if (!m_slicing_params.zero_gap_interface_top && m_object_config->thick_bridges) { // Collect all bottom surfaces, which will be extruded with a bridging flow. for (; i < object.layers().size(); ++ i) { const Layer &object_layer = *object.layers()[i]; diff --git a/src/libslic3r/Support/SupportMaterial.hpp b/src/libslic3r/Support/SupportMaterial.hpp index e489c2374a..50b8256c4a 100644 --- a/src/libslic3r/Support/SupportMaterial.hpp +++ b/src/libslic3r/Support/SupportMaterial.hpp @@ -28,7 +28,7 @@ public: bool has_support() const { return m_object_config->enable_support.value || m_object_config->enforce_support_layers; } bool build_plate_only() const { return this->has_support() && m_object_config->support_on_build_plate_only.value; } // BBS - bool synchronize_layers() const { return /*m_slicing_params.soluble_interface && */!m_print_config->independent_support_layer_height.value; } + bool synchronize_layers() const { return /*m_slicing_params.zero_gap_interface_top && */!m_print_config->independent_support_layer_height.value; } bool has_contact_loops() const { return m_object_config->support_interface_loop_pattern.value; } // Generate support material for the object. diff --git a/src/libslic3r/Support/SupportParameters.hpp b/src/libslic3r/Support/SupportParameters.hpp index 655ee0d806..c1468eb5f9 100644 --- a/src/libslic3r/Support/SupportParameters.hpp +++ b/src/libslic3r/Support/SupportParameters.hpp @@ -14,14 +14,15 @@ struct SupportParameters { const PrintObjectConfig& object_config = object.config(); const SlicingParameters& slicing_params = object.slicing_parameters(); - this->soluble_interface = slicing_params.soluble_interface; - this->soluble_interface_non_soluble_base = - // Zero z-gap between the overhangs and the support interface. - slicing_params.soluble_interface && - // Interface extruder soluble. - object_config.support_interface_filament.value > 0 && print_config.filament_soluble.get_at(object_config.support_interface_filament.value - 1) && - // Base extruder: Either "print with active extruder" not soluble. - (object_config.support_filament.value == 0 || ! print_config.filament_soluble.get_at(object_config.support_filament.value - 1)); + this->zero_gap_interface_top = slicing_params.zero_gap_interface_top; + this->zero_gap_interface_bottom = slicing_params.zero_gap_interface_bottom; + const bool soluble_interface_non_soluble_base = + // Interface extruder soluble. + object_config.support_interface_filament.value > 0 && print_config.filament_soluble.get_at(object_config.support_interface_filament.value - 1) && + // Base extruder: Either "print with active extruder" not soluble. + (object_config.support_filament.value == 0 || ! print_config.filament_soluble.get_at(object_config.support_filament.value - 1)); + const bool non_soluble_base_top = this->zero_gap_interface_top && soluble_interface_non_soluble_base; + const bool non_soluble_base_bottom = this->zero_gap_interface_bottom && soluble_interface_non_soluble_base; { this->num_top_interface_layers = std::max(0, object_config.support_interface_top_layers.value); @@ -29,19 +30,25 @@ struct SupportParameters { num_top_interface_layers : object_config.support_interface_bottom_layers; this->has_top_contacts = num_top_interface_layers > 0; this->has_bottom_contacts = num_bottom_interface_layers > 0; - if (this->soluble_interface_non_soluble_base) { - // Try to support soluble dense interfaces with non-soluble dense interfaces. - this->num_top_base_interface_layers = size_t(std::min(int(num_top_interface_layers) / 2, 2)); - this->num_bottom_base_interface_layers = size_t(std::min(int(num_bottom_interface_layers) / 2, 2)); - } else { - // BBS: if support interface and support base do not use the same filament, add a base layer to improve their adhesion - // Note: support materials (such as Supp.W) can't be used as support base now, so support interface and base are still using different filaments even if - // support_filament==0 - bool differnt_support_interface_filament = object_config.support_interface_filament != 0 && - object_config.support_interface_filament != object_config.support_filament; - this->num_top_base_interface_layers = differnt_support_interface_filament ? 1 : 0; - this->num_bottom_base_interface_layers = differnt_support_interface_filament ? 1 : 0; - } + // BBS: if support interface and support base do not use the same filament, add a base layer to improve their adhesion + // Note: support materials (such as Supp.W) can't be used as support base now, so support interface and base are still using different filaments even if + // support_filament==0 + bool different_support_interface_filament = object_config.support_interface_filament != 0 && + object_config.support_interface_filament != object_config.support_filament; + + if (non_soluble_base_top) { // ORCA: Try to support soluble dense interfaces with non-soluble dense interfaces. + this->num_top_base_interface_layers = size_t(std::min(int(num_top_interface_layers) / 2, 2)); + } else { + this->num_top_base_interface_layers = + (different_support_interface_filament && this->zero_gap_interface_top) ? 1 : 0; + } + + if (non_soluble_base_bottom) { // ORCA: Try to support soluble dense interfaces with non-soluble dense interfaces. + this->num_bottom_base_interface_layers = size_t(std::min(int(num_bottom_interface_layers) / 2, 2)); + } else { + this->num_bottom_base_interface_layers = + (different_support_interface_filament && this->zero_gap_interface_bottom) ? 1 : 0; + } } this->first_layer_flow = Slic3r::support_material_1st_layer_flow(&object, float(slicing_params.first_print_layer_height)); this->support_material_flow = Slic3r::support_material_flow(&object, float(slicing_params.layer_height)); @@ -78,7 +85,7 @@ struct SupportParameters { this->gap_xy_first_layer = object_config.support_object_first_layer_gap.value; bridge_flow_ratio /= object.num_printing_regions(); - this->support_material_bottom_interface_flow = slicing_params.soluble_interface || !object_config.thick_bridges ? + this->support_material_bottom_interface_flow = this->zero_gap_interface_bottom || !object_config.thick_bridges ? this->support_material_interface_flow.with_flow_ratio(bridge_flow_ratio) : Flow::bridging_flow(bridge_flow_ratio * this->support_material_interface_flow.nozzle_diameter(), this->support_material_interface_flow.nozzle_diameter()); @@ -95,18 +102,21 @@ struct SupportParameters { this->base_angle = Geometry::deg2rad(float(object_config.support_angle.value)); this->interface_angle = Geometry::deg2rad(float(object_config.support_angle.value + 90.)); - // Orca: Force solid support interface when using support ironing - this->interface_spacing = (this->ironing ? 0 : object_config.support_interface_spacing.value) + this->support_material_interface_flow.spacing(); - this->interface_density = std::min(1., this->support_material_interface_flow.spacing() / this->interface_spacing); - // Orca: Force solid support interface when using support ironing + // ORCA: split top/bottom interface spacing and density, and force solid top when ironing. + this->top_interface_spacing = (this->ironing ? 0 : object_config.support_interface_spacing.value) + this->support_material_interface_flow.spacing(); + this->top_interface_density = std::min(1., this->support_material_interface_flow.spacing() / this->top_interface_spacing); + // ORCA: bottom interface spacing/density separated from top settings. + this->bottom_interface_spacing = object_config.support_bottom_interface_spacing.value + this->support_material_interface_flow.spacing(); + this->bottom_interface_density = std::min(1., this->support_material_interface_flow.spacing() / this->bottom_interface_spacing); + // ORCA: force solid raft interface when ironing (top spacing). double raft_interface_spacing = (this->ironing ? 0 : object_config.support_interface_spacing.value) + this->raft_interface_flow.spacing(); this->raft_interface_density = std::min(1., this->raft_interface_flow.spacing() / raft_interface_spacing); this->support_spacing = object_config.support_base_pattern_spacing.value + this->support_material_flow.spacing(); this->support_density = std::min(1., this->support_material_flow.spacing() / this->support_spacing); if (object_config.support_interface_top_layers.value == 0) { // No interface layers allowed, print everything with the base support pattern. - this->interface_spacing = this->support_spacing; - this->interface_density = this->support_density; + this->top_interface_spacing = this->support_spacing; + this->top_interface_density = this->support_density; } SupportMaterialPattern support_pattern = object_config.support_base_pattern; @@ -114,7 +124,7 @@ struct SupportParameters { this->base_fill_pattern = support_pattern == smpHoneycomb ? ipHoneycomb : this->support_density > 0.95 || this->with_sheath ? ipRectilinear : ipSupportBase; - this->interface_fill_pattern = (this->interface_density > 0.95 ? ipRectilinear : ipSupportBase); + this->interface_fill_pattern = (this->top_interface_density > 0.95 ? ipRectilinear : ipSupportBase); this->raft_interface_fill_pattern = this->raft_interface_density > 0.95 ? ipRectilinear : ipSupportBase; if (object_config.support_interface_pattern == smipGrid) this->contact_fill_pattern = ipGrid; @@ -122,10 +132,10 @@ struct SupportParameters { this->contact_fill_pattern = ipRectilinear; else this->contact_fill_pattern = - (object_config.support_interface_pattern == smipAuto && slicing_params.soluble_interface) || + (object_config.support_interface_pattern == smipAuto && this->zero_gap_interface_top) || object_config.support_interface_pattern == smipConcentric ? ipConcentric : - (this->interface_density > 0.95 ? ipRectilinear : ipSupportBase); + (this->top_interface_density > 0.95 ? ipRectilinear : ipSupportBase); this->raft_angle_1st_layer = 0.f; this->raft_angle_base = 0.f; @@ -186,10 +196,9 @@ struct SupportParameters { } } } - // Both top / bottom contacts and interfaces are soluble. - bool soluble_interface; - // Support contact & interface are soluble, but support base is non-soluble. - bool soluble_interface_non_soluble_base; + // Zero-gap interface flags for top / bottom contact. + bool zero_gap_interface_top; + bool zero_gap_interface_bottom; // Is there at least a top contact layer extruded above support base? bool has_top_contacts; @@ -199,9 +208,9 @@ struct SupportParameters { size_t num_top_interface_layers; // Number of bottom interface layers without counting the contact layer. size_t num_bottom_interface_layers; - // Number of top base interface layers. Zero if not soluble_interface_non_soluble_base. + // Number of top base interface layers. size_t num_top_base_interface_layers; - // Number of bottom base interface layers. Zero if not soluble_interface_non_soluble_base. + // Number of bottom base interface layers. size_t num_bottom_base_interface_layers; bool has_contacts() const { return this->has_top_contacts || this->has_bottom_contacts; } @@ -233,10 +242,13 @@ struct SupportParameters { float base_angle; float interface_angle; - coordf_t interface_spacing; + coordf_t top_interface_spacing; + coordf_t bottom_interface_spacing; coordf_t support_expansion=0; - // Density of the top / bottom interface and contact layers. - coordf_t interface_density; + // Density of the top interface and contact layers. + coordf_t top_interface_density; + // Density of the bottom interface and contact layers. + coordf_t bottom_interface_density; // Density of the raft interface and contact layers. coordf_t raft_interface_density; coordf_t support_spacing; diff --git a/src/libslic3r/Support/TreeSupport.cpp b/src/libslic3r/Support/TreeSupport.cpp index e6ed4acc96..215fc12c8c 100644 --- a/src/libslic3r/Support/TreeSupport.cpp +++ b/src/libslic3r/Support/TreeSupport.cpp @@ -25,6 +25,7 @@ #include #include +#include #ifndef M_PI #define M_PI 3.1415926535897932384626433832795 @@ -73,6 +74,32 @@ inline Point normal(Point pt, double scale) return pt * (scale / length); } +// ORCA: +// Collect all polygons of a given SurfaceType from all regions of a layer. +// Used for top-contact probing across region/modifier boundaries. +static Polygons collect_region_slices_by_type(const Layer &layer, SurfaceType surface_type) +{ + size_t n_polygons_new = 0; + + for (const LayerRegion *region : layer.regions()) { + for (const Surface &surface : region->slices.surfaces) { + if (surface.surface_type == surface_type) + n_polygons_new += surface.expolygon.holes.size() + 1; + } + } + + Polygons out; + out.reserve(n_polygons_new); + + for (const LayerRegion *region : layer.regions()) { + for (const Surface &surface : region->slices.surfaces) { + if (surface.surface_type == surface_type) + polygons_append(out, surface.expolygon); + } + } + + return out; +} enum TreeSupportStage { STAGE_DETECT_OVERHANGS, @@ -1416,7 +1443,7 @@ void TreeSupport::generate_toolpaths() Flow support_flow(support_extrusion_width, ts_layer->height, nozzle_diameter); Fill* filler_interface = Fill::new_from_type(ipRectilinear); - filler_interface->angle = PI / 2; // interface should be perpendicular to base + filler_interface->angle = M_PI_2; // interface should be perpendicular to base filler_interface->spacing = support_flow.spacing(); FillParams fill_params; @@ -1436,7 +1463,7 @@ void TreeSupport::generate_toolpaths() SupportLayer *ts_layer = m_object->get_support_layer(layer_nr); Flow support_flow(support_extrusion_width, ts_layer->height, nozzle_diameter); Fill* filler_raft = Fill::new_from_type(ipRectilinear); - filler_raft->angle = PI / 2; + filler_raft->angle = M_PI_2; filler_raft->spacing = support_flow.spacing(); for (auto& poly : first_non_raft_base) make_perimeter_and_infill(ts_layer->support_fills.entities, poly, std::min(size_t(1), wall_count), support_flow, erSupportMaterial, filler_raft, interface_density, false); @@ -1446,13 +1473,8 @@ void TreeSupport::generate_toolpaths() return; BoundingBox bbox_object(Point(-scale_(1.), -scale_(1.0)), Point(scale_(1.), scale_(1.))); - - std::shared_ptr filler_interface = std::shared_ptr(Fill::new_from_type(m_support_params.contact_fill_pattern)); - std::shared_ptr filler_Roof1stLayer = std::shared_ptr(Fill::new_from_type(ipRectilinear)); - filler_interface->set_bounding_box(bbox_object); - filler_Roof1stLayer->set_bounding_box(bbox_object); - filler_interface->angle = Geometry::deg2rad(object_config.support_angle.value + 90.); - filler_Roof1stLayer->angle = Geometry::deg2rad(object_config.support_angle.value + 90.); + // ORCA: base angle used for explicit interlaced interface orientation. + const float base_support_angle = Geometry::deg2rad(object_config.support_angle.value); // generate tree support tool paths tbb::parallel_for( @@ -1471,17 +1493,28 @@ void TreeSupport::generate_toolpaths() coordf_t support_spacing = object_config.support_base_pattern_spacing.value + support_flow.spacing(); coordf_t support_density = std::min(1., support_flow.spacing() / support_spacing); ts_layer->support_fills.no_sort = false; + // ORCA: per-layer Fill instances to avoid shared-state races during interlaced interfaces. + std::shared_ptr filler_interface = std::shared_ptr(Fill::new_from_type(m_support_params.contact_fill_pattern)); + std::shared_ptr filler_Roof1stLayer = std::shared_ptr(Fill::new_from_type(ipRectilinear)); + filler_interface->set_bounding_box(bbox_object); + filler_Roof1stLayer->set_bounding_box(bbox_object); for (auto& area_group : ts_layer->area_groups) { ExPolygon& poly = *area_group.area; ExPolygons polys; FillParams fill_params; + // ORCA: reset interface Fill state per area group to keep angles deterministic. + filler_interface->fixed_angle = false; + filler_interface->layer_id = size_t(-1); + filler_interface->angle = base_support_angle + M_PI_2; // default interface angle is perpendicular to support angle if (area_group.type != SupportLayer::BaseType) { // interface if (layer_id == 0) { Flow flow = m_raft_layers == 0 ? m_object->print()->brim_flow() : support_flow; + ExtrusionRole brim_role = (area_group.type == SupportLayer::RoofType && !area_group.interface_as_base) ? + erSupportMaterialInterface : erSupportMaterial; make_perimeter_and_inner_brim(ts_layer->support_fills.entities, poly, wall_count, flow, - area_group.type == SupportLayer::RoofType ? erSupportMaterialInterface : erSupportMaterial); + brim_role); polys = std::move(offset_ex(poly, -flow.scaled_spacing())); } else if (area_group.type == SupportLayer::Roof1stLayer) { polys = std::move(offset_ex(poly, 0.5*support_flow.scaled_width())); @@ -1494,12 +1527,18 @@ void TreeSupport::generate_toolpaths() } if (area_group.type == SupportLayer::Roof1stLayer) { // roof_1st_layer + // ORCA: Roof1stLayer may be printed with base material when it acts as a contact layer. + bool interface_as_base = area_group.interface_as_base; fill_params.density = interface_density; // Note: spacing means the separation between two lines as if they are tightly extruded filler_Roof1stLayer->spacing = interface_flow.spacing(); + filler_Roof1stLayer->angle = base_support_angle; + fill_params.dont_sort = true; + Flow interface_base_flow = interface_as_base ? support_flow : interface_flow; + ExtrusionRole interface_role = interface_as_base ? erSupportMaterial : erSupportMaterialInterface; // generate a perimeter first to support interface better ExtrusionEntityCollection* temp_support_fills = new ExtrusionEntityCollection(); - make_perimeter_and_infill(temp_support_fills->entities, poly, 1, interface_flow, erSupportMaterial, + make_perimeter_and_infill(temp_support_fills->entities, poly, 1, interface_base_flow, interface_role, filler_Roof1stLayer.get(), interface_density, false); temp_support_fills->no_sort = true; // make sure loops are first if (!temp_support_fills->entities.empty()) @@ -1508,23 +1547,49 @@ void TreeSupport::generate_toolpaths() delete temp_support_fills; } else if (area_group.type == SupportLayer::FloorType) { // floor_areas + bool interface_as_base = area_group.interface_as_base; fill_params.density = bottom_interface_density; filler_interface->spacing = interface_flow.spacing(); - fill_expolygons_generate_paths(ts_layer->support_fills.entities, polys, - filler_interface.get(), fill_params, erSupportMaterialInterface, interface_flow); - } else if (area_group.type == SupportLayer::RoofType) { - // roof_areas - fill_params.density = interface_density; - filler_interface->spacing = interface_flow.spacing(); + if (m_object_config->support_interface_pattern == smipGrid) { - filler_interface->angle = Geometry::deg2rad(object_config.support_angle.value); + filler_interface->angle = base_support_angle; fill_params.dont_sort = true; } - if (m_object_config->support_interface_pattern == smipRectilinearInterlaced) - filler_interface->layer_id = area_group.interface_id; - fill_expolygons_generate_paths(ts_layer->support_fills.entities, polys, filler_interface.get(), fill_params, erSupportMaterialInterface, - interface_flow); + if (m_object_config->support_interface_pattern == smipRectilinearInterlaced) { + // ORCA: explicit 0/90 alternation for rectilinear interlaced interfaces. + filler_interface->fixed_angle = true; + filler_interface->angle = base_support_angle + ((area_group.interface_id & 1) * M_PI_2); + fill_params.dont_sort = true; + } + + + Flow interface_base_flow = interface_as_base ? support_flow : interface_flow; + ExtrusionRole interface_role = interface_as_base ? erSupportMaterial : erSupportMaterialInterface; + fill_expolygons_generate_paths(ts_layer->support_fills.entities, polys, + filler_interface.get(), fill_params, interface_role, interface_base_flow); + } else if (area_group.type == SupportLayer::RoofType) { + // roof_areas + bool interface_as_base = area_group.interface_as_base; + fill_params.density = interface_density; + filler_interface->spacing = interface_flow.spacing(); + + if (m_object_config->support_interface_pattern == smipGrid) { + filler_interface->angle = base_support_angle; + fill_params.dont_sort = true; + } + + if (m_object_config->support_interface_pattern == smipRectilinearInterlaced) { + // ORCA: explicit 0/90 alternation for rectilinear interlaced interfaces. + filler_interface->fixed_angle = true; + filler_interface->angle = base_support_angle + ((area_group.interface_id & 1) * M_PI_2); + fill_params.dont_sort = true; + } + + Flow interface_base_flow = interface_as_base ? support_flow : interface_flow; + ExtrusionRole interface_role = interface_as_base ? erSupportMaterial : erSupportMaterialInterface; + fill_expolygons_generate_paths(ts_layer->support_fills.entities, polys, filler_interface.get(), fill_params, interface_role, + interface_base_flow); } else { // base_areas @@ -1890,7 +1955,7 @@ Polygons TreeSupport::get_trim_support_regions( polygons_append(polygons_trimming, offset({ expoly }, trimming_offset, SUPPORT_SURFACES_OFFSET_PARAMETERS)); } } - if (!m_slicing_params.soluble_interface && m_object_config->thick_bridges) { + if (!m_slicing_params.zero_gap_interface_top && m_object_config->thick_bridges) { // Collect all bottom surfaces, which will be extruded with a bridging flow. for (; i < object.layers().size(); ++i) { const Layer& object_layer = *object.layers()[i]; @@ -1919,7 +1984,7 @@ void TreeSupport::draw_circles() const PrintObjectConfig &config = m_object->config(); const Print* print = m_object->print(); bool has_brim = print->has_brim(); - int bottom_gap_layers = round(m_slicing_params.gap_object_support / m_slicing_params.layer_height); + const coordf_t bottom_gap_height = m_slicing_params.gap_object_support; const coordf_t branch_radius = config.tree_support_branch_diameter.value / 2; const coordf_t branch_radius_scaled = scale_(branch_radius); bool on_buildplate_only = m_object_config->support_on_build_plate_only.value; @@ -1935,7 +2000,7 @@ void TreeSupport::draw_circles() { double angle; if (SQUARE_SUPPORT) - angle = (double) i / CIRCLE_RESOLUTION * TAU + PI / 4.0 + nodes_angle; + angle = (double) i / CIRCLE_RESOLUTION * TAU + M_PI_4 + nodes_angle; else angle = (double) i / CIRCLE_RESOLUTION * TAU; branch_circle.append(Point(cos(angle) * branch_radius_scaled, sin(angle) * branch_radius_scaled)); @@ -1999,7 +2064,7 @@ void TreeSupport::draw_circles() coordf_t max_layers_above_base = 0; coordf_t max_layers_above_roof = 0; coordf_t max_layers_above_roof1 = 0; - int interface_id = 0; + bool floor_interface_as_base = false; bool has_circle_node = false; bool need_extra_wall = false; ExPolygons collision_sharp_tails; @@ -2033,6 +2098,8 @@ void TreeSupport::draw_circles() break; const SupportNode& node = *p_node; + // ORCA: Cap top interface height in mm based on per-node support layer height. + const coordf_t top_interface_height = coordf_t(top_interface_layers) * node.height; ExPolygons area; // Generate directly from overhang polygon if one of the following is true: // 1) node is a normal part of hybrid support @@ -2084,7 +2151,10 @@ void TreeSupport::draw_circles() // Merge the overhang into the roof area so tree tips can still produce // a continuous support interface. Suppressing this for build-plate-only // support drops the roof polygons entirely in valid tree branches. - if (top_interface_layers > 0 && node.support_roof_layers_below > 0 && !node.is_sharp_tail) { + // ORCA: Only keep top interface polygons that fully fit in the mm height cap. + if (top_interface_layers > 0 && node.support_roof_layers_below > 0 && + (node.dist_mm_to_top - this->top_z_distance) < top_interface_height + EPSILON && + !node.is_sharp_tail) { ExPolygons overhang_expanded; if (node.overhang.contour.size() > 100 || node.overhang.holes.size()>1) overhang_expanded.emplace_back(node.overhang); @@ -2097,16 +2167,19 @@ void TreeSupport::draw_circles() if (obj_layer_nr>0 && node.distance_to_top < 0) append(roof_gap_areas, area); - else if (obj_layer_nr > 0 && node.support_roof_layers_below == 1 && node.is_sharp_tail==false) + // ORCA: Roof1stLayer must also fit inside the mm cap. + else if (obj_layer_nr > 0 && node.support_roof_layers_below == 1 && + (node.dist_mm_to_top - this->top_z_distance) < top_interface_height + EPSILON && node.is_sharp_tail==false) { append(roof_1st_layer, area); max_layers_above_roof1 = std::max(max_layers_above_roof1, node.dist_mm_to_top); } - else if (obj_layer_nr > 0 && node.support_roof_layers_below > 0 && node.is_sharp_tail == false) + // ORCA: Roof layers must also fit inside the mm cap. + else if (obj_layer_nr > 0 && node.support_roof_layers_below > 1 && + (node.dist_mm_to_top - this->top_z_distance) < top_interface_height + EPSILON && node.is_sharp_tail == false) { append(roof_areas, area); max_layers_above_roof = std::max(max_layers_above_roof, node.dist_mm_to_top); - interface_id = node.obj_layer_nr % top_interface_layers; } else { @@ -2135,7 +2208,6 @@ void TreeSupport::draw_circles() roof_1st_layer.clear(); max_layers_above_roof = std::max(max_layers_above_roof, max_layers_above_roof1); max_layers_above_roof1 = 0; - interface_id = obj_layer_nr % top_interface_layers; } ExPolygons roofs; append(roofs, roof_1st_layer); append(roofs, roof_areas);append(roofs, roof_gap_areas); @@ -2148,37 +2220,130 @@ void TreeSupport::draw_circles() for (auto &area : base_areas) { area.simplify(scale_(line_width / 2), &base_areas_simplified); } base_areas = std::move(base_areas_simplified); } - //Subtract support floors. We can only compute floor_areas here instead of with roof_areas, - // or we'll get much wider floor than necessary. - if (bottom_interface_layers + bottom_gap_layers > 0) + // ORCA: + // Bottom interface / bottom gap must be anchored to the *true* support-to-model contact surface. + // Do NOT window the contact search by gap or interface height. + // First find the real contact below, then enforce: + // - an empty gap below (contact_z + gap) + // - exactly N interface layers above that + if (!base_areas.empty() && !m_object_config->support_on_build_plate_only.value && + (bottom_gap_height > EPSILON || bottom_interface_layers > 0)) { - if (layer_nr >= bottom_interface_layers + bottom_gap_layers) - { - // find the lowest interface layer - // TODO the gap may not be exact when "independent support layer height" is enabled - size_t layer_nr_next = layer_nr - bottom_interface_layers; - size_t obj_layer_nr_next = m_ts_data->layer_heights[layer_nr_next].obj_layer_nr; - for (size_t i = 0; i <= bottom_gap_layers && i <= obj_layer_nr_next; i++) - { - const Layer *below_layer = m_object->get_layer(obj_layer_nr_next - i); - ExPolygons bottom_interface = intersection_ex(base_areas, below_layer->lslices); - floor_areas.insert(floor_areas.end(), bottom_interface.begin(), bottom_interface.end()); + const coordf_t interface_height = + bottom_interface_layers > 0 ? coordf_t(bottom_interface_layers) * m_slicing_params.layer_height : 0.0; + + const coordf_t layer_top_z = ts_layer->print_z; + const coordf_t layer_bottom_z = ts_layer->bottom_z(); + ExPolygons new_base_areas; + ExPolygons new_floor_areas; + struct ContactBand { + coordf_t z = 0.0; + Polygons surfaces; + }; + for (const ExPolygon& comp : base_areas) { + ExPolygons comp_poly { comp }; + bool found_contact = false; + std::vector bands; + + // Search downward for object layers whose TOP/BOTTOM surfaces intersect this component. + for (size_t idx = obj_layer_nr + 1; idx-- > 0;) { + const Layer* below_layer = m_object->get_layer(idx); + Polygons top_surfaces = collect_region_slices_by_type(*below_layer, stTop); + Polygons bottom_surfaces = collect_region_slices_by_type(*below_layer, stBottom); + Polygons surf_union = top_surfaces; + polygons_append(surf_union, bottom_surfaces); + if (surf_union.empty()) + continue; + + ExPolygons inter = intersection_ex(comp_poly, surf_union); + if (!inter.empty()) { + bands.push_back(ContactBand{ below_layer->print_z, std::move(surf_union) }); + found_contact = true; + } } + + if (found_contact) { + std::sort(bands.begin(), bands.end(), [](const ContactBand &a, const ContactBand &b) { + return a.z < b.z; + }); + } + + if (!found_contact) { + append(new_base_areas, comp_poly); + continue; + } + + bool interface_id_set = false; + bool any_gap_cleared = false; + + for (const ContactBand &band : bands) { + const coordf_t band_gap_top = band.z + bottom_gap_height; + const coordf_t band_iface_start = band_gap_top; + const bool band_applies = layer_top_z >= band.z - EPSILON; + if (!band_applies) + continue; + + + // Inside the gap: remove only the part overlapping the contact surface, keep the rest. + if (bottom_gap_height > EPSILON && layer_bottom_z < band_gap_top - EPSILON) { + any_gap_cleared = true; + comp_poly = std::move(diff_ex(comp_poly, band.surfaces)); + } + + // Overlaps interface band + if (bottom_interface_layers > 0 && + layer_bottom_z >= band_iface_start - EPSILON && + layer_bottom_z < band_iface_start + interface_height - EPSILON) { + if (!interface_id_set) { + size_t first_interface_layer = layer_nr; + while (first_interface_layer > 0) { + if (m_ts_data->layer_heights[first_interface_layer - 1].print_z <= band_iface_start + EPSILON) + break; + --first_interface_layer; + } + // ORCA: Use support-layer index for base-interface selection (robust with independent heights). + if (m_support_params.num_bottom_base_interface_layers > 0) { + const int bottom_interface_idx = + std::max(0, int(layer_nr) - int(first_interface_layer)); + const int bottom_base_start_idx = + std::max(0, int(bottom_interface_layers) - int(m_support_params.num_bottom_base_interface_layers)); + floor_interface_as_base = bottom_interface_idx >= bottom_base_start_idx; + } + interface_id_set = true; + } + + ExPolygons band_ex = union_ex(band.surfaces); + if (!band_ex.empty()) { + const coordf_t margin = scale_(m_support_params.support_extrusion_width); + ExPolygons comp_margin = offset_ex(comp_poly, margin); + ExPolygons band_clipped = intersection_ex(band_ex, comp_margin); + band_ex = std::move(band_clipped); + } + ExPolygons comp_interface = band_ex.empty() ? ExPolygons {} : intersection_ex(comp_poly, band_ex); + if (!comp_interface.empty()) { + append(new_floor_areas, comp_interface); + comp_poly = std::move(diff_ex(comp_poly, offset_ex(comp_interface, 10))); + } + } + + } + + if (any_gap_cleared && comp_poly.empty()) { + continue; + } + + if (!comp_poly.empty()) + append(new_base_areas, comp_poly); + } - if (floor_areas.empty() == false) { - //floor_areas = std::move(diff_ex(floor_areas, avoid_region_interface)); - //floor_areas = std::move(offset2_ex(floor_areas, contact_dist_scaled, -contact_dist_scaled)); - base_areas = std::move(diff_ex(base_areas, offset_ex(floor_areas, 10))); - } - } - if (bottom_gap_layers > 0 && m_ts_data->layer_heights[layer_nr].obj_layer_nr > bottom_gap_layers) { - const Layer* below_layer = m_object->get_layer(m_ts_data->layer_heights[layer_nr].obj_layer_nr - bottom_gap_layers); - ExPolygons bottom_gap_area = intersection_ex(floor_areas, below_layer->lslices); - if (!bottom_gap_area.empty()) { - floor_areas = std::move(diff_ex(floor_areas, bottom_gap_area)); - } + + + base_areas = std::move(new_base_areas); + floor_areas = std::move(new_floor_areas); } + auto &area_groups = ts_layer->area_groups; + for (auto& expoly : ts_layer->base_areas) { //if (area(expoly) < SQ(scale_(1))) continue; area_groups.emplace_back(&expoly, SupportLayer::BaseType, max_layers_above_base); @@ -2188,11 +2353,11 @@ void TreeSupport::draw_circles() for (auto& expoly : ts_layer->roof_areas) { //if (area(expoly) < SQ(scale_(1))) continue; area_groups.emplace_back(&expoly, SupportLayer::RoofType, max_layers_above_roof); - area_groups.back().interface_id = interface_id; } for (auto &expoly : ts_layer->floor_areas) { //if (area(expoly) < SQ(scale_(1))) continue; area_groups.emplace_back(&expoly, SupportLayer::FloorType, 10000); + area_groups.back().interface_as_base = floor_interface_as_base; } for (auto &expoly : ts_layer->roof_1st_layer) { //if (area(expoly) < SQ(scale_(1))) continue; @@ -2216,13 +2381,49 @@ void TreeSupport::draw_circles() //Must update bounding box which is used in avoid crossing perimeter ts_layer->lslices_bboxes.clear(); ts_layer->lslices_bboxes.reserve(ts_layer->lslices.size()); + for (const ExPolygon& expoly : ts_layer->lslices) ts_layer->lslices_bboxes.emplace_back(get_extents(expoly)); + ts_layer->backup_untyped_slices(); } }); + // ORCA: normalize interface_id sequencing to follow printed interface layers only. + const int top_base_layers = int(m_support_params.num_top_base_interface_layers); + const bool interlaced = m_object_config->support_interface_pattern == smipRectilinearInterlaced; + int roof_interface_id = 0; + int floor_interface_id = 0; + bool has_roof_interface; + bool has_floor_interface; + for (size_t layer_nr = 0; layer_nr < m_ts_data->layer_heights.size(); ++layer_nr) { + SupportLayer *ts_layer = m_object->get_support_layer(layer_nr + m_raft_layers); + if (ts_layer == nullptr) + continue; + + has_roof_interface = false; + has_floor_interface = false; + + for (auto &area_group : ts_layer->area_groups) { + if (area_group.type == SupportLayer::RoofType || area_group.type == SupportLayer::Roof1stLayer) { + if (interlaced) + area_group.interface_id = roof_interface_id; + area_group.interface_as_base = top_base_layers > 0 && roof_interface_id < top_base_layers; + has_roof_interface = true; + } else if (area_group.type == SupportLayer::FloorType) { + if (interlaced) + area_group.interface_id = floor_interface_id; + has_floor_interface = true; + } + } + + if (has_roof_interface) + ++roof_interface_id; + + if (has_floor_interface) + ++floor_interface_id; + } if (with_lightning_infill) { @@ -2488,6 +2689,7 @@ void TreeSupport::drop_nodes() layer_radius.emplace(calc_radius(node_dist)); } } + // parallel pre-compute avoidance tbb::parallel_for(tbb::blocked_range(0, contact_nodes.size() - 1), [&](const tbb::blocked_range &range) { for (size_t layer_nr = range.begin(); layer_nr < range.end(); layer_nr++) { @@ -2679,8 +2881,9 @@ void TreeSupport::drop_nodes() // Make sure the next pass doesn't drop down either of these (since that already happened). node_parent->merged_neighbours.push_front(node_parent == p_node ? neighbour : p_node); const bool to_buildplate = !is_inside_ex(get_collision(0, obj_layer_nr_next), next_position); - SupportNode* next_node = m_ts_data->create_node(next_position, node_parent->distance_to_top + 1, obj_layer_nr_next, node_parent->support_roof_layers_below - 1, to_buildplate, node_parent, - print_z_next, height_next); + SupportNode* next_node = m_ts_data->create_node(next_position, node_parent->distance_to_top + 1, obj_layer_nr_next, + node_parent->support_roof_layers_below - (node_parent->distance_to_top > 0 ? 1 : 0), + to_buildplate, node_parent, print_z_next, height_next); get_max_move_dist(next_node); m_ts_data->m_mutex.lock(); contact_nodes[layer_nr_next].push_back(next_node); @@ -2730,7 +2933,8 @@ void TreeSupport::drop_nodes() ExPolygons overhangs_next = diff_clipped({ node.overhang }, get_collision(0, obj_layer_nr_next)); for(auto& overhang:overhangs_next) { Point next_pt = overhang.contour.centroid(); - SupportNode *next_node = m_ts_data->create_node(next_pt, p_node->distance_to_top + 1, obj_layer_nr_next, p_node->support_roof_layers_below - 1, + SupportNode *next_node = m_ts_data->create_node(next_pt, p_node->distance_to_top + 1, obj_layer_nr_next, + p_node->support_roof_layers_below - (p_node->distance_to_top > 0 ? 1 : 0), to_buildplate, p_node, print_z_next, height_next); next_node->max_move_dist = 0; next_node->overhang = std::move(overhang); @@ -2876,8 +3080,9 @@ void TreeSupport::drop_nodes() } auto next_collision = get_collision(0, obj_layer_nr_next); const bool to_buildplate = !is_inside_ex(m_ts_data->m_layer_outlines[obj_layer_nr_next], next_layer_vertex); - SupportNode * next_node = m_ts_data->create_node(next_layer_vertex, node.distance_to_top + 1, obj_layer_nr_next, node.support_roof_layers_below - 1, to_buildplate, p_node, - print_z_next, height_next); + SupportNode * next_node = m_ts_data->create_node(next_layer_vertex, node.distance_to_top + 1, obj_layer_nr_next, + node.support_roof_layers_below - (node.distance_to_top > 0 ? 1 : 0), + to_buildplate, p_node, print_z_next, height_next); // don't increase radius if next node will collide partially with the object (STUDIO-7883) to_outside = projection_onto(next_collision, next_node->position); direction_to_outer = to_outside - node.position; @@ -3098,7 +3303,12 @@ std::vector TreeSupport::plan_layer_heights() // add support layers according to layer_heights int support_layer_nr = m_raft_layers; for (size_t i = 0; i < layer_heights.size(); i++, support_layer_nr++) { - SupportLayer *ts_layer = m_object->add_tree_support_layer(support_layer_nr, layer_heights[i].print_z, layer_heights[i].height, layer_heights[i].print_z); + // SupportLayer *ts_layer = m_object->add_tree_support_layer(support_layer_nr, layer_heights[i].print_z, layer_heights[i].height, layer_heights[i].print_z); + + // ORCA: add_tree_support_layer() argument order is (id, height, print_z, slice_z). + // Passing print_z as height breaks support layer geometry. + SupportLayer *ts_layer = m_object->add_tree_support_layer(support_layer_nr, layer_heights[i].height, layer_heights[i].print_z, layer_heights[i].print_z); + if (ts_layer->id() > m_raft_layers) { SupportLayer *lower_layer = m_object->get_support_layer(ts_layer->id() - 1); if (lower_layer) { @@ -3147,7 +3357,21 @@ std::vector TreeSupport::plan_layer_heights() for (SupportNode *node : contact_nodes[layer_nr]) { node->height = new_height; node->distance_to_top = -num_layers; - node->support_roof_layers_below += num_layers - 1; + } + } + + // ORCA: Recompute support_roof_layers_below from remaining interface height (independent heights). + const int top_layers = m_object->config().support_interface_top_layers.value; + if (m_support_params.independent_layer_height && top_layers > 0) { + const coordf_t interface_height_mm = coordf_t(top_layers) * m_slicing_params.layer_height; + for (int layer_nr = 0; layer_nr < contact_nodes.size(); layer_nr++) { + if (contact_nodes[layer_nr].empty()) continue; + for (SupportNode *node : contact_nodes[layer_nr]) { + if (node->height <= EPSILON) continue; + const coordf_t remaining_mm = interface_height_mm - (node->dist_mm_to_top - this->top_z_distance); + const int layers_fit = remaining_mm < -EPSILON ? 0 : int(std::floor((remaining_mm + EPSILON) / node->height)); + node->support_roof_layers_below = std::min(layers_fit, top_layers); + } } } @@ -3166,9 +3390,6 @@ void TreeSupport::generate_contact_points() const coordf_t max_bridge_length = scale_(config.max_bridge_length.value); coord_t radius_scaled = scale_(base_radius); bool on_buildplate_only = m_object_config->support_on_build_plate_only.value; - const bool roof_enabled = config.support_interface_top_layers.value > 0; - const bool force_tip_to_roof = roof_enabled && m_support_params.soluble_interface; - //First generate grid points to cover the entire area of the print. BoundingBox bounding_box = m_object->bounding_box(); const Point bounding_box_size = bounding_box.max - bounding_box.min; @@ -3200,7 +3421,7 @@ void TreeSupport::generate_contact_points() // z_distance_top = round(z_distance_top / layer_height) * layer_height; // // BBS: add extra distance if thick bridge is enabled // // Note: normal support uses print_z, but tree support uses integer layers, so we need to subtract layer_height - // if (!m_slicing_params.soluble_interface && m_object_config->thick_bridges) { + // if (!m_slicing_params.zero_gap_interface_top && m_object_config->thick_bridges) { // z_distance_top += m_object->layers()[0]->regions()[0]->region().bridging_height_avg(m_object->print()->config()) - layer_height; //} // } @@ -3208,8 +3429,6 @@ void TreeSupport::generate_contact_points() int gap_layers = z_distance_top == 0 ? 0 : 1; size_t support_roof_layers = config.support_interface_top_layers.value; - if (support_roof_layers > 0) - support_roof_layers += 1; // BBS: add a normal support layer below interface (if we have interface) coordf_t thresh_angle = std::min(89.f, config.support_threshold_angle.value < EPSILON ? 30.f : config.support_threshold_angle.value); coordf_t half_overhang_distance = scale_(tan(thresh_angle * M_PI / 180.0) * layer_height / 2); @@ -3263,13 +3482,13 @@ void TreeSupport::generate_contact_points() if (force_add || !already_inserted.count(hash_pos)) { already_inserted.emplace(hash_pos); bool to_buildplate = true; - size_t roof_layers = add_interface ? support_roof_layers : 0; + size_t roof_layers = add_interface ? (support_roof_layers > 0 ? support_roof_layers - 1 : 0) : 0; // subtract 1 because the contact node itself counts as one layer // add a new node as a virtual node which acts as the invisible gap between support and object // distance_to_top=-1: it's virtual // print_z=object_layer->bottom_z: it directly contacts the bottom // height=z_distance_top: it's height is exactly the gap distance // dist_mm_to_top=0: it directly contacts the bottom - contact_node = m_ts_data->create_node(pt, -gap_layers, layer_nr-1, roof_layers + 1, to_buildplate, SupportNode::NO_PARENT, bottom_z, z_distance_top, 0, + contact_node = m_ts_data->create_node(pt, -gap_layers, layer_nr-1, roof_layers, to_buildplate, SupportNode::NO_PARENT, bottom_z, z_distance_top, 0, radius); contact_node->overhang = overhang; contact_node->is_sharp_tail = is_sharp_tail; @@ -3305,7 +3524,7 @@ void TreeSupport::generate_contact_points() } for (auto &overhang : overhangs_regular) { - bool add_interface = (force_tip_to_roof || area(overhang) > minimum_roof_area) && !is_sharp_tail; + bool add_interface = area(overhang) > minimum_roof_area && !is_sharp_tail; BoundingBox overhang_bounds = get_extents(overhang); double radius = std::clamp(unscale_(overhang_bounds.radius()), MIN_BRANCH_RADIUS, base_radius); // add supports at corners for both auto and manual overhangs, github #2008 diff --git a/src/libslic3r/Support/TreeSupport3D.cpp b/src/libslic3r/Support/TreeSupport3D.cpp index ba3008cd83..9b6758b8c2 100644 --- a/src/libslic3r/Support/TreeSupport3D.cpp +++ b/src/libslic3r/Support/TreeSupport3D.cpp @@ -26,7 +26,6 @@ #include #include -#include #include #include #include @@ -54,7 +53,6 @@ #define _L(s) Slic3r::I18N::translate(s) #endif - //#define TREESUPPORT_DEBUG_SVG namespace Slic3r { @@ -132,7 +130,7 @@ static std::vector>> group_me const PrintObjectConfig &object_config = print_object.config(); if (object_config.support_top_z_distance < EPSILON) // || min_feature_size < scaled(0.1) that is the minimum line width - TreeSupportSettings::soluble = true; + TreeSupportSettings::zero_top_z_gap = true; } size_t largest_printed_mesh_idx = 0; @@ -283,16 +281,6 @@ static std::vector>> group_me //FIXME enforcer_overhang_offset is a fudge constant! enforced_overhangs = diff(offset(union_ex(enforced_overhangs), enforcer_overhang_offset), lower_layer.lslices); -#ifdef TREESUPPORT_DEBUG_SVG -// if (! intersecting_edges(enforced_overhangs).empty()) - { - static int irun = 0; - SVG::export_expolygons(debug_out_path("treesupport-self-intersections-%d.svg", ++irun), - { { { current_layer.lslices }, { "current_layer.lslices", "yellow", 0.5f } }, - { { lower_layer.lslices }, { "lower_layer.lslices", "gray", 0.5f } }, - { { union_ex(enforced_overhangs) }, { "enforced_overhangs", "red", "black", "", scaled(0.1f), 0.5f } } }); - } -#endif // TREESUPPORT_DEBUG_SVG //check_self_intersections(enforced_overhangs, "generate_overhangs - enforced overhangs2"); overhangs = overhangs.empty() ? std::move(enforced_overhangs) : union_(overhangs, enforced_overhangs); //check_self_intersections(overhangs, "generate_overhangs - enforcers"); @@ -718,7 +706,8 @@ static std::optional> polyline_sample_next_point_at_dis (support_params.interface_angle + (layer_idx & 1) ? float(- M_PI / 4.) : float(+ M_PI / 4.)) : support_params.base_angle; - fill_params.density = float(roof ? support_params.interface_density : scaled(filler->spacing) / (scaled(filler->spacing) + float(support_infill_distance))); + // ORCA: use top-specific interface density after separating top/bottom settings. + fill_params.density = float(roof ? support_params.top_interface_density : scaled(filler->spacing) / (scaled(filler->spacing) + float(support_infill_distance))); fill_params.dont_adjust = true; Polylines out; @@ -1291,7 +1280,7 @@ static void generate_initial_areas( ; const size_t num_support_roof_layers = mesh_group_settings.support_roof_layers; const bool roof_enabled = num_support_roof_layers > 0; - const bool force_tip_to_roof = roof_enabled && (interface_placer.support_parameters.soluble_interface || sqr(config.min_radius) * M_PI > mesh_group_settings.minimum_roof_area); + const bool force_tip_to_roof = roof_enabled && (interface_placer.support_parameters.zero_gap_interface_top || sqr(config.min_radius) * M_PI > mesh_group_settings.minimum_roof_area); // cap for how much layer below the overhang a new support point may be added, as other than with regular support every new inserted point // may cause extra material and time cost. Could also be an user setting or differently calculated. Idea is that if an overhang // does not turn valid in double the amount of layers a slope of support angle would take to travel xy_distance, nothing reasonable will come from it. @@ -1806,11 +1795,6 @@ static void increase_areas_one_layer( // Abstract representation of the model outline. If an influence area would move through it, it could teleport through a wall. volumes.getWallRestriction(support_element_collision_radius(config, parent.state), layer_idx, parent.state.use_min_xy_dist); -#ifdef TREESUPPORT_DEBUG_SVG - SVG::export_expolygons(debug_out_path("treesupport-increase_areas_one_layer-%d-%ld.svg", layer_idx, int(merging_area_idx)), - { { { union_ex(wall_restriction) }, { "wall_restricrictions", "gray", 0.5f } }, - { { union_ex(parent.influence_area) }, { "parent", "red", "black", "", scaled(0.1f), 0.5f } } }); -#endif // TREESUPPORT_DEBUG_SVG Polygons to_bp_data, to_model_data; coord_t radius = support_element_collision_radius(config, elem); @@ -1941,11 +1925,6 @@ static void increase_areas_one_layer( // was never made for precision in the single digit micron range. offset_slow = safe_offset_inc(parent.influence_area, extra_speed + extra_slow_speed + config.maximum_move_distance_slow, wall_restriction, safe_movement_distance, offset_independant_faster ? safe_movement_distance + radius : 0, 2); -#ifdef TREESUPPORT_DEBUG_SVG - SVG::export_expolygons(debug_out_path("treesupport-increase_areas_one_layer-slow-%d-%ld.svg", layer_idx, int(merging_area_idx)), - { { { union_ex(wall_restriction) }, { "wall_restricrictions", "gray", 0.5f } }, - { { union_ex(offset_slow) }, { "offset_slow", "red", "black", "", scaled(0.1f), 0.5f } } }); -#endif // TREESUPPORT_DEBUG_SVG } if (offset_fast.empty() && settings.increase_speed != slow_speed) { if (offset_independant_faster) @@ -1955,11 +1934,6 @@ static void increase_areas_one_layer( const coord_t delta_slow_fast = config.maximum_move_distance - (config.maximum_move_distance_slow + extra_slow_speed); offset_fast = safe_offset_inc(offset_slow, delta_slow_fast, wall_restriction, safe_movement_distance, safe_movement_distance + radius, offset_independant_faster ? 2 : 1); } -#ifdef TREESUPPORT_DEBUG_SVG - SVG::export_expolygons(debug_out_path("treesupport-increase_areas_one_layer-fast-%d-%ld.svg", layer_idx, int(merging_area_idx)), - { { { union_ex(wall_restriction) }, { "wall_restricrictions", "gray", 0.5f } }, - { { union_ex(offset_fast) }, { "offset_fast", "red", "black", "", scaled(0.1f), 0.5f } } }); -#endif // TREESUPPORT_DEBUG_SVG } } std::optional result; @@ -3486,18 +3460,6 @@ static void generate_support_areas(Print &print, TreeSupport* tree_support, cons move_bounds, interface_placer, throw_on_cancel); auto t_gen = std::chrono::high_resolution_clock::now(); -#ifdef TREESUPPORT_DEBUG_SVG - for (size_t layer_idx = 0; layer_idx < move_bounds.size(); ++layer_idx) { - Polygons polys; - for (auto& area : move_bounds[layer_idx]) - append(polys, area.influence_area); - if (auto begin = move_bounds[layer_idx].begin(); begin != move_bounds[layer_idx].end()) - SVG::export_expolygons(debug_out_path("treesupport-initial_areas-%d.svg", layer_idx), - { { { union_ex(volumes.getWallRestriction(support_element_collision_radius(config, begin->state), layer_idx, begin->state.use_min_xy_dist)) }, - { "wall_restricrictions", "gray", 0.5f } }, - { { union_ex(polys) }, { "parent", "red", "black", "", scaled(0.1f), 0.5f } } }); - } - #endif // TREESUPPORT_DEBUG_SVG // ### Propagate the influence areas downwards. This is an inherently serial operation. print.set_status(60, _L("Generating support")); @@ -3829,88 +3791,181 @@ void organic_draw_branches( const double bottom_z = layer_idx > 0 ? layer_z(slicing_params, config, layer_idx - 1) : 0.; slice_z.emplace_back(float(0.5 * (bottom_z + print_z))); } + std::vector slices = slice_mesh(partial_mesh, slice_z, mesh_slicing_params, throw_on_cancel); + + // ORCA: guard against empty slices from meshing. + if (slices.empty()) + continue; + bottom_contacts.clear(); + // ORCA: trim tiny fragments to reduce degenerate polygon booleans. + const double tiny_area = tiny_area_threshold(); //FIXME parallelize? for (LayerIndex i = 0; i < LayerIndex(slices.size()); ++i) { - slices[i] = diff_clipped(slices[i], volumes.getCollision(0, layer_begin + i, true)); // FIXME parent_uses_min || draw_area.element->state.use_min_xy_dist); - slices[i] = intersection(slices[i], volumes.m_bed_area); + // ORCA: safety offset when trimming collision/bed to improve robustness. + slices[i] = diff_clipped(slices[i], volumes.getCollision(0, layer_begin + i, true), ApplySafetyOffset::Yes); // FIXME parent_uses_min || draw_area.element->state.use_min_xy_dist); + slices[i] = intersection(slices[i], volumes.m_bed_area, ApplySafetyOffset::Yes); + remove_small(slices[i], tiny_area); } + size_t num_empty = 0; + if (slices.front().empty()) { // Some of the initial layers are empty. num_empty = std::find_if(slices.begin(), slices.end(), [](auto &s) { return !s.empty(); }) - slices.begin(); - } else { - if (branch.has_root) { - if (config.support_rests_on_model && branch.path.front()->state.to_model_gracious) { - if (config.settings.support_floor_layers > 0) - //FIXME one may just take the whole tree slice as bottom interface. - bottom_contacts.emplace_back(intersection_clipped(slices.front(), volumes.getPlaceableAreas(0, layer_begin, [] {}))); - } else if (layer_begin > 0) { - // Drop down areas that do rest non - gracefully on the model to ensure the branch actually rests on something. - struct BottomExtraSlice { - Polygons polygons; - double area; - }; - std::vector bottom_extra_slices; - Polygons rest_support; - coord_t bottom_radius = support_element_radius(config, *branch.path.front()); - // Don't propagate further than 1.5 * bottom radius. - //LayerIndex layers_propagate_max = 2 * bottom_radius / config.layer_height; - LayerIndex layers_propagate_max = 5 * bottom_radius / config.layer_height; - LayerIndex layer_bottommost = branch.path.front()->state.verylost ? - // If the tree bottom is hanging in the air, bring it down to some surface. - 0 : - //FIXME the "verylost" branches should stop when crossing another support. - std::max(0, layer_begin - layers_propagate_max); - double support_area_min_radius = M_PI * sqr(double(config.branch_radius)); - double support_area_stop = std::max(0.2 * M_PI * sqr(double(bottom_radius)), 0.5 * support_area_min_radius); - // Only propagate until the rest area is smaller than this threshold. - //double support_area_min = 0.1 * support_area_min_radius; - for (LayerIndex layer_idx = layer_begin - 1; layer_idx >= layer_bottommost; -- layer_idx) { - rest_support = diff_clipped(rest_support.empty() ? slices.front() : rest_support, volumes.getCollision(0, layer_idx, false)); - double rest_support_area = area(rest_support); - if (rest_support_area < support_area_stop) - // Don't propagate a fraction of the tree contact surface. - break; - bottom_extra_slices.push_back({ rest_support, rest_support_area }); - } - // Now remove those bottom slices that are not supported at all. -#if 0 - while (! bottom_extra_slices.empty()) { - Polygons this_bottom_contacts = intersection_clipped( - bottom_extra_slices.back().polygons, volumes.getPlaceableAreas(0, layer_begin - LayerIndex(bottom_extra_slices.size()), [] {})); - if (area(this_bottom_contacts) < support_area_min) - bottom_extra_slices.pop_back(); - else { - // At least a fraction of the tree bottom is considered to be supported. - if (config.settings.support_floor_layers > 0) - // Turn this fraction of the tree bottom into a contact layer. - bottom_contacts.emplace_back(std::move(this_bottom_contacts)); - break; - } - } -#endif - if (config.support_rests_on_model && config.settings.support_floor_layers > 0) - for (int i = int(bottom_extra_slices.size()) - 2; i >= 0; -- i) - bottom_contacts.emplace_back( - intersection_clipped(bottom_extra_slices[i].polygons, volumes.getPlaceableAreas(0, layer_begin - i - 1, [] {}))); - layer_begin -= LayerIndex(bottom_extra_slices.size()); - slices.insert(slices.begin(), bottom_extra_slices.size(), {}); - auto it_dst = slices.begin(); - for (auto it_src = bottom_extra_slices.rbegin(); it_src != bottom_extra_slices.rend(); ++ it_src) - *it_dst ++ = std::move(it_src->polygons); - } - } - - recover_pending_branch_roofs(interface_placer, branch.path, layer_begin, slices); } - layer_begin += LayerIndex(num_empty); + // ORCA: trim leading empty slices to keep layer indices aligned. + if (num_empty >= slices.size()) + continue; + + if (num_empty > 0) { + slices.erase(slices.begin(), slices.begin() + num_empty); + layer_begin += LayerIndex(num_empty); + } + + // ORCA: use the trimmed front slice as the contact reference. + Polygons slice_front_contact = slices.front(); + + if (branch.has_root) { + if (branch.path.front()->state.to_model_gracious) { + if (config.settings.support_floor_layers > 0) { + // If bottom Z gap is non-zero, keep bottom contacts even when not touching the model. + Polygons contacts; + + // ORCA: non-zero bottom Z should not be clipped by placeable areas. + if (config.support_rests_on_model && config.z_distance_bottom_layers > 0 && layer_begin > 0) + contacts = slice_front_contact; + else { + Polygons placeable = volumes.getPlaceableAreas(0, layer_begin, [] {}); + contacts = intersection_clipped(slice_front_contact, placeable, ApplySafetyOffset::Yes); + } + + remove_small(contacts, tiny_area); + + // ORCA: ensure bottom contacts exist if clipping removed them. + if (contacts.empty() && config.support_rests_on_model && layer_begin > 0 && !slice_front_contact.empty()) + contacts = slice_front_contact; + if (!contacts.empty()) + bottom_contacts.emplace_back(std::move(contacts)); + } + } else if (layer_begin > 0) { + // Drop down areas that do rest non - gracefully on the model to ensure the branch actually rests on something. + struct BottomExtraSlice { + Polygons polygons; + double area; + }; + std::vector bottom_extra_slices; + Polygons rest_support; + coord_t bottom_radius = support_element_radius(config, *branch.path.front()); + // Don't propagate further than 1.5 * bottom radius. + //LayerIndex layers_propagate_max = 2 * bottom_radius / config.layer_height; + LayerIndex layers_propagate_max = 5 * bottom_radius / config.layer_height; + LayerIndex layer_bottommost = branch.path.front()->state.verylost ? + // If the tree bottom is hanging in the air, bring it down to some surface. + 0 : + //FIXME the "verylost" branches should stop when crossing another support. + std::max(0, layer_begin - layers_propagate_max); + double support_area_min_radius = M_PI * sqr(double(config.branch_radius)); + double support_area_stop = std::max(0.2 * M_PI * sqr(double(bottom_radius)), 0.5 * support_area_min_radius); + // Only propagate until the rest area is smaller than this threshold. + //double support_area_min = 0.1 * support_area_min_radius; + for (LayerIndex layer_idx = layer_begin - 1; layer_idx >= layer_bottommost; -- layer_idx) { + LayerIndex collision_layer = (layer_idx == layer_begin - 1) ? layer_begin : layer_idx; + Polygons collision = volumes.getCollision(0, collision_layer, false); + rest_support = diff_clipped(rest_support.empty() ? slice_front_contact : rest_support, collision, ApplySafetyOffset::Yes); + remove_small(rest_support, tiny_area); + double rest_support_area = area(rest_support); + if (rest_support_area < support_area_stop) + // Don't propagate a fraction of the tree contact surface. + break; + bottom_extra_slices.push_back({ rest_support, rest_support_area }); + } + // Now remove those bottom slices that are not supported at all. +#if 0 + while (! bottom_extra_slices.empty()) { + Polygons this_bottom_contacts = intersection_clipped( + bottom_extra_slices.back().polygons, volumes.getPlaceableAreas(0, layer_begin - LayerIndex(bottom_extra_slices.size()), [] {})); + if (area(this_bottom_contacts) < support_area_min) + bottom_extra_slices.pop_back(); + else { + // At least a fraction of the tree bottom is considered to be supported. + if (config.settings.support_floor_layers > 0) + // Turn this fraction of the tree bottom into a contact layer. + bottom_contacts.emplace_back(std::move(this_bottom_contacts)); + break; + } + } +#endif + if (config.settings.support_floor_layers > 0) { + Polygons contacts; + if (!bottom_extra_slices.empty()) { + const int contact_idx = int(bottom_extra_slices.size()) - 1; // Use the lowest contact slice as the footprint. + + // ORCA: non-zero bottom Z should not be clipped by placeable areas. + if (config.support_rests_on_model && config.z_distance_bottom_layers > 0 && layer_begin > 0) + contacts = intersection_clipped(bottom_extra_slices[contact_idx].polygons, Polygons{volumes.m_bed_area}, ApplySafetyOffset::Yes); + else { + Polygons placeable = volumes.getPlaceableAreas(0, layer_begin, [] {}); + contacts = intersection_clipped(bottom_extra_slices[contact_idx].polygons, placeable, ApplySafetyOffset::Yes); + } + } else { + // Fallback: use the current contact slice when no propagation happened. + if (config.support_rests_on_model && config.z_distance_bottom_layers > 0 && layer_begin > 0) + contacts = slice_front_contact; + else { + Polygons placeable = volumes.getPlaceableAreas(0, layer_begin, [] {}); + contacts = intersection_clipped(slice_front_contact, placeable, ApplySafetyOffset::Yes); + } + } + + remove_small(contacts, tiny_area); + + if (!contacts.empty()) + bottom_contacts.emplace_back(std::move(contacts)); + + // ORCA: ensure bottom contacts exist if clipping removed them. + if (bottom_contacts.empty() && config.support_rests_on_model && layer_begin > 0 && !slice_front_contact.empty()) + bottom_contacts.emplace_back(slice_front_contact); + } + layer_begin -= LayerIndex(bottom_extra_slices.size()); + slices.insert(slices.begin(), bottom_extra_slices.size(), {}); + auto it_dst = slices.begin(); + for (auto it_src = bottom_extra_slices.rbegin(); it_src != bottom_extra_slices.rend(); ++ it_src) + *it_dst ++ = std::move(it_src->polygons); + } + + // ORCA: retain bottom contacts even when no placeable areas intersect. + if (branch.has_root && config.support_rests_on_model && branch.path.front()->state.layer_idx > 0 && + config.settings.support_floor_layers > 0 && config.z_distance_bottom_layers > 0 && + bottom_contacts.empty() && !slice_front_contact.empty()) + bottom_contacts.emplace_back(slice_front_contact); + + } + // ORCA: bottom contacts provide the footprint; interface layers are built later. + +#if 0 + //FIXME branch.has_tip seems to not be reliable. + if (branch.has_tip && interface_placer.support_parameters.has_top_contacts) + // Add top slices to top contacts / interfaces / base interfaces. + for (int i = int(branch.path.size()) - 1; i >= 0; -- i) { + const SupportElement &el = *branch.path[i]; + if (el.state.missing_roof_layers == 0) + break; + //FIXME Move or not? + interface_placer.add_roof(std::move(slices[int(slices.size()) - i - 1]), el.state.layer_idx, + interface_placer.support_parameters.num_top_interface_layers + 1 - el.state.missing_roof_layers); + } +#endif + while (! slices.empty() && slices.back().empty()) { slices.pop_back(); - -- layer_end; } + + // ORCA: recompute layer_end after trimming trailing empty slices. + layer_end = layer_begin + LayerIndex(slices.size()); + if (layer_begin < layer_end) { LayerIndex new_begin = tree.first_layer_id == -1 ? layer_begin : std::min(tree.first_layer_id, layer_begin); LayerIndex new_end = tree.first_layer_id == -1 ? layer_end : std::max(tree.first_layer_id + LayerIndex(tree.slices.size()), layer_end); @@ -3926,22 +3981,28 @@ void organic_draw_branches( } else if (LayerIndex dif = tree.first_layer_id - new_begin; dif > 0) tree.slices.insert(tree.slices.begin(), tree.first_layer_id - new_begin, {}); tree.slices.insert(tree.slices.end(), new_size - tree.slices.size(), {}); - layer_begin -= LayerIndex(num_empty); for (LayerIndex i = layer_begin; i != layer_end; ++ i) { int j = i - layer_begin; - if (Polygons &src = slices[j]; ! src.empty()) { + Polygons &src = slices[j]; + bool has_bottom_contacts = j < int(bottom_contacts.size()) && !bottom_contacts[j].empty(); + + // ORCA: preserve bottom contacts even if base polygons are empty. + if (!src.empty() || has_bottom_contacts) { Slice &dst = tree.slices[i - new_begin]; if (++ dst.num_branches > 1) { - append(dst.polygons, std::move(src)); - if (j < int(bottom_contacts.size())) + if (!src.empty()) + append(dst.polygons, std::move(src)); + if (has_bottom_contacts) append(dst.bottom_contacts, std::move(bottom_contacts[j])); } else { - dst.polygons = std::move(std::move(src)); - if (j < int(bottom_contacts.size())) + if (!src.empty()) + dst.polygons = std::move(src); + if (has_bottom_contacts) dst.bottom_contacts = std::move(bottom_contacts[j]); } } } + tree.first_layer_id = new_begin; } } @@ -3954,10 +4015,15 @@ void organic_draw_branches( Tree &tree = trees[tree_id]; for (Slice &slice : tree.slices) if (slice.num_branches > 1) { - slice.polygons = union_(slice.polygons); - slice.bottom_contacts = union_(slice.bottom_contacts); + // ORCA: avoid union_ on empty containers. + if (!slice.polygons.empty()) + slice.polygons = union_(slice.polygons); + if (!slice.bottom_contacts.empty()) + slice.bottom_contacts = union_(slice.bottom_contacts); + slice.num_branches = 1; } + throw_on_cancel(); } }, tbb::simple_partitioner()); @@ -3970,17 +4036,27 @@ void organic_draw_branches( std::vector slices(num_layers, Slice{}); for (Tree &tree : trees) if (tree.first_layer_id >= 0) { - for (LayerIndex i = tree.first_layer_id; i != tree.first_layer_id + LayerIndex(tree.slices.size()); ++ i) - if (Slice &src = tree.slices[i - tree.first_layer_id]; ! src.polygons.empty()) { + for (LayerIndex i = tree.first_layer_id; i != tree.first_layer_id + LayerIndex(tree.slices.size()); ++ i) { + Slice &src = tree.slices[i - tree.first_layer_id]; + bool has_bottom_contacts = !src.bottom_contacts.empty(); + + // ORCA: preserve bottom contacts even if base polygons are empty. + if (!src.polygons.empty() || has_bottom_contacts) { Slice &dst = slices[i]; + if (++ dst.num_branches > 1) { - append(dst.polygons, std::move(src.polygons)); - append(dst.bottom_contacts, std::move(src.bottom_contacts)); + if (!src.polygons.empty()) + append(dst.polygons, std::move(src.polygons)); + if (has_bottom_contacts) + append(dst.bottom_contacts, std::move(src.bottom_contacts)); } else { - dst.polygons = std::move(src.polygons); - dst.bottom_contacts = std::move(src.bottom_contacts); + if (!src.polygons.empty()) + dst.polygons = std::move(src.polygons); + if (has_bottom_contacts) + dst.bottom_contacts = std::move(src.bottom_contacts); } } + } } tbb::parallel_for(tbb::blocked_range(0, std::min(move_bounds.size(), slices.size()), 1), @@ -3988,8 +4064,11 @@ void organic_draw_branches( for (size_t layer_idx = range.begin(); layer_idx < range.end(); ++layer_idx) { Slice &slice = slices[layer_idx]; assert(intermediate_layers[layer_idx] == nullptr); - Polygons base_layer_polygons = slice.num_branches > 1 ? union_(slice.polygons) : std::move(slice.polygons); - Polygons bottom_contact_polygons = slice.num_branches > 1 ? union_(slice.bottom_contacts) : std::move(slice.bottom_contacts); + // ORCA: avoid union_ on empty inputs. + Polygons base_layer_polygons = slice.polygons.empty() ? Polygons{} : + (slice.num_branches > 1 ? union_(slice.polygons) : std::move(slice.polygons)); + Polygons bottom_contact_polygons = slice.bottom_contacts.empty() ? Polygons{} : + (slice.num_branches > 1 ? union_(slice.bottom_contacts) : std::move(slice.bottom_contacts)); if (! base_layer_polygons.empty()) { // Most of the time in this function is this union call. Can take 300+ ms when a lot of areas are to be unioned. diff --git a/src/libslic3r/Support/TreeSupportCommon.hpp b/src/libslic3r/Support/TreeSupportCommon.hpp index 39e07cfaf4..02ddada3b2 100644 --- a/src/libslic3r/Support/TreeSupportCommon.hpp +++ b/src/libslic3r/Support/TreeSupportCommon.hpp @@ -306,7 +306,7 @@ public: layer_start_bp_radius = (bp_radius - branch_radius) / bp_radius_increase_per_layer; - if (TreeSupportSettings::soluble) { + if (TreeSupportSettings::zero_top_z_gap) { // safeOffsetInc can only work in steps of the size xy_min_distance in the worst case => xy_min_distance has to be a bit larger than 0 in this worst case and should be large enough for performance to not suffer extremely // When for all meshes the z bottom and top distance is more than one layer though the worst case is xy_min_distance + min_feature_size // This is not the best solution, but the only one to ensure areas can not lag though walls at high maximum_move_distance. @@ -356,7 +356,7 @@ public: // some static variables dependent on other meshes that are not currently processed. // Has to be static because TreeSupportConfig will be used in TreeModelVolumes as this reduces redundancy. - inline static bool soluble = false; + inline static bool zero_top_z_gap = false; /*! * \brief Width of a single line of support. */ @@ -718,9 +718,15 @@ public: { assert(support_parameters.has_top_contacts); assert(dtt_roof <= support_parameters.num_top_interface_layers); + // ORCA: Reserve one top interface layer but only when top base-interface layers exist. + // This prevents all interface layers from being classified as base-interface layers + // and preserves correct top contact and interface behavior. + size_t interface_threshold = support_parameters.num_top_interface_layers_only(); + if (interface_threshold > 0 && support_parameters.num_top_base_interface_layers > 0) + --interface_threshold; SupportGeneratorLayersPtr &layers = dtt_roof == 0 ? this->top_contacts : - dtt_roof <= support_parameters.num_top_interface_layers_only() ? this->top_interfaces : this->top_base_interfaces; + dtt_roof <= interface_threshold ? this->top_interfaces : this->top_base_interfaces; SupportGeneratorLayer*& l = layers[insert_layer_idx]; if (l == nullptr) l = &layer_allocate_unguarded(layer_storage, dtt_roof == 0 ? SupporLayerType::TopContact : SupporLayerType::TopInterface,