mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-18 22:42:37 +00:00
Merge branch 'main' into weilun/speed_dial
This commit is contained in:
@@ -2293,8 +2293,6 @@ msgstr ""
|
||||
msgid "%s has been removed."
|
||||
msgstr ""
|
||||
|
||||
msgid "Switching application language"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select the language"
|
||||
msgstr ""
|
||||
@@ -8294,8 +8292,6 @@ msgstr ""
|
||||
msgid "Language selection"
|
||||
msgstr ""
|
||||
|
||||
msgid "Switching application language while some presets are modified."
|
||||
msgstr ""
|
||||
|
||||
msgid "Asia-Pacific"
|
||||
msgstr ""
|
||||
|
||||
@@ -2522,8 +2522,6 @@ msgstr "Hi ha una actualització disponible. Obriu el quadre de diàleg del paqu
|
||||
msgid "%s has been removed."
|
||||
msgstr "%s s'ha eliminat."
|
||||
|
||||
msgid "Switching application language"
|
||||
msgstr "Canvi d'idioma de l'aplicació"
|
||||
|
||||
msgid "Select the language"
|
||||
msgstr "Seleccioneu l'idioma"
|
||||
@@ -8924,8 +8922,6 @@ msgstr "Voleu continuar?"
|
||||
msgid "Language selection"
|
||||
msgstr "Selecció d'idiomes"
|
||||
|
||||
msgid "Switching application language while some presets are modified."
|
||||
msgstr "Canviant l'idioma de l'aplicació mentre es modifiquen alguns perfils."
|
||||
|
||||
msgid "Asia-Pacific"
|
||||
msgstr "Àsia-Pacífic"
|
||||
|
||||
@@ -2482,8 +2482,6 @@ msgstr "Je k dispozici aktualizace. Otevřete dialog balíčku předvoleb a prov
|
||||
msgid "%s has been removed."
|
||||
msgstr "%s bylo odstraněno."
|
||||
|
||||
msgid "Switching application language"
|
||||
msgstr "Přepnutí jazyka aplikace"
|
||||
|
||||
msgid "Select the language"
|
||||
msgstr "Zvolte jazyk"
|
||||
@@ -8882,8 +8880,6 @@ msgstr "Chcete pokračovat?"
|
||||
msgid "Language selection"
|
||||
msgstr "Výběr jazyka"
|
||||
|
||||
msgid "Switching application language while some presets are modified."
|
||||
msgstr "Přepnutí jazyka aplikace, když jsou některé předvolby upraveny."
|
||||
|
||||
msgid "Asia-Pacific"
|
||||
msgstr "Asie-Pacifik"
|
||||
|
||||
@@ -2430,8 +2430,6 @@ msgstr "Es ist ein Update verfügbar. Öffnen Sie den Profilbündel-Dialog, um e
|
||||
msgid "%s has been removed."
|
||||
msgstr "%s wurde entfernt."
|
||||
|
||||
msgid "Switching application language"
|
||||
msgstr "Wechsel der Sprache"
|
||||
|
||||
msgid "Select the language"
|
||||
msgstr "Sprache wählen"
|
||||
@@ -8754,8 +8752,6 @@ msgstr "Möchten Sie fortfahren?"
|
||||
msgid "Language selection"
|
||||
msgstr "Sprachauswahl"
|
||||
|
||||
msgid "Switching application language while some presets are modified."
|
||||
msgstr "Umschalten der Anwendungssprache, während einige Profile geändert werden."
|
||||
|
||||
msgid "Asia-Pacific"
|
||||
msgstr "Asien-Pazifik"
|
||||
|
||||
@@ -2289,8 +2289,6 @@ msgstr ""
|
||||
msgid "%s has been removed."
|
||||
msgstr ""
|
||||
|
||||
msgid "Switching application language"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select the language"
|
||||
msgstr ""
|
||||
@@ -8290,8 +8288,6 @@ msgstr ""
|
||||
msgid "Language selection"
|
||||
msgstr ""
|
||||
|
||||
msgid "Switching application language while some presets are modified."
|
||||
msgstr ""
|
||||
|
||||
msgid "Asia-Pacific"
|
||||
msgstr ""
|
||||
|
||||
@@ -2356,8 +2356,6 @@ msgstr "Hay una actualización disponible. Abra el cuadro de diálogo del paquet
|
||||
msgid "%s has been removed."
|
||||
msgstr "Se ha eliminado %s."
|
||||
|
||||
msgid "Switching application language"
|
||||
msgstr "Cambiando el idioma de la aplicación"
|
||||
|
||||
msgid "Select the language"
|
||||
msgstr "Seleccionar el idioma"
|
||||
@@ -8529,8 +8527,6 @@ msgstr "¿Quieres continuar?"
|
||||
msgid "Language selection"
|
||||
msgstr "Selección de idiomas"
|
||||
|
||||
msgid "Switching application language while some presets are modified."
|
||||
msgstr "Cambiando idioma de la aplicación mientras se modifican algunos perfiles."
|
||||
|
||||
msgid "Asia-Pacific"
|
||||
msgstr "Asia-Pacífico"
|
||||
|
||||
@@ -2390,8 +2390,6 @@ msgstr "Eguneratze bat dago erabilgarri. Ireki aurrezarpen-paketeen elkarrizketa
|
||||
msgid "%s has been removed."
|
||||
msgstr "%s kendu da."
|
||||
|
||||
msgid "Switching application language"
|
||||
msgstr "Aplikazioaren hizkuntza aldatzen"
|
||||
|
||||
msgid "Select the language"
|
||||
msgstr "Hautatu hizkuntza"
|
||||
@@ -8610,8 +8608,6 @@ msgstr "Jarraitu nahi duzu?"
|
||||
msgid "Language selection"
|
||||
msgstr "Hizkuntza-hautaketa"
|
||||
|
||||
msgid "Switching application language while some presets are modified."
|
||||
msgstr "Aplikazioaren hizkuntza aldatzen ari da aurrezarpen batzuk aldatuta dauden bitartean."
|
||||
|
||||
msgid "Asia-Pacific"
|
||||
msgstr "Asia-Pazifikoa"
|
||||
|
||||
@@ -2414,8 +2414,6 @@ msgstr "Une mise à jour est disponible. Ouvrez la boîte de dialogue du paquet
|
||||
msgid "%s has been removed."
|
||||
msgstr "%s a été supprimé."
|
||||
|
||||
msgid "Switching application language"
|
||||
msgstr "Changer la langue de l'application"
|
||||
|
||||
msgid "Select the language"
|
||||
msgstr "Sélectionner la langue"
|
||||
@@ -8678,8 +8676,6 @@ msgstr "Voulez-vous continuer ?"
|
||||
msgid "Language selection"
|
||||
msgstr "Sélection de la langue"
|
||||
|
||||
msgid "Switching application language while some presets are modified."
|
||||
msgstr "Changement de langue de l’application alors que certains préréglages sont modifiés."
|
||||
|
||||
msgid "Asia-Pacific"
|
||||
msgstr "Asie-Pacifique"
|
||||
|
||||
@@ -2460,8 +2460,6 @@ msgstr "Frissítés érhető el. Nyisd meg a beállításcsomag párbeszédablak
|
||||
msgid "%s has been removed."
|
||||
msgstr "%s eltávolítva."
|
||||
|
||||
msgid "Switching application language"
|
||||
msgstr "Alkalmazás nyelvének váltása"
|
||||
|
||||
msgid "Select the language"
|
||||
msgstr "Válaszd ki a nyelvet"
|
||||
@@ -8806,8 +8804,6 @@ msgstr "Szeretnéd folytatni?"
|
||||
msgid "Language selection"
|
||||
msgstr "Nyelv kiválasztása"
|
||||
|
||||
msgid "Switching application language while some presets are modified."
|
||||
msgstr "Alkalmazás nyelvének átváltása, miközben egyes beállítások módosultak."
|
||||
|
||||
msgid "Asia-Pacific"
|
||||
msgstr "Ázsia-Csendes-óceáni térség"
|
||||
|
||||
@@ -2466,8 +2466,6 @@ msgstr "È disponibile un aggiornamento. Apri la finestra di dialogo del bundle
|
||||
msgid "%s has been removed."
|
||||
msgstr "%s è stato rimosso."
|
||||
|
||||
msgid "Switching application language"
|
||||
msgstr "Cambio lingua applicazione"
|
||||
|
||||
msgid "Select the language"
|
||||
msgstr "Seleziona la lingua"
|
||||
@@ -8807,8 +8805,6 @@ msgstr "Vuoi continuare?"
|
||||
msgid "Language selection"
|
||||
msgstr "Selezione lingua"
|
||||
|
||||
msgid "Switching application language while some presets are modified."
|
||||
msgstr "Cambio lingua applicazione durante la modifica di alcuni profili."
|
||||
|
||||
msgid "Asia-Pacific"
|
||||
msgstr "Asia-Pacifico"
|
||||
|
||||
@@ -2473,8 +2473,6 @@ msgstr "アップデートが利用可能です。プリセットバンドルの
|
||||
msgid "%s has been removed."
|
||||
msgstr "%sを削除しました。"
|
||||
|
||||
msgid "Switching application language"
|
||||
msgstr "アプリケーション言語の切り替え"
|
||||
|
||||
msgid "Select the language"
|
||||
msgstr "言語を選択"
|
||||
@@ -8825,8 +8823,6 @@ msgstr "続行しますか?"
|
||||
msgid "Language selection"
|
||||
msgstr "言語選択"
|
||||
|
||||
msgid "Switching application language while some presets are modified."
|
||||
msgstr "アプリケーション言語を切り替える時に、プリセットの変更があります"
|
||||
|
||||
msgid "Asia-Pacific"
|
||||
msgstr "アジア太平洋地域"
|
||||
|
||||
@@ -2481,8 +2481,6 @@ msgstr "사용 가능한 업데이트가 있습니다. 사전 설정 번들 대
|
||||
msgid "%s has been removed."
|
||||
msgstr "%s이(가) 제거되었습니다."
|
||||
|
||||
msgid "Switching application language"
|
||||
msgstr "응용 프로그램 언어 전환"
|
||||
|
||||
msgid "Select the language"
|
||||
msgstr "언어 선택"
|
||||
@@ -8860,8 +8858,6 @@ msgstr "계속하시겠습니까?"
|
||||
msgid "Language selection"
|
||||
msgstr "언어 선택"
|
||||
|
||||
msgid "Switching application language while some presets are modified."
|
||||
msgstr "일부 사전 설정이 수정되는 동안 응용 프로그램 언어를 전환합니다."
|
||||
|
||||
msgid "Asia-Pacific"
|
||||
msgstr "아시아 태평양"
|
||||
|
||||
@@ -2449,8 +2449,6 @@ msgstr "Yra prieinamas atnaujinimas. Atidarykite profilių paketo dialogo langą
|
||||
msgid "%s has been removed."
|
||||
msgstr "%s buvo pašalintas."
|
||||
|
||||
msgid "Switching application language"
|
||||
msgstr "Perjungiama programos kalba"
|
||||
|
||||
msgid "Select the language"
|
||||
msgstr "Pasirinkite kalbą"
|
||||
@@ -8797,8 +8795,6 @@ msgstr "Ar norite tęsti?"
|
||||
msgid "Language selection"
|
||||
msgstr "Kalbos pasirinkimas"
|
||||
|
||||
msgid "Switching application language while some presets are modified."
|
||||
msgstr "Keičiama programos kalba, kai yra pakeistų profilių."
|
||||
|
||||
msgid "Asia-Pacific"
|
||||
msgstr "Azija-Ramusis vandenynas"
|
||||
|
||||
@@ -2679,8 +2679,6 @@ msgstr "Er is een update beschikbaar. Open het dialoogvenster voor de voorinstel
|
||||
msgid "%s has been removed."
|
||||
msgstr "%s is verwijderd."
|
||||
|
||||
msgid "Switching application language"
|
||||
msgstr "De taal van de applicatie wordt aangepast"
|
||||
|
||||
msgid "Select the language"
|
||||
msgstr "Kies de taal"
|
||||
@@ -9602,8 +9600,6 @@ msgstr "Wilt u doorgaan?"
|
||||
msgid "Language selection"
|
||||
msgstr "Taal selectie"
|
||||
|
||||
msgid "Switching application language while some presets are modified."
|
||||
msgstr "De taal van de toepassing aanpaasen terwijl sommige voorinstellingen zijn aangepast."
|
||||
|
||||
msgid "Asia-Pacific"
|
||||
msgstr "Azië-Pacific"
|
||||
|
||||
@@ -2512,8 +2512,6 @@ msgstr "Dostępna jest aktualizacja. Otwórz okno pakietu profili, aby ją zains
|
||||
msgid "%s has been removed."
|
||||
msgstr "%s został usunięty."
|
||||
|
||||
msgid "Switching application language"
|
||||
msgstr "Zmiana języka aplikacji"
|
||||
|
||||
msgid "Select the language"
|
||||
msgstr "Wybierz język"
|
||||
@@ -9016,8 +9014,6 @@ msgstr "Czy kontynuować?"
|
||||
msgid "Language selection"
|
||||
msgstr "Wybór języka"
|
||||
|
||||
msgid "Switching application language while some presets are modified."
|
||||
msgstr "Zmiana języka aplikacji przy jednoczesnym istniejących zmodyfikowanych ustawieniach."
|
||||
|
||||
msgid "Asia-Pacific"
|
||||
msgstr "Azja i Pacyfik"
|
||||
|
||||
@@ -2356,8 +2356,6 @@ msgstr "Há uma atualização disponível. Abra a caixa de diálogo do pacote de
|
||||
msgid "%s has been removed."
|
||||
msgstr "%s foi removido."
|
||||
|
||||
msgid "Switching application language"
|
||||
msgstr "Alternando o idioma do aplicativo"
|
||||
|
||||
msgid "Select the language"
|
||||
msgstr "Selecione o idioma"
|
||||
@@ -8572,8 +8570,6 @@ msgstr "Você deseja continuar?"
|
||||
msgid "Language selection"
|
||||
msgstr "Seleção de idioma"
|
||||
|
||||
msgid "Switching application language while some presets are modified."
|
||||
msgstr "Alternando idioma do aplicativo enquanto algumas predefinições são modificadas."
|
||||
|
||||
msgid "Asia-Pacific"
|
||||
msgstr "Ásia-Pacífico"
|
||||
|
||||
@@ -2431,8 +2431,6 @@ msgstr "Доступно обновление. Проверьте меню па
|
||||
msgid "%s has been removed."
|
||||
msgstr "%s был удалён."
|
||||
|
||||
msgid "Switching application language"
|
||||
msgstr "Изменение языка приложения"
|
||||
|
||||
msgid "Select the language"
|
||||
msgstr "Выбор языка"
|
||||
@@ -8852,8 +8850,6 @@ msgstr "Хотите продолжить?"
|
||||
msgid "Language selection"
|
||||
msgstr "Выбор языка"
|
||||
|
||||
msgid "Switching application language while some presets are modified."
|
||||
msgstr "Смена языка приложения при изменении некоторых профилей."
|
||||
|
||||
msgid "Asia-Pacific"
|
||||
msgstr "Азиатско-Тихоокеанский"
|
||||
|
||||
@@ -2767,8 +2767,6 @@ msgstr "Det finns en uppdatering tillgänglig. Öppna dialogrutan för förinst
|
||||
msgid "%s has been removed."
|
||||
msgstr "%s har tagits bort."
|
||||
|
||||
msgid "Switching application language"
|
||||
msgstr "Byt applikationsspråk"
|
||||
|
||||
msgid "Select the language"
|
||||
msgstr "Välj språk"
|
||||
@@ -9694,8 +9692,6 @@ msgstr "Fortsätta?"
|
||||
msgid "Language selection"
|
||||
msgstr "Språkval"
|
||||
|
||||
msgid "Switching application language while some presets are modified."
|
||||
msgstr "Byter språk medans inställningarna ändras."
|
||||
|
||||
msgid "Asia-Pacific"
|
||||
msgstr "Asien-Stillahavsområdet"
|
||||
|
||||
@@ -2456,8 +2456,6 @@ msgstr "มีอัปเดตพร้อมใช้งาน เปิด
|
||||
msgid "%s has been removed."
|
||||
msgstr "ลบ %s แล้ว"
|
||||
|
||||
msgid "Switching application language"
|
||||
msgstr "การเปลี่ยนภาษาของแอปพลิเคชัน"
|
||||
|
||||
msgid "Select the language"
|
||||
msgstr "เลือกภาษา"
|
||||
@@ -8758,8 +8756,6 @@ msgstr "ต้องการดำเนินการต่อหรือไ
|
||||
msgid "Language selection"
|
||||
msgstr "การเลือกภาษา"
|
||||
|
||||
msgid "Switching application language while some presets are modified."
|
||||
msgstr "การสลับภาษาของแอปพลิเคชันในขณะที่มีการแก้ไขค่าที่ตั้งไว้บางส่วน"
|
||||
|
||||
msgid "Asia-Pacific"
|
||||
msgstr "เอเชียแปซิฟิก"
|
||||
|
||||
@@ -2480,8 +2480,6 @@ msgstr "Kullanılabilir bir güncelleme var. Güncellemek için ön ayar paketi
|
||||
msgid "%s has been removed."
|
||||
msgstr "%s kaldırıldı."
|
||||
|
||||
msgid "Switching application language"
|
||||
msgstr "Uygulama dilini değiştirme"
|
||||
|
||||
msgid "Select the language"
|
||||
msgstr "Dili seçin"
|
||||
@@ -8860,8 +8858,6 @@ msgstr "Devam etmek istiyor musun?"
|
||||
msgid "Language selection"
|
||||
msgstr "Dil seçimi"
|
||||
|
||||
msgid "Switching application language while some presets are modified."
|
||||
msgstr "Bazı ön ayarlar değiştirilirken uygulama dilinin değiştirilmesi."
|
||||
|
||||
msgid "Asia-Pacific"
|
||||
msgstr "Asya Pasifik"
|
||||
|
||||
@@ -2424,8 +2424,6 @@ msgstr "Доступне оновлення. Відкрийте вікно на
|
||||
msgid "%s has been removed."
|
||||
msgstr "%s вилучено."
|
||||
|
||||
msgid "Switching application language"
|
||||
msgstr "Зміна мови програми"
|
||||
|
||||
msgid "Select the language"
|
||||
msgstr "Вибрати мову"
|
||||
@@ -8874,8 +8872,6 @@ msgstr "Ви хочете продовжувати?"
|
||||
msgid "Language selection"
|
||||
msgstr "Вибір мови"
|
||||
|
||||
msgid "Switching application language while some presets are modified."
|
||||
msgstr "Зміна мови програми при зміні деяких профілів."
|
||||
|
||||
msgid "Asia-Pacific"
|
||||
msgstr "Азіатсько-Тихоокеанський регіон"
|
||||
|
||||
@@ -2568,8 +2568,6 @@ msgstr "Có bản cập nhật khả dụng. Hãy mở hộp thoại gói cài
|
||||
msgid "%s has been removed."
|
||||
msgstr "%s đã bị xóa."
|
||||
|
||||
msgid "Switching application language"
|
||||
msgstr "Đang chuyển ngôn ngữ ứng dụng"
|
||||
|
||||
msgid "Select the language"
|
||||
msgstr "Chọn ngôn ngữ"
|
||||
@@ -9309,8 +9307,6 @@ msgstr "Bạn có muốn tiếp tục?"
|
||||
msgid "Language selection"
|
||||
msgstr "Chọn ngôn ngữ"
|
||||
|
||||
msgid "Switching application language while some presets are modified."
|
||||
msgstr "Đang chuyển đổi ngôn ngữ ứng dụng trong khi một số preset đã được chỉnh sửa."
|
||||
|
||||
msgid "Asia-Pacific"
|
||||
msgstr "Châu Á-Thái Bình Dương"
|
||||
|
||||
@@ -2361,8 +2361,6 @@ msgstr "有更新可用。打开预设包对话框进行更新。"
|
||||
msgid "%s has been removed."
|
||||
msgstr "%s 已被移除。"
|
||||
|
||||
msgid "Switching application language"
|
||||
msgstr "切换应用程序语言"
|
||||
|
||||
msgid "Select the language"
|
||||
msgstr "选择语言"
|
||||
@@ -8587,8 +8585,6 @@ msgstr "是否继续?"
|
||||
msgid "Language selection"
|
||||
msgstr "语言选择"
|
||||
|
||||
msgid "Switching application language while some presets are modified."
|
||||
msgstr "在切换应用语言之前发现某些参数预设有更改。"
|
||||
|
||||
msgid "Asia-Pacific"
|
||||
msgstr "亚太"
|
||||
|
||||
@@ -2425,8 +2425,6 @@ msgstr "有可用的更新。請開啟預設組合對話框進行更新。"
|
||||
msgid "%s has been removed."
|
||||
msgstr "%s 已移除。"
|
||||
|
||||
msgid "Switching application language"
|
||||
msgstr "切換應用程式語言"
|
||||
|
||||
msgid "Select the language"
|
||||
msgstr "選擇語言"
|
||||
@@ -8753,8 +8751,6 @@ msgstr "是否繼續?"
|
||||
msgid "Language selection"
|
||||
msgstr "語言選擇"
|
||||
|
||||
msgid "Switching application language while some presets are modified."
|
||||
msgstr "在切換應用程式語言之前發現某些參數預設有更改。"
|
||||
|
||||
msgid "Asia-Pacific"
|
||||
msgstr "亞太"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#pragma once
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
#include "stackwalker.h"
|
||||
#include "StackWalker.h"
|
||||
#include <eh.h>
|
||||
|
||||
class CBaseException : public CStackWalker
|
||||
|
||||
@@ -425,7 +425,7 @@ LPSTACKINFO CStackWalker::StackWalker(HANDLE hThread, const CONTEXT* context)
|
||||
else
|
||||
c = *context;
|
||||
|
||||
STACKFRAME64 sf = {0};
|
||||
STACKFRAME64 sf = {};
|
||||
DWORD imageType;
|
||||
|
||||
//intel X86
|
||||
|
||||
@@ -558,6 +558,12 @@ if (_opts)
|
||||
target_compile_options(libslic3r_cgal PRIVATE "${_opts_bad}")
|
||||
endif()
|
||||
|
||||
if (IS_CLANG_CL)
|
||||
# CGAL passes /fp:strict /fp:except-. clang-cl reports the second as overriding part of
|
||||
# the first; the settings cc1 receives are the same ones MSVC produces from that pair.
|
||||
target_compile_options(libslic3r_cgal PRIVATE -Wno-overriding-option)
|
||||
endif ()
|
||||
|
||||
target_link_libraries(libslic3r_cgal PRIVATE ${_cgal_tgt} admesh libigl mcut boost_libs)
|
||||
|
||||
if (MSVC AND "${CMAKE_SIZEOF_VOID_P}" STREQUAL "4") # 32 bit MSVC workaround
|
||||
|
||||
@@ -968,10 +968,10 @@ EmbossStyles Emboss::get_font_list_by_register() {
|
||||
}
|
||||
|
||||
// TODO: Fix global function
|
||||
bool CALLBACK EnumFamCallBack(LPLOGFONT lplf,
|
||||
LPNEWTEXTMETRIC lpntm,
|
||||
DWORD FontType,
|
||||
LPVOID aFontList)
|
||||
int CALLBACK EnumFamCallBack(const LOGFONT *lplf,
|
||||
const TEXTMETRIC *lpntm,
|
||||
DWORD FontType,
|
||||
LPARAM aFontList)
|
||||
{
|
||||
std::vector<std::wstring> *fontList =
|
||||
(std::vector<std::wstring> *) (aFontList);
|
||||
@@ -988,7 +988,7 @@ EmbossStyles Emboss::get_font_list_by_enumeration() {
|
||||
|
||||
HDC hDC = GetDC(NULL);
|
||||
std::vector<std::wstring> font_names;
|
||||
EnumFontFamilies(hDC, (LPCTSTR) NULL, (FONTENUMPROC) EnumFamCallBack,
|
||||
EnumFontFamilies(hDC, (LPCTSTR) NULL, EnumFamCallBack,
|
||||
(LPARAM) &font_names);
|
||||
|
||||
EmbossStyles font_list;
|
||||
|
||||
+36
-39
@@ -11,7 +11,7 @@
|
||||
|
||||
#include "AABBTreeLines.hpp"
|
||||
#include "ExtrusionEntity.hpp"
|
||||
#include "FillBase.hpp"
|
||||
#include "Fill.hpp"
|
||||
#include "FillRectilinear.hpp"
|
||||
#include "FillLightning.hpp"
|
||||
#include "FillConcentricInternal.hpp"
|
||||
@@ -1234,6 +1234,33 @@ std::vector<SurfaceFill> group_fills(const Layer &layer, LockRegionParam &lock_p
|
||||
return surface_fills;
|
||||
}
|
||||
|
||||
// Orca: Anchors and printed infill must share the same body origin. Keep the choice
|
||||
// here so per-model surface centering and separated sparse infill cannot drift apart.
|
||||
static BoundingBox infill_bounding_box(const Layer &layer, const SurfaceFill &fill, const ExPolygon &expoly, BoundingBox bbox)
|
||||
{
|
||||
const auto ¶ms = fill.params;
|
||||
const auto &config = layer.regions()[fill.region_id]->region().config();
|
||||
const bool external = params.extrusion_role == erTopSolidInfill || params.extrusion_role == erBottomSurface;
|
||||
const bool per_model = external && params.center_of_surface_pattern == CenterOfSurfacePattern::Each_Model &&
|
||||
(params.pattern == ipArchimedeanChords || params.pattern == ipOctagramSpiral);
|
||||
const bool separate = !external && params.separated_infills &&
|
||||
(is_separable_infill_pattern(params.pattern) || !config.solid_infill_rotate_template.value.empty() ||
|
||||
!config.sparse_infill_rotate_template.value.empty());
|
||||
if (per_model || separate) {
|
||||
double best_overlap = 0.;
|
||||
for (size_t i = 0; i < layer.lslices.size() && i < layer.lslices_separated_component_bboxes.size(); ++i) {
|
||||
const double overlap = area(intersection_ex(layer.lslices[i], expoly));
|
||||
if (overlap > best_overlap) {
|
||||
best_overlap = overlap;
|
||||
const Point center = layer.lslices_separated_component_bboxes[i].center();
|
||||
bbox = layer.object()->bounding_box();
|
||||
bbox.translate(center.x(), center.y());
|
||||
}
|
||||
}
|
||||
}
|
||||
return bbox;
|
||||
}
|
||||
|
||||
#ifdef SLIC3R_DEBUG_SLICE_PROCESSING
|
||||
void export_group_fills_to_svg(const char *path, const std::vector<SurfaceFill> &fills)
|
||||
{
|
||||
@@ -1353,19 +1380,9 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
||||
|
||||
// Orca: Checking the filling of a centered surface by drawing for each model parts
|
||||
bool is_top_or_bottom = params.extrusion_role == erTopSolidInfill || params.extrusion_role == erBottomSurface;
|
||||
bool is_centered_infill = surface_fill.params.pattern == ipArchimedeanChords || surface_fill.params.pattern == ipOctagramSpiral;
|
||||
if (is_top_or_bottom) {
|
||||
params.center_of_surface_pattern = surface_fill.params.center_of_surface_pattern; // Orca: center of surface pattern
|
||||
}
|
||||
// Orca: Each_Model centers the pattern on each model part's bbox; Each_Surface / Each_Assembly
|
||||
// fall through to the default (whole-object) bounding box below.
|
||||
bool is_per_model_center = is_top_or_bottom && params.center_of_surface_pattern == CenterOfSurfacePattern::Each_Model && is_centered_infill;
|
||||
bool is_separate_infill = !is_top_or_bottom && surface_fill.params.separated_infills &&
|
||||
(
|
||||
is_separable_infill_pattern(surface_fill.params.pattern) ||
|
||||
params.config->solid_infill_rotate_template != "" ||
|
||||
params.config->sparse_infill_rotate_template != "" );
|
||||
|
||||
if( surface_fill.params.pattern == ipLockedZag ) {
|
||||
params.locked_zag = true;
|
||||
params.infill_lock_depth = surface_fill.params.infill_lock_depth;
|
||||
@@ -1389,34 +1406,8 @@ void Layer::make_fills(FillAdaptive::Octree* adaptive_fill_octree, FillAdaptive:
|
||||
params.can_reverse = false;
|
||||
for (ExPolygon& expoly : surface_fill.expolygons) {
|
||||
|
||||
// Orca: separate infill / per-model pattern centering.
|
||||
//
|
||||
// Center the pattern on each connected body of the object independently, so every piece
|
||||
// is filled exactly as if it were sliced on its own: touching/overlapping parts merge
|
||||
// into one body sharing a center, while separate parts and disconnected islands (even
|
||||
// interleaved-but-not-touching ones, e.g. chain links) each get their own. The body each
|
||||
// island belongs to, and its full bounding box, were resolved in 3D by PrintObject::
|
||||
// infill() (lslices_separated_component_bboxes, aligned with this layer's lslices). We
|
||||
// match this fill region to the island it overlaps most, then re-use the whole-object
|
||||
// bounding box (origin-centered — identical extent to the default, so coverage and cost
|
||||
// are unchanged) re-centered on that body.
|
||||
if (is_per_model_center || is_separate_infill) {
|
||||
double best_overlap = 0.;
|
||||
BoundingBox best_component;
|
||||
for (size_t r = 0; r < this->lslices.size() && r < this->lslices_separated_component_bboxes.size(); ++ r) {
|
||||
const double overlap = area(intersection_ex(this->lslices[r], expoly));
|
||||
if (overlap > best_overlap) {
|
||||
best_overlap = overlap;
|
||||
best_component = this->lslices_separated_component_bboxes[r];
|
||||
}
|
||||
}
|
||||
if (best_component.defined) {
|
||||
const Point c = best_component.center();
|
||||
BoundingBox part_bbox = bbox; // origin-centered, whole-object extent (from above)
|
||||
part_bbox.translate(c.x(), c.y()); // re-center on this body
|
||||
f->set_bounding_box(part_bbox);
|
||||
}
|
||||
} // - End: separate infill / per-model pattern centering
|
||||
// Orca: Reuse the body origin used for bridge anchoring, resetting it for each surface.
|
||||
f->set_bounding_box(infill_bounding_box(*this, surface_fill, expoly, bbox));
|
||||
|
||||
f->no_overlap_expolygons = intersection_ex(surface_fill.no_overlap_expolygons, ExPolygons() = {expoly}, ApplySafetyOffset::Yes);
|
||||
if (params.symmetric_infill_y_axis) {
|
||||
@@ -1583,8 +1574,14 @@ Polylines Layer::generate_sparse_infill_polylines_for_anchoring(FillAdaptive::Oc
|
||||
params.multiline = surface_fill.params.multiline;
|
||||
params.gyroid_optimized = surface_fill.params.gyroid_optimized;
|
||||
params.smooth_factor = surface_fill.params.smooth_factor;
|
||||
// Orca: Match make_fills() when choosing the origin of plane-path patterns.
|
||||
// Without the sparse extrusion role, the filler uses each surface's bounds
|
||||
// instead of the object's bounds, so bridge anchors shift away from printed infill.
|
||||
params.extrusion_role = surface_fill.params.extrusion_role;
|
||||
|
||||
for (ExPolygon &expoly : surface_fill.expolygons) {
|
||||
// Orca: Match the per-body origin of make_fills() before generating physical anchors.
|
||||
f->set_bounding_box(infill_bounding_box(*this, surface_fill, expoly, bbox));
|
||||
// Spacing is modified by the filler to indicate adjustments. Reset it for each expolygon.
|
||||
f->spacing = surface_fill.params.spacing;
|
||||
surface_fill.surface.expolygon = std::move(expoly);
|
||||
|
||||
@@ -14,6 +14,12 @@ namespace Slic3r {
|
||||
|
||||
class ExtrusionEntityCollection;
|
||||
class LayerRegion;
|
||||
class PrintObject;
|
||||
|
||||
// Orca: Share the layer rotation calculation between infill generation and internal
|
||||
// bridge angle selection so both interpret rotation templates in the same way.
|
||||
double calculate_infill_rotation_angle(const PrintObject *object, size_t layer_id,
|
||||
const double &fixed_infill_angle, const std::string &template_string);
|
||||
|
||||
// An interface class to Perl, aggregating an instance of a Fill and a FillData.
|
||||
class Filler
|
||||
|
||||
@@ -1395,8 +1395,8 @@ void Filler::_fill_surface_single(
|
||||
}
|
||||
#endif /* ADAPTIVE_CUBIC_INFILL_DEBUG_OUTPUT */
|
||||
|
||||
const auto hook_length = coordf_t(std::min<float>(std::numeric_limits<coord_t>::max(), scale_(params.anchor_length)));
|
||||
const auto hook_length_max = coordf_t(std::min<float>(std::numeric_limits<coord_t>::max(), scale_(params.anchor_length_max)));
|
||||
const auto hook_length = coordf_t(scale_(params.anchor_length));
|
||||
const auto hook_length_max = coordf_t(scale_(params.anchor_length_max));
|
||||
|
||||
Polylines all_polylines_with_hooks = all_polylines.size() > 1 ? connect_lines_using_hooks(std::move(all_polylines), expolygon, this->spacing, hook_length, hook_length_max) : std::move(all_polylines);
|
||||
|
||||
|
||||
@@ -8844,7 +8844,7 @@ public:
|
||||
auto model = object.get_model();
|
||||
auto o = m_temp_model.add_object(object);
|
||||
int backup_id = model->get_object_backup_id(object);
|
||||
push_task({ AddObject, (size_t) backup_id, object.get_model()->get_backup_path(), o, 1 });
|
||||
push_task({ AddObject, (size_t) backup_id, object.get_model()->get_backup_path(), o, { 1 } });
|
||||
}
|
||||
|
||||
void remove_object_mesh(ModelObject& object) {
|
||||
@@ -8854,7 +8854,7 @@ public:
|
||||
void backup_soon() {
|
||||
boost::lock_guard lock(m_mutex);
|
||||
m_other_changes_backup = true;
|
||||
m_tasks.push_back({ Backup, 0, std::string(), nullptr, ++m_task_seq });
|
||||
m_tasks.push_back({ Backup, 0, std::string(), nullptr, { ++m_task_seq } });
|
||||
m_cond.notify_all();
|
||||
}
|
||||
|
||||
@@ -8872,7 +8872,7 @@ public:
|
||||
m_ui_tasks.clear();
|
||||
m_tasks.clear();
|
||||
}
|
||||
m_tasks.push_back({ RemoveBackup, model.id().id, model.get_backup_path(), nullptr, removeAll });
|
||||
m_tasks.push_back({ RemoveBackup, model.id().id, model.get_backup_path(), nullptr, { removeAll } });
|
||||
++m_task_seq;
|
||||
if (model.is_need_backup()) {
|
||||
m_other_changes = false;
|
||||
@@ -9087,7 +9087,7 @@ public:
|
||||
else
|
||||
m_cond.wait(lock);
|
||||
if (m_interval > 0 && boost::get_system_time() > m_next_backup) {
|
||||
m_tasks.push_back({ Backup, 0, std::string(), nullptr, ++m_task_seq });
|
||||
m_tasks.push_back({ Backup, 0, std::string(), nullptr, { ++m_task_seq } });
|
||||
m_next_backup += boost::posix_time::seconds(m_interval);
|
||||
// Maybe wakeup from power sleep
|
||||
if (m_next_backup < boost::get_system_time())
|
||||
|
||||
@@ -910,7 +910,7 @@ namespace Slic3r
|
||||
|
||||
unsigned int iterations = (1 << all_extruders.size());
|
||||
unsigned int final_state = iterations - 1;
|
||||
std::vector<std::vector<float>>cache(iterations, std::vector<float>(all_extruders.size(), 0x7fffffff));
|
||||
std::vector<std::vector<float>>cache(iterations, std::vector<float>(all_extruders.size(), std::numeric_limits<float>::max()));
|
||||
std::vector<std::vector<int>>prev(iterations, std::vector<int>(all_extruders.size(), -1));
|
||||
cache[1][0] = 0.;
|
||||
for (unsigned int state = 0; state < iterations; ++state) {
|
||||
|
||||
@@ -30,8 +30,8 @@ bool Line::intersection_infinite(const Line &other, Point* point) const
|
||||
return false;
|
||||
double t1 = cross2(v12, v2) / denom;
|
||||
Vec2d result = (a1 + t1 * v1);
|
||||
if (result.x() > std::numeric_limits<coord_t>::max() || result.x() < std::numeric_limits<coord_t>::lowest() ||
|
||||
result.y() > std::numeric_limits<coord_t>::max() || result.y() < std::numeric_limits<coord_t>::lowest()) {
|
||||
if (result.x() > double(std::numeric_limits<coord_t>::max()) || result.x() < double(std::numeric_limits<coord_t>::lowest()) ||
|
||||
result.y() > double(std::numeric_limits<coord_t>::max()) || result.y() < double(std::numeric_limits<coord_t>::lowest())) {
|
||||
// Intersection has at least one of the coordinates much bigger (or smaller) than coord_t maximum value (or minimum).
|
||||
// So it can not be stored into the Point without integer overflows. That could mean that input lines are parallel or near parallel.
|
||||
return false;
|
||||
|
||||
@@ -5430,7 +5430,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def->mode = comAdvanced;
|
||||
def->readonly = false;
|
||||
def->nullable = true;
|
||||
def->set_default_value(new ConfigOptionFloatsNullable { {0.0} });
|
||||
def->set_default_value(new ConfigOptionFloatsNullable { 0.0 });
|
||||
|
||||
def = this->add("cooling_tube_retraction", coFloat);
|
||||
def->label = L("Cooling tube position");
|
||||
|
||||
+192
-126
@@ -21,9 +21,11 @@
|
||||
#include "TriangleMeshSlicer.hpp"
|
||||
#include "Utils.hpp"
|
||||
#include "Fill/FillAdaptive.hpp"
|
||||
#include "Fill/Fill.hpp"
|
||||
#include "Fill/FillLightning.hpp"
|
||||
#include "Format/STL.hpp"
|
||||
#include "format.hpp"
|
||||
#include "AABBTreeIndirect.hpp"
|
||||
#include "AABBTreeLines.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
@@ -672,6 +674,98 @@ void PrintObject::prepare_infill()
|
||||
} // for each region
|
||||
#endif /* SLIC3R_DEBUG_SLICE_PROCESSING */
|
||||
|
||||
// Orca: precompute the object's 3D connected bodies for separated infills / per-model
|
||||
// centering. Two islands belong to the same body when their slices overlap on adjacent
|
||||
// layers; islands that only overlap in top-down projection but never touch (e.g. interleaved
|
||||
// chain links) stay separate, matching "split to objects". Each layer island then records
|
||||
// the full bounding box of its body, so its infill is centered on that body as if it were
|
||||
// sliced alone. Compute this before bridges so anchors and extrusion share the same origin.
|
||||
bool needs_separated_components = false;
|
||||
for (size_t i = 0; i < this->num_printing_regions(); ++ i) {
|
||||
const PrintRegionConfig &rc = this->printing_region(i).config();
|
||||
if (rc.separated_infills || rc.center_of_surface_pattern == CenterOfSurfacePattern::Each_Model) {
|
||||
needs_separated_components = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Orca: Fast path: the feature only changes anything when the object is made of more than one
|
||||
// connected body. Detect that cheaply the same way as "Split to objects" — more than one
|
||||
// model part, or a single part whose mesh is splittable (is_splittable() is cached). A single
|
||||
// body already shares the object center, i.e. the default, so skip the connectivity pass.
|
||||
if (needs_separated_components) {
|
||||
int parts = 0;
|
||||
const ModelVolume *first_part = nullptr;
|
||||
for (const ModelVolume *v : this->model_object()->volumes)
|
||||
if (v->is_model_part()) { ++ parts; first_part = v; }
|
||||
if (parts <= 1 && ! (first_part != nullptr && first_part->is_splittable()))
|
||||
needs_separated_components = false;
|
||||
}
|
||||
for (Layer *layer : m_layers)
|
||||
layer->lslices_separated_component_bboxes.clear();
|
||||
if (needs_separated_components) {
|
||||
const size_t nl = m_layers.size();
|
||||
std::vector<size_t> offset(nl + 1, 0); // Orca: flat index of the first island of each layer
|
||||
for (size_t i = 0; i < nl; ++ i)
|
||||
offset[i + 1] = offset[i] + m_layers[i]->lslices.size();
|
||||
const size_t nreg = offset[nl];
|
||||
// Orca: Union-find over every (layer, island).
|
||||
std::vector<size_t> parent(nreg);
|
||||
for (size_t i = 0; i < nreg; ++ i) parent[i] = i;
|
||||
auto find = [&parent](size_t x) {
|
||||
while (parent[x] != x) { parent[x] = parent[parent[x]]; x = parent[x]; }
|
||||
return x;
|
||||
};
|
||||
auto unite = [&](size_t a, size_t b) { a = find(a); b = find(b); if (a != b) parent[a] = b; };
|
||||
// Orca: Index the smaller of two consecutive layers instead of scanning every
|
||||
// pair of islands. The tree prunes distant boxes on fragmented models; exact
|
||||
// polygon intersections still decide connectivity for the remaining candidates.
|
||||
for (size_t i = 0; i + 1 < nl; ++ i) {
|
||||
m_print->throw_if_canceled();
|
||||
size_t layer_a = i, layer_b = i + 1;
|
||||
if (m_layers[layer_a]->lslices.size() < m_layers[layer_b]->lslices.size())
|
||||
std::swap(layer_a, layer_b);
|
||||
const Layer *la = m_layers[layer_a], *lb = m_layers[layer_b];
|
||||
if (lb->lslices.empty())
|
||||
continue;
|
||||
|
||||
using IslandTree = AABBTreeIndirect::Tree<2, coord_t>;
|
||||
std::vector<AABBTreeIndirect::BoundingBoxWrapper> bboxes;
|
||||
bboxes.reserve(lb->lslices.size());
|
||||
for (size_t b = 0; b < lb->lslices.size(); ++ b)
|
||||
bboxes.emplace_back(b, lb->lslices_bboxes[b]);
|
||||
IslandTree tree;
|
||||
tree.build_modify_input(bboxes);
|
||||
for (size_t a = 0; a < la->lslices.size(); ++ a) {
|
||||
const IslandTree::BoundingBox query(la->lslices_bboxes[a].min, la->lslices_bboxes[a].max);
|
||||
AABBTreeIndirect::traverse(tree,
|
||||
[&query](const IslandTree::Node &node) { return node.bbox.intersects(query); },
|
||||
[&](const IslandTree::Node &node) {
|
||||
const size_t b = node.idx;
|
||||
// Orca: Tree boxes include an epsilon, so retain the original box
|
||||
// filter. Already-connected islands cannot change the partition
|
||||
// and need no further polygon intersection.
|
||||
if (la->lslices_bboxes[a].overlap(lb->lslices_bboxes[b]) &&
|
||||
find(offset[layer_a] + a) != find(offset[layer_b] + b) &&
|
||||
! intersection_ex(la->lslices[a], lb->lslices[b]).empty())
|
||||
unite(offset[layer_a] + a, offset[layer_b] + b);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
// Orca: Full bounding box of each body, indexed by its union-find root.
|
||||
std::vector<BoundingBox> body_bbox(nreg);
|
||||
for (size_t i = 0; i < nl; ++ i)
|
||||
for (size_t a = 0; a < m_layers[i]->lslices.size(); ++ a)
|
||||
body_bbox[find(offset[i] + a)].merge(m_layers[i]->lslices_bboxes[a]);
|
||||
// Orca: Store the body bbox for every island.
|
||||
for (size_t i = 0; i < nl; ++ i) {
|
||||
Layer *layer = m_layers[i];
|
||||
layer->lslices_separated_component_bboxes.resize(layer->lslices.size());
|
||||
for (size_t a = 0; a < layer->lslices.size(); ++ a)
|
||||
layer->lslices_separated_component_bboxes[a] = body_bbox[find(offset[i] + a)];
|
||||
}
|
||||
}
|
||||
|
||||
// the following step needs to be done before combination because it may need
|
||||
// to remove only half of the combined infill
|
||||
this->bridge_over_infill();
|
||||
@@ -706,71 +800,6 @@ void PrintObject::infill()
|
||||
if (this->set_started(posInfill)) {
|
||||
m_print->set_status(35, L("Generating infill toolpath"));
|
||||
|
||||
// Orca: precompute the object's 3D connected bodies for separated infills / per-model
|
||||
// centering. Two islands belong to the same body when their slices overlap on adjacent
|
||||
// layers; islands that only overlap in top-down projection but never touch (e.g. interleaved
|
||||
// chain links) stay separate, matching "split to objects". Each layer island then records
|
||||
// the full bounding box of its body, so its infill is centered on that body as if it were
|
||||
// sliced alone. Done once here, before the parallel fill, and only when a region needs it.
|
||||
bool needs_separated_components = false;
|
||||
for (size_t i = 0; i < this->num_printing_regions(); ++ i) {
|
||||
const PrintRegionConfig &rc = this->printing_region(i).config();
|
||||
if (rc.separated_infills || rc.center_of_surface_pattern == CenterOfSurfacePattern::Each_Model) {
|
||||
needs_separated_components = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Fast path: the feature only changes anything when the object is made of more than one
|
||||
// connected body. Detect that cheaply the same way as "Split to objects" — more than one
|
||||
// model part, or a single part whose mesh is splittable (is_splittable() is cached). A single
|
||||
// body already shares the object center, i.e. the default, so skip the connectivity pass.
|
||||
if (needs_separated_components) {
|
||||
int parts = 0;
|
||||
const ModelVolume *first_part = nullptr;
|
||||
for (const ModelVolume *v : this->model_object()->volumes)
|
||||
if (v->is_model_part()) { ++ parts; first_part = v; }
|
||||
if (parts <= 1 && ! (first_part != nullptr && first_part->is_splittable()))
|
||||
needs_separated_components = false;
|
||||
}
|
||||
for (Layer *layer : m_layers)
|
||||
layer->lslices_separated_component_bboxes.clear();
|
||||
if (needs_separated_components) {
|
||||
const size_t nl = m_layers.size();
|
||||
std::vector<size_t> offset(nl + 1, 0); // flat index of the first island of each layer
|
||||
for (size_t i = 0; i < nl; ++ i)
|
||||
offset[i + 1] = offset[i] + m_layers[i]->lslices.size();
|
||||
const size_t nreg = offset[nl];
|
||||
// Union-find over every (layer, island).
|
||||
std::vector<size_t> parent(nreg);
|
||||
for (size_t i = 0; i < nreg; ++ i) parent[i] = i;
|
||||
auto find = [&parent](size_t x) {
|
||||
while (parent[x] != x) { parent[x] = parent[parent[x]]; x = parent[x]; }
|
||||
return x;
|
||||
};
|
||||
auto unite = [&](size_t a, size_t b) { a = find(a); b = find(b); if (a != b) parent[a] = b; };
|
||||
// Join islands that overlap between two consecutive layers.
|
||||
for (size_t i = 0; i + 1 < nl; ++ i) {
|
||||
const Layer *la = m_layers[i], *lb = m_layers[i + 1];
|
||||
for (size_t a = 0; a < la->lslices.size(); ++ a)
|
||||
for (size_t b = 0; b < lb->lslices.size(); ++ b)
|
||||
if (la->lslices_bboxes[a].overlap(lb->lslices_bboxes[b]) &&
|
||||
! intersection_ex(la->lslices[a], lb->lslices[b]).empty())
|
||||
unite(offset[i] + a, offset[i + 1] + b);
|
||||
}
|
||||
// Full bounding box of each body, indexed by its union-find root.
|
||||
std::vector<BoundingBox> body_bbox(nreg);
|
||||
for (size_t i = 0; i < nl; ++ i)
|
||||
for (size_t a = 0; a < m_layers[i]->lslices.size(); ++ a)
|
||||
body_bbox[find(offset[i] + a)].merge(m_layers[i]->lslices_bboxes[a]);
|
||||
// Store the body bbox for every island.
|
||||
for (size_t i = 0; i < nl; ++ i) {
|
||||
Layer *layer = m_layers[i];
|
||||
layer->lslices_separated_component_bboxes.resize(layer->lslices.size());
|
||||
for (size_t a = 0; a < layer->lslices.size(); ++ a)
|
||||
layer->lslices_separated_component_bboxes[a] = body_bbox[find(offset[i] + a)];
|
||||
}
|
||||
}
|
||||
|
||||
const auto& adaptive_fill_octree = this->m_adaptive_fill_octrees.first;
|
||||
const auto& support_fill_octree = this->m_adaptive_fill_octrees.second;
|
||||
|
||||
@@ -1401,8 +1430,6 @@ bool PrintObject::invalidate_state_by_config_options(
|
||||
|| opt_key == "infill_anchor_max"
|
||||
|| opt_key == "top_surface_line_width"
|
||||
|| opt_key == "bottom_surface_density"
|
||||
|| opt_key == "center_of_surface_pattern"
|
||||
|| opt_key == "separated_infills"
|
||||
|| opt_key == "initial_layer_line_width"
|
||||
|| opt_key == "small_area_infill_flow_compensation"
|
||||
|| opt_key == "lateral_lattice_angle_1"
|
||||
@@ -1410,6 +1437,10 @@ bool PrintObject::invalidate_state_by_config_options(
|
||||
|| opt_key == "infill_overhang_angle") {
|
||||
steps.emplace_back(posInfill);
|
||||
} else if (opt_key == "sparse_infill_pattern"
|
||||
// Orca: Body centering now also determines bridge anchors during preparation.
|
||||
// Invalidating preparation also invalidates infill, including top/bottom surfaces.
|
||||
|| opt_key == "center_of_surface_pattern"
|
||||
|| opt_key == "separated_infills"
|
||||
|| opt_key == "sparse_infill_smooth_factor"
|
||||
|| opt_key == "symmetric_infill_y_axis"
|
||||
|| opt_key == "infill_shift_step"
|
||||
@@ -1480,13 +1511,9 @@ bool PrintObject::invalidate_state_by_config_options(
|
||||
steps.emplace_back(posPerimeters);
|
||||
steps.emplace_back(posSupportMaterial);
|
||||
} else if (opt_key == "bridge_flow" || opt_key == "internal_bridge_flow") {
|
||||
if (m_config.support_top_z_distance > 0.) {
|
||||
// Only invalidate due to bridging if bridging is enabled.
|
||||
// If later "support_top_z_distance" is modified, the complete PrintObject is invalidated anyway.
|
||||
steps.emplace_back(posPerimeters);
|
||||
steps.emplace_back(posInfill);
|
||||
steps.emplace_back(posSupportMaterial);
|
||||
}
|
||||
steps.emplace_back(posPerimeters);
|
||||
steps.emplace_back(posInfill);
|
||||
steps.emplace_back(posSupportMaterial);
|
||||
} else if (
|
||||
opt_key == "wall_generator"
|
||||
|| opt_key == "wall_transition_length"
|
||||
@@ -1604,7 +1631,9 @@ bool PrintObject::invalidate_step(PrintObjectStep step)
|
||||
bool PrintObject::invalidate_all_steps()
|
||||
{
|
||||
// First call the "invalidate" functions, which may cancel background processing.
|
||||
bool result = Inherited::invalidate_all_steps() | m_print->invalidate_all_steps();
|
||||
const bool inherited_invalidated = Inherited::invalidate_all_steps();
|
||||
const bool print_invalidated = m_print->invalidate_all_steps();
|
||||
bool result = inherited_invalidated || print_invalidated;
|
||||
// Then reset some of the depending values.
|
||||
m_slicing_params.valid = false;
|
||||
return result;
|
||||
@@ -3009,21 +3038,12 @@ void PrintObject::bridge_over_infill()
|
||||
return diff(layers_sparse_infill, not_sparse_infill);
|
||||
};
|
||||
|
||||
// LAMBDA do determine optimal bridging angle
|
||||
auto determine_bridging_angle = [](const Polygons &bridged_area, const Lines &anchors, InfillPattern dominant_pattern, double infill_direction) {
|
||||
// Orca: Derive the fallback bridge direction from the supplied anchor geometry.
|
||||
// Pattern-specific angle selection belongs at the call site, where the supporting
|
||||
// layer and region are known; this helper must not override it with a base config angle.
|
||||
auto determine_bridging_angle = [](const Polygons &bridged_area, const Lines &anchors) {
|
||||
AABBTreeLines::LinesDistancer<Line> lines_tree(anchors);
|
||||
|
||||
// Orca: since 3D Honeycomb was "fixed" by forcing coordf_t layerHeight = scale_(1.0), this is no longer needed.
|
||||
// CorssHatch also does not need fixed angle.
|
||||
//
|
||||
// Check it the infill that require a fixed infill angle.
|
||||
//switch (dominant_pattern) {
|
||||
//case ip3DHoneycomb:
|
||||
//case ipCrossHatch:
|
||||
// return (infill_direction + 45.0) * 2.0 * M_PI / 360.;
|
||||
//default: break;
|
||||
//}
|
||||
|
||||
std::map<double, int> counted_directions;
|
||||
for (const Polygon &p : bridged_area) {
|
||||
double acc_distance = 0;
|
||||
@@ -3089,18 +3109,15 @@ void PrintObject::bridge_over_infill()
|
||||
if (bridging_angle == 0) {
|
||||
bridging_angle = 0.001;
|
||||
}
|
||||
switch (dominant_pattern) {
|
||||
case ipHilbertCurve: bridging_angle += 0.25 * PI; break;
|
||||
case ipOctagramSpiral: bridging_angle += (1.0 / 16.0) * PI; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
return bridging_angle;
|
||||
};
|
||||
|
||||
// LAMBDA that will fill given polygons with lines, exapand the lines to the nearest anchor, and reconstruct polygons from the newly
|
||||
// generated lines
|
||||
auto construct_anchored_polygon = [](Polygons bridged_area, Lines anchors, const Flow &bridging_flow, double bridging_angle) {
|
||||
// Orca: Extend scan sections to the nearest anchors and reconstruct the bridge area.
|
||||
// scan_spacing controls boundary sampling independently of the extrusion spacing;
|
||||
// anchoring overlap and smoothing thresholds still use the physical bridging flow.
|
||||
auto construct_anchored_polygon = [](Polygons bridged_area, Lines anchors, const Flow &bridging_flow, double bridging_angle,
|
||||
coord_t scan_spacing, bool restore_anchors = false) {
|
||||
auto lines_rotate = [](Lines &lines, double cos_angle, double sin_angle) {
|
||||
for (Line &l : lines) {
|
||||
double ax = double(l.a.x());
|
||||
@@ -3127,12 +3144,12 @@ void PrintObject::bridge_over_infill()
|
||||
BoundingBox bb_x = get_extents(bridged_area);
|
||||
BoundingBox bb_y = get_extents(anchors);
|
||||
|
||||
const size_t n_vlines = (bb_x.max.x() - bb_x.min.x() + bridging_flow.scaled_spacing() - 1) / bridging_flow.scaled_spacing();
|
||||
const size_t n_vlines = (bb_x.max.x() - bb_x.min.x() + scan_spacing - 1) / scan_spacing;
|
||||
std::vector<Line> vertical_lines(n_vlines);
|
||||
for (size_t i = 0; i < n_vlines; i++) {
|
||||
// Orca: Make sure the line is placed in the middle of the extrusion
|
||||
// coord_t x = bb_x.min.x() + i * bridging_flow.scaled_spacing();
|
||||
coord_t x = bb_x.min.x() + (i + 0.5) * bridging_flow.scaled_spacing();
|
||||
// Orca: Sample the center of each reconstructed strip. Its edges lie
|
||||
// half a scan step away, even when the sampling is finer than extrusion.
|
||||
coord_t x = bb_x.min.x() + (i + 0.5) * scan_spacing;
|
||||
coord_t y_min = bb_y.min.y() - bridging_flow.scaled_spacing();
|
||||
coord_t y_max = bb_y.max.y() + bridging_flow.scaled_spacing();
|
||||
vertical_lines[i].a = Point{x, y_min};
|
||||
@@ -3155,7 +3172,11 @@ void PrintObject::bridge_over_infill()
|
||||
auto anchors_intersections = anchors_and_walls_tree.intersections_with_line<true>(vertical_lines[i]);
|
||||
|
||||
for (Line §ion : polygon_sections[i]) {
|
||||
auto maybe_below_anchor = std::upper_bound(anchors_intersections.rbegin(), anchors_intersections.rend(), section.a,
|
||||
// Orca: A repaired boundary may already overlap its anchor by one flow width.
|
||||
// Include that overlap in the search so restoring rounded corners does not
|
||||
// extend every already anchored section into the next sparse infill cell.
|
||||
const coord_t overlap = restore_anchors ? bridging_flow.scaled_width() + SCALED_EPSILON : 0;
|
||||
auto maybe_below_anchor = std::upper_bound(anchors_intersections.rbegin(), anchors_intersections.rend(), section.a + Point{0, overlap},
|
||||
[](const Point &a, const std::pair<Point, size_t> &b) {
|
||||
return a.y() > b.first.y();
|
||||
});
|
||||
@@ -3164,7 +3185,7 @@ void PrintObject::bridge_over_infill()
|
||||
section.a.y() -= bridging_flow.scaled_width() * (0.5 + 0.5);
|
||||
}
|
||||
|
||||
auto maybe_upper_anchor = std::upper_bound(anchors_intersections.begin(), anchors_intersections.end(), section.b,
|
||||
auto maybe_upper_anchor = std::upper_bound(anchors_intersections.begin(), anchors_intersections.end(), section.b - Point{0, overlap},
|
||||
[](const Point &a, const std::pair<Point, size_t> &b) {
|
||||
return a.y() < b.first.y();
|
||||
});
|
||||
@@ -3194,7 +3215,9 @@ void PrintObject::bridge_over_infill()
|
||||
});
|
||||
}
|
||||
|
||||
// reconstruct polygon from polygon sections
|
||||
// Orca: Reconstruct the polygon from scan sections. At discontinuities and
|
||||
// strip starts/ends, use half the scan step for the X offsets; using half an
|
||||
// extrusion spacing would overlap the finer strips and distort curved anchors.
|
||||
struct TracedPoly
|
||||
{
|
||||
Points lows;
|
||||
@@ -3220,8 +3243,8 @@ void PrintObject::bridge_over_infill()
|
||||
36.0 * double(bridging_flow.scaled_spacing()) * bridging_flow.scaled_spacing()) {
|
||||
traced_poly.lows.push_back(candidate->a);
|
||||
} else {
|
||||
traced_poly.lows.push_back(traced_poly.lows.back() + Point{bridging_flow.scaled_spacing() / 2, 0});
|
||||
traced_poly.lows.push_back(candidate->a - Point{bridging_flow.scaled_spacing() / 2, 0});
|
||||
traced_poly.lows.push_back(traced_poly.lows.back() + Point{scan_spacing / 2, 0});
|
||||
traced_poly.lows.push_back(candidate->a - Point{scan_spacing / 2, 0});
|
||||
traced_poly.lows.push_back(candidate->a);
|
||||
}
|
||||
|
||||
@@ -3229,8 +3252,8 @@ void PrintObject::bridge_over_infill()
|
||||
36.0 * double(bridging_flow.scaled_spacing()) * bridging_flow.scaled_spacing()) {
|
||||
traced_poly.highs.push_back(candidate->b);
|
||||
} else {
|
||||
traced_poly.highs.push_back(traced_poly.highs.back() + Point{bridging_flow.scaled_spacing() / 2, 0});
|
||||
traced_poly.highs.push_back(candidate->b - Point{bridging_flow.scaled_spacing() / 2, 0});
|
||||
traced_poly.highs.push_back(traced_poly.highs.back() + Point{scan_spacing / 2, 0});
|
||||
traced_poly.highs.push_back(candidate->b - Point{scan_spacing / 2, 0});
|
||||
traced_poly.highs.push_back(candidate->b);
|
||||
}
|
||||
segment_added = true;
|
||||
@@ -3238,9 +3261,9 @@ void PrintObject::bridge_over_infill()
|
||||
}
|
||||
|
||||
if (!segment_added) {
|
||||
// Zero overlapping segments, we just close this polygon
|
||||
traced_poly.lows.push_back(traced_poly.lows.back() + Point{bridging_flow.scaled_spacing() / 2, 0});
|
||||
traced_poly.highs.push_back(traced_poly.highs.back() + Point{bridging_flow.scaled_spacing() / 2, 0});
|
||||
// Orca: No section continues this strip; close at its right edge.
|
||||
traced_poly.lows.push_back(traced_poly.lows.back() + Point{scan_spacing / 2, 0});
|
||||
traced_poly.highs.push_back(traced_poly.highs.back() + Point{scan_spacing / 2, 0});
|
||||
Polygon &new_poly = expanded_bridged_area.emplace_back(std::move(traced_poly.lows));
|
||||
new_poly.points.insert(new_poly.points.end(), traced_poly.highs.rbegin(), traced_poly.highs.rend());
|
||||
traced_poly.lows.clear();
|
||||
@@ -3255,9 +3278,9 @@ void PrintObject::bridge_over_infill()
|
||||
for (const auto &segment : polygon_slice) {
|
||||
if (used_segments.find(&segment) == used_segments.end()) {
|
||||
TracedPoly &new_tp = current_traced_polys.emplace_back();
|
||||
new_tp.lows.push_back(segment.a - Point{bridging_flow.scaled_spacing() / 2, 0});
|
||||
new_tp.lows.push_back(segment.a - Point{scan_spacing / 2, 0});
|
||||
new_tp.lows.push_back(segment.a);
|
||||
new_tp.highs.push_back(segment.b - Point{bridging_flow.scaled_spacing() / 2, 0});
|
||||
new_tp.highs.push_back(segment.b - Point{scan_spacing / 2, 0});
|
||||
new_tp.highs.push_back(segment.b);
|
||||
}
|
||||
}
|
||||
@@ -3364,7 +3387,10 @@ void PrintObject::bridge_over_infill()
|
||||
total_fill_area = closing(total_fill_area, float(SCALED_EPSILON));
|
||||
expansion_area = closing(expansion_area, float(SCALED_EPSILON));
|
||||
expansion_area = intersection(expansion_area, deep_infill_area);
|
||||
Polylines anchors = intersection_pl(infill_lines[lidx - 1], shrink(expansion_area, spacing));
|
||||
// Orca: Preserve the real lower-layer anchors for every candidate in this
|
||||
// layer. Replacing this shared set for one pattern also changes later regions,
|
||||
// and synthetic straight lines can claim support where no infill is printed.
|
||||
const Polylines anchors = intersection_pl(infill_lines[lidx - 1], shrink(expansion_area, spacing));
|
||||
Polygons internal_unsupported_area = shrink(deep_infill_area, spacing * 4.5);
|
||||
|
||||
#ifdef DEBUG_BRIDGE_OVER_INFILL
|
||||
@@ -3375,6 +3401,9 @@ void PrintObject::bridge_over_infill()
|
||||
std::vector<CandidateSurface> expanded_surfaces;
|
||||
expanded_surfaces.reserve(surfaces_by_layer[lidx].size());
|
||||
for (const CandidateSurface &candidate : surfaces_by_layer[lidx]) {
|
||||
const auto ®ion_config = candidate.region->region().config();
|
||||
const bool turning_pattern = region_config.sparse_infill_pattern == ipHilbertCurve ||
|
||||
region_config.sparse_infill_pattern == ipOctagramSpiral;
|
||||
const Flow &flow = candidate.region->bridging_flow(frSolidInfill, true);
|
||||
Polygons area_to_be_bridge = expand(candidate.new_polys, flow.scaled_spacing());
|
||||
area_to_be_bridge = intersection(area_to_be_bridge, deep_infill_area);
|
||||
@@ -3403,20 +3432,40 @@ void PrintObject::bridge_over_infill()
|
||||
to_lines(area_to_be_bridge), to_lines(boundary_plines), to_lines(anchors), to_lines(expansion_area));
|
||||
#endif
|
||||
|
||||
double bridging_angle = 0;
|
||||
if (!anchors.empty()) {
|
||||
bridging_angle = determine_bridging_angle(area_to_be_bridge, to_lines(anchors),
|
||||
candidate.region->region().config().sparse_infill_pattern.value,
|
||||
candidate.region->region().config().infill_direction.value);
|
||||
} else {
|
||||
// use expansion boundaries as anchors.
|
||||
// Also, use Infill pattern that is neutral for angle determination, since there are no infill lines.
|
||||
bridging_angle = determine_bridging_angle(area_to_be_bridge, to_lines(boundary_plines), InfillPattern::ipLine, 0);
|
||||
double bridging_angle = -1.;
|
||||
if (!anchors.empty() && turning_pattern) {
|
||||
// Orca: Keep adjacent bridges over Hilbert/Octagram aligned despite
|
||||
// their many local turning directions. Use the lower layer's rotation,
|
||||
// since that is the infill supporting the bridge, not the current layer's.
|
||||
for (const LayerRegion *lower_region : layer->lower_layer->regions()) {
|
||||
// Orca: Apply the configured direction only if the same region has
|
||||
// sparse infill below this bridge. A height modifier may put another
|
||||
// pattern underneath, requiring the geometry-based fallback below.
|
||||
if (&lower_region->region() != &candidate.region->region() ||
|
||||
intersection(area_to_be_bridge, to_polygons(lower_region->fill_surfaces.filter_by_type(stInternal))).empty())
|
||||
continue;
|
||||
bridging_angle = calculate_infill_rotation_angle(po, layer->lower_layer->id(), region_config.infill_direction.value,
|
||||
region_config.sparse_infill_rotate_template.value) + 0.5 * PI;
|
||||
// Orca: Apply model alignment as infill generation does, then normalize
|
||||
// the undirected bridge angle to [0, PI), including negative rotations.
|
||||
if (region_config.align_infill_direction_to_model) {
|
||||
const auto &m = po->trafo().matrix();
|
||||
bridging_angle += std::atan2(double(m(1, 0)), double(m(0, 0)));
|
||||
}
|
||||
bridging_angle = std::fmod(bridging_angle, PI);
|
||||
if (bridging_angle < 0.)
|
||||
bridging_angle += PI;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Orca: A different region below (e.g. a height modifier) needs the actual anchor
|
||||
// directions. When there are no sparse anchors, use the expansion boundaries.
|
||||
if (bridging_angle < 0.)
|
||||
bridging_angle = determine_bridging_angle(area_to_be_bridge, to_lines(anchors.empty() ? boundary_plines : anchors));
|
||||
|
||||
// ORCA: Internal bridge angle override
|
||||
// Orca: Preserve the user's absolute or relative internal bridge angle
|
||||
// override after automatic direction selection.
|
||||
if (candidate.region->region().config().internal_bridge_angle.value > 0) {
|
||||
const auto ®ion_config = candidate.region->region().config();
|
||||
const double custom_angle_rad = Geometry::deg2rad(region_config.internal_bridge_angle.value);
|
||||
if (region_config.relative_bridge_angle.value)
|
||||
bridging_angle += custom_angle_rad;
|
||||
@@ -3429,11 +3478,19 @@ void PrintObject::bridge_over_infill()
|
||||
}
|
||||
}
|
||||
|
||||
// Orca: Changing the bridge direction must not change its physical supports.
|
||||
// Extend to actual sparse infill or the existing boundary anchors, never to
|
||||
// a synthetic grid that merely has the same nominal angle and spacing.
|
||||
boundary_plines.insert(boundary_plines.end(), anchors.begin(), anchors.end());
|
||||
if (!lightning_area.empty() && !intersection(area_to_be_bridge, lightning_area).empty()) {
|
||||
boundary_plines = intersection_pl(boundary_plines, expand(area_to_be_bridge, scale_(10)));
|
||||
}
|
||||
Polygons bridging_area = construct_anchored_polygon(area_to_be_bridge, to_lines(boundary_plines), flow, bridging_angle);
|
||||
// Orca: Use four samples per extrusion spacing for Hilbert/Octagram so the
|
||||
// reconstructed boundary follows rounded anchors instead of cutting corners.
|
||||
// Keep the original step for other patterns and at least one coordinate unit
|
||||
// after integer division. This changes boundary accuracy, not infill density.
|
||||
const coord_t scan_spacing = std::max(coord_t(1), flow.scaled_spacing() / (turning_pattern ? 4 : 1));
|
||||
Polygons bridging_area = construct_anchored_polygon(area_to_be_bridge, to_lines(boundary_plines), flow, bridging_angle, scan_spacing);
|
||||
|
||||
// Check collision with other expanded surfaces
|
||||
{
|
||||
@@ -3447,7 +3504,9 @@ void PrintObject::bridge_over_infill()
|
||||
}
|
||||
}
|
||||
if (reconstruct) {
|
||||
bridging_area = construct_anchored_polygon(area_to_be_bridge, to_lines(boundary_plines), flow, bridging_angle);
|
||||
// Orca: Retain the same sampling accuracy when matching a nearby
|
||||
// bridge's direction; rebuilding must not lose the curved supports.
|
||||
bridging_area = construct_anchored_polygon(area_to_be_bridge, to_lines(boundary_plines), flow, bridging_angle, scan_spacing);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3455,6 +3514,13 @@ void PrintObject::bridge_over_infill()
|
||||
// bridging_area = opening(bridging_area, flow.scaled_spacing());
|
||||
bridging_area = opening(bridging_area, flow.scaled_spacing() * 0.75);
|
||||
bridging_area = closing(bridging_area, flow.scaled_spacing());
|
||||
// Orca: Opening/closing can pull rounded bridge ends away from their real
|
||||
// supports. Restore those contacts after smoothing, preserving the cleaned
|
||||
// area and the selected angle; do not smooth the restored contacts again.
|
||||
if (turning_pattern && !bridging_area.empty()) {
|
||||
bridging_area = union_(bridging_area, construct_anchored_polygon(bridging_area, to_lines(boundary_plines), flow,
|
||||
bridging_angle, scan_spacing, true));
|
||||
}
|
||||
bridging_area = intersection(bridging_area, limiting_area);
|
||||
bridging_area = intersection(bridging_area, total_fill_area);
|
||||
bridging_area = diff(bridging_area, total_top_area);
|
||||
|
||||
@@ -1007,7 +1007,9 @@ bool SLAPrintObject::invalidate_step(SLAPrintObjectStep step)
|
||||
|
||||
bool SLAPrintObject::invalidate_all_steps()
|
||||
{
|
||||
return Inherited::invalidate_all_steps() | m_print->invalidate_all_steps();
|
||||
const bool inherited_invalidated = Inherited::invalidate_all_steps();
|
||||
const bool print_invalidated = m_print->invalidate_all_steps();
|
||||
return inherited_invalidated || print_invalidated;
|
||||
}
|
||||
|
||||
double SLAPrintObject::get_elevation() const {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#include <limits>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "DevMapping.h"
|
||||
#include "DevFilaSystem.h"
|
||||
@@ -270,7 +272,7 @@ namespace Slic3r
|
||||
std::set<int> picked_tar;
|
||||
for (int k = 0; k < distance_map.size(); k++)
|
||||
{
|
||||
float min_val = INT_MAX;
|
||||
float min_val = std::numeric_limits<float>::max();
|
||||
int picked_src_idx = -1;
|
||||
int picked_tar_idx = -1;
|
||||
for (int i = 0; i < distance_map.size(); i++)
|
||||
|
||||
@@ -628,8 +628,10 @@ private:
|
||||
void on_button_click(wxCommandEvent &WXUNUSED(ev));
|
||||
void save_colors_to_config();
|
||||
private:
|
||||
#if !defined(__linux__) && !defined(__LINUX__)
|
||||
wxColourData* m_clrData{nullptr};
|
||||
wxColourPickerWidget* m_picker_widget{nullptr};
|
||||
#endif
|
||||
};
|
||||
|
||||
class PointCtrl : public Field {
|
||||
|
||||
+1
-222
@@ -598,7 +598,7 @@ wxString file_wildcards(FileType file_type, const std::string &custom_extension)
|
||||
static std::string libslic3r_translate_callback(const char *s) { return wxGetTranslation(wxString(s, wxConvUTF8)).utf8_str().data(); }
|
||||
|
||||
#ifdef WIN32
|
||||
static GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2, 0xF16F, 0x11CF, 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 };
|
||||
static GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } };
|
||||
|
||||
static void register_win32_device_notification_event()
|
||||
{
|
||||
@@ -7769,21 +7769,6 @@ void GUI_App::stop_http_server()
|
||||
m_http_server.stop();
|
||||
}
|
||||
|
||||
void GUI_App::switch_staff_pick(bool on)
|
||||
{
|
||||
mainframe->m_webview->SendDesignStaffpick(on);
|
||||
}
|
||||
|
||||
bool GUI_App::switch_language()
|
||||
{
|
||||
if (select_language()) {
|
||||
recreate_GUI(_L("Switching application language") + dots);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
static const wxLanguageInfo* linux_get_existing_locale_language(const wxLanguageInfo* language,
|
||||
const wxLanguageInfo* system_language)
|
||||
@@ -7878,72 +7863,6 @@ int GUI_App::GetSingleChoiceIndex(const wxString& message,
|
||||
#endif
|
||||
}
|
||||
|
||||
// select language from the list of installed languages
|
||||
bool GUI_App::select_language()
|
||||
{
|
||||
wxArrayString translations = wxTranslations::Get()->GetAvailableTranslations(SLIC3R_APP_KEY);
|
||||
std::vector<const wxLanguageInfo*> language_infos;
|
||||
language_infos.emplace_back(wxLocale::GetLanguageInfo(wxLANGUAGE_ENGLISH));
|
||||
for (size_t i = 0; i < translations.GetCount(); ++ i) {
|
||||
const wxLanguageInfo *langinfo = wxLocale::FindLanguageInfo(translations[i]);
|
||||
if (langinfo != nullptr)
|
||||
language_infos.emplace_back(langinfo);
|
||||
}
|
||||
sort_remove_duplicates(language_infos);
|
||||
std::sort(language_infos.begin(), language_infos.end(), [](const wxLanguageInfo* l, const wxLanguageInfo* r) { return l->Description < r->Description; });
|
||||
|
||||
wxArrayString names;
|
||||
names.Alloc(language_infos.size());
|
||||
|
||||
// Some valid language should be selected since the application start up.
|
||||
const wxString active_language_code = current_language_code();
|
||||
const wxLanguageInfo* active_language_info = wxLocale::FindLanguageInfo(active_language_code);
|
||||
const wxLanguage current_language = active_language_info != nullptr ? wxLanguage(active_language_info->Language) : wxLanguage(m_wxLocale->GetLanguage());
|
||||
const wxString active_lang_prefix = active_language_code.BeforeFirst('_');
|
||||
int init_selection = -1;
|
||||
int init_selection_alt = -1;
|
||||
int init_selection_default = -1;
|
||||
for (size_t i = 0; i < language_infos.size(); ++ i) {
|
||||
if (wxLanguage(language_infos[i]->Language) == current_language)
|
||||
// The dictionary matches the active language and country.
|
||||
init_selection = i;
|
||||
else if ((language_infos[i]->CanonicalName.BeforeFirst('_') == active_lang_prefix) ||
|
||||
// if the active language is Slovak, mark the Czech language as active.
|
||||
(language_infos[i]->CanonicalName.BeforeFirst('_') == "cs" && active_lang_prefix == "sk"))
|
||||
// The dictionary matches the active language, it does not necessarily match the country.
|
||||
init_selection_alt = i;
|
||||
if (language_infos[i]->CanonicalName.BeforeFirst('_') == "en")
|
||||
// This will be the default selection if the active language does not match any dictionary.
|
||||
init_selection_default = i;
|
||||
names.Add(language_infos[i]->Description);
|
||||
}
|
||||
if (init_selection == -1)
|
||||
// This is the dictionary matching the active language.
|
||||
init_selection = init_selection_alt;
|
||||
if (init_selection != -1)
|
||||
// This is the language to highlight in the choice dialog initially.
|
||||
init_selection_default = init_selection;
|
||||
|
||||
const long index = GetSingleChoiceIndex(_L("Select the language"), _L("Language"), names, init_selection_default);
|
||||
// Try to load a new language.
|
||||
if (index != -1 && (init_selection == -1 || init_selection != index)) {
|
||||
const wxLanguageInfo *new_language_info = language_infos[index];
|
||||
if (this->load_language(new_language_info->CanonicalName, false)) {
|
||||
// Save language at application config.
|
||||
// Which language to save as the selected dictionary language?
|
||||
// 1) Hopefully the language set to wxTranslations by this->load_language(), but that API is weird and we don't want to rely on its
|
||||
// stability in the future:
|
||||
// wxTranslations::Get()->GetBestTranslation(SLIC3R_APP_KEY, wxLANGUAGE_ENGLISH);
|
||||
// 2) Current locale language may not match the dictionary name, see GH issue #3901
|
||||
// m_wxLocale->GetCanonicalName()
|
||||
// 3) new_language_info->CanonicalName is a safe bet. It points to a valid dictionary name.
|
||||
app_config->set("language", new_language_info->CanonicalName.ToUTF8().data());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load gettext translation files and activate them at the start of the application,
|
||||
// based on the "language" key stored in the application config.
|
||||
@@ -8330,146 +8249,6 @@ void GUI_App::show_ip_address_enter_dialog_handler(wxCommandEvent& evt)
|
||||
show_modal_ip_address_enter_dialog(mode == -1?false:true, title);
|
||||
}
|
||||
|
||||
//void GUI_App::add_config_menu(wxMenuBar *menu)
|
||||
//void GUI_App::add_config_menu(wxMenu *menu)
|
||||
//{
|
||||
// auto local_menu = new wxMenu();
|
||||
// wxWindowID config_id_base = wxWindow::NewControlId(int(ConfigMenuCnt));
|
||||
//
|
||||
// const auto config_wizard_name = _(ConfigWizard::name(true));
|
||||
// const auto config_wizard_tooltip = from_u8((boost::format(_utf8(L("Open %s"))) % config_wizard_name).str());
|
||||
// // Cmd+, is standard on OS X - what about other operating systems?
|
||||
// if (is_editor()) {
|
||||
// local_menu->Append(config_id_base + ConfigMenuWizard, config_wizard_name + dots, config_wizard_tooltip);
|
||||
// local_menu->Append(config_id_base + ConfigMenuUpdate, _L("Check for Configuration Updates"), _L("Check for configuration updates"));
|
||||
// local_menu->AppendSeparator();
|
||||
// }
|
||||
// local_menu->Append(config_id_base + ConfigMenuPreferences, _L("Preferences") + dots +
|
||||
//#ifdef __APPLE__
|
||||
// "\tCtrl+,",
|
||||
//#else
|
||||
// "\tCtrl+P",
|
||||
//#endif
|
||||
// _L("Application preferences"));
|
||||
// wxMenu* mode_menu = nullptr;
|
||||
// if (is_editor()) {
|
||||
// local_menu->AppendSeparator();
|
||||
// mode_menu = new wxMenu();
|
||||
// mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeSimple, _L("Simple"), _L("Simple Mode"));
|
||||
// mode_menu->AppendRadioItem(config_id_base + ConfigMenuModeAdvanced, _L("Advanced"), _L("Advanced Mode"));
|
||||
// Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { if (get_mode() == comSimple) evt.Check(true); }, config_id_base + ConfigMenuModeSimple);
|
||||
// Bind(wxEVT_UPDATE_UI, [this](wxUpdateUIEvent& evt) { if (get_mode() == comAdvanced) evt.Check(true); }, config_id_base + ConfigMenuModeAdvanced);
|
||||
//
|
||||
// local_menu->AppendSubMenu(mode_menu, _L("Mode"), wxString::Format(_L("%s Mode"), SLIC3R_APP_NAME));
|
||||
// }
|
||||
// local_menu->AppendSeparator();
|
||||
// local_menu->Append(config_id_base + ConfigMenuLanguage, _L("Language"));
|
||||
// if (is_editor()) {
|
||||
// local_menu->AppendSeparator();
|
||||
// }
|
||||
//
|
||||
// local_menu->Bind(wxEVT_MENU, [this, config_id_base](wxEvent &event) {
|
||||
// switch (event.GetId() - config_id_base) {
|
||||
// case ConfigMenuWizard:
|
||||
// run_wizard(ConfigWizard::RR_USER);
|
||||
// break;
|
||||
// case ConfigMenuUpdate:
|
||||
// check_updates(true);
|
||||
// break;
|
||||
//#ifdef __linux__
|
||||
// case ConfigMenuDesktopIntegration:
|
||||
// show_desktop_integration_dialog();
|
||||
// break;
|
||||
//#endif
|
||||
// case ConfigMenuSnapshots:
|
||||
// //BBS do not support task snapshot
|
||||
// break;
|
||||
// case ConfigMenuPreferences:
|
||||
// {
|
||||
// //BBS GUI refactor: remove unuse layout logic
|
||||
// //bool app_layout_changed = false;
|
||||
// {
|
||||
// // the dialog needs to be destroyed before the call to recreate_GUI()
|
||||
// // or sometimes the application crashes into wxDialogBase() destructor
|
||||
// // so we put it into an inner scope
|
||||
// PreferencesDialog dlg(mainframe);
|
||||
// dlg.ShowModal();
|
||||
// //BBS GUI refactor: remove unuse layout logic
|
||||
// //app_layout_changed = dlg.settings_layout_changed();
|
||||
// if (dlg.seq_top_layer_only_changed())
|
||||
// this->plater_->refresh_print();
|
||||
//
|
||||
// if (dlg.recreate_GUI()) {
|
||||
// recreate_GUI(_L("Restart application") + dots);
|
||||
// return;
|
||||
// }
|
||||
//#ifdef _WIN32
|
||||
// if (is_editor()) {
|
||||
// if (app_config->get("associate_3mf") == "true")
|
||||
// associate_3mf_files();
|
||||
// if (app_config->get("associate_stl") == "true")
|
||||
// associate_stl_files();
|
||||
// }
|
||||
// else {
|
||||
// if (app_config->get("associate_gcode") == "true")
|
||||
// associate_gcode_files();
|
||||
// }
|
||||
//#endif // _WIN32
|
||||
// }
|
||||
// //BBS GUI refactor: remove unuse layout logic
|
||||
// /*if (app_layout_changed) {
|
||||
// // hide full main_sizer for mainFrame
|
||||
// mainframe->GetSizer()->Show(false);
|
||||
// mainframe->update_layout();
|
||||
// mainframe->select_tab(size_t(0));
|
||||
// }*/
|
||||
// break;
|
||||
// }
|
||||
// case ConfigMenuLanguage:
|
||||
// {
|
||||
// /* Before change application language, let's check unsaved changes on 3D-Scene
|
||||
// * and draw user's attention to the application restarting after a language change
|
||||
// */
|
||||
// {
|
||||
// // the dialog needs to be destroyed before the call to switch_language()
|
||||
// // or sometimes the application crashes into wxDialogBase() destructor
|
||||
// // so we put it into an inner scope
|
||||
// wxString title = is_editor() ? wxString(SLIC3R_APP_NAME) : wxString(GCODEVIEWER_APP_NAME);
|
||||
// title += " - " + _L("Choose language");
|
||||
// //wxMessageDialog dialog(nullptr,
|
||||
// MessageDialog dialog(nullptr,
|
||||
// _L("Switching the language requires application restart.\n") + "\n\n" +
|
||||
// _L("Do you want to continue?"),
|
||||
// title,
|
||||
// wxICON_QUESTION | wxOK | wxCANCEL);
|
||||
// if (dialog.ShowModal() == wxID_CANCEL)
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// switch_language();
|
||||
// break;
|
||||
// }
|
||||
// case ConfigMenuFlashFirmware:
|
||||
// //BBS FirmwareDialog::run(mainframe);
|
||||
// break;
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// using std::placeholders::_1;
|
||||
//
|
||||
// if (mode_menu != nullptr) {
|
||||
// auto modfn = [this](int mode, wxCommandEvent&) { if (get_mode() != mode) save_mode(mode); };
|
||||
// mode_menu->Bind(wxEVT_MENU, std::bind(modfn, comSimple, _1), config_id_base + ConfigMenuModeSimple);
|
||||
// mode_menu->Bind(wxEVT_MENU, std::bind(modfn, comAdvanced, _1), config_id_base + ConfigMenuModeAdvanced);
|
||||
// }
|
||||
//
|
||||
// // BBS
|
||||
// //menu->Append(local_menu, _L("Configuration"));
|
||||
// menu->AppendSubMenu(local_menu, _L("Configuration"));
|
||||
//}
|
||||
|
||||
void GUI_App::open_presetbundledialog(size_t open_on_tab, const std::string& highlight_option)
|
||||
{
|
||||
bool app_layout_changed = false;
|
||||
|
||||
@@ -569,7 +569,6 @@ public:
|
||||
void start_http_server(const std::string& provider = ORCA_CLOUD_PROVIDER);
|
||||
void start_http_server(int port, const std::string& provider = ORCA_CLOUD_PROVIDER);
|
||||
void stop_http_server();
|
||||
void switch_staff_pick(bool on);
|
||||
|
||||
void on_show_check_privacy_dlg(int online_login = 0, const std::string& provider = ORCA_CLOUD_PROVIDER);
|
||||
void show_check_privacy_dlg(wxCommandEvent& evt);
|
||||
@@ -583,7 +582,6 @@ public:
|
||||
void persist_window_geometry(wxTopLevelWindow *window, bool default_maximized = false);
|
||||
void update_ui_from_settings();
|
||||
|
||||
bool switch_language();
|
||||
bool load_language(wxString language, bool initial);
|
||||
|
||||
Tab* get_tab(Preset::Type type);
|
||||
@@ -804,7 +802,6 @@ private:
|
||||
bool window_pos_restore(wxTopLevelWindow* window, const std::string &name, bool default_maximized = false);
|
||||
void window_pos_sanitize(wxTopLevelWindow* window);
|
||||
void window_pos_center(wxTopLevelWindow *window);
|
||||
bool select_language();
|
||||
|
||||
// Dynamic printer agent selection - internal helpers for switch_printer_agent
|
||||
// and the plugin load/unload callbacks (init_plugin_gui_wiring).
|
||||
|
||||
@@ -69,6 +69,8 @@ CopyFileResult copy_file_gui(const std::string &from, const std::string &to, std
|
||||
HANDLE handlesrc = nullptr;
|
||||
HANDLE handledst = nullptr;
|
||||
CopyFileResult ret = SUCCESS;
|
||||
DWORD size = 0;
|
||||
DWORD dwRead = 0, dwWrite = 0;
|
||||
|
||||
handlesrc = CreateFile(src.wc_str(),
|
||||
GENERIC_READ,
|
||||
@@ -96,9 +98,8 @@ CopyFileResult copy_file_gui(const std::string &from, const std::string &to, std
|
||||
goto __finished;
|
||||
}
|
||||
|
||||
DWORD size=GetFileSize(handlesrc,NULL);
|
||||
size = GetFileSize(handlesrc,NULL);
|
||||
buff = new char[size+1];
|
||||
DWORD dwRead=0,dwWrite;
|
||||
result = ReadFile(handlesrc, buff, size, &dwRead, NULL);
|
||||
if (!result) {
|
||||
DWORD errCode = GetLastError();
|
||||
|
||||
@@ -702,7 +702,7 @@ bool GizmoObjectManipulation::reset_zero_button(ImGuiWrapper *imgui_wrapper, bo
|
||||
|
||||
for (int i = 0; i < number; i++)
|
||||
{
|
||||
char buf[3][64] = {0};
|
||||
char buf[3][64] = {};
|
||||
float buf_size[3] = {0};
|
||||
for (int j = 0; j < 3; j++) {
|
||||
ImGui::DataTypeFormatString(buf[j], IM_ARRAYSIZE(buf[j]), ImGuiDataType_Double, (void *) &vec[i][j], "%.2f");
|
||||
|
||||
@@ -790,7 +790,7 @@ void IMSlider::draw_ticks(const ImRect& slideable_region) {
|
||||
|
||||
void IMSlider::show_tooltip(const std::string tooltip) {
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, { 6 * m_scale, 3 * m_scale });
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, { 3 * m_scale });
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 3 * m_scale);
|
||||
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROUND);
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, { 0,0,0,0 });
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.00f, 1.00f, 1.00f, 1.00f));
|
||||
|
||||
@@ -1626,7 +1626,7 @@ void MainFrame::register_win32_callbacks()
|
||||
//static GUID GUID_DEVINTERFACE_USB_DEVICE = { 0xA5DCBF10, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED };
|
||||
//static GUID GUID_DEVINTERFACE_DISK = { 0x53f56307, 0xb6bf, 0x11d0, 0x94, 0xf2, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b };
|
||||
//static GUID GUID_DEVINTERFACE_VOLUME = { 0x71a27cdd, 0x812a, 0x11d0, 0xbe, 0xc7, 0x08, 0x00, 0x2b, 0xe2, 0x09, 0x2f };
|
||||
static GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2, 0xF16F, 0x11CF, 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 };
|
||||
static GUID GUID_DEVINTERFACE_HID = { 0x4D1E55B2, 0xF16F, 0x11CF, { 0x88, 0xCB, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30 } };
|
||||
|
||||
// Register USB HID (Human Interface Devices) notifications to trigger the 3DConnexion enumeration.
|
||||
DEV_BROADCAST_DEVICEINTERFACE NotificationFilter = { 0 };
|
||||
@@ -1666,7 +1666,7 @@ void MainFrame::register_win32_callbacks()
|
||||
|
||||
{
|
||||
static constexpr int device_count = 1;
|
||||
RAWINPUTDEVICE devices[device_count] = { 0 };
|
||||
RAWINPUTDEVICE devices[device_count] = {};
|
||||
// multi-axis mouse (SpaceNavigator, etc.)
|
||||
devices[0].usUsagePage = 0x01;
|
||||
devices[0].usUsage = 0x08;
|
||||
@@ -3310,98 +3310,9 @@ void MainFrame::init_menubar_as_editor()
|
||||
auto preference_item = new wxMenuItem(parent_menu, ConfigMenuPreferences + config_id_base, _L("Preferences") + "\t" + ctrl + "P", "");
|
||||
|
||||
#endif
|
||||
//auto printer_item = new wxMenuItem(parent_menu, ConfigMenuPrinter + config_id_base, _L("Printer"), "");
|
||||
//auto language_item = new wxMenuItem(parent_menu, ConfigMenuLanguage + config_id_base, _L("Switch Language"), "");
|
||||
// parent_menu->Bind(wxEVT_MENU, [this, config_id_base](wxEvent& event) {
|
||||
// switch (event.GetId() - config_id_base) {
|
||||
// //case ConfigMenuLanguage:
|
||||
// //{
|
||||
// // /* Before change application language, let's check unsaved changes on 3D-Scene
|
||||
// // * and draw user's attention to the application restarting after a language change
|
||||
// // */
|
||||
// // {
|
||||
// // // the dialog needs to be destroyed before the call to switch_language()
|
||||
// // // or sometimes the application crashes into wxDialogBase() destructor
|
||||
// // // so we put it into an inner scope
|
||||
// // wxString title = _L("Language selection");
|
||||
// // wxMessageDialog dialog(nullptr,
|
||||
// // _L("Switching the language requires application restart.\n") + "\n\n" +
|
||||
// // _L("Do you want to continue?"),
|
||||
// // title,
|
||||
// // wxICON_QUESTION | wxOK | wxCANCEL);
|
||||
// // if (dialog.ShowModal() == wxID_CANCEL)
|
||||
// // return;
|
||||
// // }
|
||||
//
|
||||
// // wxGetApp().switch_language();
|
||||
// // break;
|
||||
// //}
|
||||
// //case ConfigMenuWizard:
|
||||
// //{
|
||||
// // wxGetApp().run_wizard(ConfigWizard::RR_USER);
|
||||
// // break;
|
||||
// //}
|
||||
// case ConfigMenuPrinter:
|
||||
// {
|
||||
// wxGetApp().params_dialog()->Popup();
|
||||
// wxGetApp().get_tab(Preset::TYPE_PRINTER)->restore_last_select_item();
|
||||
// break;
|
||||
// }
|
||||
// case ConfigMenuPreferences:
|
||||
// {
|
||||
// CallAfter([this] {
|
||||
// PreferencesDialog dlg(this);
|
||||
// dlg.ShowModal();
|
||||
//#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
// if (dlg.seq_top_layer_only_changed() || dlg.seq_seq_top_gcode_indices_changed())
|
||||
//#else
|
||||
// if (dlg.seq_top_layer_only_changed())
|
||||
//#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
// plater()->refresh_print();
|
||||
//#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
|
||||
//#ifdef _WIN32
|
||||
// /*
|
||||
// if (wxGetApp().app_config()->get("associate_3mf") == "true")
|
||||
// wxGetApp().associate_3mf_files();
|
||||
// if (wxGetApp().app_config()->get("associate_stl") == "true")
|
||||
// wxGetApp().associate_stl_files();
|
||||
// /*if (wxGetApp().app_config()->get("associate_step") == "true")
|
||||
// wxGetApp().associate_step_files();*/
|
||||
//#endif // _WIN32
|
||||
//#endif
|
||||
// });
|
||||
// break;
|
||||
// }
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
// });
|
||||
|
||||
#ifdef __APPLE__
|
||||
wxString about_title = wxString::Format(_L("&About %s"), SLIC3R_APP_FULL_NAME);
|
||||
//auto about_item = new wxMenuItem(parent_menu, OrcaSlicerMenuAbout + bambu_studio_id_base, about_title, "");
|
||||
//parent_menu->Bind(wxEVT_MENU, [this, bambu_studio_id_base](wxEvent& event) {
|
||||
// switch (event.GetId() - bambu_studio_id_base) {
|
||||
// case OrcaSlicerMenuAbout:
|
||||
// Slic3r::GUI::about();
|
||||
// break;
|
||||
// case OrcaSlicerMenuPreferences:
|
||||
// CallAfter([this] {
|
||||
// PreferencesDialog dlg(this);
|
||||
// dlg.ShowModal();
|
||||
//#if ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
// if (dlg.seq_top_layer_only_changed() || dlg.seq_seq_top_gcode_indices_changed())
|
||||
//#else
|
||||
// if (dlg.seq_top_layer_only_changed())
|
||||
//#endif // ENABLE_GCODE_LINES_ID_IN_H_SLIDER
|
||||
// plater()->refresh_print();
|
||||
// });
|
||||
// break;
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
//});
|
||||
//parent_menu->Insert(0, about_item);
|
||||
append_menu_item(
|
||||
parent_menu, wxID_ANY, _L(about_title), "",
|
||||
[](wxCommandEvent &) { Slic3r::GUI::about();},
|
||||
|
||||
@@ -297,7 +297,7 @@ void MeshClipper::recalculate_triangles()
|
||||
// it so it lies on our line. This will be the figure to subtract
|
||||
// from the cut. The coordinates must not overflow after the transform,
|
||||
// make the rectangle a bit smaller.
|
||||
const coord_t size = (std::numeric_limits<coord_t>::max()/2 - scale_(std::max(std::abs(e * a), std::abs(e * b)))) / 4;
|
||||
const coord_t size = (double(std::numeric_limits<coord_t>::max()/2) - scale_(std::max(std::abs(e * a), std::abs(e * b)))) / 4;
|
||||
Polygons ep {Polygon({Point(-size, 0), Point(size, 0), Point(size, 2*size), Point(-size, 2*size)})};
|
||||
ep.front().rotate(angle);
|
||||
ep.front().translate(scale_(-e * a), scale_(-e * b));
|
||||
@@ -352,7 +352,7 @@ void MeshClipper::recalculate_triangles()
|
||||
|
||||
// To prevent overflow after scaling, downscale the input if needed:
|
||||
double extra_scale = 1.;
|
||||
coord_t limit = coord_t(std::min(std::numeric_limits<coord_t>::max() / (2. * std::max(1., scale_x)), std::numeric_limits<coord_t>::max() / (2. * std::max(1., scale_y))));
|
||||
coord_t limit = coord_t(std::min(double(std::numeric_limits<coord_t>::max()) / (2. * std::max(1., scale_x)), double(std::numeric_limits<coord_t>::max()) / (2. * std::max(1., scale_y))));
|
||||
coord_t max_coord = 0;
|
||||
for (const Point& pt : exp.contour)
|
||||
max_coord = std::max(max_coord, std::max(std::abs(pt.x()), std::abs(pt.y())));
|
||||
|
||||
@@ -66,7 +66,6 @@ class ParamsPanel : public wxPanel
|
||||
{
|
||||
#if __WXOSX__
|
||||
wxWindow* m_tmp_panel;
|
||||
int m_size_move = -1;
|
||||
#endif // __WXOSX__
|
||||
|
||||
private:
|
||||
|
||||
@@ -1112,7 +1112,7 @@ void PartPlate::show_tooltip(const std::string tooltip)
|
||||
{
|
||||
const auto scale = m_plater->get_current_canvas3D()->get_scale();
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, {6 * scale, 3 * scale});
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, {3 * scale});
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 3 * scale);
|
||||
ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BACKGROUND);
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, {0, 0, 0, 0});
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.00f, 1.00f, 1.00f, 1.00f));
|
||||
|
||||
@@ -507,26 +507,14 @@ wxBoxSizer *PreferencesDialog::create_item_language_combobox(wxString title, wxS
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// the dialog needs to be destroyed before the call to switch_language()
|
||||
// or sometimes the application crashes into wxDialogBase() destructor
|
||||
// so we put it into an inner scope
|
||||
MessageDialog msg_wingow(nullptr, _L("Switching languages requires the application to restart.\n") + "\n" + _L("Do you want to continue?"),
|
||||
L("Language selection"), wxICON_QUESTION | wxOK | wxCANCEL);
|
||||
if (msg_wingow.ShowModal() == wxID_CANCEL) {
|
||||
MessageDialog msg_window(nullptr, _L("Switching languages requires the application to restart.\n") + "\n" + _L("Do you want to continue?"),
|
||||
_L("Language selection"), wxICON_QUESTION | wxOK | wxCANCEL);
|
||||
if (msg_window.ShowModal() == wxID_CANCEL) {
|
||||
combobox->SetSelection(m_current_language_selected);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto check = [](bool yes_or_no) {
|
||||
// if (yes_or_no)
|
||||
// return true;
|
||||
int act_btns = ActionButtons::SAVE;
|
||||
return wxGetApp().check_and_keep_current_preset_changes(_L("Switching application language"),
|
||||
_L("Switching application language while some presets are modified."), act_btns);
|
||||
};
|
||||
|
||||
m_current_language_selected = combobox->GetSelection();
|
||||
if (m_current_language_selected >= 0 && m_current_language_selected < vlist.size()) {
|
||||
m_pending_language = vlist[m_current_language_selected]->CanonicalName.ToUTF8().data();
|
||||
@@ -1031,11 +1019,6 @@ wxBoxSizer *PreferencesDialog::create_item_checkbox(wxString title, wxString too
|
||||
app_config->set_bool(param, checkbox->GetValue());
|
||||
app_config->save();
|
||||
|
||||
// if (param == "staff_pick_switch") {
|
||||
// bool pbool = app_config->get("staff_pick_switch") == "true";
|
||||
// wxGetApp().switch_staff_pick(pbool);
|
||||
// }
|
||||
|
||||
if (param == "sync_user_preset") {
|
||||
bool sync = app_config->get("sync_user_preset") == "true" ? true : false;
|
||||
if (sync) {
|
||||
|
||||
@@ -1803,7 +1803,7 @@ static void* get_function(const char* name)
|
||||
return function;
|
||||
|
||||
#if defined(_MSC_VER) || defined(_WIN32)
|
||||
function = GetProcAddress(module, name);
|
||||
function = reinterpret_cast<void*>(GetProcAddress(module, name));
|
||||
#else
|
||||
function = dlsym(module, name);
|
||||
#endif
|
||||
|
||||
@@ -814,13 +814,13 @@ wxBoxSizer* SendMultiMachinePage::create_item_title(wxString title, wxWindow* pa
|
||||
wxBoxSizer* m_sizer_title = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
auto m_title = new wxStaticText(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, 0);
|
||||
m_title->SetForegroundColour(DESIGN_GRAY800_COLOR);
|
||||
m_title->SetForegroundColour(SEND_DESIGN_GRAY800_COLOR);
|
||||
m_title->SetFont(::Label::Head_13);
|
||||
m_title->Wrap(-1);
|
||||
m_title->SetToolTip(tooltip);
|
||||
|
||||
auto m_line = new wxPanel(parent, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL);
|
||||
m_line->SetBackgroundColour(DESIGN_GRAY400_COLOR);
|
||||
m_line->SetBackgroundColour(SEND_DESIGN_GRAY400_COLOR);
|
||||
|
||||
m_sizer_title->Add(m_title, 0, wxALIGN_CENTER | wxALL, 3);
|
||||
m_sizer_title->Add(0, 0, 0, wxLEFT, 9);
|
||||
@@ -843,7 +843,7 @@ wxBoxSizer* SendMultiMachinePage::create_item_checkbox(wxString title, wxWindow*
|
||||
m_sizer_checkbox->Add(0, 0, 0, wxEXPAND | wxLEFT, 8);
|
||||
|
||||
auto checkbox_title = new wxStaticText(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize, 0);
|
||||
checkbox_title->SetForegroundColour(DESIGN_GRAY900_COLOR);
|
||||
checkbox_title->SetForegroundColour(SEND_DESIGN_GRAY900_COLOR);
|
||||
checkbox_title->SetFont(::Label::Body_13);
|
||||
|
||||
auto size = checkbox_title->GetTextExtent(title);
|
||||
@@ -867,12 +867,12 @@ wxBoxSizer* SendMultiMachinePage::create_item_input(wxString str_before, wxStrin
|
||||
{
|
||||
wxBoxSizer* sizer_input = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto input_title = new wxStaticText(parent, wxID_ANY, str_before);
|
||||
input_title->SetForegroundColour(DESIGN_GRAY900_COLOR);
|
||||
input_title->SetForegroundColour(SEND_DESIGN_GRAY900_COLOR);
|
||||
input_title->SetFont(::Label::Body_13);
|
||||
input_title->SetToolTip(tooltip);
|
||||
input_title->Wrap(-1);
|
||||
|
||||
auto input = new ::TextInput(parent, wxEmptyString, wxEmptyString, wxEmptyString, wxDefaultPosition, DESIGN_INPUT_SIZE, wxTE_PROCESS_ENTER);
|
||||
auto input = new ::TextInput(parent, wxEmptyString, wxEmptyString, wxEmptyString, wxDefaultPosition, SEND_DESIGN_INPUT_SIZE, wxTE_PROCESS_ENTER);
|
||||
StateColor input_bg(std::pair<wxColour, int>(wxColour("#F0F0F1"), StateColor::Disabled), std::pair<wxColour, int>(*wxWHITE, StateColor::Enabled));
|
||||
input->SetBackgroundColor(input_bg);
|
||||
input->GetTextCtrl()->SetValue(app_config->get(param));
|
||||
@@ -880,7 +880,7 @@ wxBoxSizer* SendMultiMachinePage::create_item_input(wxString str_before, wxStrin
|
||||
input->GetTextCtrl()->SetValidator(validator);
|
||||
|
||||
auto second_title = new wxStaticText(parent, wxID_ANY, str_after, wxDefaultPosition, wxDefaultSize, wxST_ELLIPSIZE_END);
|
||||
second_title->SetForegroundColour(DESIGN_GRAY900_COLOR);
|
||||
second_title->SetForegroundColour(SEND_DESIGN_GRAY900_COLOR);
|
||||
second_title->SetFont(::Label::Body_13);
|
||||
second_title->SetToolTip(tooltip);
|
||||
second_title->Wrap(-1);
|
||||
@@ -1337,7 +1337,7 @@ wxPanel* SendMultiMachinePage::create_page()
|
||||
m_tip_text->SetMinSize(wxSize(FromDIP(DEVICE_ITEM_MAX_WIDTH), -1));
|
||||
m_tip_text->SetMaxSize(wxSize(FromDIP(DEVICE_ITEM_MAX_WIDTH), -1));
|
||||
m_tip_text->SetLabel(_L("Please select the devices you would like to manage here (up to 6 devices)"));
|
||||
m_tip_text->SetForegroundColour(DESIGN_GRAY800_COLOR);
|
||||
m_tip_text->SetForegroundColour(SEND_DESIGN_GRAY800_COLOR);
|
||||
m_tip_text->SetFont(::Label::Head_20);
|
||||
m_tip_text->Wrap(-1);
|
||||
|
||||
|
||||
@@ -22,15 +22,15 @@ namespace GUI {
|
||||
#define SEND_LEFT_DEV_STATUS 250
|
||||
#define SEND_LEFT_TAKS_STATUS 180
|
||||
|
||||
#define DESIGN_SELECTOR_NOMORE_COLOR wxColour(248, 248, 248)
|
||||
#define DESIGN_GRAY900_COLOR wxColour(38, 46, 48)
|
||||
#define DESIGN_GRAY800_COLOR wxColour(50, 58, 61)
|
||||
#define DESIGN_GRAY600_COLOR wxColour(144, 144, 144)
|
||||
#define DESIGN_GRAY400_COLOR wxColour(166, 169, 170)
|
||||
#define DESIGN_RESOUTION_PREFERENCES wxSize(FromDIP(540), -1)
|
||||
#define DESIGN_COMBOBOX_SIZE wxSize(FromDIP(140), -1)
|
||||
#define DESIGN_LARGE_COMBOBOX_SIZE wxSize(FromDIP(160), -1)
|
||||
#define DESIGN_INPUT_SIZE wxSize(FromDIP(50), -1)
|
||||
#define SEND_DESIGN_SELECTOR_NOMORE_COLOR wxColour(248, 248, 248)
|
||||
#define SEND_DESIGN_GRAY900_COLOR wxColour(38, 46, 48)
|
||||
#define SEND_DESIGN_GRAY800_COLOR wxColour(50, 58, 61)
|
||||
#define SEND_DESIGN_GRAY600_COLOR wxColour(144, 144, 144)
|
||||
#define SEND_DESIGN_GRAY400_COLOR wxColour(166, 169, 170)
|
||||
#define SEND_DESIGN_RESOUTION_PREFERENCES wxSize(FromDIP(540), -1)
|
||||
#define SEND_DESIGN_COMBOBOX_SIZE wxSize(FromDIP(140), -1)
|
||||
#define SEND_DESIGN_LARGE_COMBOBOX_SIZE wxSize(FromDIP(160), -1)
|
||||
#define SEND_DESIGN_INPUT_SIZE wxSize(FromDIP(50), -1)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -995,7 +995,7 @@ void FanControlPopupNew::init_names(MachineObject* obj) {
|
||||
radio_btn_name[AIR_DUCT::AIR_DUCT_HEATING_INTERNAL_FILT] = _L("Heating");
|
||||
radio_btn_name[AIR_DUCT::AIR_DUCT_EXHAUST] = _L("Exhaust");
|
||||
radio_btn_name[AIR_DUCT::AIR_DUCT_FULL_COOLING] = _L("Full Cooling");
|
||||
radio_btn_name[AIR_DUCT::AIR_DUCT_INIT] = L("Init");
|
||||
radio_btn_name[AIR_DUCT::AIR_DUCT_INIT] = _L("Init");
|
||||
|
||||
air_door_func_name[AIR_DOOR::AIR_DOOR_FUNC_CHAMBER] = _L("Chamber");
|
||||
air_door_func_name[AIR_DOOR::AIR_DOOR_FUNC_INNERLOOP] = _L("Innerloop");
|
||||
|
||||
@@ -349,7 +349,7 @@ void* BBLNetworkPlugin::get_function(const char* name)
|
||||
return function;
|
||||
|
||||
#if defined(_MSC_VER) || defined(_WIN32)
|
||||
function = GetProcAddress(m_networking_module, name);
|
||||
function = reinterpret_cast<void*>(GetProcAddress(m_networking_module, name));
|
||||
#else
|
||||
function = dlsym(m_networking_module, name);
|
||||
#endif
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <nlohmann/json.hpp>
|
||||
using json = nlohmann::json;
|
||||
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace Slic3r {
|
||||
@@ -90,6 +91,16 @@ OnMessageFn to_orca_messages(OnMessageFn fn)
|
||||
return [fn = std::move(fn)](std::string dev_id, std::string msg) { fn(std::move(dev_id), BBLPrinterAgent::to_orca_payload(std::move(msg))); };
|
||||
}
|
||||
|
||||
// Retypes a plug-in entry point for an older plug-in generation. The detour through the
|
||||
// generic function pointer marks the signature change as deliberate, which a direct cast
|
||||
// between two signatures does not.
|
||||
template <typename To, typename From>
|
||||
To as_abi(From fn)
|
||||
{
|
||||
static_assert(std::is_function_v<std::remove_pointer_t<From>>, "as_abi retypes a function pointer");
|
||||
return reinterpret_cast<To>(reinterpret_cast<void (*)()>(fn));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::string BBLPrinterAgent::to_orca_filament_id(const std::string& printer_filament_id) const
|
||||
@@ -141,7 +152,7 @@ int BBLPrinterAgent::send_message(std::string dev_id, std::string json_str, int
|
||||
// series through the legacy form would silently drop MessageFlag sign/encrypt.
|
||||
switch (plugin.network_abi()) {
|
||||
case NetworkAbi::Legacy: {
|
||||
auto legacy_func = reinterpret_cast<func_send_message_legacy>(func);
|
||||
auto legacy_func = as_abi<func_send_message_legacy>(func);
|
||||
return legacy_func(agent, std::move(dev_id), std::move(json_str), qos);
|
||||
}
|
||||
case NetworkAbi::V0203:
|
||||
@@ -185,7 +196,7 @@ int BBLPrinterAgent::send_message_to_printer(std::string dev_id, std::string jso
|
||||
if (func && agent) {
|
||||
switch (plugin.network_abi()) {
|
||||
case NetworkAbi::Legacy: {
|
||||
auto legacy_func = reinterpret_cast<func_send_message_to_printer_legacy>(func);
|
||||
auto legacy_func = as_abi<func_send_message_to_printer_legacy>(func);
|
||||
return legacy_func(agent, std::move(dev_id), std::move(json_str), qos);
|
||||
}
|
||||
case NetworkAbi::V0203:
|
||||
@@ -275,7 +286,7 @@ int BBLPrinterAgent::bind(std::string dev_ip, std::string dev_id, std::string de
|
||||
switch (plugin.network_abi()) {
|
||||
case NetworkAbi::Legacy:
|
||||
case NetworkAbi::V0203: {
|
||||
auto older_func = reinterpret_cast<func_bind_pre0208>(func);
|
||||
auto older_func = as_abi<func_bind_pre0208>(func);
|
||||
return older_func(agent, dev_ip, dev_id, sec_link, timezone, improved, update_fn);
|
||||
}
|
||||
case NetworkAbi::Current:
|
||||
@@ -436,9 +447,9 @@ int dispatch_start(CurrentFn func, PrintParams& params, const CallbackFns&... ca
|
||||
params.ams_mapping_info = BBLPrinterAgent::from_orca_payload(std::move(params.ams_mapping_info));
|
||||
switch (plugin.network_abi()) {
|
||||
case NetworkAbi::Legacy:
|
||||
return reinterpret_cast<LegacyFn>(func)(agent, BBLNetworkPlugin::as_legacy(params), callbacks...);
|
||||
return as_abi<LegacyFn>(func)(agent, BBLNetworkPlugin::as_legacy(params), callbacks...);
|
||||
case NetworkAbi::V0203:
|
||||
return reinterpret_cast<Fn0203>(func)(agent, BBLNetworkPlugin::as_0203(params), callbacks...);
|
||||
return as_abi<Fn0203>(func)(agent, BBLNetworkPlugin::as_0203(params), callbacks...);
|
||||
case NetworkAbi::Current:
|
||||
return func(agent, std::move(params), callbacks...);
|
||||
default:
|
||||
|
||||
@@ -57,7 +57,7 @@ void set_miniaturizable(void * window)
|
||||
while(viewObject = (NSView *)[viewEnum nextObject]) {
|
||||
if([viewObject class] == [NSTextField self]) {
|
||||
//[(NSTextField*)viewObject setTextColor : NSColor.whiteColor];
|
||||
mainframe_text_field = viewObject;
|
||||
mainframe_text_field = (NSTextField*)viewObject;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,7 @@ void set_title_colour_after_set_title(void * window)
|
||||
while(viewObject = (NSView *)[viewEnum nextObject]) {
|
||||
if([viewObject class] == [NSTextField self]) {
|
||||
[(NSTextField*)viewObject setTextColor : NSColor.whiteColor];
|
||||
mainframe_text_field = viewObject;
|
||||
mainframe_text_field = (NSTextField*)viewObject;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1106,8 +1106,8 @@ void PresetUpdater::priv::check_installed_vendor_profiles() const
|
||||
|
||||
const auto is_vendor_enabled = (vendor_name == PresetBundle::ORCA_DEFAULT_BUNDLE) // always update configs from resource to vendor for ORCA_DEFAULT_BUNDLE
|
||||
|| (enabled_vendors.find(vendor_name) != enabled_vendors.end());
|
||||
if (enabled_config_update) {
|
||||
if (is_vendor_installed(vendor_name)) {
|
||||
if (is_vendor_installed(vendor_name)) {
|
||||
if (enabled_config_update) {
|
||||
if (is_vendor_enabled) {
|
||||
// Orca: whichever form of the vendor resources ships at the newer
|
||||
// version is the one installing lays down, and the one to judge
|
||||
@@ -1122,17 +1122,12 @@ void PresetUpdater::priv::check_installed_vendor_profiles() const
|
||||
<< resource_ver.to_string() << " from resource, old version " << vendor_ver.to_string();
|
||||
bundles.insert(vendor_name);
|
||||
}
|
||||
}
|
||||
else {
|
||||
//need to be removed because not installed
|
||||
} else {
|
||||
// need to be removed because not installed
|
||||
remove_installed_vendor(vendor_name);
|
||||
}
|
||||
}
|
||||
else if (is_vendor_enabled) {
|
||||
bundles.insert(vendor_name);
|
||||
}
|
||||
}
|
||||
else if (is_vendor_enabled) {
|
||||
} else if (is_vendor_enabled) {
|
||||
bundles.insert(vendor_name);
|
||||
}
|
||||
}
|
||||
@@ -1625,7 +1620,7 @@ void PresetUpdater::priv::check_new_vendors(const std::set<std::string>& system_
|
||||
Http::get(download_url_str)
|
||||
.timeout_connect(5)
|
||||
.on_progress(check_cancel)
|
||||
.on_error([&vendor_id, &retry_count, max_retries](std::string body, std::string error, unsigned http_status) {
|
||||
.on_error([&vendor_id, &retry_count](std::string body, std::string error, unsigned http_status) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "[Orca Updater] download failed for new vendor " << vendor_id
|
||||
<< " (attempt " << retry_count << "/" << max_retries << "): " << error;
|
||||
})
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
#include "libslic3r/AABBTreeLines.hpp"
|
||||
#include "libslic3r/Fill/Fill.hpp"
|
||||
#include "libslic3r/Flow.hpp"
|
||||
#include "libslic3r/Geometry.hpp"
|
||||
@@ -1229,3 +1230,68 @@ TEST_CASE("Smoothing multiline lightning infill keeps its outlines connected", "
|
||||
REQUIRE(smooth.point_count > sharp.point_count);
|
||||
REQUIRE(smooth.sharp_turns < sharp.sharp_turns);
|
||||
}
|
||||
|
||||
TEST_CASE("Sparse plane-path anchors match the printed infill", "[Fill][InternalBridge][Regression]")
|
||||
{
|
||||
// Orca: Compare generated anchors with actual extrusion across plane-path patterns,
|
||||
// smoothing, multiline and rotations; an origin shift must not pass as valid support.
|
||||
const std::string pattern = GENERATE("hilbertcurve", "octagramspiral", "archimedeanchords");
|
||||
const std::string smoothing = GENERATE("0%", "100%");
|
||||
const int multiline = GENERATE(1, 2);
|
||||
const bool rotated = GENERATE(false, true);
|
||||
const bool separated = GENERATE(false, true);
|
||||
CAPTURE(pattern, smoothing, multiline, rotated, separated);
|
||||
|
||||
auto config = DynamicPrintConfig::full_print_config();
|
||||
config.set_deserialize_strict({{"sparse_infill_pattern", pattern},
|
||||
{"sparse_infill_density", "15%"},
|
||||
{"sparse_infill_smooth_factor", smoothing},
|
||||
{"fill_multiline", multiline},
|
||||
{"infill_direction", 45},
|
||||
{"sparse_infill_rotate_template", rotated ? "0,25,50" : ""},
|
||||
{"align_infill_direction_to_model", rotated},
|
||||
{"separated_infills", separated},
|
||||
{"top_shell_layers", 0},
|
||||
{"bottom_shell_layers", 0},
|
||||
{"top_shell_thickness", 0},
|
||||
{"bottom_shell_thickness", 0},
|
||||
{"layer_height", 0.2},
|
||||
{"initial_layer_print_height", 0.2},
|
||||
{"resolution", 0.012}});
|
||||
Print print;
|
||||
Model model;
|
||||
TriangleMesh mesh = make_cube(30, 24, 1);
|
||||
if (separated) {
|
||||
// Orca: Two disconnected bodies in one object must each use their own infill origin.
|
||||
TriangleMesh second = make_cube(30, 24, 1);
|
||||
second.translate(50, 0, 0);
|
||||
mesh.merge(second);
|
||||
}
|
||||
Slic3r::Test::init_print({mesh}, print, model, config, nullptr, false);
|
||||
if (rotated) {
|
||||
model.objects.front()->instances.front()->set_rotation(Vec3d(0., 0., Geometry::deg2rad(23.)));
|
||||
print.apply(model, config);
|
||||
}
|
||||
print.process();
|
||||
|
||||
const Layer &layer = *print.objects().front()->get_layer(4);
|
||||
Polylines printed;
|
||||
for (const LayerRegion *region : layer.regions())
|
||||
for (const ExtrusionEntity *entity : region->fills.flatten().entities)
|
||||
if (entity->role() == erInternalInfill)
|
||||
entity->collect_polylines(printed);
|
||||
REQUIRE_FALSE(printed.empty());
|
||||
const AABBTreeLines::LinesDistancer<Line> printed_tree(to_lines(printed));
|
||||
|
||||
// Orca: Exclude perimeter connections: anchoring and extrusion can trim those differently.
|
||||
const Polylines anchors = intersection_pl(layer.generate_sparse_infill_polylines_for_anchoring(nullptr, nullptr, nullptr),
|
||||
shrink(to_polygons(layer.lslices), scale_(3.)));
|
||||
REQUIRE_FALSE(anchors.empty());
|
||||
double max_distance = 0.;
|
||||
for (const Polyline &path : anchors)
|
||||
for (const Point &point : path.equally_spaced_points(scale_(0.25)))
|
||||
max_distance = std::max(max_distance, printed_tree.distance_from_lines<false>(point));
|
||||
// Orca: Allow only the configured simplification tolerance; infill-scale offsets
|
||||
// would hide anchors that no longer coincide with printed lines.
|
||||
CHECK(unscale<double>(max_distance) <= config.opt_float("resolution"));
|
||||
}
|
||||
|
||||
@@ -4,11 +4,18 @@
|
||||
#include "libslic3r/Print.hpp"
|
||||
#include "libslic3r/Layer.hpp"
|
||||
#include "libslic3r/GCodeReader.hpp"
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
#include "libslic3r/AABBTreeLines.hpp"
|
||||
|
||||
#include "test_helpers.hpp"
|
||||
|
||||
#include <cmath>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
using namespace Slic3r;
|
||||
using namespace Slic3r::Test;
|
||||
@@ -130,3 +137,438 @@ TEST_CASE("Initial layer height is honored", "[PrintObject]")
|
||||
REQUIRE_THAT(*layer_zs.begin(), Catch::Matchers::WithinAbs(0.3, 1e-4));
|
||||
REQUIRE_THAT(*std::next(layer_zs.begin()), Catch::Matchers::WithinAbs(0.5, 1e-4));
|
||||
}
|
||||
|
||||
static TriangleMesh internal_bridge_step()
|
||||
{
|
||||
// Orca: The smaller tower leaves a shoulder whose solid skin needs internal bridges
|
||||
// over the sparse infill in the base, without relying on an external model file.
|
||||
TriangleMesh mesh = make_cube(30, 24, 3);
|
||||
TriangleMesh tower = make_cube(14, 10, 1);
|
||||
tower.translate(8, 7, 3);
|
||||
mesh.merge(tower);
|
||||
return mesh;
|
||||
}
|
||||
|
||||
static DynamicPrintConfig internal_bridge_config(const std::string &pattern, int multiline)
|
||||
{
|
||||
auto config = DynamicPrintConfig::full_print_config();
|
||||
config.set_deserialize_strict({{"sparse_infill_pattern", pattern},
|
||||
{"fill_multiline", multiline},
|
||||
{"sparse_infill_density", "15%"},
|
||||
{"sparse_infill_smooth_factor", "100%"},
|
||||
{"infill_direction", 45},
|
||||
{"internal_bridge_angle", 0},
|
||||
{"thick_internal_bridges", true},
|
||||
{"top_shell_layers", 3},
|
||||
{"bottom_shell_layers", 2},
|
||||
{"top_shell_thickness", 0},
|
||||
{"bottom_shell_thickness", 0},
|
||||
{"layer_height", 0.2},
|
||||
{"initial_layer_print_height", 0.2}});
|
||||
return config;
|
||||
}
|
||||
|
||||
TEST_CASE("Internal bridge angles follow the lower infill layer and model rotation", "[PrintObject][InternalBridge][Regression]")
|
||||
{
|
||||
const std::string pattern = GENERATE("hilbertcurve", "octagramspiral");
|
||||
// Orca: Cover both a central line (odd counts) and offset pairs (even counts).
|
||||
const int multiline = GENERATE(1, 2, 3);
|
||||
CAPTURE(multiline);
|
||||
const double rotation = GENERATE(23., -123.);
|
||||
const std::vector<double> cycle{10., 30., 70.};
|
||||
auto config = internal_bridge_config(pattern, multiline);
|
||||
config.set_deserialize_strict({{"sparse_infill_rotate_template", "10,30,70"},
|
||||
{"align_infill_direction_to_model", true},
|
||||
{"separated_infills", false}});
|
||||
Print print;
|
||||
Model model;
|
||||
init_print({internal_bridge_step()}, print, model, config, nullptr, false);
|
||||
model.objects.front()->instances.front()->set_rotation(Vec3d(0., 0., Geometry::deg2rad(rotation)));
|
||||
print.apply(model, config);
|
||||
print.process();
|
||||
const PrintObject &object = *print.objects().front();
|
||||
size_t bridges = 0;
|
||||
for (size_t i = 1; i < object.layer_count(); ++i) {
|
||||
// Orca: The support is one layer below the bridge. Check the template and model
|
||||
// rotation together, including normalization when the resulting angle is negative.
|
||||
double expected = std::fmod(cycle[(i - 1) % cycle.size()] + 90. + rotation, 180.);
|
||||
if (expected < 0.) expected += 180.;
|
||||
for (const LayerRegion *region : object.get_layer(i)->regions())
|
||||
for (const Surface *surface : region->fill_surfaces.filter_by_type(stInternalBridge)) {
|
||||
CAPTURE(pattern, rotation, i);
|
||||
CHECK_THAT(Geometry::rad2deg(surface->bridge_angle), Catch::Matchers::WithinAbs(expected, 0.001));
|
||||
++bridges;
|
||||
}
|
||||
}
|
||||
REQUIRE(bridges > 0);
|
||||
}
|
||||
|
||||
TEST_CASE("Turning infill does not replace the anchors of another region", "[PrintObject][InternalBridge][Regression]")
|
||||
{
|
||||
// Orca: Keep the right-hand region fixed while changing the left-hand pattern in the
|
||||
// same object. Its bridge areas must be independent of a previous candidate's anchors.
|
||||
const int multiline = GENERATE(1, 2, 3);
|
||||
CAPTURE(multiline);
|
||||
auto right_bridges = [multiline](const std::string &left_pattern) {
|
||||
auto config = internal_bridge_config(left_pattern, multiline);
|
||||
Print print;
|
||||
Model model;
|
||||
init_print({internal_bridge_step()}, print, model, config, nullptr, false);
|
||||
TriangleMesh right = internal_bridge_step();
|
||||
right.translate(50, 0, 0);
|
||||
ModelVolume *volume = model.objects.front()->add_volume(std::move(right));
|
||||
volume->config.set_key_value("sparse_infill_pattern", new ConfigOptionEnum<InfillPattern>(ipRectilinear));
|
||||
volume->config.set_key_value("infill_direction", new ConfigOptionFloat(17.));
|
||||
print.apply(model, config);
|
||||
print.process();
|
||||
std::map<size_t, Polygons> result;
|
||||
const PrintObject &object = *print.objects().front();
|
||||
for (size_t i = 0; i < object.layer_count(); ++i)
|
||||
for (const LayerRegion *region : object.get_layer(i)->regions())
|
||||
if (region->region().config().infill_direction == 17.)
|
||||
polygons_append(result[i], to_polygons(region->fill_surfaces.filter_by_type(stInternalBridge)));
|
||||
return result;
|
||||
};
|
||||
const auto baseline = right_bridges("rectilinear");
|
||||
const auto actual = right_bridges(GENERATE("hilbertcurve", "octagramspiral"));
|
||||
REQUIRE(actual.size() == baseline.size());
|
||||
double total_area = 0.;
|
||||
for (const auto &[layer, expected] : baseline) {
|
||||
CAPTURE(layer);
|
||||
const auto &polys = actual.at(layer);
|
||||
CHECK(area(diff(expected, polys)) < scaled<double>(1.) * scaled<double>(1.) * 1e-6);
|
||||
CHECK(area(diff(polys, expected)) < scaled<double>(1.) * scaled<double>(1.) * 1e-6);
|
||||
total_area += area(expected);
|
||||
}
|
||||
REQUIRE(total_area > 0.);
|
||||
}
|
||||
|
||||
TEST_CASE("Rounded internal bridges end on printed support", "[PrintObject][InternalBridge][Regression]")
|
||||
{
|
||||
const std::string pattern = GENERATE("hilbertcurve", "octagramspiral");
|
||||
const bool separated = GENERATE(false, true);
|
||||
CAPTURE(pattern, separated);
|
||||
auto config = internal_bridge_config(pattern, 1);
|
||||
config.set_deserialize_strict({{"infill_wall_overlap", "0%"}, {"separated_infills", separated}});
|
||||
TriangleMesh mesh = internal_bridge_step();
|
||||
if (separated) {
|
||||
TriangleMesh second = internal_bridge_step();
|
||||
second.translate(50, 0, 0);
|
||||
mesh.merge(second);
|
||||
}
|
||||
Print print;
|
||||
Model model;
|
||||
init_print({mesh}, print, model, config, nullptr, false);
|
||||
print.process();
|
||||
|
||||
// Orca: Check final extrusion endpoints after polygon cleanup and fill generation.
|
||||
// A correct bridge angle and correct sparse anchors alone do not guarantee contact.
|
||||
const PrintObject &object = *print.objects().front();
|
||||
size_t checked = 0;
|
||||
for (size_t i = 1; i < object.layer_count(); ++i) {
|
||||
Polygons support;
|
||||
Polylines walls;
|
||||
for (const LayerRegion *region : object.get_layer(i - 1)->regions()) {
|
||||
region->perimeters.polygons_covered_by_width(support, 0.f);
|
||||
region->fills.polygons_covered_by_width(support, 0.f);
|
||||
region->perimeters.collect_polylines(walls);
|
||||
}
|
||||
REQUIRE_FALSE(support.empty());
|
||||
const AABBTreeLines::LinesDistancer<Line> support_tree(to_lines(union_(support)));
|
||||
const AABBTreeLines::LinesDistancer<Line> wall_tree(to_lines(walls));
|
||||
for (const LayerRegion *region : object.get_layer(i)->regions())
|
||||
for (const ExtrusionEntity *entity : region->fills.flatten().entities) {
|
||||
if (entity->role() != erInternalBridgeInfill)
|
||||
continue;
|
||||
const auto *path = dynamic_cast<const ExtrusionPath *>(entity);
|
||||
REQUIRE(path != nullptr);
|
||||
for (const Line &line : path->polyline.to_polyline().lines()) {
|
||||
// Orca: Sample span ends, excluding short connectors and wall overlap.
|
||||
if (line.length() < scale_(std::max(0.7, 3. * path->width)))
|
||||
continue;
|
||||
for (const Point &point : {line.a, line.b}) {
|
||||
if (wall_tree.distance_from_lines<false>(point) <= scale_(0.5))
|
||||
continue;
|
||||
CAPTURE(i, point.x(), point.y());
|
||||
const double gap = unscale<double>(support_tree.distance_from_lines<true>(point)) - 0.5 * path->width;
|
||||
CHECK(gap <= 0.1);
|
||||
++checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
REQUIRE(checked > 0);
|
||||
}
|
||||
|
||||
TEST_CASE("Enabling separated infill recomputes body origins", "[PrintObject][InternalBridge][Regression]")
|
||||
{
|
||||
const std::string pattern = GENERATE("hilbertcurve", "octagramspiral", "archimedeanchords");
|
||||
CAPTURE(pattern);
|
||||
auto footprint = [&](bool reslice) {
|
||||
auto config = internal_bridge_config(pattern, 2);
|
||||
config.set_deserialize_strict({{"separated_infills", !reslice}});
|
||||
TriangleMesh mesh = internal_bridge_step();
|
||||
TriangleMesh second = internal_bridge_step();
|
||||
second.translate(50, 0, 0);
|
||||
mesh.merge(second);
|
||||
Print print;
|
||||
Model model;
|
||||
init_print({mesh}, print, model, config, nullptr, false);
|
||||
print.process();
|
||||
if (reslice) {
|
||||
// Orca: Enabling centering after a completed slice must rebuild the body
|
||||
// origins now shared by bridge preparation and printed infill.
|
||||
config.set_deserialize_strict({{"separated_infills", true}});
|
||||
print.apply(model, config);
|
||||
print.process();
|
||||
}
|
||||
Polygons result;
|
||||
for (const LayerRegion *region : print.objects().front()->get_layer(4)->regions())
|
||||
region->fills.polygons_covered_by_width(result, 0.f);
|
||||
return union_(result);
|
||||
};
|
||||
const Polygons fresh = footprint(false);
|
||||
const Polygons resliced = footprint(true);
|
||||
REQUIRE_FALSE(fresh.empty());
|
||||
CHECK(area(diff(fresh, resliced)) < scaled<double>(1.) * scaled<double>(1.) * 1e-6);
|
||||
CHECK(area(diff(resliced, fresh)) < scaled<double>(1.) * scaled<double>(1.) * 1e-6);
|
||||
}
|
||||
|
||||
TEST_CASE("Surface centering survives changes to separated infill settings", "[PrintObject][SurfaceInfill][Regression]")
|
||||
{
|
||||
const std::string pattern = GENERATE("archimedeanchords", "octagramspiral");
|
||||
const std::string initial_center = GENERATE("each_surface", "each_model", "each_assembly");
|
||||
const std::string final_center = GENERATE("each_surface", "each_model", "each_assembly");
|
||||
const bool separated = GENERATE(false, true);
|
||||
const std::string top_order = GENERATE("default", "outward", "inward");
|
||||
const std::string bottom_order = top_order == "outward" ? "inward" : top_order == "inward" ? "outward" : "default";
|
||||
const std::string density = GENERATE("80%", "100%");
|
||||
const bool change_center = initial_center != final_center;
|
||||
CAPTURE(pattern, initial_center, final_center, separated, top_order, bottom_order, density);
|
||||
|
||||
auto config = DynamicPrintConfig::full_print_config();
|
||||
config.set_deserialize_strict({{"top_surface_pattern", pattern},
|
||||
{"bottom_surface_pattern", pattern},
|
||||
{"top_surface_fill_order", top_order},
|
||||
{"bottom_surface_fill_order", bottom_order},
|
||||
{"top_surface_density", density},
|
||||
{"bottom_surface_density", density},
|
||||
{"center_of_surface_pattern", initial_center},
|
||||
{"separated_infills", change_center ? separated : !separated},
|
||||
{"sparse_infill_pattern", "rectilinear"},
|
||||
{"sparse_infill_density", "15%"},
|
||||
{"top_shell_layers", 2},
|
||||
{"bottom_shell_layers", 2},
|
||||
{"top_shell_thickness", 0},
|
||||
{"bottom_shell_thickness", 0},
|
||||
{"layer_height", 0.2},
|
||||
{"initial_layer_print_height", 0.2}});
|
||||
|
||||
// Orca: Two disconnected bodies exercise per-body centering. The offset tower also
|
||||
// makes each-surface and each-model centering differ on the top surfaces.
|
||||
TriangleMesh mesh = make_cube(30, 24, 2);
|
||||
TriangleMesh tower = make_cube(12, 10, 1);
|
||||
tower.translate(4, 3, 2);
|
||||
mesh.merge(tower);
|
||||
TriangleMesh second = mesh;
|
||||
second.translate(50, 0, 0);
|
||||
mesh.merge(second);
|
||||
|
||||
// Orca: Equal footprints can hide reordered or reversed paths. Retain their point
|
||||
// sequences and ordering protection to cover the directional surface behavior too.
|
||||
struct SurfaceFillSnapshot {
|
||||
std::map<bool, std::vector<Points>> paths;
|
||||
bool protected_order = true;
|
||||
};
|
||||
auto surface_fills = [](const Print &print) {
|
||||
std::map<std::pair<size_t, ExtrusionRole>, SurfaceFillSnapshot> result;
|
||||
const PrintObject &object = *print.objects().front();
|
||||
for (size_t i = 0; i < object.layer_count(); ++i) {
|
||||
auto collect = [&](const auto &self, const ExtrusionEntity &entity, bool no_sort) -> void {
|
||||
if (const auto *collection = dynamic_cast<const ExtrusionEntityCollection *>(&entity)) {
|
||||
for (const ExtrusionEntity *child : collection->entities)
|
||||
self(self, *child, no_sort || collection->no_sort);
|
||||
} else if (entity.role() == erTopSolidInfill || entity.role() == erBottomSurface) {
|
||||
const auto *path = dynamic_cast<const ExtrusionPath *>(&entity);
|
||||
REQUIRE(path != nullptr);
|
||||
auto &snapshot = result[{i, entity.role()}];
|
||||
// Orca: The centered test model has one body on either side of X=0.
|
||||
// Their traversal order may vary; preserve path order within each body.
|
||||
Points points = path->polyline.to_polyline().points;
|
||||
REQUIRE_FALSE(points.empty());
|
||||
snapshot.paths[points.front().x() > 0].push_back(std::move(points));
|
||||
snapshot.protected_order &= no_sort && !path->can_reverse();
|
||||
}
|
||||
};
|
||||
for (const LayerRegion *region : object.get_layer(i)->regions())
|
||||
collect(collect, region->fills, false);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
Print print;
|
||||
Model model;
|
||||
init_print({mesh}, print, model, config, nullptr, false);
|
||||
print.process();
|
||||
const auto initial = surface_fills(print);
|
||||
config.set_deserialize_strict({{"center_of_surface_pattern", final_center}, {"separated_infills", separated}});
|
||||
print.apply(model, config);
|
||||
// Orca: Preparation owns the body origins, and its invalidation must also force
|
||||
// regeneration of top/bottom extrusion paths, even when sparse infill is unchanged.
|
||||
CHECK_FALSE(print.objects().front()->is_step_done(posPrepareInfill));
|
||||
CHECK_FALSE(print.objects().front()->is_step_done(posInfill));
|
||||
print.process();
|
||||
const auto resliced = surface_fills(print);
|
||||
|
||||
Print fresh_print;
|
||||
Model fresh_model;
|
||||
init_print({mesh}, fresh_print, fresh_model, config, nullptr, false);
|
||||
fresh_print.process();
|
||||
const auto fresh = surface_fills(fresh_print);
|
||||
REQUIRE_FALSE(fresh.empty());
|
||||
REQUIRE(resliced.size() == fresh.size());
|
||||
std::set<ExtrusionRole> roles;
|
||||
bool changed_paths = false;
|
||||
for (const auto &entry : fresh) {
|
||||
CAPTURE(entry.first.first, entry.first.second);
|
||||
REQUIRE_FALSE(entry.second.paths.empty());
|
||||
roles.insert(entry.first.second);
|
||||
REQUIRE(resliced.count(entry.first) == 1);
|
||||
REQUIRE(initial.count(entry.first) == 1);
|
||||
const auto &actual = resliced.at(entry.first);
|
||||
const auto &expected = entry.second;
|
||||
const auto &before = initial.at(entry.first);
|
||||
CHECK((actual.paths == expected.paths));
|
||||
if (!change_center)
|
||||
CHECK((actual.paths == before.paths));
|
||||
if (top_order != "default") {
|
||||
CHECK(expected.protected_order);
|
||||
CHECK(actual.protected_order);
|
||||
CHECK(before.protected_order);
|
||||
}
|
||||
changed_paths |= expected.paths != before.paths;
|
||||
}
|
||||
CHECK(roles.count(erTopSolidInfill) == 1);
|
||||
CHECK(roles.count(erBottomSurface) == 1);
|
||||
// Orca: Guard against a vacuous comparison: changing surface centering must change
|
||||
// the printed pattern, while toggling separated sparse infill must leave it alone.
|
||||
CHECK(changed_paths == change_center);
|
||||
}
|
||||
|
||||
TEST_CASE("Separated infill keeps fragmented and nested bodies independent", "[PrintObject][SurfaceInfill][Regression]")
|
||||
{
|
||||
constexpr size_t grid_size = 8;
|
||||
TriangleMesh mesh;
|
||||
auto add_box = [&](double x, double y, double width, double depth) {
|
||||
TriangleMesh box = make_cube(width, depth, 0.6);
|
||||
box.translate(x, y, 0);
|
||||
mesh.merge(box);
|
||||
};
|
||||
// Orca: Many small islands exercise spatial pruning and the tree's original
|
||||
// island indices. A pillar inside a frame also overlaps its bounding box,
|
||||
// but must remain a separate body because it lies entirely inside the hole.
|
||||
for (size_t x = 0; x < grid_size; ++ x)
|
||||
for (size_t y = 0; y < grid_size; ++ y)
|
||||
add_box(6 * x, 6 * y, 3, 3);
|
||||
add_box(54, 0, 20, 4);
|
||||
add_box(54, 16, 20, 4);
|
||||
add_box(54, 0, 4, 20);
|
||||
add_box(70, 0, 4, 20);
|
||||
add_box(62, 8, 4, 4);
|
||||
|
||||
auto config = DynamicPrintConfig::full_print_config();
|
||||
config.set_deserialize_strict({{"separated_infills", true},
|
||||
{"center_of_surface_pattern", "each_surface"},
|
||||
{"layer_height", 0.2},
|
||||
{"initial_layer_print_height", 0.2},
|
||||
{"elefant_foot_compensation", 0},
|
||||
{"wall_loops", 1}});
|
||||
Print print;
|
||||
Model model;
|
||||
init_print({mesh}, print, model, config, nullptr, false);
|
||||
// Orca: Prepare body bounds through the public pipeline, then inspect the object read-only.
|
||||
print.process();
|
||||
const PrintObject &object = *print.objects().front();
|
||||
REQUIRE(object.layer_count() > 1);
|
||||
for (const Layer *layer : object.layers()) {
|
||||
REQUIRE(layer->lslices.size() == grid_size * grid_size + 2);
|
||||
REQUIRE(layer->lslices_separated_component_bboxes.size() == layer->lslices.size());
|
||||
size_t holes = 0;
|
||||
for (size_t i = 0; i < layer->lslices.size(); ++ i) {
|
||||
const BoundingBox &body = layer->lslices_separated_component_bboxes[i];
|
||||
const BoundingBox &island = layer->lslices_bboxes[i];
|
||||
CHECK(body.min == island.min);
|
||||
CHECK(body.max == island.max);
|
||||
holes += layer->lslices[i].holes.size();
|
||||
}
|
||||
CHECK(holes == 1);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("Body centering survives islands merging and splitting between layers", "[PrintObject][SurfaceInfill][Regression]")
|
||||
{
|
||||
const bool separated = GENERATE(false, true);
|
||||
CAPTURE(separated);
|
||||
// Orca: Four posts join through horizontal then vertical rails, creating a
|
||||
// cycle of overlaps before splitting into four islands again. This exercises
|
||||
// redundant connections and indexing either adjacent layer. A fifth post
|
||||
// stays separate at every height.
|
||||
TriangleMesh mesh;
|
||||
for (int x : {0, 8})
|
||||
for (int y : {0, 8}) {
|
||||
TriangleMesh post = make_cube(4, 4, 1);
|
||||
post.translate(x, y, 0);
|
||||
mesh.merge(post);
|
||||
}
|
||||
for (int y : {0, 8}) {
|
||||
TriangleMesh rail = make_cube(12, 4, 0.2);
|
||||
rail.translate(0, y, 0.2);
|
||||
mesh.merge(rail);
|
||||
}
|
||||
for (int x : {0, 8}) {
|
||||
TriangleMesh rail = make_cube(4, 12, 0.2);
|
||||
rail.translate(x, 0, 0.4);
|
||||
mesh.merge(rail);
|
||||
}
|
||||
TriangleMesh isolated = make_cube(4, 4, 1);
|
||||
isolated.translate(20, 0, 0);
|
||||
mesh.merge(isolated);
|
||||
|
||||
auto config = DynamicPrintConfig::full_print_config();
|
||||
config.set_deserialize_strict({{"separated_infills", separated},
|
||||
{"center_of_surface_pattern", separated ? "each_surface" : "each_model"},
|
||||
{"layer_height", 0.2},
|
||||
{"initial_layer_print_height", 0.2},
|
||||
{"elefant_foot_compensation", 0},
|
||||
{"wall_loops", 1}});
|
||||
Print print;
|
||||
Model model;
|
||||
init_print({mesh}, print, model, config, nullptr, false);
|
||||
// Orca: Prepare body bounds through the public pipeline, then inspect the object read-only.
|
||||
print.process();
|
||||
const PrintObject &object = *print.objects().front();
|
||||
REQUIRE(object.layer_count() == 5);
|
||||
REQUIRE(object.get_layer(0)->lslices.size() == 5);
|
||||
REQUIRE(object.get_layer(1)->lslices.size() == 3);
|
||||
REQUIRE(object.get_layer(2)->lslices.size() == 3);
|
||||
REQUIRE(object.get_layer(4)->lslices.size() == 5);
|
||||
|
||||
BoundingBox isolated_bbox = object.get_layer(0)->lslices_bboxes.front();
|
||||
for (const BoundingBox &bbox : object.get_layer(0)->lslices_bboxes)
|
||||
if (bbox.min.x() > isolated_bbox.min.x())
|
||||
isolated_bbox = bbox;
|
||||
BoundingBox connected_bbox;
|
||||
for (const Layer *layer : object.layers())
|
||||
for (const BoundingBox &bbox : layer->lslices_bboxes)
|
||||
if (bbox.min.x() < isolated_bbox.min.x())
|
||||
connected_bbox.merge(bbox);
|
||||
for (const Layer *layer : object.layers()) {
|
||||
REQUIRE(layer->lslices_separated_component_bboxes.size() == layer->lslices.size());
|
||||
for (size_t i = 0; i < layer->lslices.size(); ++ i) {
|
||||
const BoundingBox &expected = layer->lslices_bboxes[i].min.x() < isolated_bbox.min.x() ? connected_bbox : isolated_bbox;
|
||||
const BoundingBox &actual = layer->lslices_separated_component_bboxes[i];
|
||||
CHECK(actual.min == expected.min);
|
||||
CHECK(actual.max == expected.max);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif
|
||||
|
||||
#include <catch2/catch_all.hpp>
|
||||
#include "test_utils.hpp"
|
||||
|
||||
Reference in New Issue
Block a user