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,