diff --git a/.gitignore b/.gitignore index 4389e41..5ea57b4 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,9 @@ SVG **/machine_full/ **/filament_full/ .idea/ +<<<<<<< HEAD (ad709f NEW: Official filament color selection approved) +======= +test.js +/.cache/ +.clangd +>>>>>>> CHANGE (a6529d ENH: support parts skipping function) \ No newline at end of file diff --git a/BuildMac.sh b/BuildMac.sh new file mode 100644 index 0000000..8f48cde --- /dev/null +++ b/BuildMac.sh @@ -0,0 +1,244 @@ +#!/bin/bash + +set -e +set -o pipefail + +while getopts "1dpa:st:xbc:h" opt; do + case "${opt}" in + d ) + export BUILD_TARGET="deps" + ;; + p ) + export PACK_DEPS="1" + ;; + a ) + export ARCH="$OPTARG" + ;; + s ) + export BUILD_TARGET="slicer" + ;; + t ) + export OSX_DEPLOYMENT_TARGET="$OPTARG" + ;; + x ) + export SLICER_CMAKE_GENERATOR="Ninja" + export SLICER_BUILD_TARGET="all" + export DEPS_CMAKE_GENERATOR="Ninja" + ;; + b ) + export BUILD_ONLY="1" + ;; + c ) + export BUILD_CONFIG="$OPTARG" + ;; + 1 ) + export CMAKE_BUILD_PARALLEL_LEVEL=1 + ;; + h ) echo "Usage: ./BuildMac.sh [-1][-d][-s][-x][-b][-c]" + echo " -d: Build deps" + echo " -a: Set ARCHITECTURE (arm64 or x86_64 or universal)" + echo " -s: Build slicer only" + echo " -t: Specify minimum version of the target platform, default is 10.15" + echo " -x: Use Ninja CMake generator, default is Xcode" + echo " -b: Build without reconfiguring CMake" + echo " -c: Set CMake build configuration, default is Release" + echo " -1: limit builds to 1 core (where possible)" + exit 0 + ;; + * ) + ;; + esac +done + +if [ -z "$ARCH" ]; then + ARCH="$(uname -m)" + export ARCH +fi + +if [ -z "$BUILD_CONFIG" ]; then + export BUILD_CONFIG="Release" +fi + +if [ -z "$BUILD_TARGET" ]; then + export BUILD_TARGET="all" +fi + +if [ -z "$SLICER_CMAKE_GENERATOR" ]; then + export SLICER_CMAKE_GENERATOR="Xcode" +fi + +if [ -z "$SLICER_BUILD_TARGET" ]; then + export SLICER_BUILD_TARGET="ALL_BUILD" +fi + +if [ -z "$DEPS_CMAKE_GENERATOR" ]; then + export DEPS_CMAKE_GENERATOR="Unix Makefiles" +fi + +if [ -z "$OSX_DEPLOYMENT_TARGET" ]; then + export OSX_DEPLOYMENT_TARGET="10.15" +fi + +echo "Build params:" +echo " - ARCH: $ARCH" +echo " - BUILD_CONFIG: $BUILD_CONFIG" +echo " - BUILD_TARGET: $BUILD_TARGET" +echo " - CMAKE_GENERATOR: $SLICER_CMAKE_GENERATOR for Slicer, $DEPS_CMAKE_GENERATOR for deps" +echo " - OSX_DEPLOYMENT_TARGET: $OSX_DEPLOYMENT_TARGET" +echo " - CMAKE_BUILD_PARALLEL_LEVEL: $CMAKE_BUILD_PARALLEL_LEVEL" +echo + +PROJECT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_BUILD_DIR="$PROJECT_DIR/build/$ARCH" +DEPS_DIR="$PROJECT_DIR/deps" +DEPS_BUILD_DIR="$DEPS_DIR/build/$ARCH" +DEPS="$DEPS_BUILD_DIR/QIDIStudio_deps" + +if [ "$SLICER_CMAKE_GENERATOR" == "Xcode" ]; then + export BUILD_DIR_CONFIG_SUBDIR="/$BUILD_CONFIG" +else + export BUILD_DIR_CONFIG_SUBDIR="" +fi + +function build_deps() { + # iterate over two architectures: x86_64 and arm64 + for _ARCH in x86_64 arm64; do + # if ARCH is universal or equal to _ARCH + if [ "$ARCH" == "universal" ] || [ "$ARCH" == "$_ARCH" ]; then + + PROJECT_BUILD_DIR="$PROJECT_DIR/build/$_ARCH" + DEPS_BUILD_DIR="$DEPS_DIR/build/$_ARCH" + DEPS="$DEPS_BUILD_DIR/QIDIStudio_deps" + + echo "Building deps..." + ( + set -x + mkdir -p "$DEPS" + cd "$DEPS_BUILD_DIR" + if [ "1." != "$BUILD_ONLY". ]; then + cmake "${DEPS_DIR}" \ + -G "${DEPS_CMAKE_GENERATOR}" \ + -DDESTDIR="$DEPS" \ + -DOPENSSL_ARCH="darwin64-${_ARCH}-cc" \ + -DCMAKE_BUILD_TYPE="$BUILD_CONFIG" \ + -DCMAKE_OSX_ARCHITECTURES:STRING="${_ARCH}" \ + -DCMAKE_OSX_DEPLOYMENT_TARGET="${OSX_DEPLOYMENT_TARGET}" + fi + cmake --build . --parallel ${CMAKE_BUILD_PARALLEL_LEVEL} --config "$BUILD_CONFIG" --target deps + ) + fi + done +} + +function pack_deps() { + echo "Packing deps..." + ( + set -x + cd "$DEPS_DIR" + tar -zcvf "QIDIStudio_dep_mac_${ARCH}_$(date +"%Y%m%d").tar.gz" "build" + ) +} + +function build_slicer() { + # iterate over two architectures: x86_64 and arm64 + for _ARCH in x86_64 arm64; do + # if ARCH is universal or equal to _ARCH + if [ "$ARCH" == "universal" ] || [ "$ARCH" == "$_ARCH" ]; then + + PROJECT_BUILD_DIR="$PROJECT_DIR/build/$_ARCH" + DEPS_BUILD_DIR="$DEPS_DIR/build/$_ARCH" + DEPS="$DEPS_BUILD_DIR/QIDIStudio_deps" + + echo "Building slicer for $_ARCH..." + ( + set -x + mkdir -p "$PROJECT_BUILD_DIR" + cd "$PROJECT_BUILD_DIR" + if [ "1." != "$BUILD_ONLY". ]; then + cmake "${PROJECT_DIR}" \ + -G "${SLICER_CMAKE_GENERATOR}" \ + -DQDT_RELEASE_TO_PUBLIC=1 \ + -DQDT_INTERNAL_TESTING=0 \ + -DCMAKE_PREFIX_PATH="$DEPS/usr/local" \ + -DCMAKE_INSTALL_PREFIX="$PWD/QIDIStudio" \ + -DCMAKE_BUILD_TYPE="$BUILD_CONFIG" \ + -DCMAKE_MACOSX_RPATH=ON \ + -DCMAKE_INSTALL_RPATH="${DEPS}/usr/local" \ + -DCMAKE_MACOSX_BUNDLE=ON \ + -DCMAKE_OSX_ARCHITECTURES="${_ARCH}" \ + -DCMAKE_OSX_DEPLOYMENT_TARGET="${OSX_DEPLOYMENT_TARGET}" + fi + cmake --build . --config "$BUILD_CONFIG" --target "$SLICER_BUILD_TARGET" + ) + + + echo "Fix macOS app package..." + ( + cd "$PROJECT_BUILD_DIR" + mkdir -p QIDIStudio + cd QIDIStudio + # remove previously built app + rm -rf ./QIDIStudio.app + # fully copy newly built app + cp -pR "../src$BUILD_DIR_CONFIG_SUBDIR/QIDIStudio.app" ./QIDIStudio.app + # fix resources + resources_path=$(readlink ./QIDIStudio.app/Contents/Resources) + rm ./QIDIStudio.app/Contents/Resources + cp -R "$resources_path" ./QIDIStudio.app/Contents/Resources + # delete .DS_Store file + find ./QIDIStudio.app/ -name '.DS_Store' -delete + ) + + fi + done +} + +function build_universal() { + echo "Building universal binary..." + + PROJECT_BUILD_DIR="$PROJECT_DIR/build/$ARCH" + + # Create universal binary + echo "Creating universal binary..." + # PROJECT_BUILD_DIR="$PROJECT_DIR/build_Universal" + mkdir -p "$PROJECT_BUILD_DIR/QIDIStudio" + UNIVERSAL_APP="$PROJECT_BUILD_DIR/QIDIStudio/QIDIStudio.app" + rm -rf "$UNIVERSAL_APP" + cp -R "$PROJECT_DIR/build/arm64/QIDIStudio/QIDIStudio.app" "$UNIVERSAL_APP" + + # Get the binary path inside the .app bundle + BINARY_PATH="Contents/MacOS/QIDIStudio" + + # Create universal binary using lipo + lipo -create \ + "$PROJECT_DIR/build/x86_64/QIDIStudio/QIDIStudio.app/$BINARY_PATH" \ + "$PROJECT_DIR/build/arm64/QIDIStudio/QIDIStudio.app/$BINARY_PATH" \ + -output "$UNIVERSAL_APP/$BINARY_PATH" + + echo "Universal binary created at $UNIVERSAL_APP" +} + +case "${BUILD_TARGET}" in + all) + build_deps + build_slicer + ;; + deps) + build_deps + ;; + slicer) + build_slicer + ;; + *) + echo "Unknown target: $BUILD_TARGET. Available targets: deps, slicer, all." + exit 1 + ;; +esac + +if [ "$ARCH" = "universal" ] && [ "$BUILD_TARGET" != "deps" ]; then + build_universal +fi + +if [ "1." == "$PACK_DEPS". ]; then + pack_deps +fi \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index dfe2ff9..78fd42d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,7 +55,10 @@ if (APPLE) if (CMAKE_MACOSX_BUNDLE) set(CMAKE_INSTALL_RPATH @executable_path/../Frameworks) endif() - set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version" FORCE) + if (NOT CMAKE_OSX_DEPLOYMENT_TARGET) + set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version" FORCE) + endif () + message(STATUS "CMAKE_OSX_DEPLOYMENT_TARGET: ${CMAKE_OSX_DEPLOYMENT_TARGET}") elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux") set(CMAKE_INSTALL_RPATH "$ORIGIN") endif () @@ -247,6 +250,8 @@ if (NOT MSVC AND ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMP # On GCC and Clang, no return from a non-void function is a warning only. Here, we make it an error. add_compile_options(-Werror=return-type) + add_compile_options(-Wno-unused-function -Wno-unused-variable -Wno-unused-but-set-variable -Wno-unused-label -Wno-unused-local-typedefs) + # removes LOTS of extraneous Eigen warnings (GCC only supports it since 6.1) # https://eigen.tuxfamily.org/bz/show_bug.cgi?id=1221 if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 6.0) @@ -260,6 +265,10 @@ if (NOT MSVC AND ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMP add_compile_options(-Wno-deprecated-declarations) endif() + if((${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang" OR ${CMAKE_CXX_COMPILER_ID} STREQUAL "AppleClang") AND ${CMAKE_CXX_COMPILER_VERSION} VERSION_GREATER 15) + add_compile_options(-Wno-error=enum-constexpr-conversion) + endif() + #GCC generates loads of -Wunknown-pragmas when compiling igl. The fix is not easy due to a bug in gcc, see # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66943 or # https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53431 @@ -314,7 +323,7 @@ if(WIN32) add_definitions(-D_USE_MATH_DEFINES -D_WIN32 -D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS) if(MSVC) # BOOST_ALL_NO_LIB: Avoid the automatic linking of Boost libraries on Windows. Rather rely on explicit linking. - add_definitions(-DBOOST_ALL_NO_LIB -DBOOST_USE_WINAPI_VERSION=0x601 -DBOOST_SYSTEM_USE_UTF8 ) + add_definitions(-DBOOST_ALL_NO_LIB -DBOOST_USE_WINAPI_VERSION=0x602 -DBOOST_SYSTEM_USE_UTF8 ) # Force the source code encoding to UTF-8. See QIDIStudio GH pull request #5583 add_compile_options("$<$:/utf-8>") add_compile_options("$<$:/utf-8>") @@ -356,7 +365,7 @@ endif() # set(Boost_COMPILER "-mgw81") # boost::process was introduced first in version 1.64.0, # boost::beast::detail::base64 was introduced first in version 1.66.0 -set(MINIMUM_BOOST_VERSION "1.66.0") +set(MINIMUM_BOOST_VERSION "1.83.0") set(_boost_components "system;filesystem;thread;log;locale;regex;chrono;atomic;date_time;iostreams") find_package(Boost ${MINIMUM_BOOST_VERSION} REQUIRED COMPONENTS ${_boost_components}) diff --git a/Dockerfile b/Dockerfile index 9220b28..3205806 100644 --- a/Dockerfile +++ b/Dockerfile @@ -68,7 +68,7 @@ WORKDIR /QIDIStudio # It might conflict with your mapped user, remove if user ubuntu exist RUN if id "ubuntu" >/dev/null 2>&1; then userdel -r ubuntu; fi -# It's easier to run Bambu Studio as the same username, +# It's easier to run QIDI Studio as the same username, # UID and GID as your workstation. Since we bind mount # your home directory into the container, it's handy # to keep permissions the same. Just in case, defaults diff --git a/qdt/i18n/QIDIStudio.pot b/qdt/i18n/QIDIStudio.pot index e319e92..c237f58 100644 --- a/qdt/i18n/QIDIStudio.pot +++ b/qdt/i18n/QIDIStudio.pot @@ -13682,7 +13682,7 @@ msgstr "" msgid "Successfully sent. Close current page in %s s." msgstr "" -msgid "The selected printer is not connect box, please check." +msgid "The selected printer is not connected to the BOX or the filaments are not synchronized. Please check." msgstr "" msgid "Failed to set the box printing slot..." diff --git a/qdt/i18n/cs/QIDIStudio_cs.po b/qdt/i18n/cs/QIDIStudio_cs.po index 5afd7bf..fb372fe 100644 --- a/qdt/i18n/cs/QIDIStudio_cs.po +++ b/qdt/i18n/cs/QIDIStudio_cs.po @@ -15411,8 +15411,8 @@ msgstr "Úspěšně odesláno. Automatický přechod na stránku zařízení za msgid "Successfully sent. Close current page in %s s." msgstr "Úspěšně odesláno. Uzavření aktuální stránky za %s s." -msgid "The selected printer is not connect box, please check." -msgstr "Vybraná tiskárna není připojena k BOXu, prosím zkontrolujte." +msgid "The selected printer is not connected to the BOX or the filaments are not synchronized. Please check." +msgstr "Vybraná tiskárna není připojena k BOX nebo filamenty nejsou synchronizovány. Prosím, zkontrolujte." msgid "Failed to set the box printing slot..." msgstr "Nastavení tiskového slotu BOXu selhalo..." @@ -15460,4 +15460,13 @@ msgid "Current printer is printing now" msgstr "Aktuální tiskárna právě tiskne" msgid "Please do not mix-use the Ext with BOX" -msgstr "Prosím, nemíchejte Ext s BOX" \ No newline at end of file +msgstr "Prosím, nemíchejte Ext s BOX" + +msgid "Recommended box temperature" +msgstr "Doporučená teplota boxu" + +msgid "Recommended box temperature range of this filament. 0 means no set" +msgstr "Doporučený teplotní rozsah boxu pro toto vlákno. 0 znamená žádné nastavení." + +msgid "Set the temperature of the box during printing, set to 0 (representing off)." +msgstr "Nastavte teplotu boxu během tisku. Nastavte na 0 (vypnuto)." diff --git a/qdt/i18n/de/QIDIStudio_de.po b/qdt/i18n/de/QIDIStudio_de.po index 263f038..62fe211 100644 --- a/qdt/i18n/de/QIDIStudio_de.po +++ b/qdt/i18n/de/QIDIStudio_de.po @@ -17216,8 +17216,8 @@ msgstr "Erfolgreich gesendet. Automatische Weiterleitung zur Geräteseite in %s msgid "Successfully sent. Close current page in %s s." msgstr "Erfolgreich gesendet. Schließe aktuelle Seite in %s s." -msgid "The selected printer is not connect box, please check." -msgstr "Der gewählte Drucker ist nicht mit der BOX verbunden, bitte überprüfen." +msgid "The selected printer is not connected to the BOX or the filaments are not synchronized. Please check." +msgstr "Der ausgewählte Drucker ist nicht mit der BOX verbunden oder die Filamente sind nicht synchronisiert. Bitte überprüfen Sie dies." msgid "Failed to set the box printing slot..." msgstr "Fehler beim Einrichten des Druckfachs der BOX..." @@ -17265,4 +17265,13 @@ msgid "Current printer is printing now" msgstr "Der aktuelle Drucker druckt gerade"​ msgid "Please do not mix-use the Ext with BOX" -msgstr "Bitte verwenden Sie das Externe nicht zusammen mit dem BOX." \ No newline at end of file +msgstr "Bitte verwenden Sie das Externe nicht zusammen mit dem BOX." + +msgid "Recommended box temperature" +msgstr "Empfohlene Box-Temperatur" + +msgid "Recommended box temperature range of this filament. 0 means no set" +msgstr "Empfohlener Temperaturbereich der Box für dieses Filament. 0 bedeutet keine Einstellung." + +msgid "Set the temperature of the box during printing, set to 0 (representing off)." +msgstr "Stellen Sie die Temperatur der Box während des Drucks ein. Auf 0 setzen (ausgeschaltet)." \ No newline at end of file diff --git a/qdt/i18n/en/QIDIStudio_en.po b/qdt/i18n/en/QIDIStudio_en.po index b6dd992..c065f86 100644 --- a/qdt/i18n/en/QIDIStudio_en.po +++ b/qdt/i18n/en/QIDIStudio_en.po @@ -16875,7 +16875,7 @@ msgstr "" msgid "Successfully sent. Close current page in %s s." msgstr "" -msgid "The selected printer is not connect box, please check." +msgid "The selected printer is not connected to the BOX or the filaments are not synchronized. Please check." msgstr "" msgid "Failed to set the box printing slot..." @@ -16924,4 +16924,13 @@ msgid "Current printer is printing now"​​ ​msgstr ""​ msgid "Please do not mix-use the Ext with BOX" +msgstr "" + +msgid "Recommended box temperature" +msgstr "" + +msgid "Recommended box temperature range of this filament. 0 means no set" +msgstr "" + +msgid "Set the temperature of the box during printing, set to 0 (representing off)." msgstr "" \ No newline at end of file diff --git a/qdt/i18n/es/QIDIStudio_es.po b/qdt/i18n/es/QIDIStudio_es.po index 32a4a33..64b2377 100644 --- a/qdt/i18n/es/QIDIStudio_es.po +++ b/qdt/i18n/es/QIDIStudio_es.po @@ -17214,8 +17214,8 @@ msgstr "Enviado correctamente. Redirección automática a la página del disposi msgid "Successfully sent. Close current page in %s s." msgstr "Enviado correctamente. Cierre de página actual en %s s." -msgid "The selected printer is not connect box, please check." -msgstr "La impresora seleccionada no está conectada a la BOX, por favor verifique." +msgid "The selected printer is not connected to the BOX or the filaments are not synchronized. Please check." +msgstr "La impresora seleccionada no está conectada a la BOX o los filamentos no están sincronizados. Por favor, compruébelo." msgid "Failed to set the box printing slot..." msgstr "Error al configurar el compartimento de impresión de la BOX..." @@ -17263,4 +17263,13 @@ msgid "Current printer is printing now"​​ msgstr "La impresora actual está imprimiendo ahora"​ msgid "Please do not mix-use the Ext with BOX" -msgstr "Por favor, no mezcle Ext con BOX" \ No newline at end of file +msgstr "Por favor, no mezcle Ext con BOX" + +msgid "Recommended box temperature" +msgstr "Temperatura recomendada de la cámara" + +msgid "Recommended box temperature range of this filament. 0 means no set" +msgstr "Rango de temperatura recomendado de la cámara para este filamento. 0 significa sin ajustar" + +msgid "Set the temperature of the box during printing, set to 0 (representing off)." +msgstr "Ajuste la temperatura de la cámara durante la impresión. Configúrela a 0 (apagado)." \ No newline at end of file diff --git a/qdt/i18n/fr/QIDIStudio_fr.po b/qdt/i18n/fr/QIDIStudio_fr.po index 37a9efa..96e98ef 100644 --- a/qdt/i18n/fr/QIDIStudio_fr.po +++ b/qdt/i18n/fr/QIDIStudio_fr.po @@ -17167,8 +17167,8 @@ msgstr "Envoyé avec succès. Redirection automatique vers la page de l'appareil msgid "Successfully sent. Close current page in %s s." msgstr "Envoyé avec succès. Fermeture de la page actuelle dans %s s." -msgid "The selected printer is not connect box, please check." -msgstr "L'imprimante sélectionnée n'est pas connectée à la BOÎTE, veuillez vérifier." +msgid "The selected printer is not connected to the BOX or the filaments are not synchronized. Please check." +msgstr "L’imprimante sélectionnée n’est pas connectée à la BOX ou les filaments ne sont pas synchronisés. Veuillez vérifier." msgid "Failed to set the box printing slot..." msgstr "Échec de la configuration du compartiment d'impression de la BOÎTE..." @@ -17216,4 +17216,13 @@ msgid "Current printer is printing now"​​ ​msgstr "L'imprimante actuelle est en cours d'impression"​ msgid "Please do not mix-use the Ext with BOX" -msgstr "Ne pas mélanger l'Ext avec l'BXO" \ No newline at end of file +msgstr "Ne pas mélanger l'Ext avec l'BXO" + +msgid "Recommended box temperature" +msgstr "Température recommandée de l'enceinte" + +msgid "Recommended box temperature range of this filament. 0 means no set" +msgstr "Plage de température recommandée de l'enceinte pour ce filament. 0 signifie non réglé" + +msgid "Set the temperature of the box during printing, set to 0 (representing off)." +msgstr "Réglez la température de l'enceinte pendant l'impression. Mettez à 0 (désactivé)." \ No newline at end of file diff --git a/qdt/i18n/hu/QIDIStudio_hu.po b/qdt/i18n/hu/QIDIStudio_hu.po index 3335c71..015388e 100644 --- a/qdt/i18n/hu/QIDIStudio_hu.po +++ b/qdt/i18n/hu/QIDIStudio_hu.po @@ -17210,8 +17210,8 @@ msgstr "Sikeresen elküldve. Automatikusan átirányítja az eszköz oldalra %s msgid "Successfully sent. Close current page in %s s." msgstr "Sikeresen elküldve. Az aktuális oldal bezárása %s másodperc múlva." -msgid "The selected printer is not connect box, please check." -msgstr "A kiválasztott nyomtató nincs csatlakoztatva a BOX-hoz, kérjük ellenőrizze." +msgid "The selected printer is not connected to the BOX or the filaments are not synchronized. Please check." +msgstr "A kiválasztott nyomtató nincs csatlakoztatva a BOX-hoz, vagy a filamentek nincsenek szinkronban. Kérjük, ellenőrizze." msgid "Failed to set the box printing slot..." msgstr "A BOX nyomtatási rekeszének beállítása sikertelen..." @@ -17259,4 +17259,13 @@ msgid "Current printer is printing now"​​ ​msgstr "Az aktuális nyomtató épp nyomtat"​ msgid "Please do not mix-use the Ext with BOX" -msgstr "Ne keverd a külső anyagot az BOX-sel" \ No newline at end of file +msgstr "Ne keverd a külső anyagot az BOX-sel" + +msgid "Recommended box temperature" +msgstr "Ajánlott kamra hőmérséklet" + +msgid "Recommended box temperature range of this filament. 0 means no set" +msgstr "Az ilyen típusú filament ajánlott kamrahőmérséklet-tartománya. 0 értéknél nincs beállítás" + +msgid "Set the temperature of the box during printing, set to 0 (representing off)." +msgstr "Állítsa be a kamra hőmérsékletét nyomtatás közben. Állítsa 0-ra (kikapcsolva)." \ No newline at end of file diff --git a/qdt/i18n/it/QIDIStudio_it.po b/qdt/i18n/it/QIDIStudio_it.po index 575bd5b..5eebdba 100644 --- a/qdt/i18n/it/QIDIStudio_it.po +++ b/qdt/i18n/it/QIDIStudio_it.po @@ -17213,8 +17213,8 @@ msgstr "Inviato con successo. Reindirizzamento automatico alla pagina del dispos msgid "Successfully sent. Close current page in %s s." msgstr "Inviato con successo. Chiusura pagina corrente tra %s s." -msgid "The selected printer is not connect box, please check." -msgstr "La stampante selezionata non è connessa alla BOX, verificare." +msgid "The selected printer is not connected to the BOX or the filaments are not synchronized. Please check." +msgstr "La stampante selezionata non è connessa alla BOX oppure i filamenti non sono sincronizzati. Si prega di verificare." msgid "Failed to set the box printing slot..." msgstr "Impostazione dello slot di stampa della BOX fallita..." @@ -17262,4 +17262,13 @@ msgid "Current printer is printing now"​​ ​msgstr "La stampante corrente sta stampando"​ msgid "Please do not mix-use the Ext with BOX" -msgstr "Non utilizzare contemporaneamente Ext con BOX." \ No newline at end of file +msgstr "Non utilizzare contemporaneamente Ext con BOX." + +msgid "Recommended box temperature" +msgstr "Temperatura consigliata della camera" + +msgid "Recommended box temperature range of this filament. 0 means no set" +msgstr "Intervallo di temperatura consigliato della camera per questo filamento. 0 significa non impostato" + +msgid "Set the temperature of the box during printing, set to 0 (representing off)." +msgstr "Imposta la temperatura della camera durante la stampa. Imposta a 0 (spento)." \ No newline at end of file diff --git a/qdt/i18n/ja/QIDIStudio_ja.po b/qdt/i18n/ja/QIDIStudio_ja.po index 185b749..b24ab81 100644 --- a/qdt/i18n/ja/QIDIStudio_ja.po +++ b/qdt/i18n/ja/QIDIStudio_ja.po @@ -17192,8 +17192,8 @@ msgstr "送信に成功しました。%s秒後にデバイスページへ自動 msgid "Successfully sent. Close current page in %s s." msgstr "送信に成功しました。%s秒後に現在のページを閉じます。" -msgid "The selected printer is not connect box, please check." -msgstr "選択したプリンターはBOXに接続されていません。確認してください。" +msgid "The selected printer is not connected to the BOX or the filaments are not synchronized. Please check." +msgstr "選択されたプリンターがBOXに接続されていないか、フィラメントが同期されていません。確認してください。" msgid "Failed to set the box printing slot..." msgstr "BOX印刷スロットの設定に失敗しました..." @@ -17241,4 +17241,13 @@ msgid "Current printer is printing now"​​ ​msgstr "現在のプリンターは印刷中です"​ msgid "Please do not mix-use the Ext with BOX" -msgstr "ExtとBOXの混合使用はお控えください。" \ No newline at end of file +msgstr "ExtとBOXの混合使用はお控えください。" + +msgid "Recommended box temperature" +msgstr "推奨チャンバー温度" + +msgid "Recommended box temperature range of this filament. 0 means no set" +msgstr "このフィラメントの推奨チャンバー温度範囲。0は未設定を意味します" + +msgid "Set the temperature of the box during printing, set to 0 (representing off)." +msgstr "印刷中のチャンバー温度を設定します。0に設定(オフ)" \ No newline at end of file diff --git a/qdt/i18n/ko/QIDIStudio_ko.po b/qdt/i18n/ko/QIDIStudio_ko.po index 2421b67..fd2db56 100644 --- a/qdt/i18n/ko/QIDIStudio_ko.po +++ b/qdt/i18n/ko/QIDIStudio_ko.po @@ -15099,8 +15099,8 @@ msgstr "성공적으로 전송되었습니다. %s초 후 장치 페이지로 자 msgid "Successfully sent. Close current page in %s s." msgstr "성공적으로 전송되었습니다. %s초 후 현재 페이지를 닫습니다." -msgid "The selected printer is not connect box, please check." -msgstr "선택한 프린터가 BOX에 연결되지 않았습니다. 확인해 주세요." +msgid "The selected printer is not connected to the BOX or the filaments are not synchronized. Please check." +msgstr "선택한 프린터가 BOX에 연결되어 있지 않거나 필라멘트가 동기화되지 않았습니다. 확인해 주세요." msgid "Failed to set the box printing slot..." msgstr "BOX 인쇄 슬롯 설정에 실패했습니다..." @@ -15148,4 +15148,13 @@ msgid "Current printer is printing now"​​ ​msgstr "현재 프린터가 인쇄 중입니다"​ msgid "Please do not mix-use the Ext with BOX" -msgstr "Ext를 BOX와 혼용하지 마세요" \ No newline at end of file +msgstr "Ext를 BOX와 혼용하지 마세요" + +msgid "Recommended box temperature" +msgstr "권장 챔버 온도" + +msgid "Recommended box temperature range of this filament. 0 means no set" +msgstr "이 필라멘트의 권장 챔버 온도 범위. 0은 미설정을 의미합니다" + +msgid "Set the temperature of the box during printing, set to 0 (representing off)." +msgstr "프린팅 중 챔버 온도를 설정하세요. 0으로 설정(꺼짐)" \ No newline at end of file diff --git a/qdt/i18n/nl/QIDIStudio_nl.po b/qdt/i18n/nl/QIDIStudio_nl.po index 5e61e8e..34e88aa 100644 --- a/qdt/i18n/nl/QIDIStudio_nl.po +++ b/qdt/i18n/nl/QIDIStudio_nl.po @@ -17200,8 +17200,8 @@ msgstr "Succesvol verzonden. Wordt automatisch doorgestuurd naar apparaatpagina msgid "Successfully sent. Close current page in %s s." msgstr "Succesvol verzonden. Huidige pagina sluiten over %s s." -msgid "The selected printer is not connect box, please check." -msgstr "De geselecteerde printer is niet verbonden met de BOX, controleer dit." +msgid "The selected printer is not connected to the BOX or the filaments are not synchronized. Please check." +msgstr "De geselecteerde printer is niet verbonden met de BOX of de filamenten zijn niet gesynchroniseerd. Controleer dit alstublieft." msgid "Failed to set the box printing slot..." msgstr "Instellen BOX-printvak mislukt..." @@ -17249,4 +17249,13 @@ msgid "Current printer is printing now"​​ ​msgstr "De huidige printer is aan het printen"​ msgid "Please do not mix-use the Ext with BOX" -msgstr "Gebruik de Ext niet samen met BOX" \ No newline at end of file +msgstr "Gebruik de Ext niet samen met BOX" + +msgid "Recommended box temperature" +msgstr "Aanbevolen kamertemperatuur" + +msgid "Recommended box temperature range of this filament. 0 means no set" +msgstr "Aanbevolen temperatuurbereik voor dit filament. 0 betekent niet ingesteld" + +msgid "Set the temperature of the box during printing, set to 0 (representing off)." +msgstr "Stel de kamertemperatuur in tijdens het printen. Stel in op 0 (uitgeschakeld)." \ No newline at end of file diff --git a/qdt/i18n/pl/QIDIStudio_pl.po b/qdt/i18n/pl/QIDIStudio_pl.po index bb330b7..fb813a1 100644 --- a/qdt/i18n/pl/QIDIStudio_pl.po +++ b/qdt/i18n/pl/QIDIStudio_pl.po @@ -17118,8 +17118,8 @@ msgstr "Wysłano pomyślnie. Automatyczne przejście do strony urządzenia za %s msgid "Successfully sent. Close current page in %s s." msgstr "Wysłano pomyślnie. Zamknięcie bieżącej strony za %s s." -msgid "The selected printer is not connect box, please check." -msgstr "Wybrana drukarka nie jest podłączona do BOX-a, proszę sprawdzić." +msgid "The selected printer is not connected to the BOX or the filaments are not synchronized. Please check." +msgstr "Wybrana drukarka nie jest podłączona do BOX-a lub filamenty nie są zsynchronizowane. Proszę to sprawdzić." msgid "Failed to set the box printing slot..." msgstr "Nie udało się ustawić gniazda drukującego BOX-a..." @@ -17167,4 +17167,13 @@ msgid "Current printer is printing now"​​ ​msgstr "Aktualna drukarka drukuje"​ msgid "Please do not mix-use the Ext with BOX" -msgstr "Nie używaj jednocześnie szpuli zewnętrznej i BOX" \ No newline at end of file +msgstr "Nie używaj jednocześnie szpuli zewnętrznej i BOX" + +msgid "Recommended box temperature" +msgstr "Zalecana temperatura komory" + +msgid "Recommended box temperature range of this filament. 0 means no set" +msgstr "Zalecany zakres temperatury komory dla tego filamentu. 0 oznacza brak ustawienia" + +msgid "Set the temperature of the box during printing, set to 0 (representing off)." +msgstr "Ustaw temperaturę komory podczas drukowania. Ustaw na 0 (wyłączone)." \ No newline at end of file diff --git a/qdt/i18n/pt-BR/QIDIStudio_pt-BR.po b/qdt/i18n/pt-BR/QIDIStudio_pt-BR.po index 1b53688..138d1a1 100644 --- a/qdt/i18n/pt-BR/QIDIStudio_pt-BR.po +++ b/qdt/i18n/pt-BR/QIDIStudio_pt-BR.po @@ -17153,8 +17153,8 @@ msgstr "Enviado com sucesso. Será redirecionado automaticamente para a página msgid "Successfully sent. Close current page in %s s." msgstr "Enviado com sucesso. Fechando página atual em %s s." -msgid "The selected printer is not connect box, please check." -msgstr "A impressora selecionada não está conectada à BOX, por favor verifique." +msgid "The selected printer is not connected to the BOX or the filaments are not synchronized. Please check." +msgstr "A impressora selecionada não está conectada à BOX ou os filamentos não estão sincronizados. Por favor, verifique." msgid "Failed to set the box printing slot..." msgstr "Falha ao configurar o slot de impressão da BOX..." @@ -17202,4 +17202,13 @@ msgid "Current printer is printing now"​​ ​msgstr "A impressora atual está imprimindo"​ msgid "Please do not mix-use the Ext with BOX" -msgstr "Por favor, não misture o Ext com o BOX" \ No newline at end of file +msgstr "Por favor, não misture o Ext com o BOX" + +msgid "Recommended box temperature" +msgstr "Temperatura recomendada da câmara" + +msgid "Recommended box temperature range of this filament. 0 means no set" +msgstr "Faixa de temperatura recomendada da câmara para este filamento. 0 significa não definido" + +msgid "Set the temperature of the box during printing, set to 0 (representing off)." +msgstr "Ajuste a temperatura da câmara durante a impressão. Defina como 0 (desligado)." \ No newline at end of file diff --git a/qdt/i18n/ru/QIDIStudio_ru.po b/qdt/i18n/ru/QIDIStudio_ru.po index f335181..bd77b7e 100644 --- a/qdt/i18n/ru/QIDIStudio_ru.po +++ b/qdt/i18n/ru/QIDIStudio_ru.po @@ -15208,8 +15208,8 @@ msgstr "Успешно отправлено. Автоматический пер msgid "Successfully sent. Close current page in %s s." msgstr "Успешно отправлено. Закрытие текущей страницы через %s с." -msgid "The selected printer is not connect box, please check." -msgstr "Выбранный принтер не подключен к BOX, проверьте." +msgid "The selected printer is not connected to the BOX or the filaments are not synchronized. Please check." +msgstr "Выбранный принтер не подключён к BOX или нити не синхронизированы. Пожалуйста, проверьте." msgid "Failed to set the box printing slot..." msgstr "Не удалось установить слот печати BOX..." @@ -15257,4 +15257,13 @@ msgid "Current printer is printing now"​​ ​msgstr "Текущий принтер печатает"​ msgid "Please do not mix-use the Ext with BOX" -msgstr "Пожалуйста, не используйте Ext и BOX вместе." \ No newline at end of file +msgstr "Пожалуйста, не используйте Ext и BOX вместе." + +msgid "Recommended box temperature" +msgstr "Рекомендуемая температура камеры" + +msgid "Recommended box temperature range of this filament. 0 means no set" +msgstr "Рекомендуемый диапазон температуры камеры для этого филамента. 0 означает отсутствие настройки" + +msgid "Set the temperature of the box during printing, set to 0 (representing off)." +msgstr "Установите температуру камеры во время печати. Установите на 0 (выключено)." \ No newline at end of file diff --git a/qdt/i18n/sv/QIDIStudio_sv.po b/qdt/i18n/sv/QIDIStudio_sv.po index eae08dc..bdecf54 100644 --- a/qdt/i18n/sv/QIDIStudio_sv.po +++ b/qdt/i18n/sv/QIDIStudio_sv.po @@ -17247,8 +17247,8 @@ msgstr "Skickat. Kommer automatiskt att gå till enhetssidan om %s s." msgid "Successfully sent. Close current page in %s s." msgstr "Skickat. Stänger denna sida om %s s." -msgid "The selected printer is not connect box, please check." -msgstr "Den valda skrivaren är inte ansluten till BOX, kontrollera." +msgid "The selected printer is not connected to the BOX or the filaments are not synchronized. Please check." +msgstr "Den valda skrivaren är inte ansluten till BOX eller så är filamenten inte synkroniserade. Kontrollera detta." msgid "Failed to set the box printing slot..." msgstr "Misslyckades att ställa in BOX:s utskriftsfack..." @@ -17296,4 +17296,13 @@ msgid "Current printer is printing now"​​ ​msgstr "Den aktuella skrivaren skriver ut"​ msgid "Please do not mix-use the Ext with BOX" -msgstr "Blanda inte Ext med BOX" \ No newline at end of file +msgstr "Blanda inte Ext med BOX" + +msgid "Recommended box temperature" +msgstr "Rekommenderad kammartemperatur" + +msgid "Recommended box temperature range of this filament. 0 means no set" +msgstr "Rekommenderat temperaturområde för kammaren för detta filament. 0 betyder inte inställt" + +msgid "Set the temperature of the box during printing, set to 0 (representing off)." +msgstr "Ställ in kammarens temperatur under utskrift. Ställ in på 0 (avstängd)." \ No newline at end of file diff --git a/qdt/i18n/tr/QIDIStudio_tr.po b/qdt/i18n/tr/QIDIStudio_tr.po index 0cd9261..dcf8d32 100644 --- a/qdt/i18n/tr/QIDIStudio_tr.po +++ b/qdt/i18n/tr/QIDIStudio_tr.po @@ -15427,8 +15427,8 @@ msgstr "Başarıyla gönderildi. %s saniye sonra cihaz sayfasına otomatik geçi msgid "Successfully sent. Close current page in %s s." msgstr "Başarıyla gönderildi. %s saniye sonra mevcut sayfa kapatılacak." -msgid "The selected printer is not connect box, please check." -msgstr "Seçilen yazıcı BOX'a bağlı değil, lütfen kontrol edin." +msgid "The selected printer is not connected to the BOX or the filaments are not synchronized. Please check." +msgstr "Seçilen yazıcı BOX'a bağlı değil veya filamentler senkronize edilmemiş. Lütfen kontrol edin." msgid "Failed to set the box printing slot..." msgstr "BOX yazdırma yuvası ayarlanamadı..." @@ -15476,4 +15476,13 @@ msgid "Current printer is printing now"​​ ​msgstr "Mevcut yazıcı şu anda yazdırıyor"​ msgid "Please do not mix-use the Ext with BOX" -msgstr "Lütfen Ext'i BOX ile karıştırarak kullanmayın" \ No newline at end of file +msgstr "Lütfen Ext'i BOX ile karıştırarak kullanmayın" + +msgid "Recommended box temperature" +msgstr "Önerilen hazne sıcaklığı" + +msgid "Recommended box temperature range of this filament. 0 means no set" +msgstr "Bu filament için önerilen hazne sıcaklık aralığı. 0, ayar yapılmadı anlamına gelir" + +msgid "Set the temperature of the box during printing, set to 0 (representing off)." +msgstr "Yazdırma sırasında hazne sıcaklığını ayarlayın. 0'a ayarlayın (kapalı)." \ No newline at end of file diff --git a/qdt/i18n/uk/QIDIStudio_uk.po b/qdt/i18n/uk/QIDIStudio_uk.po index 277487d..38fd1b5 100644 --- a/qdt/i18n/uk/QIDIStudio_uk.po +++ b/qdt/i18n/uk/QIDIStudio_uk.po @@ -17178,8 +17178,8 @@ msgstr "Успішно відправлено. Автоматичний пере msgid "Successfully sent. Close current page in %s s." msgstr "Успішно відправлено. Закриття поточної сторінки через %s с." -msgid "The selected printer is not connect box, please check." -msgstr "Вибраний принтер не підключений до BOX, будь ласка, перевірте." +msgid "The selected printer is not connected to the BOX or the filaments are not synchronized. Please check." +msgstr "Вибраний принтер не підключений до BOX або нитки не синхронізовані. Будь ласка, перевірте." msgid "Failed to set the box printing slot..." msgstr "Не вдалося налаштувати слот для друку BOX..." @@ -17227,4 +17227,13 @@ msgid "Current printer is printing now"​​ ​msgstr "Поточний принтер друкує"​ msgid "Please do not mix-use the Ext with BOX" -msgstr "Будь ласка, не використовуйте Ext та BOX разом." \ No newline at end of file +msgstr "Будь ласка, не використовуйте Ext та BOX разом." + +msgid "Recommended box temperature" +msgstr "Рекомендована температура камери" + +msgid "Recommended box temperature range of this filament. 0 means no set" +msgstr "Рекомендований діапазон температури камери для цього філаменту. 0 означає відсутність налаштувань" + +msgid "Set the temperature of the box during printing, set to 0 (representing off)." +msgstr "Встановіть температуру камери під час друку. Встановіть на 0 (вимкнено)." \ No newline at end of file diff --git a/qdt/i18n/zh_cn/QIDIStudio_zh_CN.po b/qdt/i18n/zh_cn/QIDIStudio_zh_CN.po index e59341d..bfb99de 100644 --- a/qdt/i18n/zh_cn/QIDIStudio_zh_CN.po +++ b/qdt/i18n/zh_cn/QIDIStudio_zh_CN.po @@ -21055,8 +21055,8 @@ msgstr "发送成功。%s秒后将自动跳转至设备页面。" msgid "Successfully sent. Close current page in %s s." msgstr "发送成功。%s秒后将关闭当前页面。" -msgid "The selected printer is not connect box, please check." -msgstr "所选打印机未连接BOX,请检查。" +msgid "The selected printer is not connected to the BOX or the filaments are not synchronized. Please check." +msgstr "所选打印机未连接到BOX或耗材未同步。请检查。" msgid "Failed to set the box printing slot..." msgstr "设置BOX打印槽位失败..." @@ -21104,4 +21104,13 @@ msgid "Current printer is printing now" msgstr "当前打印机正在打印中" msgid "Please do not mix-use the Ext with BOX" -msgstr "请勿将Ext与BOX混用" \ No newline at end of file +msgstr "请勿将Ext与BOX混用" + +msgid "Recommended box temperature" +msgstr "推荐盒子温度" + +msgid "Recommended box temperature range of this filament. 0 means no set" +msgstr "该耗材推荐的盒子温度范围。0表示未设置" + +msgid "Set the temperature of the box during printing, set to 0 (representing off)." +msgstr "设置打印过程中的盒子温度,设为0(表示关闭)" \ No newline at end of file diff --git a/resources/i18n/cs/QIDIStudio.mo b/resources/i18n/cs/QIDIStudio.mo index d5c3f62..4c99a68 100644 Binary files a/resources/i18n/cs/QIDIStudio.mo and b/resources/i18n/cs/QIDIStudio.mo differ diff --git a/resources/i18n/de/QIDIStudio.mo b/resources/i18n/de/QIDIStudio.mo index 8285877..bf534a2 100644 Binary files a/resources/i18n/de/QIDIStudio.mo and b/resources/i18n/de/QIDIStudio.mo differ diff --git a/resources/i18n/es/QIDIStudio.mo b/resources/i18n/es/QIDIStudio.mo index 55ace35..10abe96 100644 Binary files a/resources/i18n/es/QIDIStudio.mo and b/resources/i18n/es/QIDIStudio.mo differ diff --git a/resources/i18n/fr/QIDIStudio.mo b/resources/i18n/fr/QIDIStudio.mo index 61ad0b0..05ee1d0 100644 Binary files a/resources/i18n/fr/QIDIStudio.mo and b/resources/i18n/fr/QIDIStudio.mo differ diff --git a/resources/i18n/hu/QIDIStudio.mo b/resources/i18n/hu/QIDIStudio.mo index 564ebba..6d2c46e 100644 Binary files a/resources/i18n/hu/QIDIStudio.mo and b/resources/i18n/hu/QIDIStudio.mo differ diff --git a/resources/i18n/it/QIDIStudio.mo b/resources/i18n/it/QIDIStudio.mo index f338138..bc00ff5 100644 Binary files a/resources/i18n/it/QIDIStudio.mo and b/resources/i18n/it/QIDIStudio.mo differ diff --git a/resources/i18n/ja/QIDIStudio.mo b/resources/i18n/ja/QIDIStudio.mo index 9192fd2..f1b252c 100644 Binary files a/resources/i18n/ja/QIDIStudio.mo and b/resources/i18n/ja/QIDIStudio.mo differ diff --git a/resources/i18n/ko/QIDIStudio.mo b/resources/i18n/ko/QIDIStudio.mo index 975e4ee..98068a1 100644 Binary files a/resources/i18n/ko/QIDIStudio.mo and b/resources/i18n/ko/QIDIStudio.mo differ diff --git a/resources/i18n/nl/QIDIStudio.mo b/resources/i18n/nl/QIDIStudio.mo index 7da7fc3..73793bb 100644 Binary files a/resources/i18n/nl/QIDIStudio.mo and b/resources/i18n/nl/QIDIStudio.mo differ diff --git a/resources/i18n/pl/QIDIStudio.mo b/resources/i18n/pl/QIDIStudio.mo index a947a90..cce1532 100644 Binary files a/resources/i18n/pl/QIDIStudio.mo and b/resources/i18n/pl/QIDIStudio.mo differ diff --git a/resources/i18n/pt-BR/QIDIStudio.mo b/resources/i18n/pt-BR/QIDIStudio.mo index b4aa15f..6ffd25b 100644 Binary files a/resources/i18n/pt-BR/QIDIStudio.mo and b/resources/i18n/pt-BR/QIDIStudio.mo differ diff --git a/resources/i18n/pt_br/QIDIStudio.mo b/resources/i18n/pt_br/QIDIStudio.mo index b4aa15f..6ffd25b 100644 Binary files a/resources/i18n/pt_br/QIDIStudio.mo and b/resources/i18n/pt_br/QIDIStudio.mo differ diff --git a/resources/i18n/ru/QIDIStudio.mo b/resources/i18n/ru/QIDIStudio.mo index 27152fe..e0208b8 100644 Binary files a/resources/i18n/ru/QIDIStudio.mo and b/resources/i18n/ru/QIDIStudio.mo differ diff --git a/resources/i18n/sv/QIDIStudio.mo b/resources/i18n/sv/QIDIStudio.mo index dca70bd..0247d75 100644 Binary files a/resources/i18n/sv/QIDIStudio.mo and b/resources/i18n/sv/QIDIStudio.mo differ diff --git a/resources/i18n/tr/QIDIStudio.mo b/resources/i18n/tr/QIDIStudio.mo index 6eb4ed7..06a532c 100644 Binary files a/resources/i18n/tr/QIDIStudio.mo and b/resources/i18n/tr/QIDIStudio.mo differ diff --git a/resources/i18n/uk/QIDIStudio.mo b/resources/i18n/uk/QIDIStudio.mo index 7d611a5..8473c51 100644 Binary files a/resources/i18n/uk/QIDIStudio.mo and b/resources/i18n/uk/QIDIStudio.mo differ diff --git a/resources/i18n/zh_cn/QIDIStudio.mo b/resources/i18n/zh_cn/QIDIStudio.mo index e256b1a..8903d8e 100644 Binary files a/resources/i18n/zh_cn/QIDIStudio.mo and b/resources/i18n/zh_cn/QIDIStudio.mo differ diff --git a/resources/images/canvas_drag.svg b/resources/images/canvas_drag.svg new file mode 100644 index 0000000..821fa70 --- /dev/null +++ b/resources/images/canvas_drag.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/canvas_drag_active.svg b/resources/images/canvas_drag_active.svg new file mode 100644 index 0000000..5aa863e --- /dev/null +++ b/resources/images/canvas_drag_active.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/canvas_zoom_in.svg b/resources/images/canvas_zoom_in.svg new file mode 100644 index 0000000..3fbf01e --- /dev/null +++ b/resources/images/canvas_zoom_in.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/canvas_zoom_in_disable.svg b/resources/images/canvas_zoom_in_disable.svg new file mode 100644 index 0000000..dc33884 --- /dev/null +++ b/resources/images/canvas_zoom_in_disable.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/canvas_zoom_out.svg b/resources/images/canvas_zoom_out.svg new file mode 100644 index 0000000..70d98c9 --- /dev/null +++ b/resources/images/canvas_zoom_out.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/canvas_zoom_out_disable.svg b/resources/images/canvas_zoom_out_disable.svg new file mode 100644 index 0000000..be4f1d5 --- /dev/null +++ b/resources/images/canvas_zoom_out_disable.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/delete2.svg b/resources/images/delete2.svg new file mode 100644 index 0000000..a8598bd --- /dev/null +++ b/resources/images/delete2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/images/menu_export_toolpaths.svg b/resources/images/menu_export_toolpaths.svg new file mode 100644 index 0000000..1f09490 --- /dev/null +++ b/resources/images/menu_export_toolpaths.svg @@ -0,0 +1,3 @@ + + + diff --git a/resources/images/more.svg b/resources/images/more.svg new file mode 100644 index 0000000..971a27e --- /dev/null +++ b/resources/images/more.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/resources/images/more_dark.svg b/resources/images/more_dark.svg new file mode 100644 index 0000000..1a851cc --- /dev/null +++ b/resources/images/more_dark.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/resources/images/partskip_retry.svg b/resources/images/partskip_retry.svg new file mode 100644 index 0000000..ff104e9 --- /dev/null +++ b/resources/images/partskip_retry.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/preset_empty.svg b/resources/images/preset_empty.svg new file mode 100644 index 0000000..77c23b8 --- /dev/null +++ b/resources/images/preset_empty.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/preset_empty_dark.svg b/resources/images/preset_empty_dark.svg new file mode 100644 index 0000000..35956e6 --- /dev/null +++ b/resources/images/preset_empty_dark.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/resources/images/print_control_partskip.svg b/resources/images/print_control_partskip.svg new file mode 100644 index 0000000..21fe0bd --- /dev/null +++ b/resources/images/print_control_partskip.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/images/print_control_partskip_disable.svg b/resources/images/print_control_partskip_disable.svg new file mode 100644 index 0000000..79d8a90 --- /dev/null +++ b/resources/images/print_control_partskip_disable.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/images/print_control_partskip_hover.svg b/resources/images/print_control_partskip_hover.svg new file mode 100644 index 0000000..b41ffcc --- /dev/null +++ b/resources/images/print_control_partskip_hover.svg @@ -0,0 +1,4 @@ + + + + diff --git a/resources/images/toolbar_fuzzyskin.svg b/resources/images/toolbar_fuzzyskin.svg new file mode 100644 index 0000000..a22ad11 --- /dev/null +++ b/resources/images/toolbar_fuzzyskin.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/resources/images/toolbar_fuzzyskin_dark.svg b/resources/images/toolbar_fuzzyskin_dark.svg new file mode 100644 index 0000000..34a08fb --- /dev/null +++ b/resources/images/toolbar_fuzzyskin_dark.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/resources/profiles/Q Series/filament/Generic TPU 95A.json b/resources/profiles/Q Series/filament/Generic TPU 95A.json index 4e01588..cc791a5 100644 --- a/resources/profiles/Q Series/filament/Generic TPU 95A.json +++ b/resources/profiles/Q Series/filament/Generic TPU 95A.json @@ -21,5 +21,7 @@ "pressure_advance": ["0.1"], "slow_down_layer_time": ["8"], "temperature_vitrification": ["30"], + "textured_plate_temp_initial_layer" : ["35"], + "textured_plate_temp" : ["35"], "compatible_printers": [] } diff --git a/resources/profiles/Q Series/filament/QIDI TPU 95A-HF.json b/resources/profiles/Q Series/filament/QIDI TPU 95A-HF.json index 0a9e296..b4968ae 100644 --- a/resources/profiles/Q Series/filament/QIDI TPU 95A-HF.json +++ b/resources/profiles/Q Series/filament/QIDI TPU 95A-HF.json @@ -22,5 +22,7 @@ "pressure_advance": ["0.1"], "slow_down_layer_time": ["8"], "temperature_vitrification": ["30"], + "textured_plate_temp_initial_layer" : ["35"], + "textured_plate_temp" : ["35"], "compatible_printers": [] } diff --git a/resources/profiles/X 3 Series/filament/Generic TPU 95A.json b/resources/profiles/X 3 Series/filament/Generic TPU 95A.json index e03c2f6..681508c 100644 --- a/resources/profiles/X 3 Series/filament/Generic TPU 95A.json +++ b/resources/profiles/X 3 Series/filament/Generic TPU 95A.json @@ -21,5 +21,7 @@ "pressure_advance": ["0.1"], "slow_down_layer_time": ["8"], "temperature_vitrification": ["30"], + "textured_plate_temp_initial_layer" : ["35"], + "textured_plate_temp" : ["35"], "compatible_printers": [] } diff --git a/resources/profiles/X 3 Series/filament/QIDI TPU 95A-HF.json b/resources/profiles/X 3 Series/filament/QIDI TPU 95A-HF.json index 591caf2..f210eef 100644 --- a/resources/profiles/X 3 Series/filament/QIDI TPU 95A-HF.json +++ b/resources/profiles/X 3 Series/filament/QIDI TPU 95A-HF.json @@ -22,5 +22,7 @@ "pressure_advance": ["0.1"], "slow_down_layer_time": ["8"], "temperature_vitrification": ["30"], + "textured_plate_temp_initial_layer" : ["35"], + "textured_plate_temp" : ["35"], "compatible_printers": [] } diff --git a/resources/profiles/X 4 Series/filament/Generic TPU 95A.json b/resources/profiles/X 4 Series/filament/Generic TPU 95A.json index a02a76a..b267403 100644 --- a/resources/profiles/X 4 Series/filament/Generic TPU 95A.json +++ b/resources/profiles/X 4 Series/filament/Generic TPU 95A.json @@ -18,5 +18,7 @@ "nozzle_temperature": ["230"], "pressure_advance": ["0.1"], "temperature_vitrification": ["30"], + "textured_plate_temp_initial_layer" : ["35"], + "textured_plate_temp" : ["35"], "compatible_printers": [] } diff --git a/resources/profiles/X 4 Series/filament/QIDI TPU 95A-HF.json b/resources/profiles/X 4 Series/filament/QIDI TPU 95A-HF.json index 4ebbac5..b470e78 100644 --- a/resources/profiles/X 4 Series/filament/QIDI TPU 95A-HF.json +++ b/resources/profiles/X 4 Series/filament/QIDI TPU 95A-HF.json @@ -19,5 +19,7 @@ "nozzle_temperature": ["230"], "pressure_advance": ["0.1"], "temperature_vitrification": ["30"], + "textured_plate_temp_initial_layer" : ["35"], + "textured_plate_temp" : ["35"], "compatible_printers": [] } diff --git a/resources/web/filament/index.html b/resources/web/filament/index.html index d0e357c..f689f81 100644 --- a/resources/web/filament/index.html +++ b/resources/web/filament/index.html @@ -42,7 +42,7 @@ border: none; } - tr:nth-child(2) td, tr:nth-child(3) td, tr:nth-child(4) td, tr:nth-child(5) td, tr:nth-child(6) td, tr:nth-child(9) td, tr:nth-child(13) td, tr:nth-child(14) td, tr:nth-child(15) td, tr:nth-child(16) td, tr:nth-child(17) td, tr:nth-child(18) td, tr:nth-child(19) td { + tr:nth-child(2) td, tr:nth-child(3) td, tr:nth-child(4) td, tr:nth-child(5) td, tr:nth-child(6) td, tr:nth-child(9) td, tr:nth-child(13) td, tr:nth-child(14) td, tr:nth-child(15) td, tr:nth-child(16) td, tr:nth-child(17) td, tr:nth-child(18) td, tr:nth-child(19),tr:nth-child(20) td { height: 20px; } @@ -145,6 +145,9 @@ tr:nth-child(18),tr:nth-child(18) td:nth-child(1) { border-bottom: 1px solid #ccc; } + tr:nth-child(19),tr:nth-child(19) td:nth-child(1) { + border-bottom: 1px solid #ccc; + } .unselected { @@ -359,6 +362,8 @@ "Print speed": "Print speed", "Post processing": "Post processing", "Anneal": "Anneal", + "QIDI BOX settings": "QIDI BOX settings", + "Is QIDI BOX applicable": "Is QIDI BOX applicable", "Compare filaments": "Compare filaments:" }, zh_CN: { @@ -384,6 +389,8 @@ "Print speed": "打印速度", "Post processing": "打印后处理", "Anneal": "退火", + "QIDI BOX settings": "QIDI BOX设置", + "Is QIDI BOX applicable": "是否适用QIDI BOX", "Compare filaments": "比较耗材:" }, fr_FR: { @@ -409,6 +416,8 @@ "Print speed": "Vitesse d'impression", "Post processing": "Post - traitement", "Anneal": "Anneal", + "QIDI BOX settings": "Paramètres QIDI BOX", + "Is QIDI BOX applicable": "Est-ce que QIDI BOX est applicable", "Compare filaments": "Comparer les filaments:" }, cs_CZ: { @@ -434,6 +443,8 @@ "Print speed": "Rychlost tisku", "Post processing": "Pozpracování", "Anneal": "Rozšíření", + "QIDI BOX settings": "Nastavení QIDI BOX", + "Is QIDI BOX applicable": "Je použitelná QIDI BOX", "Compare filaments": "Porovnat vlákna:" }, de_DE: { @@ -459,6 +470,8 @@ "Print speed": "Druckgeschwindigkeit", "Post processing": "Nachbearbeitung", "Anneal": "Anneal", + "QIDI BOX settings": "QIDI BOX-Einstellungen", + "Is QIDI BOX applicable": "Ist QIDI BOX anwendbar", "Compare filaments": "Filamente vergleichen:" }, es_ES: { @@ -484,6 +497,8 @@ "Print speed": "Velocidad de impresión", "Post processing": "Reprocesamiento", "Anneal": "Anneal", + "QIDI BOX settings": "Configuración de QIDI BOX", + "Is QIDI BOX applicable": "Es aplicable QIDI BOX", "Compare filaments": "Filamentos comparativos:" }, hu_HU: { @@ -509,6 +524,8 @@ "Print speed": "Nyomtatási sebesség", "Post processing": "Utófeldolgozás", "Anneal": "Melléklet", + "QIDI BOX settings": "QIDI BOX beállítások", + "Is QIDI BOX applicable": "QIDI BOX alkalmazandó", "Compare filaments": "Szálak összehasonlítása:" }, it_IT: { @@ -534,6 +551,8 @@ "Print speed": "Velocità di stampa", "Post processing": "Post-elaborazione", "Anneal": "Allegato", + "QIDI BOX settings": "Impostazioni QIDI BOX", + "Is QIDI BOX applicable": "È applicabile QIDI BOX", "Compare filaments": "Confronta filamenti:" }, ja_JP: { @@ -559,6 +578,8 @@ "Print speed": "印刷速度", "Post processing": "後処理", "Anneal": "Anneal", + "QIDI BOX settings": "QIDI BOX設定", + "Is QIDI BOX applicable": "QIDIボックス適用か", "Compare filaments": "ひかくフィラメント:" }, ko_KR: { @@ -584,6 +605,8 @@ "Print speed": "인쇄 속도", "Post processing": "후처리", "Anneal": "Anneal", + "QIDI BOX settings": "QIDI BOX 설정", + "Is QIDI BOX applicable": "QIDI BOX 적용 가능합니까", "Compare filaments": "비교적 긴 실:" }, nl_NL: { @@ -609,6 +632,8 @@ "Print speed": "Afdruksnelheid", "Post processing": "Postverwerking", "Anneal": "Anneal", + "QIDI BOX settings": "QIDI BOX instellingen", + "Is QIDI BOX applicable": "Is QIDI BOX van toepassing", "Compare filaments": "Filamenten vergelijken:" }, pl_PL: { @@ -634,6 +659,8 @@ "Print speed": "Szybkość drukowania", "Post processing": "Przetwarzanie po przetwarzaniu", "Anneal": "Wyżej", + "QIDI BOX settings": "Ustawienia QIDI BOX", + "Is QIDI BOX applicable": "Czy stosuje się QIDI BOX", "Compare filaments": "Porównaj filamenty:" }, ru_RU: { @@ -659,6 +686,8 @@ "Print speed": "Скорость печати", "Post processing": "Постобработка", "Anneal": "Температура обжига", + "QIDI BOX settings": "Настройки QIDI BOX", + "Is QIDI BOX applicable": "применяется ли QIDI BOX", "Compare filaments": "Сравнить" }, sv_SE: { @@ -684,6 +713,8 @@ "Print speed": "Utskriftshastighet", "Post processing": "Efterbehandling", "Anneal": "Bilaga", + "QIDI BOX settings": "QIDI BOX-inställningar", + "Is QIDI BOX applicable": "Är QIDI BOX tillämplig", "Compare filaments": "Jämför glödtrådar:" }, tr_TR: { @@ -709,6 +740,8 @@ "Print speed": "Bastırma hızı", "Post processing": "İşlemi", "Anneal": "Annealfrance. kgm", + "QIDI BOX settings": "QIDI BOX ayarları", + "Is QIDI BOX applicable": "QIDI BOX uygulanabilir mi", "Compare filaments": "Kıyafetleri karşılaştır:" }, uk_UA: { @@ -734,6 +767,8 @@ "Print speed": "Швидкість друку", "Post processing": "Посля обробки", "Anneal": "Анналstar name", + "QIDI BOX settings": "Налаштування QIDI BOX", + "Is QIDI BOX applicable": "Чи застосовується QIDI BOX", "Compare filaments": "Порівняти гілки:" }, pt_BR: { @@ -759,6 +794,8 @@ "Print speed": "Velocidade de impressão", "Post processing": "Pós-processamento", "Anneal": "Anexo", + "QIDI BOX settings": "QIDI BOX settings", + "Is QIDI BOX applicable": "É aplicável o QIDI BOX", "Compare filaments": "Comparar filamentos:" }, @@ -1287,7 +1324,7 @@ document.write('260-280℃');//support for pet/pa document.write('270-280℃');//support for paht document.write(''); - + /*Print speed*/ document.write(''); document.write(''); @@ -1319,7 +1356,7 @@ document.write('<120mm/s');//support for pet/pa document.write('<120mm/s');//support for paht document.write(''); - + /*Post processing*/ document.write(''); document.write('' + getTranslation('Post processing', lang) + ''); @@ -1352,7 +1389,38 @@ document.write('/');//support for paht document.write(''); - + + /*Is QIDI BOX applicable*/ + document.write(''); + document.write('' + getTranslation('QIDI BOX settings', lang) + ''); + document.write('' + getTranslation('Is QIDI BOX applicable', lang) + ''); + document.write('√');//pla + document.write('√');//pla matte + document.write('√');//pla metal + document.write('√');//pla silk + document.write('√');//pla cf + document.write('√');//abs + document.write('√');//abs odorless + document.write('√');//abs metal + document.write('√');//abs gf + document.write('√');//asa + document.write('√');//asa aero + document.write('√');//pa12 cf + document.write('√');//paht cf + document.write('√');//paht gf + document.write('√');//pc abs fr + document.write('√');//pet cf + document.write('√');//pet gf + document.write('√');//petg + document.write('√');//pps-cf + document.write('×');//tpu + document.write('√');//ultra pa + document.write('√');//ultra pa-cf25 + document.write('√');//wood rapido + document.write('×');//tpu aero + document.write('√');//support for pet/pa + document.write('√');//support for paht + document.write(''); diff --git a/src/QIDIStudio.cpp b/src/QIDIStudio.cpp index 0890822..b746e0a 100644 --- a/src/QIDIStudio.cpp +++ b/src/QIDIStudio.cpp @@ -3539,19 +3539,36 @@ int CLI::run(int argc, char **argv) std::vector& flush_multipliers = m_print_config.option("flush_multiplier", true)->values; flush_multipliers.resize(new_extruder_count, 1.f); - ConfigOptionEnumsGeneric* nozzle_volume_opt = nullptr; - if (m_print_config.has("nozzle_volume_type")) - nozzle_volume_opt = m_print_config.option("nozzle_volume_type"); - if (m_extra_config.has("nozzle_volume_type")) - nozzle_volume_opt = m_extra_config.option("nozzle_volume_type"); + std::vector nozzle_flush_dataset(new_extruder_count, 0); + { + std::vector nozzle_flush_dataset_full = m_print_config.option("nozzle_flush_dataset",true)->values; + if (m_print_config.has("printer_extruder_variant")) + nozzle_flush_dataset_full.resize(m_print_config.option("printer_extruder_variant")->size(), 0); + else + nozzle_flush_dataset_full.resize(1, 0); - std::vector volume_type_list; - if (nozzle_volume_opt) { - for (size_t idx = 0; idx < nozzle_volume_opt->values.size(); ++idx) { - volume_type_list.emplace_back(NozzleVolumeType(nozzle_volume_opt->values[idx])); + std::vector extruders; + if (m_print_config.has("extruder_type")) + extruders = m_print_config.option("extruder_type")->values; + else + extruders.resize(1,int(ExtruderType::etDirectDrive)); + + std::vector volume_types; + if (m_print_config.has("nozzle_volume_type")) + volume_types = m_print_config.option("nozzle_volume_type")->values; // get volume type from 3mf + else + volume_types.resize(1, int(NozzleVolumeType::nvtStandard)); + + if(m_extra_config.has("nozzle_volume_type")) // get volume type from input + volume_types = m_extra_config.option("nozzle_volume_type")->values; + + for (int eidx = 0; eidx < new_extruder_count; ++eidx) { + int index = 0; + if (m_print_config.has("printer_extruder_id") && m_print_config.has("printer_extruder_variant")) + index = m_print_config.get_index_for_extruder(eidx + 1, "printer_extruder_id", ExtruderType(extruders[eidx]), NozzleVolumeType(volume_types[eidx]), "printer_extruder_variant"); + nozzle_flush_dataset[eidx] = nozzle_flush_dataset_full[index]; } } - volume_type_list.resize(new_extruder_count, NozzleVolumeType::nvtStandard); for (size_t nozzle_id = 0; nozzle_id < new_extruder_count; ++nozzle_id) { std::vector flush_vol_mtx = get_flush_volumes_matrix(flush_vol_matrix, nozzle_id, new_extruder_count); @@ -3573,7 +3590,7 @@ int CLI::run(int argc, char **argv) unsigned char to_rgb[4] = {}; Slic3r::GUI::BitmapCache::parse_color4(to_color, to_rgb); - Slic3r::FlushVolCalculator calculator(min_flush_volumes[from_idx], Slic3r::g_max_flush_volume, new_extruder_count > 1, volume_type_list[nozzle_id]); + Slic3r::FlushVolCalculator calculator(min_flush_volumes[from_idx], Slic3r::g_max_flush_volume,nozzle_flush_dataset[nozzle_id]); flushing_volume = calculator.calc_flush_vol(from_rgb[3], from_rgb[0], from_rgb[1], from_rgb[2], to_rgb[3], to_rgb[0], to_rgb[1], to_rgb[2]); if (is_from_support) { flushing_volume = std::max(Slic3r::g_min_flush_volume_from_support, flushing_volume); } } @@ -4901,6 +4918,37 @@ int CLI::run(int argc, char **argv) BOOST_LOG_TRIVIAL(debug) << boost::format("plate %1%: no arrange, directly translate object %2% by {%3%, %4%}") % (i+1) % mo->name %plate_origin(0) %plate_origin(1); } + + bool is_seq_print = false; + get_print_sequence(cur_plate, m_print_config, is_seq_print); + + if (!is_seq_print && assemble_plate.filaments_count > 1) + { + //prepare the wipe tower + auto printer_structure_opt = m_print_config.option>("printer_structure"); + // set the default position, the same with print config(left top) + float x = WIPE_TOWER_DEFAULT_X_POS; + float y = WIPE_TOWER_DEFAULT_Y_POS; + if (printer_structure_opt && printer_structure_opt->value == PrinterStructure::psI3) { + x = I3_WIPE_TOWER_DEFAULT_X_POS; + y = I3_WIPE_TOWER_DEFAULT_Y_POS; + } + if (x < WIPE_TOWER_MARGIN) { + x = WIPE_TOWER_MARGIN; + } + if (y < WIPE_TOWER_MARGIN) { + y = WIPE_TOWER_MARGIN; + } + + //create the options using default if neccessary + ConfigOptionFloats* wipe_x_option = m_print_config.option("wipe_tower_x", true); + ConfigOptionFloats* wipe_y_option = m_print_config.option("wipe_tower_y", true); + ConfigOptionFloat wt_x_opt(x); + ConfigOptionFloat wt_y_opt(y); + + wipe_x_option->set_at(&wt_x_opt, i, 0); + wipe_y_option->set_at(&wt_y_opt, i, 0); + } } } diff --git a/src/earcut/CHANGELOG.md b/src/earcut/CHANGELOG.md new file mode 100644 index 0000000..41d9e67 --- /dev/null +++ b/src/earcut/CHANGELOG.md @@ -0,0 +1,27 @@ +## Earcut.hpp changelog + +### master + + - Fixed a bunch of rare edge cases that led to bad triangulation (parity with Earcut v2.2.2) + - Removed use of deprecated `std::allocator::construct` + - Fixed a minor z-order hashing bug + - Improved visualization app, better docs + +### v0.12.4 + + - Fixed a crash in Crash in Earcut::findHoleBridge + - Added coverage checks + - Added macOS, MinGW builds + +### v0.12.3 + + - Fixed -Wunused-lambda-capture + +### v0.12.2 + + - Fixed potential division by zero + - Fixed -fsanitize=integer warning + +### v0.12.1 + + - Fixed cast precision warning diff --git a/src/earcut/CMakeLists.txt b/src/earcut/CMakeLists.txt new file mode 100644 index 0000000..08c7bb1 --- /dev/null +++ b/src/earcut/CMakeLists.txt @@ -0,0 +1,151 @@ +cmake_minimum_required(VERSION 3.2) +project(earcut_hpp LANGUAGES CXX C) + +option(EARCUT_BUILD_TESTS "Build the earcut test program" ON) +option(EARCUT_BUILD_BENCH "Build the earcut benchmark program" ON) +option(EARCUT_BUILD_VIZ "Build the earcut visualizer program" ON) +option(EARCUT_WARNING_IS_ERROR "Treat warnings as errors" OFF) + +if (NOT CMAKE_BUILD_TYPE AND NOT GENERATOR_IS_MULTI_CONFIG) + message(STATUS "No build type specified. Setting to 'Release'") + set(CMAKE_BUILD_TYPE "Release" CACHE STRING "The type of build." FORCE) +endif() + + +include(GNUInstallDirs) + +add_library(earcut_hpp INTERFACE) +add_library(earcut_hpp::earcut_hpp ALIAS earcut_hpp) + +target_include_directories(earcut_hpp INTERFACE + $ + $ +) + +set(CMAKE_CXX_STANDARD 11) + +if("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}" GREATER 3.7) + # Allow C++11 requirements to propagate when using recent CMake versions + target_compile_features(earcut_hpp INTERFACE cxx_std_11) +endif() + +file(GLOB FIXTURE_SOURCE_FILES test/fixtures/*.cpp test/fixtures/*.hpp) +source_group(fixtures FILES ${FIXTURE_SOURCE_FILES}) +add_library(fixtures OBJECT ${FIXTURE_SOURCE_FILES}) +target_compile_options(fixtures PRIVATE $<$:/Od>) + +# In CMake 3.12, use target_link_libraries(fixtures PUBLIC earcut_hpp libtess2). +# Since we support down to CMake 3.2, we need to manually propagate usage requirements of earcut_hpp +target_include_directories(fixtures PRIVATE "$") +target_compile_features(fixtures PRIVATE "$") + + +file(GLOB COMPARISON_SOURCE_FILES test/comparison/*.cpp test/comparison/*.hpp) +source_group(comparison FILES ${COMPARISON_SOURCE_FILES}) +# this is interface since there is no cpp files in the comparison directory +add_library(comparison INTERFACE) + + +file(GLOB LIBTESS2_SOURCE_FILES test/comparison/libtess2/*.c test/comparison/libtess2/*.h) +source_group(comparison/libtess2 FILES ${LIBTESS2_SOURCE_FILES}) +add_library(libtess2 ${LIBTESS2_SOURCE_FILES}) +target_compile_options(libtess2 PRIVATE + $<$:/wd4244 /wd4267> + $<$>:-w> +) + +add_library(common INTERFACE) +target_link_libraries(common INTERFACE libtess2 comparison) + +# optional: -march=native (builds with the optimizations available on the build machine (only for local use!)) +target_compile_options(common INTERFACE + $<$>:-pipe -Wall -Wextra -Wconversion -Wpedantic> +) + +if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang$" OR CMAKE_COMPILER_IS_GNUCXX) + if ("${CMAKE_CXX_FLAGS}" MATCHES "--coverage") + # We disable debug code for the coverage so it won't see assertion and other things only enabled for debugging + target_compile_definitions(common INTERFACE NDEBUG) + else() + # Here we enable the undefined behavior sanitizer for the tests, benchmarks and the viz + include(CheckCXXCompilerFlag) + check_cxx_compiler_flag("-fsanitize=undefined" HAVE_FLAG_SANITIZE_UNDEFINED) + if(HAVE_FLAG_SANITIZE_UNDEFINED) + target_compile_options(common INTERFACE $<$:-fsanitize=undefined>) + # TODO: Replace with target link option once we support CMake 3.13 + target_link_libraries(common INTERFACE $<$:-fsanitize=undefined>) + endif() + endif() +endif() + +if (EARCUT_WARNING_IS_ERROR) + target_compile_options(common INTERFACE + $<$:/WX> + $<$>:-Werror> + ) +endif() + +if (EARCUT_BUILD_TESTS) + enable_testing() + add_executable(tests test/tap.cpp test/tap.hpp test/test.cpp $) + target_link_libraries(tests PRIVATE earcut_hpp common) + add_test(NAME earcut_tests COMMAND tests) +endif() +if (EARCUT_BUILD_BENCH) + add_executable(bench test/bench.cpp $) + target_link_libraries(bench PRIVATE earcut_hpp common) +endif() +if (EARCUT_BUILD_VIZ) + add_executable(viz test/viz.cpp $) + + # Setup viz target + # OpenGL + # linux: xorg-dev libgl1-mesa-glx libgl1-mesa-dev + # windows: in the windows sdk + find_package(OpenGL REQUIRED) + + # GLFW3 + find_package(glfw3 QUIET) # try to use the system default + if (NOT glfw3_FOUND) + if(EXISTS "${PROJECT_SOURCE_DIR}/.gitmodules") + find_package(Git REQUIRED) + execute_process( + COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} + OUTPUT_QUIET + ERROR_QUIET + ) + endif() + + set(GLFW_BUILD_EXAMPLES OFF CACHE BOOL "Build the GLFW example programs" FORCE) + set(GLFW_BUILD_TESTS OFF CACHE BOOL "Build the GLFW test programs" FORCE) + set(GLFW_BUILD_DOCS OFF CACHE BOOL "Build the GLFW documentation" FORCE) + set(GLFW_INSTALL OFF CACHE BOOL "Generate installation target" FORCE) + add_subdirectory(glfw) + endif() + + target_compile_definitions(viz PRIVATE GL_SILENCE_DEPRECATION) + + # TODO: Using old variables for OpenGL package since they were added in CMake 3.8 + target_link_libraries(viz PRIVATE earcut_hpp common glfw ${OPENGL_LIBRARIES}) + target_include_directories(viz PRIVATE ${OPENGL_INCLUDE_DIR}) +endif() + +install( + DIRECTORY include/mapbox + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} FILES_MATCHING PATTERN "*.hpp" +) + +install(TARGETS earcut_hpp EXPORT earcut_hpp-config) + +# Since there is two projects, we need to export into the parent directory +export( + TARGETS earcut_hpp + NAMESPACE earcut_hpp:: + FILE "${PROJECT_BINARY_DIR}/earcut_hpp-config.cmake" +) + +install(EXPORT earcut_hpp-config + DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/earcut_hpp" + NAMESPACE earcut_hpp:: +) diff --git a/src/earcut/LICENSE b/src/earcut/LICENSE new file mode 100644 index 0000000..8bafb57 --- /dev/null +++ b/src/earcut/LICENSE @@ -0,0 +1,15 @@ +ISC License + +Copyright (c) 2015, Mapbox + +Permission to use, copy, modify, and/or distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright notice +and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS +OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF +THIS SOFTWARE. diff --git a/src/earcut/README.md b/src/earcut/README.md new file mode 100644 index 0000000..67f235b --- /dev/null +++ b/src/earcut/README.md @@ -0,0 +1,131 @@ +## Earcut + +A C++ port of [earcut.js](https://github.com/mapbox/earcut), a fast, [header-only](https://github.com/mapbox/earcut.hpp/blob/master/include/mapbox/earcut.hpp) polygon triangulation library. + +[![Travis](https://img.shields.io/travis/com/mapbox/earcut.hpp.svg)](https://travis-ci.com/github/mapbox/earcut.hpp) +[![AppVeyor](https://ci.appveyor.com/api/projects/status/a1ysrqd69mqn7coo/branch/master?svg=true)](https://ci.appveyor.com/project/Mapbox/earcut-hpp-8wm4o/branch/master) +[![Coverage](https://img.shields.io/coveralls/github/mapbox/earcut.hpp.svg)](https://coveralls.io/github/mapbox/earcut.hpp) +[![Coverity Scan](https://img.shields.io/coverity/scan/14000.svg)](https://scan.coverity.com/projects/14000) +[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/mapbox/earcut.hpp.svg)](http://isitmaintained.com/project/mapbox/earcut.hpp "Average time to resolve an issue") +[![Percentage of issues still open](http://isitmaintained.com/badge/open/mapbox/earcut.hpp.svg)](http://isitmaintained.com/project/mapbox/earcut.hpp "Percentage of issues still open") +[![Mourner](https://img.shields.io/badge/simply-awesome-brightgreen.svg)](https://github.com/mourner/projects) + +The library implements a modified ear slicing algorithm, optimized by [z-order curve](http://en.wikipedia.org/wiki/Z-order_curve) hashing and extended to handle holes, twisted polygons, degeneracies and self-intersections in a way that doesn't _guarantee_ correctness of triangulation, but attempts to always produce acceptable results for practical data like geographical shapes. + +It's based on ideas from [FIST: Fast Industrial-Strength Triangulation of Polygons](http://www.cosy.sbg.ac.at/~held/projects/triang/triang.html) by Martin Held and [Triangulation by Ear Clipping](http://www.geometrictools.com/Documentation/TriangulationByEarClipping.pdf) by David Eberly. + +## Usage + +```cpp +#include +``` +```cpp +// The number type to use for tessellation +using Coord = double; + +// The index type. Defaults to uint32_t, but you can also pass uint16_t if you know that your +// data won't have more than 65536 vertices. +using N = uint32_t; + +// Create array +using Point = std::array; +std::vector> polygon; + +// Fill polygon structure with actual data. Any winding order works. +// The first polyline defines the main polygon. +polygon.push_back({{100, 0}, {100, 100}, {0, 100}, {0, 0}}); +// Following polylines define holes. +polygon.push_back({{75, 25}, {75, 75}, {25, 75}, {25, 25}}); + +// Run tessellation +// Returns array of indices that refer to the vertices of the input polygon. +// e.g: the index 6 would refer to {25, 75} in this example. +// Three subsequent indices form a triangle. Output triangles are clockwise. +std::vector indices = mapbox::earcut(polygon); +``` + +Earcut can triangulate a simple, planar polygon of any winding order including holes. It will even return a robust, acceptable solution for non-simple poygons. Earcut works on a 2D plane. If you have three or more dimensions, you can project them onto a 2D surface before triangulation, or use a more suitable library for the task (e.g [CGAL](https://doc.cgal.org/latest/Triangulation_3/index.html)). + + +It is also possible to use your custom point type as input. There are default accessors defined for `std::tuple`, `std::pair`, and `std::array`. For a custom type (like Clipper's `IntPoint` type), do this: + +```cpp +// struct IntPoint { +// int64_t X, Y; +// }; + +namespace mapbox { +namespace util { + +template <> +struct nth<0, IntPoint> { + inline static auto get(const IntPoint &t) { + return t.X; + }; +}; +template <> +struct nth<1, IntPoint> { + inline static auto get(const IntPoint &t) { + return t.Y; + }; +}; + +} // namespace util +} // namespace mapbox +``` + +You can also use a custom container type for your polygon. Similar to std::vector, it has to meet the requirements of [Container](https://en.cppreference.com/w/cpp/named_req/Container), in particular `size()`, `empty()` and `operator[]`. + +

+ example triangulation +

+ +## Additional build instructions +In case you just want to use the earcut triangulation library; copy and include the header file [``](https://github.com/mapbox/earcut.hpp/blob/master/include/mapbox/earcut.hpp) in your project and follow the steps documented in the section [Usage](#usage). + +If you want to build the test, benchmark and visualization programs instead, follow these instructions: + +### Dependencies + +Before you continue, make sure to have the following tools and libraries installed: + * git ([Ubuntu](https://help.ubuntu.com/lts/serverguide/git.html)/[Windows/macOS](http://git-scm.com/downloads)) + * cmake 3.2+ ([Ubuntu](https://launchpad.net/~george-edison55/+archive/ubuntu/cmake-3.x)/[Windows/macOS](https://cmake.org/download/)) + * OpenGL SDK ([Ubuntu](http://packages.ubuntu.com/de/trusty/libgl1-mesa-dev)/[Windows](https://dev.windows.com/en-us/downloads/windows-10-sdk)/[macOS](https://developer.apple.com/opengl/)) + * Compiler such as [GCC 4.9+, Clang 3.4+](https://launchpad.net/~ubuntu-toolchain-r/+archive/ubuntu/test), [MSVC12+](https://www.visualstudio.com/) + +Note: On some operating systems such as Windows, manual steps are required to add cmake and [git](http://blog.countableset.ch/2012/06/07/adding-git-to-windows-7-path/) to your PATH environment variable. + +### Manual compilation + +```bash +git clone --recursive https://github.com/mapbox/earcut.hpp.git +cd earcut.hpp +mkdir build +cd build +cmake .. +make +# ./tests +# ./bench +# ./viz +``` + +### [Visual Studio](https://www.visualstudio.com/), [Eclipse](https://eclipse.org/), [XCode](https://developer.apple.com/xcode/), ... + +```batch +git clone --recursive https://github.com/mapbox/earcut.hpp.git +cd earcut.hpp +mkdir project +cd project +cmake .. -G "Visual Studio 14 2015" +::you can also generate projects for "Visual Studio 12 2013", "XCode", "Eclipse CDT4 - Unix Makefiles" +``` +After completion, open the generated project with your IDE. + + +### [CLion](https://www.jetbrains.com/clion/), [Visual Studio 2017+](https://www.visualstudio.com/) + +Import the project from https://github.com/mapbox/earcut.hpp.git and you should be good to go! + +## Status + +This is currently based on [earcut 2.2.4](https://github.com/mapbox/earcut#224-jul-5-2022). diff --git a/src/earcut/earcut.hpp b/src/earcut/earcut.hpp new file mode 100644 index 0000000..fd33814 --- /dev/null +++ b/src/earcut/earcut.hpp @@ -0,0 +1,814 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mapbox { + +namespace util { + +template struct nth { + inline static typename std::tuple_element::type + get(const T& t) { return std::get(t); }; +}; + +} + +namespace detail { + +template +class Earcut { +public: + std::vector indices; + std::size_t vertices = 0; + + template + void operator()(const Polygon& points); + +private: + struct Node { + Node(N index, double x_, double y_) : i(index), x(x_), y(y_) {} + Node(const Node&) = delete; + Node& operator=(const Node&) = delete; + Node(Node&&) = delete; + Node& operator=(Node&&) = delete; + + const N i; + const double x; + const double y; + + // previous and next vertice nodes in a polygon ring + Node* prev = nullptr; + Node* next = nullptr; + + // z-order curve value + int32_t z = 0; + + // previous and next nodes in z-order + Node* prevZ = nullptr; + Node* nextZ = nullptr; + + // indicates whether this is a steiner point + bool steiner = false; + }; + + template Node* linkedList(const Ring& points, const bool clockwise); + Node* filterPoints(Node* start, Node* end = nullptr); + void earcutLinked(Node* ear, int pass = 0); + bool isEar(Node* ear); + bool isEarHashed(Node* ear); + Node* cureLocalIntersections(Node* start); + void splitEarcut(Node* start); + template Node* eliminateHoles(const Polygon& points, Node* outerNode); + Node* eliminateHole(Node* hole, Node* outerNode); + Node* findHoleBridge(Node* hole, Node* outerNode); + bool sectorContainsSector(const Node* m, const Node* p); + void indexCurve(Node* start); + Node* sortLinked(Node* list); + int32_t zOrder(const double x_, const double y_); + Node* getLeftmost(Node* start); + bool pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const; + bool isValidDiagonal(Node* a, Node* b); + double area(const Node* p, const Node* q, const Node* r) const; + bool equals(const Node* p1, const Node* p2); + bool intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2); + bool onSegment(const Node* p, const Node* q, const Node* r); + int sign(double val); + bool intersectsPolygon(const Node* a, const Node* b); + bool locallyInside(const Node* a, const Node* b); + bool middleInside(const Node* a, const Node* b); + Node* splitPolygon(Node* a, Node* b); + template Node* insertNode(std::size_t i, const Point& p, Node* last); + void removeNode(Node* p); + + bool hashing; + double minX, maxX; + double minY, maxY; + double inv_size = 0; + + template > + class ObjectPool { + public: + ObjectPool() { } + ObjectPool(std::size_t blockSize_) { + reset(blockSize_); + } + ~ObjectPool() { + clear(); + } + template + T* construct(Args&&... args) { + if (currentIndex >= blockSize) { + currentBlock = alloc_traits::allocate(alloc, blockSize); + allocations.emplace_back(currentBlock); + currentIndex = 0; + } + T* object = ¤tBlock[currentIndex++]; + alloc_traits::construct(alloc, object, std::forward(args)...); + return object; + } + void reset(std::size_t newBlockSize) { + for (auto allocation : allocations) { + alloc_traits::deallocate(alloc, allocation, blockSize); + } + allocations.clear(); + blockSize = std::max(1, newBlockSize); + currentBlock = nullptr; + currentIndex = blockSize; + } + void clear() { reset(blockSize); } + private: + T* currentBlock = nullptr; + std::size_t currentIndex = 1; + std::size_t blockSize = 1; + std::vector allocations; + Alloc alloc; + typedef typename std::allocator_traits alloc_traits; + }; + ObjectPool nodes; +}; + +template template +void Earcut::operator()(const Polygon& points) { + // reset + indices.clear(); + vertices = 0; + + if (points.empty()) return; + + double x; + double y; + int threshold = 80; + std::size_t len = 0; + + for (size_t i = 0; threshold >= 0 && i < points.size(); i++) { + threshold -= static_cast(points[i].size()); + len += points[i].size(); + } + + //estimate size of nodes and indices + nodes.reset(len * 3 / 2); + indices.reserve(len + points[0].size()); + + Node* outerNode = linkedList(points[0], true); + if (!outerNode || outerNode->prev == outerNode->next) return; + + if (points.size() > 1) outerNode = eliminateHoles(points, outerNode); + + // if the shape is not too simple, we'll use z-order curve hash later; calculate polygon bbox + hashing = threshold < 0; + if (hashing) { + Node* p = outerNode->next; + minX = maxX = outerNode->x; + minY = maxY = outerNode->y; + do { + x = p->x; + y = p->y; + minX = std::min(minX, x); + minY = std::min(minY, y); + maxX = std::max(maxX, x); + maxY = std::max(maxY, y); + p = p->next; + } while (p != outerNode); + + // minX, minY and inv_size are later used to transform coords into integers for z-order calculation + inv_size = std::max(maxX - minX, maxY - minY); + inv_size = inv_size != .0 ? (32767. / inv_size) : .0; + } + + earcutLinked(outerNode); + + nodes.clear(); +} + +// create a circular doubly linked list from polygon points in the specified winding order +template template +typename Earcut::Node* +Earcut::linkedList(const Ring& points, const bool clockwise) { + using Point = typename Ring::value_type; + double sum = 0; + const std::size_t len = points.size(); + std::size_t i, j; + Node* last = nullptr; + + // calculate original winding order of a polygon ring + for (i = 0, j = len > 0 ? len - 1 : 0; i < len; j = i++) { + const auto& p1 = points[i]; + const auto& p2 = points[j]; + const double p20 = util::nth<0, Point>::get(p2); + const double p10 = util::nth<0, Point>::get(p1); + const double p11 = util::nth<1, Point>::get(p1); + const double p21 = util::nth<1, Point>::get(p2); + sum += (p20 - p10) * (p11 + p21); + } + + // link points into circular doubly-linked list in the specified winding order + if (clockwise == (sum > 0)) { + for (i = 0; i < len; i++) last = insertNode(vertices + i, points[i], last); + } else { + for (i = len; i-- > 0;) last = insertNode(vertices + i, points[i], last); + } + + if (last && equals(last, last->next)) { + removeNode(last); + last = last->next; + } + + vertices += len; + + return last; +} + +// eliminate colinear or duplicate points +template +typename Earcut::Node* +Earcut::filterPoints(Node* start, Node* end) { + if (!end) end = start; + + Node* p = start; + bool again; + do { + again = false; + + if (!p->steiner && (equals(p, p->next) || area(p->prev, p, p->next) == 0)) { + removeNode(p); + p = end = p->prev; + + if (p == p->next) break; + again = true; + + } else { + p = p->next; + } + } while (again || p != end); + + return end; +} + +// main ear slicing loop which triangulates a polygon (given as a linked list) +template +void Earcut::earcutLinked(Node* ear, int pass) { + if (!ear) return; + + // interlink polygon nodes in z-order + if (!pass && hashing) indexCurve(ear); + + Node* stop = ear; + Node* prev; + Node* next; + + // iterate through ears, slicing them one by one + while (ear->prev != ear->next) { + prev = ear->prev; + next = ear->next; + + if (hashing ? isEarHashed(ear) : isEar(ear)) { + // cut off the triangle + indices.emplace_back(prev->i); + indices.emplace_back(ear->i); + indices.emplace_back(next->i); + + removeNode(ear); + + // skipping the next vertice leads to less sliver triangles + ear = next->next; + stop = next->next; + + continue; + } + + ear = next; + + // if we looped through the whole remaining polygon and can't find any more ears + if (ear == stop) { + // try filtering points and slicing again + if (!pass) earcutLinked(filterPoints(ear), 1); + + // if this didn't work, try curing all small self-intersections locally + else if (pass == 1) { + ear = cureLocalIntersections(filterPoints(ear)); + earcutLinked(ear, 2); + + // as a last resort, try splitting the remaining polygon into two + } else if (pass == 2) splitEarcut(ear); + + break; + } + } +} + +// check whether a polygon node forms a valid ear with adjacent nodes +template +bool Earcut::isEar(Node* ear) { + const Node* a = ear->prev; + const Node* b = ear; + const Node* c = ear->next; + + if (area(a, b, c) >= 0) return false; // reflex, can't be an ear + + // now make sure we don't have other points inside the potential ear + Node* p = ear->next->next; + + while (p != ear->prev) { + if (pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) && + area(p->prev, p, p->next) >= 0) return false; + p = p->next; + } + + return true; +} + +template +bool Earcut::isEarHashed(Node* ear) { + const Node* a = ear->prev; + const Node* b = ear; + const Node* c = ear->next; + + if (area(a, b, c) >= 0) return false; // reflex, can't be an ear + + // triangle bbox; min & max are calculated like this for speed + const double minTX = std::min(a->x, std::min(b->x, c->x)); + const double minTY = std::min(a->y, std::min(b->y, c->y)); + const double maxTX = std::max(a->x, std::max(b->x, c->x)); + const double maxTY = std::max(a->y, std::max(b->y, c->y)); + + // z-order range for the current triangle bbox; + const int32_t minZ = zOrder(minTX, minTY); + const int32_t maxZ = zOrder(maxTX, maxTY); + + // first look for points inside the triangle in increasing z-order + Node* p = ear->nextZ; + + while (p && p->z <= maxZ) { + if (p != ear->prev && p != ear->next && + pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) && + area(p->prev, p, p->next) >= 0) return false; + p = p->nextZ; + } + + // then look for points in decreasing z-order + p = ear->prevZ; + + while (p && p->z >= minZ) { + if (p != ear->prev && p != ear->next && + pointInTriangle(a->x, a->y, b->x, b->y, c->x, c->y, p->x, p->y) && + area(p->prev, p, p->next) >= 0) return false; + p = p->prevZ; + } + + return true; +} + +// go through all polygon nodes and cure small local self-intersections +template +typename Earcut::Node* +Earcut::cureLocalIntersections(Node* start) { + Node* p = start; + do { + Node* a = p->prev; + Node* b = p->next->next; + + // a self-intersection where edge (v[i-1],v[i]) intersects (v[i+1],v[i+2]) + if (!equals(a, b) && intersects(a, p, p->next, b) && locallyInside(a, b) && locallyInside(b, a)) { + indices.emplace_back(a->i); + indices.emplace_back(p->i); + indices.emplace_back(b->i); + + // remove two nodes involved + removeNode(p); + removeNode(p->next); + + p = start = b; + } + p = p->next; + } while (p != start); + + return filterPoints(p); +} + +// try splitting polygon into two and triangulate them independently +template +void Earcut::splitEarcut(Node* start) { + // look for a valid diagonal that divides the polygon into two + Node* a = start; + do { + Node* b = a->next->next; + while (b != a->prev) { + if (a->i != b->i && isValidDiagonal(a, b)) { + // split the polygon in two by the diagonal + Node* c = splitPolygon(a, b); + + // filter colinear points around the cuts + a = filterPoints(a, a->next); + c = filterPoints(c, c->next); + + // run earcut on each half + earcutLinked(a); + earcutLinked(c); + return; + } + b = b->next; + } + a = a->next; + } while (a != start); +} + +// link every hole into the outer loop, producing a single-ring polygon without holes +template template +typename Earcut::Node* +Earcut::eliminateHoles(const Polygon& points, Node* outerNode) { + const size_t len = points.size(); + + std::vector queue; + for (size_t i = 1; i < len; i++) { + Node* list = linkedList(points[i], false); + if (list) { + if (list == list->next) list->steiner = true; + queue.push_back(getLeftmost(list)); + } + } + std::sort(queue.begin(), queue.end(), [](const Node* a, const Node* b) { + return a->x < b->x; + }); + + // process holes from left to right + for (size_t i = 0; i < queue.size(); i++) { + outerNode = eliminateHole(queue[i], outerNode); + } + + return outerNode; +} + +// find a bridge between vertices that connects hole with an outer ring and and link it +template +typename Earcut::Node* +Earcut::eliminateHole(Node* hole, Node* outerNode) { + Node* bridge = findHoleBridge(hole, outerNode); + if (!bridge) { + return outerNode; + } + + Node* bridgeReverse = splitPolygon(bridge, hole); + + // filter collinear points around the cuts + filterPoints(bridgeReverse, bridgeReverse->next); + + // Check if input node was removed by the filtering + return filterPoints(bridge, bridge->next); +} + +// David Eberly's algorithm for finding a bridge between hole and outer polygon +template +typename Earcut::Node* +Earcut::findHoleBridge(Node* hole, Node* outerNode) { + Node* p = outerNode; + double hx = hole->x; + double hy = hole->y; + double qx = -std::numeric_limits::infinity(); + Node* m = nullptr; + + // find a segment intersected by a ray from the hole's leftmost Vertex to the left; + // segment's endpoint with lesser x will be potential connection Vertex + do { + if (hy <= p->y && hy >= p->next->y && p->next->y != p->y) { + double x = p->x + (hy - p->y) * (p->next->x - p->x) / (p->next->y - p->y); + if (x <= hx && x > qx) { + qx = x; + m = p->x < p->next->x ? p : p->next; + if (x == hx) return m; // hole touches outer segment; pick leftmost endpoint + } + } + p = p->next; + } while (p != outerNode); + + if (!m) return 0; + + // look for points inside the triangle of hole Vertex, segment intersection and endpoint; + // if there are no points found, we have a valid connection; + // otherwise choose the Vertex of the minimum angle with the ray as connection Vertex + + const Node* stop = m; + double tanMin = std::numeric_limits::infinity(); + double tanCur = 0; + + p = m; + double mx = m->x; + double my = m->y; + + do { + if (hx >= p->x && p->x >= mx && hx != p->x && + pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p->x, p->y)) { + + tanCur = std::abs(hy - p->y) / (hx - p->x); // tangential + + if (locallyInside(p, hole) && + (tanCur < tanMin || (tanCur == tanMin && (p->x > m->x || sectorContainsSector(m, p))))) { + m = p; + tanMin = tanCur; + } + } + + p = p->next; + } while (p != stop); + + return m; +} + +// whether sector in vertex m contains sector in vertex p in the same coordinates +template +bool Earcut::sectorContainsSector(const Node* m, const Node* p) { + return area(m->prev, m, p->prev) < 0 && area(p->next, m, m->next) < 0; +} + +// interlink polygon nodes in z-order +template +void Earcut::indexCurve(Node* start) { + assert(start); + Node* p = start; + + do { + p->z = p->z ? p->z : zOrder(p->x, p->y); + p->prevZ = p->prev; + p->nextZ = p->next; + p = p->next; + } while (p != start); + + p->prevZ->nextZ = nullptr; + p->prevZ = nullptr; + + sortLinked(p); +} + +// Simon Tatham's linked list merge sort algorithm +// http://www.chiark.greenend.org.uk/~sgtatham/algorithms/listsort.html +template +typename Earcut::Node* +Earcut::sortLinked(Node* list) { + assert(list); + Node* p; + Node* q; + Node* e; + Node* tail; + int i, numMerges, pSize, qSize; + int inSize = 1; + + for (;;) { + p = list; + list = nullptr; + tail = nullptr; + numMerges = 0; + + while (p) { + numMerges++; + q = p; + pSize = 0; + for (i = 0; i < inSize; i++) { + pSize++; + q = q->nextZ; + if (!q) break; + } + + qSize = inSize; + + while (pSize > 0 || (qSize > 0 && q)) { + + if (pSize == 0) { + e = q; + q = q->nextZ; + qSize--; + } else if (qSize == 0 || !q) { + e = p; + p = p->nextZ; + pSize--; + } else if (p->z <= q->z) { + e = p; + p = p->nextZ; + pSize--; + } else { + e = q; + q = q->nextZ; + qSize--; + } + + if (tail) tail->nextZ = e; + else list = e; + + e->prevZ = tail; + tail = e; + } + + p = q; + } + + tail->nextZ = nullptr; + + if (numMerges <= 1) return list; + + inSize *= 2; + } +} + +// z-order of a Vertex given coords and size of the data bounding box +template +int32_t Earcut::zOrder(const double x_, const double y_) { + // coords are transformed into non-negative 15-bit integer range + int32_t x = static_cast((x_ - minX) * inv_size); + int32_t y = static_cast((y_ - minY) * inv_size); + + x = (x | (x << 8)) & 0x00FF00FF; + x = (x | (x << 4)) & 0x0F0F0F0F; + x = (x | (x << 2)) & 0x33333333; + x = (x | (x << 1)) & 0x55555555; + + y = (y | (y << 8)) & 0x00FF00FF; + y = (y | (y << 4)) & 0x0F0F0F0F; + y = (y | (y << 2)) & 0x33333333; + y = (y | (y << 1)) & 0x55555555; + + return x | (y << 1); +} + +// find the leftmost node of a polygon ring +template +typename Earcut::Node* +Earcut::getLeftmost(Node* start) { + Node* p = start; + Node* leftmost = start; + do { + if (p->x < leftmost->x || (p->x == leftmost->x && p->y < leftmost->y)) + leftmost = p; + p = p->next; + } while (p != start); + + return leftmost; +} + +// check if a point lies within a convex triangle +template +bool Earcut::pointInTriangle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) const { + return (cx - px) * (ay - py) >= (ax - px) * (cy - py) && + (ax - px) * (by - py) >= (bx - px) * (ay - py) && + (bx - px) * (cy - py) >= (cx - px) * (by - py); +} + +// check if a diagonal between two polygon nodes is valid (lies in polygon interior) +template +bool Earcut::isValidDiagonal(Node* a, Node* b) { + return a->next->i != b->i && a->prev->i != b->i && !intersectsPolygon(a, b) && // dones't intersect other edges + ((locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible + (area(a->prev, a, b->prev) != 0.0 || area(a, b->prev, b) != 0.0)) || // does not create opposite-facing sectors + (equals(a, b) && area(a->prev, a, a->next) > 0 && area(b->prev, b, b->next) > 0)); // special zero-length case +} + +// signed area of a triangle +template +double Earcut::area(const Node* p, const Node* q, const Node* r) const { + return (q->y - p->y) * (r->x - q->x) - (q->x - p->x) * (r->y - q->y); +} + +// check if two points are equal +template +bool Earcut::equals(const Node* p1, const Node* p2) { + return p1->x == p2->x && p1->y == p2->y; +} + +// check if two segments intersect +template +bool Earcut::intersects(const Node* p1, const Node* q1, const Node* p2, const Node* q2) { + int o1 = sign(area(p1, q1, p2)); + int o2 = sign(area(p1, q1, q2)); + int o3 = sign(area(p2, q2, p1)); + int o4 = sign(area(p2, q2, q1)); + + if (o1 != o2 && o3 != o4) return true; // general case + + if (o1 == 0 && onSegment(p1, p2, q1)) return true; // p1, q1 and p2 are collinear and p2 lies on p1q1 + if (o2 == 0 && onSegment(p1, q2, q1)) return true; // p1, q1 and q2 are collinear and q2 lies on p1q1 + if (o3 == 0 && onSegment(p2, p1, q2)) return true; // p2, q2 and p1 are collinear and p1 lies on p2q2 + if (o4 == 0 && onSegment(p2, q1, q2)) return true; // p2, q2 and q1 are collinear and q1 lies on p2q2 + + return false; +} + +// for collinear points p, q, r, check if point q lies on segment pr +template +bool Earcut::onSegment(const Node* p, const Node* q, const Node* r) { + return q->x <= std::max(p->x, r->x) && + q->x >= std::min(p->x, r->x) && + q->y <= std::max(p->y, r->y) && + q->y >= std::min(p->y, r->y); +} + +template +int Earcut::sign(double val) { + return (0.0 < val) - (val < 0.0); +} + +// check if a polygon diagonal intersects any polygon segments +template +bool Earcut::intersectsPolygon(const Node* a, const Node* b) { + const Node* p = a; + do { + if (p->i != a->i && p->next->i != a->i && p->i != b->i && p->next->i != b->i && + intersects(p, p->next, a, b)) return true; + p = p->next; + } while (p != a); + + return false; +} + +// check if a polygon diagonal is locally inside the polygon +template +bool Earcut::locallyInside(const Node* a, const Node* b) { + return area(a->prev, a, a->next) < 0 ? + area(a, b, a->next) >= 0 && area(a, a->prev, b) >= 0 : + area(a, b, a->prev) < 0 || area(a, a->next, b) < 0; +} + +// check if the middle Vertex of a polygon diagonal is inside the polygon +template +bool Earcut::middleInside(const Node* a, const Node* b) { + const Node* p = a; + bool inside = false; + double px = (a->x + b->x) / 2; + double py = (a->y + b->y) / 2; + do { + if (((p->y > py) != (p->next->y > py)) && p->next->y != p->y && + (px < (p->next->x - p->x) * (py - p->y) / (p->next->y - p->y) + p->x)) + inside = !inside; + p = p->next; + } while (p != a); + + return inside; +} + +// link two polygon vertices with a bridge; if the vertices belong to the same ring, it splits +// polygon into two; if one belongs to the outer ring and another to a hole, it merges it into a +// single ring +template +typename Earcut::Node* +Earcut::splitPolygon(Node* a, Node* b) { + Node* a2 = nodes.construct(a->i, a->x, a->y); + Node* b2 = nodes.construct(b->i, b->x, b->y); + Node* an = a->next; + Node* bp = b->prev; + + a->next = b; + b->prev = a; + + a2->next = an; + an->prev = a2; + + b2->next = a2; + a2->prev = b2; + + bp->next = b2; + b2->prev = bp; + + return b2; +} + +// create a node and util::optionally link it with previous one (in a circular doubly linked list) +template template +typename Earcut::Node* +Earcut::insertNode(std::size_t i, const Point& pt, Node* last) { + Node* p = nodes.construct(static_cast(i), util::nth<0, Point>::get(pt), util::nth<1, Point>::get(pt)); + + if (!last) { + p->prev = p; + p->next = p; + + } else { + assert(last); + p->next = last->next; + p->prev = last; + last->next->prev = p; + last->next = p; + } + return p; +} + +template +void Earcut::removeNode(Node* p) { + p->next->prev = p->prev; + p->prev->next = p->next; + + if (p->prevZ) p->prevZ->nextZ = p->nextZ; + if (p->nextZ) p->nextZ->prevZ = p->prevZ; +} +} + +template +std::vector earcut(const Polygon& poly) { + mapbox::detail::Earcut earcut; + earcut(poly); + return std::move(earcut.indices); +} +} diff --git a/src/libslic3r/GCode/GCodeProcessor.cpp b/src/libslic3r/GCode/GCodeProcessor.cpp index 119cccb..5f3693b 100644 --- a/src/libslic3r/GCode/GCodeProcessor.cpp +++ b/src/libslic3r/GCode/GCodeProcessor.cpp @@ -335,7 +335,7 @@ static void planner_forward_pass_kernel(GCodeProcessor::TimeBlock& prev, GCodePr // speeds have already been reset, maximized, and reverse planned by reverse planner. // If nominal length is true, max junction speed is guaranteed to be reached. No need to recheck. - // ǰһblockɼ٣ôҪٵٶȣ¼㵱ǰblockentryٶ + // 如果前一个block不能完成加速,那么要根据其加速到的速度,重新计算当前block的entry速度 if (!prev.flags.nominal_length) { if (prev.feedrate_profile.entry < curr.feedrate_profile.entry) { float entry_speed = std::min(curr.feedrate_profile.entry, max_allowable_speed(-prev.acceleration, prev.feedrate_profile.entry, prev.distance)); @@ -3805,7 +3805,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) TimeMachine::State& prev = machine.prev; std::vector& blocks = machine.blocks; - // m_feedrate gcodeнãڱ֤СٶƵ¸feedrate + // m_feedrate 从gcode中解析获得,在保证最小速度限制的情况下赋给feedrate curr.feedrate = (delta_pos[E] == 0.0f) ? minimum_travel_feedrate(static_cast(i), m_feedrate) : minimum_feedrate(static_cast(i), m_feedrate); @@ -3828,7 +3828,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) block.layer_id = std::max(1, m_layer_id); block.flags.prepare_stage = m_processing_start_custom_gcode; - // calculates block acceleration㵱ǰblockܵļٶ + // calculates block acceleration,计算当前block的最高能到达的加速度 float acceleration = (type == EMoveType::Travel) ? get_travel_acceleration(static_cast(i)) : (is_extrusion_only_move(delta_pos) ? @@ -3862,7 +3862,7 @@ void GCodeProcessor::process_G1(const GCodeReader::GCodeLine& line) } // calculates block cruise feedrate - // ٳǰϵ㵱ǰblockܵٶ + // 刨除前后关系,单纯计算当前block最高能到达的速度 float min_feedrate_factor = 1.0f; for (unsigned char a = X; a <= E; ++a) { curr.axis_feedrate[a] = curr.feedrate * delta_pos[a] * inv_distance; diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index 5df1863..6699c60 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -94,6 +94,8 @@ set(SLIC3R_GUI_SOURCES GUI/Auxiliary.hpp GUI/DailyTips.cpp GUI/DailyTips.hpp + GUI/EncodedFilament.hpp + GUI/EncodedFilament.cpp GUI/Project.cpp GUI/Project.hpp GUI/BackgroundSlicingProcess.cpp @@ -146,6 +148,8 @@ set(SLIC3R_GUI_SOURCES GUI/Gizmos/GLGizmoSimplify.hpp GUI/Gizmos/GLGizmoMmuSegmentation.cpp GUI/Gizmos/GLGizmoMmuSegmentation.hpp + GUI/Gizmos/GLGizmoFuzzySkin.cpp + GUI/Gizmos/GLGizmoFuzzySkin.hpp GUI/Gizmos/GLGizmoFaceDetector.cpp GUI/Gizmos/GLGizmoFaceDetector.hpp GUI/Gizmos/GLGizmoMeasure.cpp @@ -316,6 +320,10 @@ set(SLIC3R_GUI_SOURCES GUI/wxExtensions.hpp GUI/ObjColorDialog.cpp GUI/ObjColorDialog.hpp + GUI/FilamentPickerDialog.cpp + GUI/FilamentPickerDialog.hpp + GUI/FilamentBitmapUtils.cpp + GUI/FilamentBitmapUtils.hpp GUI/WipeTowerDialog.cpp GUI/WipeTowerDialog.hpp GUI/RemovableDriveManager.cpp @@ -332,6 +340,8 @@ set(SLIC3R_GUI_SOURCES GUI/SyncBoxInfoDialog.hpp GUI/SurfaceDrag.cpp GUI/SurfaceDrag.hpp + GUI/TextLines.cpp + GUI/TextLines.hpp GUI/PlateSettingsDialog.cpp GUI/PlateSettingsDialog.hpp GUI/ImGuiWrapper.hpp @@ -376,6 +386,10 @@ set(SLIC3R_GUI_SOURCES GUI/Jobs/UpgradeNetworkJob.cpp GUI/Jobs/ArrangeJob.hpp GUI/Jobs/ArrangeJob.cpp + GUI/Jobs/CreateFontNameImageJob.cpp + GUI/Jobs/CreateFontNameImageJob.hpp + GUI/Jobs/CreateFontStyleImagesJob.cpp + GUI/Jobs/CreateFontStyleImagesJob.hpp GUI/Jobs/OrientJob.hpp GUI/Jobs/OrientJob.cpp GUI/Jobs/RotoptimizeJob.hpp @@ -430,6 +444,8 @@ set(SLIC3R_GUI_SOURCES GUI/NotificationManager.hpp GUI/UnsavedChangesDialog.cpp GUI/UnsavedChangesDialog.hpp + GUI/UserPresetsDialog.cpp + GUI/UserPresetsDialog.hpp GUI/ExtraRenderers.cpp GUI/ExtraRenderers.hpp GUI/ProjectDirtyStateManager.hpp @@ -448,6 +464,11 @@ set(SLIC3R_GUI_SOURCES GUI/BonjourDialog.hpp GUI/BindDialog.cpp GUI/BindDialog.hpp + GUI/PartSkipCommon.hpp + GUI/PartSkipDialog.cpp + GUI/PartSkipDialog.hpp + GUI/SkipPartCanvas.cpp + GUI/SkipPartCanvas.hpp GUI/ModelMall.hpp GUI/ModelMall.cpp GUI/SelectMachine.hpp @@ -506,12 +527,18 @@ set(SLIC3R_GUI_SOURCES GUI/SendMultiMachinePage.cpp GUI/TaskManager.cpp GUI/TaskManager.hpp + Utils/WxFontUtils.cpp + Utils/WxFontUtils.hpp + Utils/EmbossStyleManager.cpp + Utils/EmbossStyleManager.hpp Utils/Http.cpp Utils/Http.hpp Utils/FixModelByWin10.cpp Utils/FixModelByWin10.hpp Utils/Bonjour.cpp Utils/Bonjour.hpp + Utils/QDTUtil.hpp + Utils/QDTUtil.cpp Utils/FileHelp.cpp Utils/FileHelp.hpp Utils/PresetUpdater.cpp diff --git a/src/slic3r/GUI/3DBed.cpp b/src/slic3r/GUI/3DBed.cpp index 224a1f3..de14cff 100644 --- a/src/slic3r/GUI/3DBed.cpp +++ b/src/slic3r/GUI/3DBed.cpp @@ -18,8 +18,6 @@ #include #include #include -//B -// #include static const float GROUND_Z = -0.04f; static const std::array DEFAULT_MODEL_COLOR = { 0.3255f, 0.337f, 0.337f, 1.0f }; @@ -270,16 +268,7 @@ bool Bed3D::set_shape(const Pointfs& printable_area, const double printable_heig //QDS: add part plate logic //QDS add default bed -#if 1 - ExPolygon poly{ Polygon::new_scale(printable_area) }; -#else - ExPolygon poly; - for (const Vec2d& p : printable_area) { - poly.contour.append(Point(scale_(p(0) + m_position.x()), scale_(p(1) + m_position.y()))); - } -#endif - - calc_triangles(poly); + m_triangles.reset(); //no need gridline for 3dbed //const BoundingBox& bed_bbox = poly.contour.bounding_box(); @@ -675,6 +664,7 @@ void Bed3D::update_bed_triangles() calc_triangles(poly); // update extended bounding box const_cast(m_extended_bounding_box) = calc_extended_bounding_box(); + } void Bed3D::render_model() const @@ -705,6 +695,9 @@ void Bed3D::render_model() const const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", view_normal_matrix); if (m_build_volume.get_extruder_area_count() > 0) { + auto printable_area = m_build_volume.printable_area(); + std::array full_print_volume = {(float)printable_area[0].x(), (float)printable_area[0].y(), (float)printable_area[2].x(), (float)printable_area[2].y()}; + shader->set_uniform("full_print_volume", full_print_volume); const BuildVolume::BuildSharedVolume& shared_volume = m_build_volume.get_shared_volume(); std::array xy_data = shared_volume.data; shader->set_uniform("print_volume.type", shared_volume.type); diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index b8f5031..d05bae4 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -1731,8 +1731,12 @@ void GLVolumeCollection::render(GUI::ERenderPipelineStage render_pip if (GUI::ERenderPipelineStage::Silhouette != render_pipeline_stage) { shader->set_uniform("is_text_shape", volume.first->is_text_shape); shader->set_uniform("uniform_color", volume.first->render_color); - shader->set_uniform("z_range", m_z_range, 2); - shader->set_uniform("clipping_plane", m_clipping_plane, 4); + shader->set_uniform("z_range", m_z_range); + shader->set_uniform("clipping_plane", m_clipping_plane); + shader->set_uniform("use_color_clip_plane", m_use_color_clip_plane); + shader->set_uniform("color_clip_plane", m_color_clip_plane); + shader->set_uniform("uniform_color_clip_plane_1", m_color_clip_plane_colors[0]); + shader->set_uniform("uniform_color_clip_plane_2", m_color_clip_plane_colors[1]); //BOOST_LOG_TRIVIAL(info) << boost::format("set uniform_color to {%1%, %2%, %3%, %4%}, with_outline=%5%, selected %6%") // %volume.first->render_color[0]%volume.first->render_color[1]%volume.first->render_color[2]%volume.first->render_color[3] // %with_outline%volume.first->selected; diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 5a12c72..86adbf2 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -669,10 +669,15 @@ private: PrintVolume m_render_volume; // z range for clipping in shaders - float m_z_range[2]; + std::array m_z_range; // plane coeffs for clipping in shaders - float m_clipping_plane[4]; + std::array m_clipping_plane; + // plane coeffs for render volumes with different colors in shaders + // used by cut gizmo + std::array m_color_clip_plane; + bool m_use_color_clip_plane{false}; + std::array m_color_clip_plane_colors{ColorRGBA::RED(), ColorRGBA::BLUE()}; struct Slope { @@ -784,6 +789,17 @@ public: m_clipping_plane[3] = coeffs[3]; } + const std::array & get_z_range() const { return m_z_range; } + const std::array &get_clipping_plane() const { return m_clipping_plane; } + + void set_use_color_clip_plane(bool use) { m_use_color_clip_plane = use; } + void set_color_clip_plane(const Vec3d &cp_normal, double offset) + { + for (int i = 0; i < 3; ++i) m_color_clip_plane[i] = -cp_normal[i]; + m_color_clip_plane[3] = offset; + } + void set_color_clip_plane_colors(const std::array &colors) { m_color_clip_plane_colors = colors; } + bool is_slope_GlobalActive() const { return m_slope.isGlobalActive; } bool is_slope_active() const { return m_slope.active; } void set_slope_active(bool active) { m_slope.active = active; } diff --git a/src/slic3r/GUI/AMSMaterialsSetting.cpp b/src/slic3r/GUI/AMSMaterialsSetting.cpp index 9d61282..ce57f69 100644 --- a/src/slic3r/GUI/AMSMaterialsSetting.cpp +++ b/src/slic3r/GUI/AMSMaterialsSetting.cpp @@ -9,6 +9,8 @@ #include #include "CalibUtils.hpp" #include "../Utils/ColorSpaceConvert.hpp" +#include "EncodedFilament.hpp" + namespace Slic3r { namespace GUI { wxDEFINE_EVENT(EVT_SELECTED_COLOR, wxCommandEvent); @@ -178,6 +180,11 @@ void AMSMaterialsSetting::create_panel_normal(wxWindow* parent) m_clr_picker->Bind(wxEVT_LEFT_DOWN, &AMSMaterialsSetting::on_clr_picker, this); m_sizer_colour->Add(m_clr_picker, 0, 0, 0); + m_clr_name = new Label(parent, wxEmptyString); + m_clr_name->SetForegroundColour(*wxBLACK); + m_clr_name->SetBackgroundColour(*wxWHITE); + m_clr_name->SetFont(Label::Body_13); + m_sizer_colour->Add(m_clr_name, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(10)); wxBoxSizer* m_sizer_temperature = new wxBoxSizer(wxHORIZONTAL); m_title_temperature = new wxStaticText(parent, wxID_ANY, _L("Nozzle\nTemperature"), wxDefaultPosition, wxSize(AMS_MATERIALS_SETTING_LABEL_WIDTH, -1), 0); @@ -724,18 +731,34 @@ void AMSMaterialsSetting::set_color(wxColour color) //m_clrData->SetColour(color); m_clr_picker->is_empty(false); m_clr_picker->set_color(color); + + FilamentColor fila_color; + fila_color.m_colors.insert(color); + fila_color.EndSet(m_clr_picker->ctype); + auto clr_query = GUI::wxGetApp().get_filament_color_code_query(); + m_clr_name->SetLabelText(clr_query->GetFilaColorName(ams_filament_id, fila_color)); } void AMSMaterialsSetting::set_empty_color(wxColour color) { m_clr_picker->is_empty(true); m_clr_picker->set_color(color); + m_clr_name->SetLabelText(wxEmptyString); } void AMSMaterialsSetting::set_colors(std::vector colors) { //m_clrData->SetColour(color); m_clr_picker->set_colors(colors); + + if (!colors.empty()) + { + FilamentColor fila_color; + for (const auto& clr : colors) { fila_color.m_colors.insert(clr); } + fila_color.EndSet(m_clr_picker->ctype); + auto clr_query = GUI::wxGetApp().get_filament_color_code_query(); + m_clr_name->SetLabelText(clr_query->GetFilaColorName(ams_filament_id, fila_color)); + } } void AMSMaterialsSetting::set_ctype(int ctype) @@ -1075,11 +1098,6 @@ void AMSMaterialsSetting::post_select_event(int index) { wxPostEvent(m_comboBox_filament, event); } -void AMSMaterialsSetting::msw_rescale() -{ - m_clr_picker->msw_rescale(); -} - void AMSMaterialsSetting::on_select_cali_result(wxCommandEvent &evt) { m_pa_cali_select_id = evt.GetSelection(); @@ -1241,9 +1259,7 @@ void AMSMaterialsSetting::on_select_filament(wxCommandEvent &evt) PACalibResult default_item; default_item.cali_idx = -1; default_item.filament_id = ams_filament_id; - std::vector machine_list = {"N1", "N2S", "C11", "C12", "C13", "BL-P001", "BL-P002"}; - auto iter = std::find(machine_list.begin(), machine_list.end(), obj->printer_type); - if (iter == machine_list.end()) { + if (obj->is_support_auto_flow_calibration) { default_item.k_value = -1; default_item.n_coef = -1; } @@ -1335,7 +1351,7 @@ void AMSMaterialsSetting::on_dpi_changed(const wxRect &suggested_rect) m_input_nozzle_max->GetTextCtrl()->SetSize(wxSize(-1, FromDIP(20))); m_input_nozzle_min->GetTextCtrl()->SetSize(wxSize(-1, FromDIP(20))); m_input_k_val->GetTextCtrl()->SetSize(wxSize(-1, FromDIP(20))); - //m_clr_picker->msw_rescale(); + m_clr_picker->msw_rescale(); degree->msw_rescale(); bitmap_max_degree->SetBitmap(degree->bmp()); bitmap_min_degree->SetBitmap(degree->bmp()); @@ -1369,6 +1385,7 @@ void ColorPicker::msw_rescale() { m_bitmap_border = create_scaled_bitmap("color_picker_border", nullptr, 25); m_bitmap_border_dark = create_scaled_bitmap("color_picker_border_dark", nullptr, 25); + m_bitmap_transparent = create_scaled_bitmap("transparent_color_picker", nullptr, 25); Refresh(); } @@ -1423,7 +1440,10 @@ void ColorPicker::doRender(wxDC& dc) if (m_selected) radius -= FromDIP(1); if (alpha == 0) { - dc.DrawBitmap(m_bitmap_transparent, 0, 0); + wxSize bmp_size = m_bitmap_transparent.GetSize(); + int center_x = (size.x - bmp_size.x) / 2; + int center_y = (size.y - bmp_size.y) / 2; + dc.DrawBitmap(m_bitmap_transparent, center_x, center_y); } else if (alpha != 254 && alpha != 255) { if (transparent_changed) { @@ -1439,7 +1459,11 @@ void ColorPicker::doRender(wxDC& dc) replace.push_back(fill_replace); m_bitmap_transparent = ScalableBitmap(this, "transparent_color_picker", 25, false, false, true, replace).bmp(); transparent_changed = false; - dc.DrawBitmap(m_bitmap_transparent, 0, 0); + + wxSize bmp_size = m_bitmap_transparent.GetSize(); + int center_x = (size.x - bmp_size.x) / 2; + int center_y = (size.y - bmp_size.y) / 2; + dc.DrawBitmap(m_bitmap_transparent, center_x, center_y); } } else { diff --git a/src/slic3r/GUI/AMSMaterialsSetting.hpp b/src/slic3r/GUI/AMSMaterialsSetting.hpp index 939604d..76bad8c 100644 --- a/src/slic3r/GUI/AMSMaterialsSetting.hpp +++ b/src/slic3r/GUI/AMSMaterialsSetting.hpp @@ -108,7 +108,6 @@ public: wxString k = wxEmptyString, wxString n = wxEmptyString); void post_select_event(int index); - void msw_rescale(); void set_color(wxColour color); void set_empty_color(wxColour color); void set_colors(std::vector colors); @@ -130,6 +129,7 @@ public: std::string m_filament_type; ColorPickerPopup m_color_picker_popup; ColorPicker * m_clr_picker; + Label* m_clr_name; std::vector m_pa_profile_items; protected: diff --git a/src/slic3r/GUI/AmsMappingPopup.cpp b/src/slic3r/GUI/AmsMappingPopup.cpp index 744fb68..9174062 100644 --- a/src/slic3r/GUI/AmsMappingPopup.cpp +++ b/src/slic3r/GUI/AmsMappingPopup.cpp @@ -1354,7 +1354,7 @@ void AmsMapingPopup::add_ams_mapping(std::vector tray_data, bool remai // temp if (tray_data[i].type == EMPTY) { - m_mapping_item->set_data(m_tag_material, wxColour(0xCE, 0xCE, 0xCE), "-", remain_detect_flag, tray_data[i]); + m_mapping_item->set_data(m_tag_material, wxColour(0xEE, 0xEE, 0xEE), "-", remain_detect_flag, tray_data[i]); m_mapping_item->Bind(wxEVT_LEFT_DOWN, [this, tray_data, i, m_mapping_item](wxMouseEvent &e) { if (!m_mapping_from_multi_machines) { @@ -1392,7 +1392,7 @@ void AmsMapingPopup::add_ext_ams_mapping(TrayData tray_data, MappingItem* item) item->set_data(m_tag_material, tray_data.colour, tray_data.name, false, tray_data); } else { - item->set_data(m_tag_material, wxColour(0xEE, 0xEE, 0xEE), tray_data.name, false, tray_data, true); + item->set_data(m_tag_material, tray_data.colour, tray_data.name, false, tray_data, true); m_has_unmatch_filament = true; } @@ -1507,8 +1507,8 @@ static void _DrawRemainArea(const MappingItem *item, const TrayData &dd, bool su int full_range_width = size.x; /*range background*/ - dc.SetPen(wxColour(0xE4E4E4)); - dc.SetBrush(wxColour(0xE4E4E4)); + dc.SetPen(wxColour("#E4E4E4")); + dc.SetBrush(wxColour("#E4E4E4")); int bg_height = item->FromDIP(6); int bg_width = full_range_width - (2 * x_margin); dc.DrawRoundedRectangle(x_margin, y_margin, bg_width, bg_height, item->FromDIP(2)); @@ -1565,7 +1565,10 @@ void MappingItem::render(wxDC &dc) dc.SetFont(::Label::Head_13); auto txt_colour = m_coloul.GetLuminance() < 0.6 ? *wxWHITE : wxColour(0x26, 0x2E, 0x30); - txt_colour = m_unmatch ? wxColour(0xCE, 0xCE, 0xCE) : txt_colour; + + if (m_unmatch || m_name == "-") { txt_colour = wxColour(0xCE, 0xCE, 0xCE); } + // txt_colour = m_unmatch ? wxColour(0xCE, 0xCE, 0xCE) : txt_colour; + if (m_coloul.Alpha() == 0) txt_colour = wxColour(0x26, 0x2E, 0x30); dc.SetTextForeground(txt_colour); @@ -1598,7 +1601,7 @@ void MappingItem::set_data(const wxString &tag_name, wxColour colour, wxString n if (!m_name.IsEmpty() && (m_name != "-")) { const wxString &msg = wxString::Format(_L("Note: the filament type(%s) does not match with the filament type(%s) in the slicing file. " "If you want to use this slot, you can install %s instead of %s and change slot information on the 'Device' page."), - m_name, tag_name, m_name, tag_name); + m_name, tag_name, tag_name, m_name); SetToolTip(msg); } else { const wxString &msg = wxString::Format(_L("Note: the slot is empty or undefined. If you want to use this slot, you can install %s and change slot information on the 'Device' page."), tag_name); @@ -1606,7 +1609,7 @@ void MappingItem::set_data(const wxString &tag_name, wxColour colour, wxString n } } else { - SetToolTip(_L("Note: Only the BOX slots loaded with the same material type can be selected.")); + SetToolTip(_L("Note: Only filament-loaded slots can be selected.")); } } else @@ -1655,14 +1658,14 @@ void MappingItem::doRender(wxDC &dc) } } } - else if (color.Alpha() == 0 && !m_unmatch) { + else if (color.Alpha() == 0) { dc.DrawBitmap(m_transparent_mapping_item.bmp(), 0, (size.y - MAPPING_ITEM_REAL_SIZE.y) / 2 + get_remain_area_height()); } else { dc.DrawRectangle(0, (size.y - MAPPING_ITEM_REAL_SIZE.y) / 2 + get_remain_area_height(), MAPPING_ITEM_REAL_SIZE.x, MAPPING_ITEM_REAL_SIZE.y); } - wxColour side_colour = wxColour(0xE4E4E4); + wxColour side_colour = wxColour("#E4E4E4"); dc.SetPen(side_colour); dc.SetBrush(wxBrush(side_colour)); @@ -2012,14 +2015,14 @@ AmsIntroducePopup::AmsIntroducePopup(wxWindow* parent) m_staticText_top = new Label(this, _L("Do not Enable BOX")); m_staticText_top->SetFont(::Label::Head_13); - // m_staticText_top->SetForegroundColour(wxColour(0x323A3D)); + // m_staticText_top->SetForegroundColour(wxColour("#323A3D")); m_staticText_top->Wrap(-1); bSizer4->Add(m_staticText_top, 0, wxALL, 5); m_staticText_bottom = new Label(this, _L("Print using materials mounted on the back of the case")); m_staticText_bottom->Wrap(-1); m_staticText_bottom->SetFont(::Label::Body_13); - m_staticText_bottom->SetForegroundColour(wxColour(0x6B6B6B)); + m_staticText_bottom->SetForegroundColour(wxColour("#6B6B6B")); bSizer4->Add(m_staticText_bottom, 0, wxALL, 5); wxBoxSizer* bSizer5; @@ -2583,7 +2586,7 @@ void AmsRMGroup::doRender(wxDC& dc) dc.SetPen(*wxTRANSPARENT_PEN); - if (tray_color == *wxWHITE) dc.SetPen(wxPen(wxColour(0xEEEEEE), 2)); + if (tray_color == *wxWHITE) dc.SetPen(wxPen(wxColour("#EEEEEE"), 2)); dc.SetBrush(wxBrush(tray_color)); int x = size.x / 2; @@ -2633,14 +2636,14 @@ void AmsRMGroup::doRender(wxDC& dc) //draw tray dc.SetFont(::Label::Body_12); auto text_size = dc.GetTextExtent(tray_name); - dc.SetTextForeground(tray_color.GetLuminance() < 0.6 ? *wxWHITE : wxColour(0x262E30)); - if (tray_color.Alpha() == 0) {dc.SetTextForeground(wxColour(0x262E30));} + dc.SetTextForeground(tray_color.GetLuminance() < 0.6 ? *wxWHITE : wxColour("#262E30")); + if (tray_color.Alpha() == 0) { dc.SetTextForeground(wxColour("#262E30")); } dc.DrawText(tray_name, x_center - text_size.x / 2, size.y - y_center - text_size.y / 2); //draw split line dc.SetPen(wxPen(*wxWHITE, 2)); - if (tray_color.Alpha() == 0) {dc.SetPen(wxPen(wxColour(0xCECECE), 2));} + if (tray_color.Alpha() == 0) { dc.SetPen(wxPen(wxColour("#CECECE"), 2)); } dc.SetBrush(*wxTRANSPARENT_BRUSH); auto pos_sp_start = CalculateEndpoint(wxPoint(x, y), (360 - startAngle), size.x / 2 - FromDIP(3)); dc.DrawLine(wxPoint(x, y), pos_sp_start); @@ -2664,7 +2667,7 @@ void AmsRMGroup::doRender(wxDC& dc) //dc.DrawBitmap(bitmap_backup_tips_1.bmp(), wxPoint((size.x - bitmap_backup_tips_1.GetBmpSize().x) / 2, (size.y - bitmap_backup_tips_1.GetBmpSize().y) / 2)); //draw material - dc.SetTextForeground(wxColour(0x323A3D)); + dc.SetTextForeground(wxColour("#323A3D")); dc.SetFont(Label::Head_15); auto text_size = dc.GetTextExtent(m_material_name); dc.DrawText(m_material_name, (size.x - text_size.x) / 2,(size.y - text_size.y) / 2 - FromDIP(12)); @@ -2738,7 +2741,7 @@ void AmsHumidityLevelList::doRender(wxDC& dc) //dry / wet - dc.SetTextForeground(wxColour(0x989898)); + dc.SetTextForeground(wxColour("#989898")); dc.SetFont(::Label::Head_20); auto font_top = GetSize().y - dc.GetTextExtent(_L("DRY")).GetHeight(); diff --git a/src/slic3r/GUI/BonjourDialog.cpp b/src/slic3r/GUI/BonjourDialog.cpp index 9d65254..6edf300 100644 --- a/src/slic3r/GUI/BonjourDialog.cpp +++ b/src/slic3r/GUI/BonjourDialog.cpp @@ -86,6 +86,8 @@ BonjourDialog::BonjourDialog(wxWindow *parent, Slic3r::PrinterTechnology tech) , timer_state(0) , tech(tech) { + SetBackgroundColour(*wxWHITE); + const int em = GUI::wxGetApp().em_unit(); list->SetMinSize(wxSize(40 * em, 30 * em)); list->SetTextColour(StateColor::darkModeColorFor(wxColour("#323A3C"))); diff --git a/src/slic3r/GUI/Calibration.cpp b/src/slic3r/GUI/Calibration.cpp index faf6ff7..c911316 100644 --- a/src/slic3r/GUI/Calibration.cpp +++ b/src/slic3r/GUI/Calibration.cpp @@ -118,7 +118,7 @@ CalibrationDialog::CalibrationDialog(Plater *plater) wxBoxSizer *cali_right_sizer_h = new wxBoxSizer(wxHORIZONTAL); wxBoxSizer *cali_right_sizer_v = new wxBoxSizer(wxVERTICAL); - auto cali_right_panel = new StaticBox(body_panel, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(182), FromDIP(160))); + auto cali_right_panel = new StaticBox(body_panel, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(182), FromDIP(200))); cali_right_panel->SetBackgroundColor(BG_COLOR); cali_right_panel->SetBorderColor(BG_COLOR); @@ -222,7 +222,7 @@ void CalibrationDialog::update_cali(MachineObject *obj) select_xcam_cali->Hide(); m_checkbox_list["xcam_cali"]->SetValue(false); } - + if(obj->is_support_bed_leveling != 0){ select_bed_leveling->Show(); }else{ @@ -258,12 +258,14 @@ void CalibrationDialog::update_cali(MachineObject *obj) if (obj->is_calibration_done()) { m_calibration_btn->Enable(); m_calibration_btn->SetLabel(_L("Completed")); + } else { // RUNNING && IDLE m_calibration_btn->Disable(); m_calibration_btn->SetLabel(_L("Calibrating")); + } - auto size = wxSize(CALI_FLOW_CONTENT_WIDTH, obj->stage_list_info.size() * FromDIP(44)); + auto size = wxSize(CALI_FLOW_CONTENT_WIDTH, obj->stage_list_info.size() * FromDIP(35)); if (m_calibration_flow->GetSize().y != size.y) { m_calibration_flow->SetSize(size); m_calibration_flow->SetMinSize(size); @@ -271,6 +273,7 @@ void CalibrationDialog::update_cali(MachineObject *obj) m_calibration_flow->Refresh(); Layout(); + } if (is_stage_list_info_changed(obj)) { // change items if stage_list_info changed diff --git a/src/slic3r/GUI/CalibrationWizard.cpp b/src/slic3r/GUI/CalibrationWizard.cpp index e8bdd92..e8cfadd 100644 --- a/src/slic3r/GUI/CalibrationWizard.cpp +++ b/src/slic3r/GUI/CalibrationWizard.cpp @@ -7,6 +7,7 @@ #include "Tabbook.hpp" #include "CaliHistoryDialog.hpp" #include "CalibUtils.hpp" +#include "QDTUtil.hpp" //w29 #include "MainFrame.hpp" @@ -18,7 +19,6 @@ wxDEFINE_EVENT(EVT_DEVICE_CHANGED, wxCommandEvent); wxDEFINE_EVENT(EVT_CALIBRATION_JOB_FINISHED, wxCommandEvent); static const wxString NA_STR = _L("N/A"); -static const float MIN_PA_K_VALUE_STEP = 0.001; static const int MAX_PA_HISTORY_RESULTS_NUMS = 16; //w29 @@ -26,7 +26,7 @@ CalibrationWizard::CalibrationWizard(wxWindow* parent, CalibMode mode, wxWindowI : wxPanel(parent, id, pos, size, style) , m_mode(mode) { - SetBackgroundColour(wxColour(0xEEEEEE)); + SetBackgroundColour(wxColour("#EEEEEE")); wxBoxSizer* main_sizer = new wxBoxSizer(wxVERTICAL); diff --git a/src/slic3r/GUI/CalibrationWizardPage.cpp b/src/slic3r/GUI/CalibrationWizardPage.cpp index 7363b27..cbabed7 100644 --- a/src/slic3r/GUI/CalibrationWizardPage.cpp +++ b/src/slic3r/GUI/CalibrationWizardPage.cpp @@ -2,6 +2,7 @@ #include "I18N.hpp" #include "Widgets/Label.hpp" #include "MsgDialog.hpp" +#include "QDTUtil.hpp" namespace Slic3r { namespace GUI { diff --git a/src/slic3r/GUI/CalibrationWizardPage.hpp b/src/slic3r/GUI/CalibrationWizardPage.hpp index 20dd27e..4828947 100644 --- a/src/slic3r/GUI/CalibrationWizardPage.hpp +++ b/src/slic3r/GUI/CalibrationWizardPage.hpp @@ -226,9 +226,6 @@ public: virtual void set_cali_method(CalibrationMethod method) { m_cali_method = method; - if (method == CalibrationMethod::CALI_METHOD_MANUAL) { - set_cali_filament_mode(CalibrationFilamentMode::CALI_MODEL_SINGLE); - } } virtual void msw_rescale(); diff --git a/src/slic3r/GUI/CalibrationWizardPresetPage.cpp b/src/slic3r/GUI/CalibrationWizardPresetPage.cpp index 59de2d0..5028346 100644 --- a/src/slic3r/GUI/CalibrationWizardPresetPage.cpp +++ b/src/slic3r/GUI/CalibrationWizardPresetPage.cpp @@ -5,6 +5,7 @@ #include "MsgDialog.hpp" #include "libslic3r/Print.hpp" #include "Tab.hpp" +#include "QDTUtil.hpp" #define CALIBRATION_LABEL_SIZE wxSize(FromDIP(150), FromDIP(24)) #define SYNC_BUTTON_SIZE (wxSize(FromDIP(50), FromDIP(50))) diff --git a/src/slic3r/GUI/ConfigManipulation.cpp b/src/slic3r/GUI/ConfigManipulation.cpp index 245f09b..bbef7df 100644 --- a/src/slic3r/GUI/ConfigManipulation.cpp +++ b/src/slic3r/GUI/ConfigManipulation.cpp @@ -245,6 +245,28 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con is_msg_dlg_already_exist = false; } + //limit scarf start height + double seam_slope_start_height = config->option("seam_slope_start_height")->get_abs_value(1); + bool reset_slope_start_height = false; + if (config->option("seam_slope_start_height")->percent) { + if (seam_slope_start_height >= 1) + reset_slope_start_height = true; + } else { + if (seam_slope_start_height >= config->opt_float("layer_height")) + reset_slope_start_height = true; + } + + if (reset_slope_start_height) { + const wxString msg_text = _(L("Should not large than layer height.\nReset to 10%")); + MessageDialog dialog(nullptr, msg_text, "", wxICON_WARNING | wxOK); + DynamicPrintConfig new_conf = *config; + is_msg_dlg_already_exist = true; + dialog.ShowModal(); + new_conf.set_key_value("seam_slope_start_height", new ConfigOptionFloatOrPercent{10, true}); + apply(config, &new_conf); + is_msg_dlg_already_exist = false; + } + //QDS: top_area_threshold showed if the top one wall function be applyed bool top_one_wall_apply = config->opt_enum("top_one_wall_type") == TopOneWallType::None; toggle_line("top_area_threshold", !top_one_wall_apply); @@ -602,10 +624,13 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in { bool have_perimeters = config->opt_int("wall_loops") > 0; for (auto el : { "ensure_vertical_shell_thickness", "detect_thin_wall", "detect_overhang_wall", - "seam_position","seam_gap","wipe_speed", "wall_sequence", "outer_wall_line_width", + "seam_position","seam_placement_away_from_overhangs","seam_gap","wipe_speed", "wall_sequence", "outer_wall_line_width", "inner_wall_speed", "outer_wall_speed","small_perimeter_speed", "small_perimeter_threshold" }) toggle_field(el, have_perimeters); + SeamPosition seam_pos = config->option>("seam_position")->value; + toggle_line("seam_placement_away_from_overhangs", seam_pos == SeamPosition::spAligned || seam_pos == SeamPosition::spRear); + bool have_infill = config->option("sparse_infill_density")->value > 0; // sparse_infill_filament uses the same logic as in Print::extruders() for (auto el : { "sparse_infill_pattern", "sparse_infill_anchor_max", "infill_combination", "minimum_sparse_infill_area", "sparse_infill_filament", "infill_shift_step", "infill_rotate_step", "symmetric_infill_y_axis"}) @@ -619,7 +644,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in bool is_locked_zig = config->option>("sparse_infill_pattern")->value == InfillPattern::ipLockedZag; toggle_line("infill_shift_step", is_cross_zag || is_locked_zig); - for (auto el : { "skeleton_infill_density", "skin_infill_density", "infill_lock_depth", "skin_infill_depth","skin_infill_line_width", "skeleton_infill_line_width" }) + for (auto el : {"skeleton_infill_density", "skin_infill_density", "infill_lock_depth", "skin_infill_depth", "skin_infill_line_width", "skeleton_infill_line_width", "locked_skin_infill_pattern", "locked_skeleton_infill_pattern"}) toggle_line(el, is_locked_zig); bool is_zig_zag = config->option>("sparse_infill_pattern")->value == InfillPattern::ipZigZag; @@ -703,13 +728,11 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in //toggle_field("support_closing_radius", have_support_material && support_style == smsSnug); bool support_is_tree = config->opt_bool("enable_support") && is_tree(support_type); - //1.9.5 for (auto el : {"tree_support_branch_angle", "tree_support_branch_distance", "tree_support_branch_diameter", "tree_support_branch_diameter_angle"}) toggle_field(el, support_is_tree); // hide tree support settings when normal is selected - //1.9.5 -for (auto el : {"tree_support_branch_angle", "tree_support_branch_distance", "tree_support_branch_diameter", "tree_support_branch_diameter_angle", "max_bridge_length"}) + for (auto el : {"tree_support_branch_angle", "tree_support_branch_distance", "tree_support_branch_diameter", "tree_support_branch_diameter_angle", "max_bridge_length"}) toggle_line(el, support_is_tree); toggle_line("support_critical_regions_only", is_auto(support_type) && support_is_tree); @@ -753,8 +776,8 @@ for (auto el : {"tree_support_branch_angle", "tree_support_branch_distance", "tr bool have_prime_tower = config->opt_bool("enable_prime_tower"); for (auto el : - {"prime_tower_width", "prime_tower_brim_width", "prime_tower_skip_points", "prime_tower_rib_wall", "prime_tower_infill_gap", "prime_tower_enable_framework", "prime_tower_max_speed"}) - toggle_line(el, have_prime_tower); + {"prime_tower_width", "prime_tower_brim_width", "prime_tower_skip_points", "prime_tower_rib_wall", "prime_tower_infill_gap", "prime_tower_enable_framework", "prime_tower_max_speed"}) + toggle_line(el, have_prime_tower); bool have_rib_wall = config->opt_bool("prime_tower_rib_wall")&&have_prime_tower; for (auto el : {"prime_tower_extra_rib_length", "prime_tower_rib_width", "prime_tower_fillet_wall"}) @@ -770,6 +793,10 @@ for (auto el : {"tree_support_branch_angle", "tree_support_branch_distance", "tr for (auto el : { "overhang_1_4_speed", "overhang_2_4_speed", "overhang_3_4_speed", "overhang_4_4_speed"}) toggle_line(el, has_overhang_speed, variant_index); + bool has_height_slowdown = config->opt_bool("enable_height_slowdown", variant_index); + for (auto el : { "slowdown_start_height", "slowdown_start_speed", "slowdown_start_acc", "slowdown_end_height", "slowdown_end_speed", "slowdown_end_acc" }) + toggle_line(el, has_height_slowdown, variant_index); + toggle_line("flush_into_objects", !is_global_config); toggle_line("print_flow_ratio", !is_global_config); @@ -814,6 +841,11 @@ for (auto el : {"tree_support_branch_angle", "tree_support_branch_distance", "tr toggle_field("xy_contour_compensation", !enable_auto_hole_and_contour_compensation); toggle_line("circle_compensation_manual_offset", enable_auto_hole_and_contour_compensation); + // override filament scarf seam settings + bool override_filament_scarf_seam_settings = config->opt_bool("override_filament_scarf_seam_setting"); + for (auto el : {"seam_slope_type", "seam_slope_start_height", "seam_slope_gap", "seam_slope_min_length"}) + toggle_line(el, override_filament_scarf_seam_settings); + //w16 bool is_resonance_avoidance = config->opt_bool("resonance_avoidance", 0); toggle_line("min_resonance_avoidance_speed", is_resonance_avoidance); diff --git a/src/slic3r/GUI/CreatePresetsDialog.cpp b/src/slic3r/GUI/CreatePresetsDialog.cpp index aaf59ac..635cc57 100644 --- a/src/slic3r/GUI/CreatePresetsDialog.cpp +++ b/src/slic3r/GUI/CreatePresetsDialog.cpp @@ -928,7 +928,7 @@ wxBoxSizer *CreateFilamentPresetDialog::create_button_item() m_button_create = new Button(this, _L("Create")); m_button_create->SetBackgroundColor(btn_bg_blue); m_button_create->SetBorderColor(*wxWHITE); - m_button_create->SetTextColor(wxColour(0xFFFFFE)); + m_button_create->SetTextColor(wxColour("#FFFFFE")); m_button_create->SetFont(Label::Body_12); m_button_create->SetSize(wxSize(FromDIP(58), FromDIP(24))); m_button_create->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); @@ -1362,9 +1362,9 @@ void CreateFilamentPresetDialog::update_dialog_size() m_filament_preset_panel->SetSizerAndFit(m_filament_presets_sizer); int width = m_filament_preset_panel->GetSize().GetWidth(); int height = m_filament_preset_panel->GetSize().GetHeight(); - m_scrolled_preset_panel->SetMinSize(wxSize(std::min(1400, width + FromDIP(26)), std::min(600, height + FromDIP(18)))); - m_scrolled_preset_panel->SetMaxSize(wxSize(std::min(1400, width + FromDIP(26)), std::min(600, height + FromDIP(18)))); - m_scrolled_preset_panel->SetSize(wxSize(std::min(1500, width + FromDIP(26)), std::min(600, height + FromDIP(18)))); + m_scrolled_preset_panel->SetMinSize(wxSize(std::min(1400, width + FromDIP(26)), std::min(450, height + FromDIP(18)))); + m_scrolled_preset_panel->SetMaxSize(wxSize(std::min(1400, width + FromDIP(26)), std::min(450, height + FromDIP(18)))); + m_scrolled_preset_panel->SetSize(wxSize(std::min(1500, width + FromDIP(26)), std::min(450, height + FromDIP(18)))); Layout(); Fit(); Refresh(); @@ -2001,7 +2001,7 @@ wxBoxSizer *CreatePrinterPresetDialog::create_page1_btns_item(wxWindow *parent) m_button_OK = new Button(parent, _L("OK")); m_button_OK->SetBackgroundColor(btn_bg_blue); m_button_OK->SetBorderColor(*wxWHITE); - m_button_OK->SetTextColor(wxColour(0xFFFFFE)); + m_button_OK->SetTextColor(wxColour("#FFFFFE")); m_button_OK->SetFont(Label::Body_12); m_button_OK->SetSize(wxSize(FromDIP(58), FromDIP(24))); m_button_OK->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); @@ -2686,7 +2686,7 @@ wxBoxSizer *CreatePrinterPresetDialog::create_page2_btns_item(wxWindow *parent) m_button_create = new Button(parent, _L("Create")); m_button_create->SetBackgroundColor(btn_bg_blue); m_button_create->SetBorderColor(*wxWHITE); - m_button_create->SetTextColor(wxColour(0xFFFFFE)); + m_button_create->SetTextColor(wxColour("#FFFFFE")); m_button_create->SetFont(Label::Body_12); m_button_create->SetSize(wxSize(FromDIP(58), FromDIP(24))); m_button_create->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); @@ -4319,7 +4319,7 @@ wxBoxSizer *ExportConfigsDialog::create_button_item(wxWindow* parent) m_button_ok = new Button(this, _L("OK")); m_button_ok->SetBackgroundColor(btn_bg_blue); m_button_ok->SetBorderColor(*wxWHITE); - m_button_ok->SetTextColor(wxColour(0xFFFFFE)); + m_button_ok->SetTextColor(wxColour("#FFFFFE")); m_button_ok->SetFont(Label::Body_12); m_button_ok->SetSize(wxSize(FromDIP(58), FromDIP(24))); m_button_ok->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); @@ -4873,7 +4873,7 @@ wxBoxSizer *EditFilamentPresetDialog::create_button_sizer() m_del_filament_btn = new Button(this, _L("Delete Filament")); m_del_filament_btn->SetBackgroundColor(*wxRED); m_del_filament_btn->SetBorderColor(*wxWHITE); - m_del_filament_btn->SetTextColor(wxColour(0xFFFFFE)); + m_del_filament_btn->SetTextColor(wxColour("#FFFFFE")); m_del_filament_btn->SetFont(Label::Body_12); m_del_filament_btn->SetSize(wxSize(FromDIP(58), FromDIP(24))); m_del_filament_btn->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); @@ -4888,7 +4888,7 @@ wxBoxSizer *EditFilamentPresetDialog::create_button_sizer() m_ok_btn = new Button(this, _L("OK")); m_ok_btn->SetBackgroundColor(btn_bg_blue); m_ok_btn->SetBorderColor(*wxWHITE); - m_ok_btn->SetTextColor(wxColour(0xFFFFFE)); + m_ok_btn->SetTextColor(wxColour("#FFFFFE")); m_ok_btn->SetFont(Label::Body_12); m_ok_btn->SetSize(wxSize(FromDIP(58), FromDIP(24))); m_ok_btn->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); @@ -5088,7 +5088,7 @@ wxBoxSizer *CreatePresetForPrinterDialog::create_button_sizer() m_ok_btn = new Button(this, _L("OK")); m_ok_btn->SetBackgroundColor(btn_bg_blue); m_ok_btn->SetBorderColor(*wxWHITE); - m_ok_btn->SetTextColor(wxColour(0xFFFFFE)); + m_ok_btn->SetTextColor(wxColour("#FFFFFE")); m_ok_btn->SetFont(Label::Body_12); m_ok_btn->SetSize(wxSize(FromDIP(58), FromDIP(24))); m_ok_btn->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); @@ -5253,7 +5253,7 @@ wxPanel *PresetTree::get_child_item(wxPanel *parent, std::shared_ptr pre if (base_id_error) { del_preset_btn->SetBackgroundColor(btn_bg_blue); del_preset_btn->SetBorderColor(btn_bg_blue); - del_preset_btn->SetTextColor(wxColour(0xFFFFFE)); + del_preset_btn->SetTextColor(wxColour("#FFFFFE")); } else { del_preset_btn->SetBackgroundColor(flush_bg_col); del_preset_btn->SetBorderColor(flush_bd_col); diff --git a/src/slic3r/GUI/DeviceManager.cpp b/src/slic3r/GUI/DeviceManager.cpp index 161788d..86ba801 100644 --- a/src/slic3r/GUI/DeviceManager.cpp +++ b/src/slic3r/GUI/DeviceManager.cpp @@ -20,6 +20,8 @@ #include #include "fast_float/fast_float.h" +#include "slic3r/Utils/QDTUtil.hpp" + #define CALI_DEBUG #define MINUTE_30 1800000 //ms #define TIME_OUT 5000 //ms @@ -48,11 +50,11 @@ wxString get_stage_string(int stage) case 5: return _L("M400 pause"); case 6: - return _L("Paused due to filament runout"); + return _L("Paused (filament ran out)"); case 7: - return _L("Heating hotend"); + return _L("Heating nozzle"); case 8: - return _L("Calibrating extrusion"); + return _L("Calibrating dynamic flow"); case 9: return _L("Scanning bed surface"); case 10: @@ -68,51 +70,51 @@ wxString get_stage_string(int stage) case 15: return _L("Checking extruder temperature"); case 16: - return _L("Printing was paused by the user"); + return _L("Paused by the user"); case 17: - return _L("Pause of front cover falling"); + return _L("Pause (front cover fall off)"); case 18: return _L("Calibrating the micro lidar"); case 19: - return _L("Calibrating extrusion flow"); + return _L("Calibrating flow ratio"); case 20: - return _L("Paused due to nozzle temperature malfunction"); + return _L("Pause (nozzle temperature malfunction)"); case 21: - return _L("Paused due to heat bed temperature malfunction"); + return _L("Pause (heatbed temperature malfunction)"); case 22: return _L("Filament unloading"); case 23: - return _L("Skip step pause"); + return _L("Pause (step loss)"); case 24: return _L("Filament loading"); case 25: return _L("Motor noise cancellation"); case 26: - return _L("Paused due to BOX lost"); + return _L("Pause (BOX offline)"); case 27: - return _L("Paused due to low speed of the heat break fan"); + return _L("Pause (low speed of the heatbreak fan)"); case 28: - return _L("Paused due to chamber temperature control error"); + return _L("Pause (chamber temperature control problem)"); case 29: return _L("Cooling chamber"); case 30: - return _L("Paused by the Gcode inserted by user"); + return _L("Pause (Gcode inserted by user)"); case 31: return _L("Motor noise showoff"); case 32: - return _L("Nozzle filament covered detected pause"); + return _L("Pause (nozzle clumping)"); case 33: - return _L("Cutter error pause"); + return _L("Pause (cutter error)"); case 34: - return _L("First layer error pause"); + return _L("Pause (first layer error)"); case 35: - return _L("Nozzle clog pause"); + return _L("Pause (nozzle clog)"); case 36: - return _L("Check printer absolute accuracy before calibration"); + return _L("Measuring motion percision"); case 37: - return _L("Absolute accuracy calibration"); + return _L("Enhancing motion percision"); case 38: - return _L("Check printer absolute accuracy after calibration"); + return _L("Measure motion accuracy"); case 39: return _L("Nozzle offset calibration"); case 40: @@ -126,9 +128,9 @@ wxString get_stage_string(int stage) case 44: return _L("Auto Check: Platform"); case 45: - return _L("Confirming birdeye camera position"); + return _L("Confirming BirdsEye Camera location"); case 46: - return _L("Calibrating birdeye camera"); + return _L("Calibrating BirdsEye Camera"); case 47: return _L("Auto bed leveling -phase 1"); case 48: @@ -136,11 +138,21 @@ wxString get_stage_string(int stage) case 49: return _L("Heating chamber"); case 50: - return _L("Heated bed cooling"); + return _L("Cooling heatbed"); case 51: return _L("Printing calibration lines"); + case 52: + return _L("Auto Check: Material"); + case 53: + return _L("Live View Camera Calibration"); + case 54: + return _L("Waiting for heatbed to reach target temperature"); + case 55: + return _L("Auto Check: Material Position"); + case 56: + return _L("Cutting Module Offset Calibration"); default: - ; + BOOST_LOG_TRIVIAL(info) << "stage = " << stage; } return ""; } @@ -314,7 +326,7 @@ bool check_filaments_printable(const std::string &tag_vendor, const std::string if (filament_info.has_value() && !(filament_info->filament_printable >> extruder_idx & 1)) { wxString extruder_name = extruder_idx == 0 ? _L("left") : _L("right"); ac = "prohibition"; - info = (wxString::Format(_L("%s is not supported by %s extruder."), tag_type, extruder_name)).ToUTF8().data(); + info = wxString::Format(_L("%s is not supported by %s extruder."), tag_type, extruder_name); in_blacklist = true; return false; } @@ -618,6 +630,14 @@ std::string MachineObject::convertToIp(long long ip) return ss.str(); } +std::string MachineObject::get_show_printer_type() const +{ + std::string printer_type = this->printer_type; + if (this->is_support_upgrade_kit && this->installed_upgrade_kit) + printer_type = "C12"; + return printer_type; +} + PrinterSeries MachineObject::get_printer_series() const { std::string series = DeviceManager::get_printer_series(printer_type); @@ -922,6 +942,10 @@ std::string MachineObject::get_filament_id(std::string ams_id, std::string tray_ return this->get_tray(ams_id, tray_id).setting_id; } +std::string MachineObject::get_filament_type(const std::string& ams_id, const std::string& tray_id) const { + return this->get_tray(ams_id, tray_id).type; +} + void MachineObject::_parse_ams_status(int ams_status) { ams_status_sub = ams_status & 0xFF; @@ -1178,7 +1202,7 @@ int MachineObject::ams_filament_mapping( // traverse the mapping std::set picked_src; std::set picked_tar; - //y59 + //y59 y68 for (int k = 0; k < distance_map.size(); k++) { float min_val = INT_MAX; int picked_src_idx = -1; @@ -1190,14 +1214,14 @@ int MachineObject::ams_filament_mapping( // try to mapping to different tray for (int j = 0; j < distance_map[i].size(); j++) { if (picked_tar.find(j) != picked_tar.end()) { - if (distance_map[i][j].is_same_color - && distance_map[i][j].is_type_match - && distance_map[i][j].distance < (float)0.0001) { - min_val = distance_map[i][j].distance; - picked_src_idx = i; - picked_tar_idx = j; - tray_filaments[picked_tar_idx].distance = min_val; - } + //if (distance_map[i][j].is_same_color + // && distance_map[i][j].is_type_match + // && distance_map[i][j].distance < (float)0.0001) { + // min_val = distance_map[i][j].distance; + // //picked_src_idx = i; + // //picked_tar_idx = j; + // tray_filaments[picked_tar_idx].distance = min_val; + //} continue; } @@ -1209,29 +1233,29 @@ int MachineObject::ams_filament_mapping( picked_tar_idx = j; tray_filaments[picked_tar_idx].distance = min_val; } - else if (min_val == distance_map[i][j].distance && filaments[picked_src_idx].filament_id!= box_filament_infos[picked_src_idx].filament_id && filaments[i].filament_id == box_filament_infos[j].filament_id) { - picked_src_idx = i; - picked_tar_idx = j; - } + //else if (min_val == distance_map[i][j].distance && filaments[i].filament_id == box_filament_infos[j].filament_id) { + // picked_src_idx = i; + // picked_tar_idx = j; + //} } } - // take a retry to mapping to used tray - if (picked_src_idx < 0 || picked_tar_idx < 0) { - for (int j = 0; j < distance_map[i].size(); j++) { - if (distance_map[i][j].is_same_color && distance_map[i][j].is_type_match) { - if (min_val > distance_map[i][j].distance) { - min_val = distance_map[i][j].distance; - picked_src_idx = i; - picked_tar_idx = j; - tray_filaments[picked_tar_idx].distance = min_val; - } else if (min_val == distance_map[i][j].distance && filaments[picked_src_idx].filament_id != tray_filaments[picked_tar_idx].filament_id && filaments[i].filament_id == tray_filaments[j].filament_id) { - picked_src_idx = i; - picked_tar_idx = j; - } - } - } - } + // // take a retry to mapping to used tray + // if (picked_src_idx < 0 || picked_tar_idx < 0) { + // for (int j = 0; j < distance_map[i].size(); j++) { + // if (distance_map[i][j].is_same_color && distance_map[i][j].is_type_match) { + // if (min_val > distance_map[i][j].distance) { + // min_val = distance_map[i][j].distance; + // picked_src_idx = i; + // picked_tar_idx = j; + // tray_filaments[picked_tar_idx].distance = min_val; + // } else if (min_val == distance_map[i][j].distance && filaments[picked_src_idx].filament_id != tray_filaments[picked_tar_idx].filament_id && filaments[i].filament_id == tray_filaments[j].filament_id) { + // picked_src_idx = i; + // picked_tar_idx = j; + // } + // } + // } + // } } //y59 @@ -1641,8 +1665,6 @@ bool MachineObject::is_in_calibration() return false; } - - bool MachineObject::is_calibration_done() { return calibration_done; @@ -1895,13 +1917,11 @@ bool MachineObject::canEnableTimelapse(wxString &error_message) const int MachineObject::command_select_extruder(int id) { - BOOST_LOG_TRIVIAL(info) << "select_extruder"; - json j; j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); j["print"]["command"] = "select_extruder"; j["print"]["extruder_index"] = id; - int rtn = this->publish_json(j.dump(), 1); + int rtn = this->publish_json(j, 1); if (rtn == 0) { targ_nozzle_id_from_pc = id; @@ -1912,22 +1932,20 @@ int MachineObject::command_select_extruder(int id) int MachineObject::command_get_version(bool with_retry) { - BOOST_LOG_TRIVIAL(info) << "command_get_version"; json j; j["info"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); j["info"]["command"] = "get_version"; if (with_retry) get_version_retry = GET_VERSION_RETRYS; - return this->publish_json(j.dump(), 1); + return this->publish_json(j, 1); } int MachineObject::command_get_access_code() { - BOOST_LOG_TRIVIAL(info) << "command_get_access_code"; json j; j["system"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); j["system"]["command"] = "get_access_code"; - return this->publish_json(j.dump()); + return this->publish_json(j); } @@ -1938,15 +1956,15 @@ int MachineObject::command_request_push_all(bool request_now) if (diff.count() < REQUEST_PUSH_MIN_TIME) { if (request_now) { - BOOST_LOG_TRIVIAL(trace) << "static: command_request_push_all, dev_id=" << dev_id; + BOOST_LOG_TRIVIAL(trace) << "static: command_request_push_all, dev_id=" << QDTCrossTalk::Crosstalk_DevId(dev_id); last_request_push = std::chrono::system_clock::now(); } else { - BOOST_LOG_TRIVIAL(trace) << "static: command_request_push_all: send request too fast, dev_id=" << dev_id; + BOOST_LOG_TRIVIAL(trace) << "static: command_request_push_all: send request too fast, dev_id=" << QDTCrossTalk::Crosstalk_DevId(dev_id); return -1; } } else { - BOOST_LOG_TRIVIAL(trace) << "static: command_request_push_all, dev_id=" << dev_id; + BOOST_LOG_TRIVIAL(trace) << "static: command_request_push_all, dev_id=" << QDTCrossTalk::Crosstalk_DevId(dev_id); last_request_push = std::chrono::system_clock::now(); } @@ -1955,7 +1973,7 @@ int MachineObject::command_request_push_all(bool request_now) j["pushing"]["command"] = "pushall"; j["pushing"]["version"] = 1; j["pushing"]["push_target"] = 1; - return this->publish_json(j.dump()); + return this->publish_json(j); } int MachineObject::command_pushing(std::string cmd) @@ -1963,11 +1981,11 @@ int MachineObject::command_pushing(std::string cmd) auto curr_time = std::chrono::system_clock::now(); auto diff = std::chrono::duration_cast(curr_time - last_request_start); if (diff.count() < REQUEST_START_MIN_TIME) { - BOOST_LOG_TRIVIAL(trace) << "static: command_request_start: send request too fast, dev_id=" << dev_id; + BOOST_LOG_TRIVIAL(trace) << "static: command_request_start: send request too fast, dev_id=" << QDTCrossTalk::Crosstalk_DevId(dev_id); return -1; } else { - BOOST_LOG_TRIVIAL(trace) << "static: command_request_start, dev_id=" << dev_id; + BOOST_LOG_TRIVIAL(trace) << "static: command_request_start, dev_id=" << QDTCrossTalk::Crosstalk_DevId(dev_id); last_request_start = std::chrono::system_clock::now(); } @@ -1975,41 +1993,38 @@ int MachineObject::command_pushing(std::string cmd) json j; j["pushing"]["command"] = cmd; j["pushing"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); - return this->publish_json(j.dump()); + return this->publish_json(j); } return -1; } int MachineObject::command_clean_print_error(std::string subtask_id, int print_error) { - BOOST_LOG_TRIVIAL(info) << "command_clean_print_error, id = " << subtask_id; json j; j["print"]["command"] = "clean_print_error"; j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); j["print"]["subtask_id"] = subtask_id; j["print"]["print_error"] = print_error; - return this->publish_json(j.dump()); + return this->publish_json(j); } int MachineObject::command_upgrade_confirm() { - BOOST_LOG_TRIVIAL(info) << "command_upgrade_confirm"; json j; j["upgrade"]["command"] = "upgrade_confirm"; j["upgrade"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); j["upgrade"]["src_id"] = 1; // 1 for slicer - return this->publish_json(j.dump()); + return this->publish_json(j); } int MachineObject::command_consistency_upgrade_confirm() { - BOOST_LOG_TRIVIAL(info) << "command_consistency_upgrade_confirm"; json j; j["upgrade"]["command"] = "consistency_confirm"; j["upgrade"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); j["upgrade"]["src_id"] = 1; // 1 for slicer - return this->publish_json(j.dump()); + return this->publish_json(j); } int MachineObject::command_upgrade_firmware(FirmwareInfo info) @@ -2026,7 +2041,7 @@ int MachineObject::command_upgrade_firmware(FirmwareInfo info) j["upgrade"]["version"] = info.version; j["upgrade"]["src_id"] = 1; - return this->publish_json(j.dump()); + return this->publish_json(j); } int MachineObject::command_upgrade_module(std::string url, std::string module_type, std::string version) @@ -2039,7 +2054,7 @@ int MachineObject::command_upgrade_module(std::string url, std::string module_ty j["upgrade"]["version"] = version; j["upgrade"]["src_id"] = 1; - return this->publish_json(j.dump()); + return this->publish_json(j); } int MachineObject::command_xyz_abs() @@ -2064,11 +2079,10 @@ int MachineObject::command_go_home() int MachineObject::command_go_home2() { - BOOST_LOG_TRIVIAL(info) << "New protocol of command_go_home2"; json j; j["print"]["command"] = "back_to_center"; j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); - return this->publish_json(j.dump()); + return this->publish_json(j); } // Old protocol @@ -2090,51 +2104,56 @@ int MachineObject::command_control_fan(int fan_type, int val) // New protocol int MachineObject::command_control_fan_new(int fan_id, int val, const CommandCallBack &cb) { - BOOST_LOG_TRIVIAL(info) << "New protocol of fan setting(set speed), fan_id = " << fan_id; m_callback_list[std::to_string(m_sequence_id)] = cb; json j; j["print"]["command"] = "set_fan"; j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); j["print"]["fan_index"] = fan_id; - j["print"]["speed"] = val; - BOOST_LOG_TRIVIAL(info) << "MachineObject::command_control_fan_val, set the speed of fan, fan_id = " << fan_id; - return this->publish_json(j.dump()); + return this->publish_json(j); } -int MachineObject::command_control_air_duct(int mode_id, const CommandCallBack &cb) +int MachineObject::command_control_air_duct(int mode_id, int submode, const CommandCallBack &cb) { - BOOST_LOG_TRIVIAL(info) << "MachineObject::command_control_air_duct, set air duct, d = " << mode_id; m_callback_list[std::to_string(m_sequence_id)] = cb; json j; j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); j["print"]["command"] = "set_airduct"; j["print"]["modeId"] = mode_id; + j["print"]["submode"] = submode; - return this->publish_json(j.dump()); + return this->publish_json(j); +} + +int MachineObject::command_task_partskip(std::vector part_ids) +{ + json j; + j["print"]["command"] = "skip_objects"; + j["print"]["obj_list"] = part_ids; + j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); + + return this->publish_json(j, 1); } int MachineObject::command_task_abort() { - BOOST_LOG_TRIVIAL(trace) << "command_task_abort: "; json j; j["print"]["command"] = "stop"; j["print"]["param"] = ""; j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); - return this->publish_json(j.dump(), 1); + return this->publish_json(j, 1); } int MachineObject::command_task_cancel(std::string job_id) { - BOOST_LOG_TRIVIAL(trace) << "command_task_cancel: " << job_id; json j; j["print"]["command"] = "stop"; j["print"]["param"] = ""; j["print"]["job_id"] = job_id; j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); - return this->publish_json(j.dump(), 1); + return this->publish_json(j, 1); } int MachineObject::command_task_pause() @@ -2144,7 +2163,7 @@ int MachineObject::command_task_pause() j["print"]["param"] = ""; j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); - return this->publish_json(j.dump(), 1); + return this->publish_json(j, 1); } int MachineObject::command_task_resume() @@ -2156,7 +2175,7 @@ int MachineObject::command_task_resume() j["print"]["param"] = ""; j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); - return this->publish_json(j.dump(), 1); + return this->publish_json(j, 1); } int MachineObject::command_hms_idle_ignore(const std::string &error_str, int type) @@ -2168,7 +2187,7 @@ int MachineObject::command_hms_idle_ignore(const std::string &error_str, int typ j["print"]["err"] = error_str; j["print"]["type"] = type; j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); - return this->publish_json(j.dump(), 1); + return this->publish_json(j, 1); } int MachineObject::command_hms_resume(const std::string& error_str, const std::string& job_id) @@ -2182,7 +2201,7 @@ int MachineObject::command_hms_resume(const std::string& error_str, const std::s j["print"]["job_id"] = job_id; j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); - return this->publish_json(j.dump(), 1); + return this->publish_json(j, 1); } int MachineObject::command_hms_ignore(const std::string& error_str, const std::string& job_id) @@ -2196,7 +2215,7 @@ int MachineObject::command_hms_ignore(const std::string& error_str, const std::s j["print"]["job_id"] = job_id; j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); - return this->publish_json(j.dump(), 1); + return this->publish_json(j, 1); } int MachineObject::command_hms_stop(const std::string &error_str, const std::string &job_id) { @@ -2207,7 +2226,7 @@ int MachineObject::command_hms_stop(const std::string &error_str, const std::str j["print"]["job_id"] = job_id; j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); - return this->publish_json(j.dump(), 1); + return this->publish_json(j, 1); } int MachineObject::command_stop_buzzer() @@ -2217,11 +2236,20 @@ int MachineObject::command_stop_buzzer() j["print"]["mode"] = 0; j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); - return this->publish_json(j.dump(), 1); + return this->publish_json(j, 1); } int MachineObject::command_set_bed(int temp) { + if (m_support_mqtt_bet_ctrl) + { + json j; + j["print"]["command"] = "set_bed_temp"; + j["print"]["temp"] = temp; + j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); + return this->publish_json(j); + } + std::string gcode_str = (boost::format("M140 S%1%\n") % temp).str(); try { json j; @@ -2253,15 +2281,13 @@ int MachineObject::command_set_nozzle(int temp) int MachineObject::command_set_nozzle_new(int nozzle_id, int temp) { - BOOST_LOG_TRIVIAL(info) << "set_nozzle_temp"; - json j; j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); j["print"]["command"] = "set_nozzle_temp"; j["print"]["extruder_index"] = nozzle_id; j["print"]["target_temp"] = temp; - return this->publish_json(j.dump(), 1); + return this->publish_json(j, 1); } int MachineObject::command_set_chamber(int temp) @@ -2271,7 +2297,7 @@ int MachineObject::command_set_chamber(int temp) j["print"]["command"] = "set_ctt"; j["print"]["ctt_val"] = temp; - return this->publish_json(j.dump(), 1); + return this->publish_json(j, 1); } int MachineObject::check_resume_condition() @@ -2314,7 +2340,7 @@ int MachineObject::command_ams_change_filament(bool load, std::string ams_id, st } } catch (const std::exception &) {} - return this->publish_json(j.dump()); + return this->publish_json(j); } int MachineObject::command_ams_user_settings(int ams_id, bool start_read_opt, bool tray_read_opt, bool remain_flag) @@ -2332,7 +2358,7 @@ int MachineObject::command_ams_user_settings(int ams_id, bool start_read_opt, bo ams_calibrate_remain_flag = remain_flag; ams_user_setting_start = time(nullptr); - return this->publish_json(j.dump()); + return this->publish_json(j); } int MachineObject::command_ams_calibrate(int ams_id) @@ -2354,10 +2380,6 @@ int MachineObject::command_ams_filament_settings(int ams_id, int slot_id, std::s tag_tray_id = tag_slot_id; } - - BOOST_LOG_TRIVIAL(info) << "command_ams_filament_settings, ams_id = " << tag_ams_id << ", slot_id = " << tag_slot_id << ", tray_id = " << tag_tray_id << ", tray_color = " << tray_color - << ", tray_type = " << tray_type << ", filament_id = " << filament_id - << ", setting_id = " << setting_id << ", temp_min: = " << nozzle_temp_min << ", temp_max: = " << nozzle_temp_max; json j; j["print"]["command"] = "ams_filament_setting"; j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); @@ -2372,7 +2394,7 @@ int MachineObject::command_ams_filament_settings(int ams_id, int slot_id, std::s j["print"]["nozzle_temp_max"] = nozzle_temp_max; j["print"]["tray_type"] = tray_type; - return this->publish_json(j.dump()); + return this->publish_json(j); } int MachineObject::command_ams_refresh_rfid(std::string tray_id) @@ -2389,7 +2411,7 @@ int MachineObject::command_ams_refresh_rfid2(int ams_id, int slot_id) j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); j["print"]["ams_id"] = ams_id; j["print"]["slot_id"] = slot_id; - return this->publish_json(j.dump()); + return this->publish_json(j); } @@ -2410,12 +2432,11 @@ int MachineObject::command_ams_control(std::string action) j["print"]["command"] = "ams_control"; j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); j["print"]["param"] = action; - return this->publish_json(j.dump()); + return this->publish_json(j); } return -1; } - int MachineObject::command_set_chamber_light(LIGHT_EFFECT effect, int on_time, int off_time, int loops, int interval) { json j; @@ -2427,7 +2448,7 @@ int MachineObject::command_set_chamber_light(LIGHT_EFFECT effect, int on_time, i j["system"]["led_off_time"] = off_time; j["system"]["loop_times"] = loops; j["system"]["interval_time"] = interval; - return this->publish_json(j.dump()); + return this->publish_json(j); } @@ -2442,26 +2463,26 @@ int MachineObject::command_set_chamber_light2(LIGHT_EFFECT effect, int on_time / j["system"]["led_off_time"] = off_time; j["system"]["loop_times"] = loops; j["system"]["interval_time"] = interval; - return this->publish_json(j.dump()); + return this->publish_json(j); } int MachineObject::command_set_printer_nozzle(std::string nozzle_type, float diameter) { nozzle_setting_hold_count = HOLD_COUNT_MAX * 2; - BOOST_LOG_TRIVIAL(info) << "command_set_printer_nozzle, nozzle_type = " << nozzle_type << " diameter = " << diameter; + json j; j["system"]["command"] = "set_accessories"; j["system"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); j["system"]["accessory_type"] = "nozzle"; j["system"]["nozzle_type"] = nozzle_type; j["system"]["nozzle_diameter"] = diameter; - return this->publish_json(j.dump()); + return this->publish_json(j); } int MachineObject::command_set_printer_nozzle2(int id, std::string nozzle_type, float diameter) { nozzle_setting_hold_count = HOLD_COUNT_MAX * 2; - BOOST_LOG_TRIVIAL(info) << "command_set_printer_nozzle2, nozzle_type = " << nozzle_type << " diameter = " << diameter; + json j; j["print"]["command"] = "set_nozzle"; j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); @@ -2469,7 +2490,7 @@ int MachineObject::command_set_printer_nozzle2(int id, std::string nozzle_type, j["print"]["type"] = nozzle_type; j["print"]["diameter"] = diameter; j["print"]["wear"] = 0; - return this->publish_json(j.dump()); + return this->publish_json(j); } @@ -2485,14 +2506,11 @@ int MachineObject::command_set_work_light(LIGHT_EFFECT effect, int on_time, int j["system"]["loop_times"] = loops; j["system"]["interval_time"] = interval; - return this->publish_json(j.dump()); + return this->publish_json(j); } int MachineObject::command_start_extrusion_cali(int tray_index, int nozzle_temp, int bed_temp, float max_volumetric_speed, std::string setting_id) { - BOOST_LOG_TRIVIAL(trace) << "extrusion_cali: tray_id = " << tray_index << ", nozzle_temp = " << nozzle_temp << ", bed_temp = " << bed_temp - << ", max_volumetric_speed = " << max_volumetric_speed; - json j; j["print"]["command"] = "extrusion_cali"; j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); @@ -2505,8 +2523,7 @@ int MachineObject::command_start_extrusion_cali(int tray_index, int nozzle_temp, // enter extusion cali last_extrusion_cali_start_time = std::chrono::system_clock::now(); - BOOST_LOG_TRIVIAL(trace) << "extrusion_cali: " << j.dump(); - return this->publish_json(j.dump()); + return this->publish_json(j); } int MachineObject::command_stop_extrusion_cali() @@ -2520,8 +2537,6 @@ int MachineObject::command_stop_extrusion_cali() int MachineObject::command_extrusion_cali_set(int tray_index, std::string setting_id, std::string name, float k, float n, int bed_temp, int nozzle_temp, float max_volumetric_speed) { - BOOST_LOG_TRIVIAL(trace) << "extrusion_cali: tray_id = " << tray_index << ", setting_id = " << setting_id << ", k = " << k - << ", n = " << n; json j; j["print"]["command"] = "extrusion_cali_set"; j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); @@ -2536,7 +2551,7 @@ int MachineObject::command_extrusion_cali_set(int tray_index, std::string settin j["print"]["nozzle_temp"] = nozzle_temp; j["print"]["max_volumetric_speed"] = max_volumetric_speed; } - return this->publish_json(j.dump()); + return this->publish_json(j); } @@ -2547,7 +2562,7 @@ int MachineObject::command_set_printing_speed(PrintingSpeedLevel lvl) j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); j["print"]["param"] = std::to_string((int)lvl); - return this->publish_json(j.dump()); + return this->publish_json(j); } int MachineObject::command_set_printing_option(bool auto_recovery) @@ -2559,7 +2574,7 @@ int MachineObject::command_set_printing_option(bool auto_recovery) j["print"]["option"] = print_option; j["print"]["auto_recovery"] = auto_recovery; - return this->publish_json(j.dump()); + return this->publish_json(j); } int MachineObject::command_nozzle_blob_detect(bool nozzle_blob_detect) @@ -2570,7 +2585,7 @@ int MachineObject::command_nozzle_blob_detect(bool nozzle_blob_detect) j["print"]["nozzle_blob_detect"] = nozzle_blob_detect; nozzle_blob_detection_enabled = nozzle_blob_detect; nozzle_blob_detection_hold_start = time(nullptr); - return this->publish_json(j.dump()); + return this->publish_json(j); } int MachineObject::command_set_prompt_sound(bool prompt_sound){ @@ -2579,7 +2594,7 @@ int MachineObject::command_set_prompt_sound(bool prompt_sound){ j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); j["print"]["sound_enable"] = prompt_sound; - return this->publish_json(j.dump()); + return this->publish_json(j); } int MachineObject::command_set_filament_tangle_detect(bool filament_tangle_detect) { @@ -2588,7 +2603,7 @@ int MachineObject::command_set_filament_tangle_detect(bool filament_tangle_detec j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); j["print"]["filament_tangle_detect"] = filament_tangle_detect; - return this->publish_json(j.dump()); + return this->publish_json(j); } int MachineObject::command_ams_switch_filament(bool switch_filament) @@ -2599,10 +2614,9 @@ int MachineObject::command_ams_switch_filament(bool switch_filament) j["print"]["auto_switch_filament"] = switch_filament; ams_auto_switch_filament_flag = switch_filament; - BOOST_LOG_TRIVIAL(trace) << "command_ams_filament_settings:" << switch_filament; ams_switch_filament_start = time(nullptr); - return this->publish_json(j.dump()); + return this->publish_json(j); } int MachineObject::command_ams_air_print_detect(bool air_print_detect) @@ -2613,14 +2627,24 @@ int MachineObject::command_ams_air_print_detect(bool air_print_detect) j["print"]["air_print_detect"] = air_print_detect; ams_air_print_status = air_print_detect; - BOOST_LOG_TRIVIAL(trace) << "command_ams_air_print_detect:" << air_print_detect; - return this->publish_json(j.dump()); + return this->publish_json(j); } int MachineObject::command_axis_control(std::string axis, double unit, double input_val, int speed) { + if (m_support_mqtt_axis_control) + { + json j; + j["print"]["command"] = "xyz_ctrl"; + j["print"]["axis"] = axis; + j["print"]["dir"] = input_val > 0 ? 1 : -1; + j["print"]["mode"] = (std::abs(input_val) >= 10) ? 1 : 0; + j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); + return this->publish_json(j); + } + double value = input_val; if (!is_core_xy()) { if ( axis.compare("Y") == 0 @@ -2662,7 +2686,7 @@ int MachineObject::command_extruder_control(int nozzle_id, double val) j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); j["print"]["extruder_index"] = nozzle_id; j["print"]["length"] = (int)val; - return this->publish_json(j.dump()); + return this->publish_json(j); } bool MachineObject::is_support_command_calibration() @@ -2685,7 +2709,7 @@ int MachineObject::command_start_calibration(bool vibration, bool bed_leveling, j["print"]["command"] = "gcode_file"; j["print"]["param"] = "/usr/etc/print/auto_cali_for_user.gcode"; j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); - return this->publish_json(j.dump()); + return this->publish_json(j); } else { json j; j["print"]["command"] = "calibration"; @@ -2696,7 +2720,7 @@ int MachineObject::command_start_calibration(bool vibration, bool bed_leveling, + (vibration ? 1 << 2 : 0) + (bed_leveling ? 1 << 1 : 0) + (xcam_cali ? 1 << 0 : 0); - return this->publish_json(j.dump()); + return this->publish_json(j); } } @@ -2729,8 +2753,6 @@ int MachineObject::command_start_pa_calibration(const X1CCalibInfos &pa_data, in filament_ids += pa_data.calib_datas[i].filament_id; } - BOOST_LOG_TRIVIAL(info) << "extrusion_cali: " << j.dump(); - try { json js; js["cali_type"] = "cali_pa_auto"; @@ -2741,7 +2763,7 @@ int MachineObject::command_start_pa_calibration(const X1CCalibInfos &pa_data, in if (agent) agent->track_event("cali", js.dump()); } catch (...) {} - return this->publish_json(j.dump()); + return this->publish_json(j); } int MachineObject::command_set_pa_calibration(const std::vector &pa_calib_values, bool is_auto_cali) @@ -2775,8 +2797,7 @@ int MachineObject::command_set_pa_calibration(const std::vector & j["print"]["filaments"][i]["n_coef"] = "0.0"; } - BOOST_LOG_TRIVIAL(info) << "extrusion_cali_set: " << j.dump(); - return this->publish_json(j.dump()); + return this->publish_json(j); } return -1; @@ -2793,8 +2814,7 @@ int MachineObject::command_delete_pa_calibration(const PACalibIndexInfo& pa_cali j["print"]["cali_idx"] = pa_calib.cali_idx; j["print"]["nozzle_diameter"] = to_string_nozzle_diameter(pa_calib.nozzle_diameter); - BOOST_LOG_TRIVIAL(info) << "extrusion_cali_del: " << j.dump(); - return this->publish_json(j.dump()); + return this->publish_json(j); } //w @@ -2808,8 +2828,7 @@ int MachineObject::command_get_pa_calibration_tab(float nozzle_diameter, const s j["print"]["filament_id"] = filament_id; j["print"]["nozzle_diameter"] = to_string_nozzle_diameter(nozzle_diameter); - BOOST_LOG_TRIVIAL(trace) << "extrusion_cali_get: " << j.dump(); - return this->publish_json(j.dump()); + return this->publish_json(j); } int MachineObject::command_get_pa_calibration_tab(const PACalibExtruderInfo &calib_info) @@ -2826,9 +2845,8 @@ int MachineObject::command_get_pa_calibration_tab(const PACalibExtruderInfo &cal j["print"]["nozzle_id"] = generate_nozzle_id(calib_info.nozzle_volume_type, to_string_nozzle_diameter(calib_info.nozzle_diameter)).ToStdString(); j["print"]["nozzle_diameter"] = to_string_nozzle_diameter(calib_info.nozzle_diameter); - BOOST_LOG_TRIVIAL(info) << "extrusion_cali_get: " << j.dump(); request_tab_from_qds = true; - return this->publish_json(j.dump()); + return this->publish_json(j); } int MachineObject::command_get_pa_calibration_result(float nozzle_diameter) @@ -2838,8 +2856,7 @@ int MachineObject::command_get_pa_calibration_result(float nozzle_diameter) j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); j["print"]["nozzle_diameter"] = to_string_nozzle_diameter(nozzle_diameter); - BOOST_LOG_TRIVIAL(info) << "extrusion_cali_get_result: " << j.dump(); - return this->publish_json(j.dump()); + return this->publish_json(j); } int MachineObject::commnad_select_pa_calibration(const PACalibIndexInfo& pa_calib_info) @@ -2854,8 +2871,7 @@ int MachineObject::commnad_select_pa_calibration(const PACalibIndexInfo& pa_cali j["print"]["filament_id"] = pa_calib_info.filament_id; j["print"]["nozzle_diameter"] = to_string_nozzle_diameter(pa_calib_info.nozzle_diameter); - BOOST_LOG_TRIVIAL(info) << "extrusion_cali_sel: " << j.dump(); - return this->publish_json(j.dump()); + return this->publish_json(j); } int MachineObject::command_start_flow_ratio_calibration(const X1CCalibInfos& calib_data) @@ -2878,6 +2894,9 @@ int MachineObject::command_start_flow_ratio_calibration(const X1CCalibInfos& cal j["print"]["filaments"][i]["nozzle_temp"] = calib_data.calib_datas[i].nozzle_temp; j["print"]["filaments"][i]["def_flow_ratio"] = std::to_string(calib_data.calib_datas[i].flow_rate); j["print"]["filaments"][i]["max_volumetric_speed"] = std::to_string(calib_data.calib_datas[i].max_volumetric_speed); + j["print"]["filaments"][i]["extruder_id"] = calib_data.calib_datas[i].extruder_id; + j["print"]["filaments"][i]["ams_id"] = calib_data.calib_datas[i].ams_id; + j["print"]["filaments"][i]["slot_id"] = calib_data.calib_datas[i].slot_id; if (i > 0) filament_ids += ","; @@ -2894,8 +2913,7 @@ int MachineObject::command_start_flow_ratio_calibration(const X1CCalibInfos& cal if (agent) agent->track_event("cali", js.dump()); } catch (...) {} - BOOST_LOG_TRIVIAL(info) << "flowrate_cali: " << j.dump(); - return this->publish_json(j.dump()); + return this->publish_json(j); } return -1; } @@ -2907,37 +2925,33 @@ int MachineObject::command_get_flow_ratio_calibration_result(float nozzle_diamet j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); j["print"]["nozzle_diameter"] = to_string_nozzle_diameter(nozzle_diameter); - BOOST_LOG_TRIVIAL(info) << "flowrate_get_result: " << j.dump(); - return this->publish_json(j.dump()); + return this->publish_json(j); } int MachineObject::command_ipcam_record(bool on_off) { - BOOST_LOG_TRIVIAL(info) << "command_ipcam_record = " << on_off; json j; j["camera"]["command"] = "ipcam_record_set"; j["camera"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); j["camera"]["control"] = on_off ? "enable" : "disable"; camera_recording_ctl_start = time(nullptr); this->camera_recording_when_printing = on_off; - return this->publish_json(j.dump()); + return this->publish_json(j); } int MachineObject::command_ipcam_timelapse(bool on_off) { - BOOST_LOG_TRIVIAL(info) << "command_ipcam_timelapse " << on_off; json j; j["camera"]["command"] = "ipcam_timelapse"; j["camera"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); j["camera"]["control"] = on_off ? "enable" : "disable"; camera_timelapse_hold_count = HOLD_COUNT_CAMERA; this->camera_timelapse = on_off; - return this->publish_json(j.dump()); + return this->publish_json(j); } int MachineObject::command_ipcam_resolution_set(std::string resolution) { - BOOST_LOG_TRIVIAL(info) << "command:ipcam_resolution_set" << ", resolution:" << resolution; json j; j["camera"]["command"] = "ipcam_resolution_set"; j["camera"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); @@ -2945,7 +2959,7 @@ int MachineObject::command_ipcam_resolution_set(std::string resolution) camera_resolution_hold_count = HOLD_COUNT_CAMERA; camera_recording_ctl_start = time(nullptr); this->camera_resolution = resolution; - return this->publish_json(j.dump()); + return this->publish_json(j); } int MachineObject::command_xcam_control(std::string module_name, bool on_off, std::string lvl) @@ -2960,8 +2974,8 @@ int MachineObject::command_xcam_control(std::string module_name, bool on_off, st if (!lvl.empty()) { j["xcam"]["halt_print_sensitivity"] = lvl; } - BOOST_LOG_TRIVIAL(info) << "command:xcam_control_set" << ", module_name:" << module_name << ", control:" << on_off << ", halt_print_sensitivity:" << lvl; - return this->publish_json(j.dump()); + + return this->publish_json(j); } int MachineObject::command_xcam_control_ai_monitoring(bool on_off, std::string lvl) @@ -2974,6 +2988,49 @@ int MachineObject::command_xcam_control_ai_monitoring(bool on_off, std::string l return command_xcam_control("printing_monitor", on_off, lvl); } +// refine printer function options +int MachineObject::command_xcam_control_spaghetti_detection(bool on_off, std::string lvl) +{ + bool print_halt = (lvl == "never_halt") ? false : true; + + xcam_spaghetti_detection = on_off; + xcam_ai_monitoring_hold_start = time(nullptr); + xcam_spaghetti_detection_sensitivity = lvl; + return command_xcam_control("spaghetti_detector", on_off, lvl); +} + +int MachineObject::command_xcam_control_purgechutepileup_detection(bool on_off, std::string lvl) +{ + bool print_halt = (lvl == "never_halt") ? false : true; + + xcam_purgechutepileup_detection = on_off; + xcam_ai_monitoring_hold_start = time(nullptr); + xcam_purgechutepileup_detection_sensitivity = lvl; + return command_xcam_control("pileup_detector", on_off, lvl); +} + +int MachineObject::command_xcam_control_nozzleclumping_detection(bool on_off, std::string lvl) +{ + bool print_halt = (lvl == "never_halt") ? false : true; + + xcam_nozzleclumping_detection = on_off; + xcam_ai_monitoring_hold_start = time(nullptr); + xcam_nozzleclumping_detection_sensitivity = lvl; + return command_xcam_control("clump_detector", on_off, lvl); +} + + +int MachineObject::command_xcam_control_airprinting_detection(bool on_off, std::string lvl) +{ + bool print_halt = (lvl == "never_halt") ? false : true; + + xcam_airprinting_detection = on_off; + xcam_ai_monitoring_hold_start = time(nullptr); + xcam_airprinting_detection_sensitivity = lvl; + return command_xcam_control("airprint_detector", on_off, lvl); +} + + int MachineObject::command_xcam_control_buildplate_marker_detector(bool on_off) { xcam_buildplate_marker_detector = on_off; @@ -3107,14 +3164,14 @@ bool MachineObject::is_core_xy() void MachineObject::reset_update_time() { - BOOST_LOG_TRIVIAL(trace) << "reset reset_update_time, dev_id =" << dev_id; + BOOST_LOG_TRIVIAL(trace) << "reset reset_update_time, dev_id =" << QDTCrossTalk::Crosstalk_DevId(dev_id); last_update_time = std::chrono::system_clock::now(); subscribe_counter = SUBSCRIBE_RETRY_COUNT; } void MachineObject::reset() { - BOOST_LOG_TRIVIAL(trace) << "reset dev_id=" << dev_id; + BOOST_LOG_TRIVIAL(trace) << "reset dev_id=" << QDTCrossTalk::Crosstalk_DevId(dev_id); last_update_time = std::chrono::system_clock::now(); subscribe_counter = SUBSCRIBE_RETRY_COUNT; m_push_count = 0; @@ -3152,6 +3209,7 @@ void MachineObject::reset() } } subtask_ = nullptr; + m_partskip_ids.clear(); } void MachineObject::nt_reset_data() @@ -3196,7 +3254,7 @@ bool MachineObject::is_connected() std::chrono::system_clock::time_point curr_time = std::chrono::system_clock::now(); auto diff = std::chrono::duration_cast(curr_time - last_update_time); if (diff.count() > DISCONNECT_TIMEOUT) { - BOOST_LOG_TRIVIAL(trace) << "machine_object: dev_id=" << dev_id <<", diff count = " << diff.count(); + BOOST_LOG_TRIVIAL(trace) << "machine_object: dev_id=" << QDTCrossTalk::Crosstalk_DevId(dev_id) <<", diff count = " << diff.count(); return false; } @@ -3251,19 +3309,19 @@ bool MachineObject::is_camera_busy_off() return false; } -int MachineObject::publish_json(std::string json_str, int qos, int flag) +int MachineObject::publish_json(const json& json_item, int qos, int flag) { int rtn = 0; if (is_lan_mode_printer()) { - rtn = local_publish_json(json_str, qos, flag); + rtn = local_publish_json(json_item.dump(), qos, flag); } else { - rtn = cloud_publish_json(json_str, qos, flag); + rtn = cloud_publish_json(json_item.dump(), qos, flag); } if (rtn == 0) { - BOOST_LOG_TRIVIAL(info) << "publish_json: " << json_str << " code: " << rtn; + BOOST_LOG_TRIVIAL(info) << "publish_json: " << QDTCrossTalk::Crosstalk_JsonLog(json_item) << " code: " << rtn; } else { - BOOST_LOG_TRIVIAL(error) << "publish_json: " << json_str << " code: " << rtn; + BOOST_LOG_TRIVIAL(error) << "publish_json: " << QDTCrossTalk::Crosstalk_JsonLog(json_item) << " code: " << rtn; } return rtn; @@ -3358,7 +3416,7 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ if (j_pre["print"]["command"].get() == "push_status") { if (j_pre["print"].contains("msg")) { if (j_pre["print"]["msg"].get() == 0) { //all message - BOOST_LOG_TRIVIAL(trace) << "static: get push_all msg, dev_id=" << dev_id; + BOOST_LOG_TRIVIAL(trace) << "static: get push_all msg, dev_id=" << QDTCrossTalk::Crosstalk_DevId(dev_id); m_push_count++; m_full_msg_count++; @@ -3398,6 +3456,29 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ print_json.diff2all_base_reset(j_pre); } + + if (j_pre["print"].contains("s_obj")){ + if(j_pre["print"]["s_obj"].is_array()){ + m_partskip_ids.clear(); + for(auto it=j_pre["print"]["s_obj"].begin(); it!=j_pre["print"]["s_obj"].end(); it++){ + m_partskip_ids.push_back(it.value().get()); + } + } + } + } + } + if (j_pre["print"].contains("plate_idx")){ // && m_plate_index == -1 + if (j_pre["print"]["plate_idx"].is_number()) + { + m_plate_index = j_pre["print"]["plate_idx"].get(); + } + else if (j_pre["print"]["plate_idx"].is_string()) + { + try + { + m_plate_index = std::stoi(j_pre["print"]["plate_idx"].get()); + } + catch (...) { BOOST_LOG_TRIVIAL(error) << "parse_json: failed to convert plate_idx to int"; } } } } @@ -3493,12 +3574,14 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ message_delay.push_back(std::make_tuple(message_type, t_utc, delay)); } else + { last_utc_time = last_update_time; + } if (Slic3r::get_logging_level() < level_string_to_boost("trace")) { - BOOST_LOG_TRIVIAL(info) << "parse_json: dev_id=" << dev_id << ", origin playload=" << j_pre.dump(0); + BOOST_LOG_TRIVIAL(info) << "parse_json: dev_id=" << QDTCrossTalk::Crosstalk_DevId(dev_id) << ", origin playload=" << QDTCrossTalk::Crosstalk_JsonLog(j_pre); } else { - BOOST_LOG_TRIVIAL(trace) << "parse_json: dev_id=" << dev_id << ", tunnel is=" << tunnel << ", merged playload=" << j.dump(0); + BOOST_LOG_TRIVIAL(trace) << "parse_json: dev_id=" << QDTCrossTalk::Crosstalk_DevId(dev_id) << ", tunnel is=" << tunnel << ", merged playload=" << QDTCrossTalk::Crosstalk_JsonLog(j); } // Parse version info first, as if version arrive or change, 'print' need parse again with new compatible settings @@ -3816,9 +3899,20 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ if (jj["command"].get() == "ams_change_filament") { if (jj.contains("errno")) { if (jj["errno"].is_number()) { - if (jj["errno"].get() == -2) { - wxString text = _L("The current chamber temperature or the target chamber temperature exceeds 45\u2103.In order to avoid extruder clogging,low temperature filament(PLA/PETG/TPU) is not allowed to be loaded."); - GUI::wxGetApp().push_notification(this, text); + if (jj.contains("soft_temp")) { + int soft_temp = jj["soft_temp"].get(); + if (jj["errno"].get() == -2) { + wxString text = wxString::Format(_L("The chamber temperature is too high, which may cause the filament to soften. Please wait until the chamber temperature drops below %d\u2103. You may open the front door or enable fans to cool down."), soft_temp); + GUI::wxGetApp().push_notification(this, text); + } else if (jj["errno"].get() == -4) { + wxString text = wxString::Format(_L("AMS temperature is too high, which may cause the filament to soften. Please wait until the AMS temperature drops below %d\u2103."), soft_temp); + GUI::wxGetApp().push_notification(this, text); + } + } else { + if (jj["errno"].get() == -2) { + wxString text = _L("The current chamber temperature or the target chamber temperature exceeds 45\u2103.In order to avoid extruder clogging,low temperature filament(PLA/PETG/TPU) is not allowed to be loaded."); + GUI::wxGetApp().push_notification(this, text); + } } } } @@ -3960,7 +4054,7 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ online_rfid = false; } } - std::string str = jj.dump(); + if (jj["online"].contains("version")) { online_version = jj["online"]["version"].get(); } @@ -4492,7 +4586,49 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ try { if (jj.contains("xcam")) { if (time(nullptr) - xcam_ai_monitoring_hold_start > HOLD_TIME_3SEC) { - if (jj["xcam"].contains("printing_monitor")) { + + if (jj["xcam"].contains("cfg")) { + xcam_disable_ai_detection_display = true; + // std::string cfg = jj["xcam"]["cfg"].get(); + + int cfg = jj["xcam"]["cfg"].get(); + xcam_spaghetti_detection = get_flag_bits(cfg,7); + switch (get_flag_bits(cfg, 8, 2)) { + case 0: xcam_spaghetti_detection_sensitivity = "low"; break; + case 1: xcam_spaghetti_detection_sensitivity = "medium"; break; + case 2: xcam_spaghetti_detection_sensitivity = "high"; break; + default: break; + } + + xcam_purgechutepileup_detection = get_flag_bits(cfg, 10); + switch (get_flag_bits(cfg, 11, 2)) { + + case 0: xcam_purgechutepileup_detection_sensitivity = "low"; break; + case 1: xcam_purgechutepileup_detection_sensitivity = "medium"; break; + case 2: xcam_purgechutepileup_detection_sensitivity = "high"; break; + default: break; + } + + xcam_nozzleclumping_detection = get_flag_bits(cfg, 13); + switch (get_flag_bits(cfg, 14, 2)) { + + case 0: xcam_nozzleclumping_detection_sensitivity = "low"; break; + case 1: xcam_nozzleclumping_detection_sensitivity = "medium"; break; + case 2: xcam_nozzleclumping_detection_sensitivity = "high"; break; + default: break; + } + + xcam_airprinting_detection = get_flag_bits(cfg, 16); + switch (get_flag_bits(cfg, 17, 2)) { + + case 0: xcam_airprinting_detection_sensitivity = "low"; break; + case 1: xcam_airprinting_detection_sensitivity = "medium"; break; + case 2: xcam_airprinting_detection_sensitivity = "high"; break; + default: break; + } + + } + else if (jj["xcam"].contains("printing_monitor")) { // new protocol xcam_ai_monitoring = jj["xcam"]["printing_monitor"].get(); } else { @@ -4505,9 +4641,11 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ } } } + if (jj["xcam"].contains("halt_print_sensitivity")) { xcam_ai_monitoring_sensitivity = jj["xcam"]["halt_print_sensitivity"].get(); } + } if (time(nullptr) - xcam_first_layer_hold_start > HOLD_TIME_3SEC) { @@ -4565,14 +4703,13 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ int ams_status = jj["ams_status"].get(); this->_parse_ams_status(ams_status); } - std::string str_j = jj.dump(); + if (jj.contains("cali_version")) { cali_version = jj["cali_version"].get(); } else { cali_version = -1; } - std::string str = jj.dump(); } catch (...) { ; @@ -4937,8 +5074,6 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ } } - std::string temp = tray_it->dump(); - if (tray_it->contains("cali_idx")) { curr_tray->cali_idx = (*tray_it)["cali_idx"].get(); } @@ -5031,8 +5166,6 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ } catch (...) {} #pragma endregion } else if (jj["command"].get() == "gcode_line") { - //ack of gcode_line - BOOST_LOG_TRIVIAL(debug) << "parse_json, ack of gcode_line = " << j.dump(4); if (m_agent && is_studio_cmd(sequence_id)) { json t; t["dev_id"] = this->dev_id; @@ -5041,8 +5174,6 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ m_agent->track_event("ack_cmd_gcode_line", t.dump()); } } else if (jj["command"].get() == "project_prepare") { - //ack of project file - BOOST_LOG_TRIVIAL(info) << "parse_json, ack of project_prepare = " << j.dump(4); if (m_agent) { if (jj.contains("job_id")) { this->job_id_ = JsonValParser::get_longlong_val(jj["job_id"]); @@ -5050,8 +5181,6 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ } } else if (jj["command"].get() == "project_file") { - //ack of project file - BOOST_LOG_TRIVIAL(debug) << "parse_json, ack of project_file = " << j.dump(4); if (m_agent) { json t; t["dev_id"] = this->dev_id; @@ -5226,10 +5355,6 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ } } } -#ifdef CALI_DEBUG - std::string str = jj.dump(); - BOOST_LOG_TRIVIAL(info) << "extrusion_cali_set: " << str; -#endif int ams_id = -1; int tray_id = -1; int curr_tray_id = -1; @@ -5279,10 +5404,6 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ } } -#ifdef CALI_DEBUG - std::string str = jj.dump(); - BOOST_LOG_TRIVIAL(info) << "extrusion_cali_sel: " << str; -#endif int ams_id = -1; int slot_id = -1; int tray_id = -1; @@ -5340,9 +5461,7 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ } else if (jj["command"].get() == "extrusion_cali_get") { - std::string str = jj.dump(); if (request_tab_from_qds) { - BOOST_LOG_TRIVIAL(info) << "qds extrusion_cali_get: " << str; request_tab_from_qds = false; reset_pa_cali_history_result(); bool is_succeed = true; @@ -5411,12 +5530,10 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ } // notify cali history to update } else { - BOOST_LOG_TRIVIAL(info) << "printer extrusion_cali_get: " << str; + BOOST_LOG_TRIVIAL(info) << "printer extrusion_cali_get: "; } } else if (jj["command"].get() == "extrusion_cali_get_result") { - std::string str = jj.dump(); - BOOST_LOG_TRIVIAL(info) << "extrusion_cali_get_result: " << str; reset_pa_cali_result(); bool is_succeed = true; if (jj.contains("result") && jj.contains("reason")) { @@ -5508,10 +5625,6 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ get_flow_calib_result = true; if (jj.contains("filaments") && jj["filaments"].is_array()) { try { -#ifdef CALI_DEBUG - std::string str = jj.dump(); - BOOST_LOG_TRIVIAL(info) << "flowrate_get_result: " << str; -#endif for (auto it = jj["filaments"].begin(); it != jj["filaments"].end(); it++) { FlowRatioCalibResult flow_ratio_calib_result; flow_ratio_calib_result.tray_id = (*it)["tray_id"].get(); @@ -5616,8 +5729,11 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_ parse_state_changed_event(); } } - catch (...) { - BOOST_LOG_TRIVIAL(trace) << "parse_json failed! dev_id=" << this->dev_id <<", payload = " << payload; + catch (const nlohmann::json::exception& e) { + // Handle JSON parsing exceptions if necessary + BOOST_LOG_TRIVIAL(trace) << "parse_json failed! dev_id=" << QDTCrossTalk::Crosstalk_DevId(this->dev_id) <<", ewhat = " << e.what(); + } catch (...) { + BOOST_LOG_TRIVIAL(trace) << "parse_json failed! dev_id=" << QDTCrossTalk::Crosstalk_DevId(this->dev_id); } std::chrono::system_clock::time_point clock_stop = std::chrono::system_clock::now(); @@ -5664,7 +5780,7 @@ int MachineObject::publish_gcode(std::string gcode_str) m_agent->track_event("cmd_gcode_line", t.dump()); } - return publish_json(j.dump(), 0); + return publish_json(j); } QDTSubTask* MachineObject::get_subtask() @@ -5828,10 +5944,12 @@ void MachineObject::update_slice_info(std::string project_id, std::string profil if (!m_agent) return; if (!slice_info) return; + if (!get_slice_info_thread) return;/*STUDIO-12264*/ if (get_slice_info_thread->interruption_requested()) { return;} if (plate_idx >= 0) { plate_index = plate_idx; + this->m_plate_index = plate_idx; } else { std::string subtask_json; @@ -5878,7 +5996,10 @@ void MachineObject::update_slice_info(std::string project_id, std::string profil slice_info->filaments_info.push_back(f); } } + + #if !QDT_RELEASE_TO_PUBLIC BOOST_LOG_TRIVIAL(trace) << "task_info: thumbnail url=" << slice_info->thumbnail_url; + #endif } } } @@ -5894,8 +6015,7 @@ void MachineObject::update_slice_info(std::string project_id, std::string profil BOOST_LOG_TRIVIAL(error) << "task_info: get subtask id failed!"; } } - - this->m_plate_index = plate_index; + // this->m_plate_index = plate_index; }); } } @@ -6008,6 +6128,7 @@ std::string MachineObject::get_string_from_fantype(int type) void MachineObject::nt_condition_local_tunnel() { + return; int full_msg_count_limit = 2; if (!nt_try_local_tunnel && nt_cloud_full_msg_count == full_msg_count_limit) { connect(Slic3r::GUI::wxGetApp().app_config->get("enable_ssl_for_mqtt") == "true" ? true : false); @@ -6171,6 +6292,12 @@ AmsTray MachineObject::parse_vt_tray(json vtray) else { vt_tray.color = ""; } + if (vtray.contains("ctype")) { + vt_tray.ctype = vtray["ctype"].get(); + } + else { + vt_tray.ctype = 1; + } if (vtray.contains("nozzle_temp_max")) vt_tray.nozzle_temp_max = vtray["nozzle_temp_max"].get(); else @@ -6194,11 +6321,13 @@ AmsTray MachineObject::parse_vt_tray(json vtray) vt_tray.cali_idx = -1; vt_tray.cols.clear(); if (vtray.contains("cols")) { - if (vtray.is_array()) { - for (auto it = vtray.begin(); it != vtray.end(); it++) { + if (vtray["cols"].is_array()) { + for (auto it = vtray["cols"].begin(); it != vtray["cols"].end(); it++) { vt_tray.cols.push_back(it.value().get()); } } + } else { + vt_tray.cols.push_back(vt_tray.color); } if (vtray.contains("remain")) { @@ -6393,6 +6522,17 @@ void MachineObject::parse_new_info(json print) is_support_internal_timelapse = get_flag_bits(fun, 28); is_support_command_homing = get_flag_bits(fun, 32); is_support_brtc = get_flag_bits(fun, 31); + m_support_mqtt_axis_control = get_flag_bits(fun, 38); + m_support_mqtt_bet_ctrl = get_flag_bits(fun, 39); + + is_support_spaghetti_detection = get_flag_bits(fun, 42); + is_support_purgechutepileup_detection = get_flag_bits(fun, 43); + is_support_nozzleclumping_detection = get_flag_bits(fun, 44); + is_support_airprinting_detection = get_flag_bits(fun, 45); + + m_air_duct_data.m_support_cooling_filter = get_flag_bits(fun, 46); + is_support_ext_change_assist = get_flag_bits(fun, 48); + is_support_partskip = get_flag_bits(fun, 49); } /*aux*/ @@ -6424,9 +6564,9 @@ void MachineObject::parse_new_info(json print) m_air_duct_data.modes.clear(); m_air_duct_data.parts.clear(); - m_air_duct_data.curren_mode = device["airduct"]["modeCur"].get(); - const json& airduct = device["airduct"]; + if (airduct.contains("modeCur")) { m_air_duct_data.curren_mode = airduct["modeCur"].get();} + if (airduct.contains("subMode")) { m_air_duct_data.m_sub_mode = airduct["subMode"].get(); } if (airduct.contains("modeList") && airduct["modeList"].is_array()) { auto list = airduct["modeList"].get>(); @@ -6450,6 +6590,7 @@ void MachineObject::parse_new_info(json print) } } + if (AIR_DUCT(mode.id) == AIR_DUCT::AIR_DUCT_EXHAUST) { continue; } /*STUDIO-12796*/ m_air_duct_data.modes[mode.id] = mode; } } @@ -6823,6 +6964,7 @@ void MachineObject::check_ams_filament_valid() auto &filament_list = data.filament_list; auto &checked_filament = data.checked_filament; for (const auto &[slot_id, curr_tray] : ams->trayList) { + if (curr_tray->setting_id.size() == 8 && curr_tray->setting_id[0] == 'P' && filament_list.find(curr_tray->setting_id) == filament_list.end()) { if (checked_filament.find(curr_tray->setting_id) == checked_filament.end()) { need_checked_filament_id[nozzle_diameter_str].insert(curr_tray->setting_id); @@ -6966,7 +7108,7 @@ void MachineObject::command_set_door_open_check(DoorOpenCheckState state) default: assert(0); return; } - if (publish_json(j.dump()) == 0) + if (publish_json(j) == 0) { xcam_door_open_check = state; xcam_door_open_check_start_time = time(nullptr); @@ -6983,7 +7125,7 @@ void MachineObject::command_set_save_remote_print_file_to_storage(bool save) j["system"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++); j["system"]["config"] = save ? true : false; - if (publish_json(j.dump()) == 0) + if (publish_json(j) == 0) { xcam__save_remote_print_file_to_storage = save; xcam__save_remote_print_file_to_storage_start_time = time(nullptr); @@ -6991,6 +7133,23 @@ void MachineObject::command_set_save_remote_print_file_to_storage(bool save) } } +wxString MachineObject::get_nozzle_replace_url() const +{ + const wxString& strLanguage = GUI::wxGetApp().app_config->get("language"); + const wxString& lan_code = strLanguage.BeforeFirst('_'); + + const json& link_map = DeviceManager::get_json_from_config(printer_type, "print", "nozzle_replace_wiki"); + if (link_map.contains(lan_code.ToStdString())) { + return link_map[lan_code.ToStdString()].get(); + } + + if (link_map.contains("en")){ + return link_map["en"].get(); + }/*retry with en*/ + + return "https://wiki.qidi3d.com/"; +} + bool DeviceManager::EnableMultiMachine = false; bool DeviceManager::key_field_only = false; @@ -7118,7 +7277,6 @@ void DeviceManager::check_pushing() void DeviceManager::on_machine_alive(std::string json_str) { try { - //BOOST_LOG_TRIVIAL(trace) << "DeviceManager::SsdpDiscovery, json" << json_str; json j = json::parse(json_str); std::string dev_name = j["dev_name"].get(); std::string dev_id = j["dev_id"].get(); @@ -7166,7 +7324,12 @@ void DeviceManager::on_machine_alive(std::string json_str) it->second->bind_sec_link = sec_link; it->second->dev_connection_type = connect_type; it->second->bind_ssdp_version = ssdp_version; - BOOST_LOG_TRIVIAL(trace) << "DeviceManager::SsdpDiscovery, update userMachineList json" << json_str; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " UpdateUserMachineInfo" + << ", dev_id= " << QDTCrossTalk::Crosstalk_DevId(dev_id) + << ", ip = " << QDTCrossTalk::Crosstalk_DevIP(dev_ip) + << ", printer_name= " << QDTCrossTalk::Crosstalk_DevName(dev_name) + << ", con_type= " << connect_type << ", signal= " << printer_signal + << ", bind_state= " << bind_state; } } @@ -7179,12 +7342,12 @@ void DeviceManager::on_machine_alive(std::string json_str) if (obj->dev_ip.compare(dev_ip) != 0) { if ( connection_name.empty() ) { - BOOST_LOG_TRIVIAL(info) << "MachineObject IP changed from " << Slic3r::GUI::wxGetApp().format_IP(obj->dev_ip) << " to " << Slic3r::GUI::wxGetApp().format_IP(dev_ip); + BOOST_LOG_TRIVIAL(info) << "MachineObject IP changed from " << QDTCrossTalk::Crosstalk_DevIP(obj->dev_ip) << " to " << QDTCrossTalk::Crosstalk_DevIP(dev_ip); obj->dev_ip = dev_ip; } else { if ( obj->dev_connection_name.empty() || obj->dev_connection_name.compare(connection_name) == 0) { - BOOST_LOG_TRIVIAL(info) << "MachineObject IP changed from " << Slic3r::GUI::wxGetApp().format_IP(obj->dev_ip) << " to " << Slic3r::GUI::wxGetApp().format_IP(dev_ip) << " connection_name is " << connection_name; + BOOST_LOG_TRIVIAL(info) << "MachineObject IP changed from " << QDTCrossTalk::Crosstalk_DevIP(obj->dev_ip) << " to " << QDTCrossTalk::Crosstalk_DevIP(dev_ip) << " connection_name is " << connection_name; if(obj->dev_connection_name.empty()){obj->dev_connection_name = connection_name;} obj->dev_ip = dev_ip; } @@ -7200,20 +7363,41 @@ void DeviceManager::on_machine_alive(std::string json_str) obj->bind_ssdp_version != ssdp_version || obj->printer_type != MachineObject::parse_printer_type(printer_type_str)) { + if (obj->dev_connection_type != connect_type || + obj->bind_state != bind_state || + obj->bind_sec_link != sec_link || + obj->bind_ssdp_version != ssdp_version || + obj->printer_type != MachineObject::parse_printer_type(printer_type_str)) + { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " UpdateLocalMachineInfo" + << ", dev_id= " << QDTCrossTalk::Crosstalk_DevId(dev_id) + << ", ip = " << QDTCrossTalk::Crosstalk_DevIP(dev_ip) + << ", printer_name= " << QDTCrossTalk::Crosstalk_DevName(dev_name) + << ", con_type= " << connect_type << ", signal= " << printer_signal + << ", bind_state= " << bind_state; + } + else + { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " UpdateLocalMachineInfo_WIFI" + << ", dev_id= " << QDTCrossTalk::Crosstalk_DevId(dev_id) + << ", ip = " << QDTCrossTalk::Crosstalk_DevIP(dev_ip) + << ", printer_name= " << QDTCrossTalk::Crosstalk_DevName(dev_name) + << ", con_type= " << connect_type << ", signal= " << printer_signal + << ", bind_state= " << bind_state; + } + obj->wifi_signal = printer_signal; obj->dev_connection_type = connect_type; obj->bind_state = bind_state; obj->bind_sec_link = sec_link; obj->bind_ssdp_version = ssdp_version; obj->printer_type = MachineObject::parse_printer_type(printer_type_str); - BOOST_LOG_TRIVIAL(trace) << "DeviceManager::SsdpDiscovery, update localMachineList json" << json_str; } // U0 firmware if (obj->dev_connection_type.empty() && obj->bind_state.empty()) obj->bind_state = "free"; - //BOOST_LOG_TRIVIAL(debug) << "SsdpDiscovery:: Update Machine Info, printer_sn = " << dev_id << ", signal = " << printer_signal; obj->last_alive = Slic3r::Utils::get_current_time_utc(); obj->m_is_online = true; @@ -7246,9 +7430,9 @@ void DeviceManager::on_machine_alive(std::string json_str) Slic3r::GUI::wxGetApp().app_config->set_str("ip_address", obj->dev_id, obj->dev_ip); Slic3r::GUI::wxGetApp().app_config->save(); }*/ - BOOST_LOG_TRIVIAL(info) << "SsdpDiscovery::New Machine, ip= " << Slic3r::GUI::wxGetApp().format_IP(dev_ip) << ", printer_name= " << dev_name - << ", printer_type= " << printer_type_str << ", con_type= " - << connect_type <<", signal= " << printer_signal << ", bind_state= " << bind_state; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " New Machine, dev_id= " << QDTCrossTalk::Crosstalk_DevId(dev_id) + << ", ip = " << QDTCrossTalk::Crosstalk_DevIP(dev_ip) <<", printer_name = " << QDTCrossTalk::Crosstalk_DevName(dev_name) + << ", con_type= " << connect_type <<", signal= " << printer_signal << ", bind_state= " << bind_state; } } catch (...) { @@ -7261,13 +7445,22 @@ MachineObject* DeviceManager::insert_local_device(std::string dev_name, std::str MachineObject* obj; obj = new MachineObject(m_agent, dev_name, dev_id, dev_ip); obj->printer_type = MachineObject::parse_printer_type("C11"); - obj->dev_connection_type = connection_type; - obj->bind_state = bind_state; + obj->dev_connection_type = connection_type == "farm" ? "lan":connection_type; + obj->bind_state = connection_type == "farm" ? "free":bind_state; obj->bind_sec_link = "secure"; obj->bind_ssdp_version = version; obj->m_is_online = true; obj->set_access_code(access_code, false); obj->set_user_access_code(access_code, false); + + + auto it = localMachineList.find(dev_id); + if (it != localMachineList.end()) { + localMachineList[dev_id] = obj; + } else { + localMachineList.insert(std::make_pair(dev_id, obj)); + } + return obj; } @@ -7401,7 +7594,7 @@ void DeviceManager::clean_user_info() bool DeviceManager::set_selected_machine(std::string dev_id, bool need_disconnect) { - BOOST_LOG_TRIVIAL(info) << "set_selected_machine=" << dev_id; + BOOST_LOG_TRIVIAL(info) << "set_selected_machine=" << QDTCrossTalk::Crosstalk_DevId(dev_id); auto my_machine_list = get_my_machine_list(); auto it = my_machine_list.find(dev_id); @@ -7412,8 +7605,6 @@ bool DeviceManager::set_selected_machine(std::string dev_id, bool need_disconnec if (last_selected->second->connection_type() == "lan") { if (last_selected->second->is_connecting() && !need_disconnect) return false; - - if (!need_disconnect) {m_agent->disconnect_printer(); } } } @@ -7431,7 +7622,6 @@ bool DeviceManager::set_selected_machine(std::string dev_id, bool need_disconnec } else { // lan mode printer reconnect printer if (m_agent) { - if (!need_disconnect) {m_agent->disconnect_printer();} it->second->reset(); #if !QDT_RELEASE_TO_PUBLIC it->second->connect(Slic3r::GUI::wxGetApp().app_config->get("enable_ssl_for_mqtt") == "true" ? true : false); @@ -7461,7 +7651,6 @@ bool DeviceManager::set_selected_machine(std::string dev_id, bool need_disconnec #else it->second->connect(it->second->local_use_ssl_for_mqtt); #endif - m_agent->set_user_selected_machine(dev_id); it->second->set_lan_mode_connection_state(true); } } @@ -7491,28 +7680,6 @@ MachineObject* DeviceManager::get_selected_machine() return nullptr; } -void DeviceManager::add_user_subscribe() -{ - /* user machine */ - std::vector dev_list; - for (auto it = userMachineList.begin(); it != userMachineList.end(); it++) { - dev_list.push_back(it->first); - BOOST_LOG_TRIVIAL(trace) << "add_user_subscribe: " << it->first; - } - m_agent->add_subscribe(dev_list); -} - -void DeviceManager::del_user_subscribe() -{ - /* user machine */ - std::vector dev_list; - for (auto it = userMachineList.begin(); it != userMachineList.end(); it++) { - dev_list.push_back(it->first); - BOOST_LOG_TRIVIAL(trace) << "del_user_subscribe: " << it->first; - } - m_agent->del_subscribe(dev_list); -} - void DeviceManager::subscribe_device_list(std::vector dev_list) { std::vector unsub_list; @@ -7520,7 +7687,7 @@ void DeviceManager::subscribe_device_list(std::vector dev_list) for (auto& it : subscribe_list_cache) { if (it != selected_machine) { unsub_list.push_back(it); - BOOST_LOG_TRIVIAL(trace) << "subscribe_device_list: unsub dev id = " << it; + BOOST_LOG_TRIVIAL(trace) << "subscribe_device_list: unsub dev id = " << QDTCrossTalk::Crosstalk_DevId(it); } } BOOST_LOG_TRIVIAL(trace) << "subscribe_device_list: unsub_list size = " << unsub_list.size(); @@ -7530,7 +7697,7 @@ void DeviceManager::subscribe_device_list(std::vector dev_list) } for (auto& it : dev_list) { subscribe_list_cache.push_back(it); - BOOST_LOG_TRIVIAL(trace) << "subscribe_device_list: sub dev id = " << it; + BOOST_LOG_TRIVIAL(trace) << "subscribe_device_list: sub dev id = " << QDTCrossTalk::Crosstalk_DevId(it); } BOOST_LOG_TRIVIAL(trace) << "subscribe_device_list: sub_list size = " << subscribe_list_cache.size(); if (!unsub_list.empty()) @@ -7553,6 +7720,7 @@ std::map DeviceManager::get_my_machine_list() for (auto it = localMachineList.begin(); it != localMachineList.end(); it++) { if (!it->second) continue; + if (it->second->has_access_right() && it->second->is_avaliable() && it->second->is_lan_mode_printer()) { // remove redundant in userMachineList if (result.find(it->first) == result.end()) { @@ -7893,16 +8061,38 @@ bool DeviceManager::load_filaments_blacklist_config() return true; } else { - BOOST_LOG_TRIVIAL(error) << "load filaments blacklist config failed, file = " << config_file; + BOOST_LOG_TRIVIAL(error) << "load filaments blacklist config failed, file = filaments_blacklist.json"; } } catch (...) { - BOOST_LOG_TRIVIAL(error) << "load filaments blacklist config failed, file = " << config_file; + BOOST_LOG_TRIVIAL(error) << "load filaments blacklist config failed, file = filaments_blacklist.json"; return false; } return true; } +string DeviceManager::get_fan_text(const std::string& type_str, const std::string& key) +{ + std::vector filaments; + std::string config_file = Slic3r::resources_dir() + "/printers/" + type_str + ".json"; + boost::nowide::ifstream json_file(config_file.c_str()); + try + { + json jj; + if (json_file.is_open()) { + json_file >> jj; + if (jj.contains("00.00.00.00")) { + json const& printer = jj["00.00.00.00"]; + if (printer.contains("fan") && printer["fan"].contains(key)) { + return printer["fan"][key].get(); + } + } + } + } + catch (...) {} + return string(); +} + bool DeviceManager::is_virtual_slot(int ams_id) { if (ams_id == VIRTUAL_TRAY_MAIN_ID || ams_id == VIRTUAL_TRAY_DEPUTY_ID) @@ -7947,30 +8137,23 @@ void DeviceManager::OnSelectedMachineLost() { GUI::wxGetApp().sidebar().load_ams_list(string(), nullptr); } -// moved from tao.wang and zhimin.zeng -void check_filaments_for_ams_slot(std::string model_id, - std::string tag_vendor, - std::string tag_type, - int ams_id, - int slot_id, - std::string tag_name, - bool& in_blacklist, - std::string& ac, - wxString& info) + +void check_filaments(std::string model_id, + std::string tag_vendor, + std::string tag_type, + int ams_id, + int slot_id, + std::string tag_name, + bool& in_blacklist, + std::string& ac, + wxString& info, + wxString& wiki_url) { if (tag_name.empty()) { tag_name = DeviceManager::get_filament_name_from_ams(ams_id, slot_id); } - std::unordered_map blacklist_prompt = - { - {"TPU: not supported", _L("TPU is not supported by Box.")}, - {"PVA: flexible", _L("Damp PVA will become flexible and get stuck inside Box,please take care to dry it before use.")}, - {"CF/GF: hard and brittle", _L("CF/GF filaments are hard and brittle, It's easy to break or get stuck in Box, please use with caution.")}, - {"PLA-Glow", _L("The rough surface of PLA Glow can accelerate wear on the Box system, particularly on the internal components of the Box Lite.")} - }; - in_blacklist = false; std::transform(tag_vendor.begin(), tag_vendor.end(), tag_vendor.begin(), ::tolower); @@ -7983,6 +8166,7 @@ void check_filaments_for_ams_slot(std::string model_id, std::string type = filament_item.contains("type") ? filament_item["type"].get() : ""; std::string type_suffix = filament_item.contains("type_suffix") ? filament_item["type_suffix"].get() : ""; std::string name = filament_item.contains("name") ? filament_item["name"].get() : ""; + std::string slot = filament_item.contains("slot") ? filament_item["slot"].get() : ""; std::vector model_ids = filament_item.contains("model_id") ? filament_item["model_id"].get>() : std::vector(); std::string action = filament_item.contains("action") ? filament_item["action"].get() : ""; std::string description = filament_item.contains("description") ? filament_item["description"].get() : ""; @@ -8016,14 +8200,37 @@ void check_filaments_for_ams_slot(std::string model_id, std::transform(name.begin(), name.end(), name.begin(), ::tolower); if (!name.empty() && (name != tag_name)) { continue;} + // check loc + if (!slot.empty()) { + bool is_virtual_slot = DeviceManager::is_virtual_slot(ams_id); + bool check_virtual_slot = (slot == "ext"); + bool check_ams_slot = (slot == "ams"); + if (is_virtual_slot && !check_virtual_slot) { + continue; + } else if (!is_virtual_slot && !check_ams_slot) { + continue; + } + } + in_blacklist = true; ac = action; - info = blacklist_prompt[description]; + info = _L(description); + wiki_url = filament_item.contains("wiki") ? filament_item["wiki"].get() : ""; return; + + // Using in description + L("TPU is not supported by AMS."); + L("Damp PVA will become flexible and get stuck inside AMS,please take care to dry it before use."); + L("The rough surface of PLA Glow can accelerate wear on the AMS system, particularly on the internal components of the AMS Lite."); + L("CF/GF filaments are hard and brittle, It's easy to break or get stuck in AMS, please use with caution."); + L("PPS-CF is brittle and could break in bended PTFE tube above Toolhead."); + L("PPA-CF is brittle and could break in bended PTFE tube above Toolhead."); } } } + + void DeviceManager::check_filaments_in_blacklist(std::string model_id, std::string tag_vendor, std::string tag_type, @@ -8035,17 +8242,23 @@ void DeviceManager::check_filaments_in_blacklist(std::string model_id, std::string &ac, wxString &info) { - if (ams_id < 0 || slot_id < 0) { + wxString wiki_url; + check_filaments_in_blacklist_url(model_id, tag_vendor, tag_type, filament_id, ams_id, slot_id, tag_name, in_blacklist, ac, info, wiki_url); +} + +void DeviceManager::check_filaments_in_blacklist_url(std::string model_id, std::string tag_vendor, std::string tag_type, const std::string& filament_id, int ams_id, int slot_id, std::string tag_name, bool& in_blacklist, std::string& ac, wxString& info, wxString& wiki_url) +{ + if (ams_id < 0 || slot_id < 0) + { return; } - if (!check_filaments_printable(tag_vendor, tag_type, filament_id, ams_id, in_blacklist, ac, info)) { + if (!check_filaments_printable(tag_vendor, tag_type, filament_id, ams_id, in_blacklist, ac, info)) + { return; } - if (!DeviceManager::is_virtual_slot(ams_id)) { - check_filaments_for_ams_slot(model_id, tag_vendor, tag_type, ams_id, slot_id, tag_name, in_blacklist, ac, info); - } + check_filaments(model_id, tag_vendor, tag_type, ams_id, slot_id, tag_name, in_blacklist, ac, info, wiki_url); } std::string DeviceManager::load_gcode(std::string type_str, std::string gcode_file) @@ -8060,7 +8273,7 @@ std::string DeviceManager::load_gcode(std::string type_str, std::string gcode_fi return gcode_str.str(); } } catch(...) { - BOOST_LOG_TRIVIAL(error) << "load gcode file failed, file = " << gcode_file << ", path = " << gcode_full_path; + BOOST_LOG_TRIVIAL(error) << "load gcode file failed, file = " << gcode_file; } diff --git a/src/slic3r/GUI/DeviceManager.hpp b/src/slic3r/GUI/DeviceManager.hpp index 720ffaa..b860a87 100644 --- a/src/slic3r/GUI/DeviceManager.hpp +++ b/src/slic3r/GUI/DeviceManager.hpp @@ -250,6 +250,11 @@ struct AirMode // If the fan is off, it cannot be controlled and is displayed as off std::vector off; // If the fan is not off or ctrl, it will be displayed as auto + +public: + bool operator ==(const AirMode& other) const { + return (id == other.id) && (ctrl == other.ctrl) && (off == other.off); + }; }; struct AirParts @@ -260,6 +265,11 @@ struct AirParts int state{ 0 };// 100% int range_start{ 0 };// 100% int range_end{ 0 };// 100% + +public: + bool operator ==(const AirParts& other) const { + return (type == other.type) && (id == other.id) && (func == other.func) && (state == other.state) && (range_start == other.range_start) && (range_end == other.range_end); + }; }; struct AirDuctData @@ -267,6 +277,22 @@ struct AirDuctData int curren_mode{ 0 }; std::unordered_map modes; std::vector parts; + + int m_sub_mode = -1;// the submode of airduct, for cooling: 0-filter, 1-cooling + bool m_support_cooling_filter = false;// support switch filter on cooling mode or not + +public: + bool operator ==(const AirDuctData& other) const { + return (curren_mode == other.curren_mode) && (modes == other.modes) && (parts == other.parts) && + (m_sub_mode == other.m_sub_mode) && (m_support_cooling_filter == other.m_support_cooling_filter); + }; + + bool operator !=(const AirDuctData& other) const { + return !(operator==(other)); + }; + + bool IsSupportCoolingFilter() const { return m_support_cooling_filter;} + bool IsCoolingFilerOn() const { return m_sub_mode == 0;} }; struct RatingInfo { @@ -484,7 +510,6 @@ enum AIR_DUCT { AIR_DUCT_HEATING_INTERNAL_FILT, AIR_DUCT_EXHAUST, AIR_DUCT_FULL_COOLING, - AIR_DUCT_NUM, AIR_DUCT_INIT = 0xFF //Initial mode, only used within mc }; @@ -668,6 +693,7 @@ public: //PRINTER_TYPE printer_type = PRINTER_3DPrinter_UKNOWN; std::string printer_type; /* model_id */ + std::string get_show_printer_type() const; PrinterSeries get_printer_series() const; PrinterArch get_printer_arch() const; std::string get_printer_ams_type() const; @@ -771,6 +797,7 @@ public: AmsTray *get_ams_tray(std::string ams_id, std::string tray_id); std::string get_filament_id(std::string ams_id, std::string tray_id) const; + std::string get_filament_type(const std::string& ams_id, const std::string& tray_id) const; // parse amsStatusMain and ams_status_sub void _parse_ams_status(int ams_status); @@ -819,6 +846,7 @@ public: [[nodiscard]] bool is_nozzle_flow_type_supported() const { return is_enable_np; }; [[nodiscard]] NozzleFlowType get_nozzle_flow_type(int extruder_id) const; [[nodiscard]] Extder get_current_extruder() const; + [[nodiscard]] wxString get_nozzle_replace_url() const; //new fan data AirDuctData m_air_duct_data; @@ -1033,9 +1061,21 @@ public: int nozzle_setting_hold_count = 0; + //refine printer bool xcam_ai_monitoring{ false }; + bool xcam_disable_ai_detection_display{false}; + bool xcam_spaghetti_detection{false}; + bool xcam_purgechutepileup_detection{false}; + bool xcam_nozzleclumping_detection{false}; + bool xcam_airprinting_detection{false}; + time_t xcam_ai_monitoring_hold_start = 0; std::string xcam_ai_monitoring_sensitivity; + std::string xcam_spaghetti_detection_sensitivity; + std::string xcam_purgechutepileup_detection_sensitivity; + std::string xcam_nozzleclumping_detection_sensitivity; + std::string xcam_airprinting_detection_sensitivity; + bool xcam_buildplate_marker_detector{ false }; time_t xcam_buildplate_marker_hold_start = 0; bool xcam_auto_recovery_step_loss{ false }; @@ -1047,6 +1087,9 @@ public: int nozzle_selected_count = 0; bool flag_update_nozzle = {true}; + // part skip + std::vector m_partskip_ids; + /*target from Studio-SwitchBoard, default to INVALID_NOZZLE_ID if no switching control from PC*/ int targ_nozzle_id_from_pc = INVALID_NOZZLE_ID; @@ -1055,7 +1098,7 @@ public: bool is_support_extrusion_cali{false}; bool is_support_first_layer_inspect{false}; bool is_support_ai_monitoring {false}; - bool is_support_lidar_calibration {false}; + bool is_support_lidar_calibration {false};// the lidar calibration for 3D Studio bool is_support_build_plate_marker_detect{false}; PlateMakerDectect m_plate_maker_detect_type{ POS_CHECK }; @@ -1096,6 +1139,14 @@ public: bool is_support_internal_timelapse { false };// fun[28], support timelapse without SD card bool is_support_command_homing { false };// fun[32] bool is_support_brtc{false}; // fun[31], support tcp and upload protocol + bool is_support_ext_change_assist{false}; + bool is_support_partskip{false}; + + // refine printer function options + bool is_support_spaghetti_detection{false}; + bool is_support_purgechutepileup_detection{false}; + bool is_support_nozzleclumping_detection{false}; + bool is_support_airprinting_detection{false}; bool installed_upgrade_kit{false}; int bed_temperature_limit = -1; @@ -1199,9 +1250,10 @@ public: int command_go_home2(); int command_control_fan(int fan_type, int val); // Old protocol int command_control_fan_new(int fan_id, int val, const CommandCallBack &cb); // New protocol - int command_control_air_duct(int mode_id, const CommandCallBack& cb); + int command_control_air_duct(int mode_id, int submode, const CommandCallBack& cb); int command_task_abort(); /* cancelled the job_id */ + int command_task_partskip(std::vector part_ids); int command_task_cancel(std::string job_id); int command_task_pause(); int command_task_resume(); @@ -1213,7 +1265,9 @@ public: int command_stop_buzzer(); /* temp*/ + bool m_support_mqtt_bet_ctrl = false; int command_set_bed(int temp); + int command_set_nozzle(int temp); int command_set_nozzle_new(int nozzle_id, int temp); int command_set_chamber(int temp); @@ -1253,6 +1307,7 @@ public: int command_nozzle_blob_detect(bool nozzle_blob_detect); // axis string is X, Y, Z, E + bool m_support_mqtt_axis_control = false; int command_axis_control(std::string axis, double unit = 1.0f, double input_val = 1.0f, int speed = 3000); int command_extruder_control(int nozzle_id, double val); @@ -1281,7 +1336,14 @@ public: int command_ipcam_timelapse(bool on_off); int command_ipcam_resolution_set(std::string resolution); int command_xcam_control(std::string module_name, bool on_off, std::string lvl = ""); + + //refine printer int command_xcam_control_ai_monitoring(bool on_off, std::string lvl); + int command_xcam_control_spaghetti_detection(bool on_off, std::string lvl); + int command_xcam_control_purgechutepileup_detection(bool on_off, std::string lvl); + int command_xcam_control_nozzleclumping_detection(bool on_off, std::string lvl); + int command_xcam_control_airprinting_detection(bool on_off, std::string lvl); + int command_xcam_control_first_layer_inspector(bool on_off, bool print_halt); int command_xcam_control_buildplate_marker_detector(bool on_off); int command_xcam_control_auto_recovery_step_loss(bool on_off); @@ -1319,7 +1381,8 @@ public: /* Msg for display MsgFn */ typedef std::function MsgFn; - int publish_json(std::string json_str, int qos = 0, int flag = 0); + int publish_json(const json& json_item, int qos = 0, int flag = 0) ; + int publish_json(const std::string& json_str, int qos = 0, int flag = 0) = delete; int cloud_publish_json(std::string json_str, int qos = 0, int flag = 0); int local_publish_json(std::string json_str, int qos = 0, int flag = 0); int parse_json(std::string tunnel, std::string payload, bool key_filed_only = false); @@ -1452,8 +1515,6 @@ public: bool set_selected_machine(std::string dev_id, bool need_disconnect = false); MachineObject* get_selected_machine(); - void add_user_subscribe(); - void del_user_subscribe(); void subscribe_device_list(std::vector dev_list); @@ -1505,6 +1566,32 @@ public: return T(); } + static json get_json_from_config(const std::string& type_str, const std::string& key1, const std::string& key2 = std::string()) { + std::string config_file = Slic3r::resources_dir() + "/printers/" + type_str + ".json"; + boost::nowide::ifstream json_file(config_file.c_str()); + try { + json jj; + if (json_file.is_open()) { + json_file >> jj; + if (jj.contains("00.00.00.00")) { + json const& printer = jj["00.00.00.00"]; + if (printer.contains(key1)) { + json const& key1_item = printer[key1]; + if (key2.empty()) { + return key1_item; + } + + if (key1_item.contains(key2)) { + return key1_item[key2]; + } + } + } + } + } + catch (...) {} + return json(); + } + static std::string parse_printer_type(std::string type_str); static std::string get_printer_display_name(std::string type_str); static std::string get_printer_thumbnail_img(std::string type_str); @@ -1518,10 +1605,14 @@ public: static bool get_printer_is_enclosed(std::string type_str); static bool get_printer_can_set_nozzle(std::string type_str);// can set nozzle from studio static bool load_filaments_blacklist_config(); + + static string get_fan_text(const std::string& type_str, const std::string& key); + static std::vector get_resolution_supported(std::string type_str); static std::vector get_compatible_machine(std::string type_str); static std::vector get_unsupport_auto_cali_filaments(std::string type_str); static void check_filaments_in_blacklist(std::string model_id, std::string tag_vendor, std::string tag_type, const std::string& filament_id, int ams_id, int slot_id, std::string tag_name, bool &in_blacklist, std::string &ac, wxString &info); + static void check_filaments_in_blacklist_url(std::string model_id, std::string tag_vendor, std::string tag_type, const std::string& filament_id, int ams_id, int slot_id, std::string tag_name, bool& in_blacklist, std::string& ac, wxString& info, wxString& wiki_url); static boost::bimaps::bimap get_all_model_id_with_name(); static std::string load_gcode(std::string type_str, std::string gcode_file); static bool is_virtual_slot(int ams_id); diff --git a/src/slic3r/GUI/DeviceTab/uiAmsHumidityPopup.cpp b/src/slic3r/GUI/DeviceTab/uiAmsHumidityPopup.cpp index 93d188e..c075485 100644 --- a/src/slic3r/GUI/DeviceTab/uiAmsHumidityPopup.cpp +++ b/src/slic3r/GUI/DeviceTab/uiAmsHumidityPopup.cpp @@ -151,7 +151,23 @@ void uiAmsPercentHumidityDryPopup::UpdateContents() if (m_left_dry_time > 0) { - const wxString& time_str = wxString::Format(_L("%d : %d"), m_left_dry_time / 60, m_left_dry_time % 60); + wxString display_hour_str; + int left_hours = m_left_dry_time / 60; + if (left_hours < 10) { + display_hour_str = wxString::Format("0%d", left_hours); + } else { + display_hour_str = wxString::Format("%d", left_hours); + } + + wxString display_min_str; + int left_minutes = m_left_dry_time % 60; + if (left_minutes < 10) { + display_min_str = wxString::Format("0%d", left_minutes); + } else { + display_min_str = wxString::Format("%d", left_minutes); + } + + const wxString& time_str = wxString::Format("%s : %s", display_hour_str, display_min_str); left_dry_time_label->SetLabel(time_str); } else diff --git a/src/slic3r/GUI/EncodedFilament.cpp b/src/slic3r/GUI/EncodedFilament.cpp new file mode 100644 index 0000000..3a486bb --- /dev/null +++ b/src/slic3r/GUI/EncodedFilament.cpp @@ -0,0 +1,217 @@ +#include "EncodedFilament.hpp" + +#include "GUI_App.hpp" + +namespace Slic3r +{ + +static wxString _ColourToString(const wxColour& color) +{ + return wxString::Format("#%02X%02X%02X%02X", color.Red(), color.Green(), color.Blue(), color.Alpha()); +} + +FilamentColorCodeQuery::FilamentColorCodeQuery() +{ + m_fila_id2colors_map = new std::unordered_map; + m_fila_path = data_dir() + "/system/QDT/filament/filaments_color_codes.json"; + LoadFromLocal(); +} + + +FilamentColorCodeQuery::~FilamentColorCodeQuery() +{ + for (auto& pair : *m_fila_id2colors_map) { delete pair.second; } + + delete m_fila_id2colors_map; + m_fila_id2colors_map = nullptr; +} + +FilamentColorCodes* FilamentColorCodeQuery::GetFilaInfoMap(const wxString& fila_id) const +{ + const auto& iter = m_fila_id2colors_map->find(fila_id); + return (iter != m_fila_id2colors_map->end()) ? iter->second : nullptr; +} + +Slic3r::FilamentColorCode* FilamentColorCodeQuery::GetFilaInfo(const wxString& fila_id, const FilamentColor& colors) const +{ + FilamentColorCodes* color_info_map = GetFilaInfoMap(fila_id); + +#if 0 + if (color_info_map && !color_info_map->GetColorCode(colors)) + { + wxString clr_strs; + for (const auto& clr : colors.m_colors) + { + clr_strs += " "; + clr_strs += _ColourToString(clr); + } + + BOOST_LOG_TRIVIAL(warning) << "FilamentColorCodeQuery::GetFilaInfo: No color code found for " << fila_id << " with color type " << (int)colors.m_color_type << "colors" << clr_strs; + color_info_map->Debug(" "); + } +#endif + + return color_info_map ? color_info_map->GetColorCode(colors) : nullptr; +} + +wxString FilamentColorCodeQuery::GetFilaColorName(const wxString& fila_id, const FilamentColor& colors) const +{ + FilamentColorCode* color_info = GetFilaInfo(fila_id, colors); + return (color_info) ? color_info->GetFilaColorName() : wxString(); +} + +void FilamentColorCodeQuery::LoadFromLocal() +{ + std::ifstream json_file(encode_path(m_fila_path.c_str())); + try + { + if (json_file.is_open()) + { + const json& json_content = json::parse(json_file); + if (!json_content.contains("data")) { return; } + + const json& json_data = json_content["data"]; + for (const auto& json_data_item : json_data) + { + const wxString& fila_id = json_data_item.contains("fila_id") ? json_data_item["fila_id"].get() : wxString(); + const wxString& fila_type = json_data_item.contains("fila_type") ? json_data_item["fila_type"].get() : wxString(); + const wxString& fila_color_code = json_data_item.contains("fila_color_code") ? json_data_item["fila_color_code"].get() : wxString(); + + FilamentColor fila_color; + if (json_data_item.contains("fila_color")) + { + const auto& fila_color_strs = json_data_item["fila_color"].get>(); + for (const auto& color_str : fila_color_strs) { + if (color_str.size() > 3) /* Skip the value like "#0"*/{ + fila_color.m_colors.emplace(wxColour(color_str)); + } + } + } + + if (fila_color.m_colors.empty()) { + BOOST_LOG_TRIVIAL(warning) << "FilamentColorCodeQuery::LoadFromLocal: No colors found for fila_color_code: " << fila_color_code; + continue; // Skip if no colors are defined + }; + + const wxString& fila_color_type = json_data_item.contains("fila_color_type") ? wxString::FromUTF8(json_data_item["fila_color_type"].get()) : wxString(); + if (fila_color_type == wxString::FromUTF8("单色")) { + fila_color.m_color_type = FilamentColor::ColorType::SINGLE_CLR; + } else if (fila_color_type == wxString::FromUTF8("多拼色")) { + fila_color.m_color_type = FilamentColor::ColorType::MULTI_CLR; + } else if (fila_color_type == wxString::FromUTF8("渐变色")) + { + fila_color.m_color_type = FilamentColor::ColorType::GRADIENT_CLR; + }; + + std::unordered_map fila_color_names; + if (json_data_item.contains("fila_color_name")) + { + const json& color_names_json = json_data_item["fila_color_name"]; + for (const auto& color_name_item : color_names_json.items()) + { + const wxString& lang_code = wxString::FromUTF8(color_name_item.key()); + const wxString& color_name = wxString::FromUTF8(color_name_item.value().get()); + fila_color_names[lang_code] = color_name; + } + } + + CreateFilaCode(fila_id, fila_type, fila_color_code, std::move(fila_color), std::move(fila_color_names)); + } + } + } + catch (...) + { + assert(0 && "FilamentColorCodeQuery::LoadFromLocal failed"); + BOOST_LOG_TRIVIAL(error) << "FilamentColorCodeQuery::LoadFromLocal failed"; + } +} + +void FilamentColorCodeQuery::CreateFilaCode(const wxString& fila_id, + const wxString& fila_type, + const wxString& fila_color_code, + FilamentColor&& fila_color, + std::unordered_map&& fila_color_names) +{ + FilamentColorCodes* color_codes = GetFilaInfoMap(fila_id); + if (!color_codes) + { + color_codes = new FilamentColorCodes(fila_id, fila_type); + (*m_fila_id2colors_map)[fila_id] = color_codes; + } + + FilamentColorCode* color_code = new FilamentColorCode(fila_color_code, color_codes, std::move(fila_color), std::move(fila_color_names)); + color_codes->AddColorCode(color_code); +} +// End of class EncodedFilamentQuery + + +wxString FilamentColorCode::GetFilaColorName() const +{ + const wxString& strLanguage = Slic3r::GUI::wxGetApp().app_config->get("language"); + const wxString& lang_code = strLanguage.BeforeFirst('_'); + auto it = m_fila_color_names.find(lang_code); + if (it != m_fila_color_names.end() && !it->second.empty()) { return it->second; } + + it = m_fila_color_names.find("en");// retry with English as fallback + return (it != m_fila_color_names.end()) ? it->second : "Unknown"; +} + +FilamentColorCode::FilamentColorCode(const wxString& color_code, FilamentColorCodes* owner, FilamentColor&& color, std::unordered_map&& name_map) + : m_fila_color_code(color_code), + m_owner(owner), + m_fila_color(std::move(color)), + m_fila_color_names(std::move(name_map)) +{ +} + +void FilamentColorCode::Debug(const char* prefix) +{ + BOOST_LOG_TRIVIAL(debug) << prefix << "Fila Color Code: " << m_fila_color_code + << ", Colors: " << m_fila_color.ColorCount() + << ", Type: " << static_cast(m_fila_color.m_color_type); + for (const auto& color : m_fila_color.m_colors) { BOOST_LOG_TRIVIAL(debug) << prefix << " Color: " << _ColourToString(color); } + //for (const auto& name_pair : m_fila_color_names) { BOOST_LOG_TRIVIAL(debug) << prefix << " Color Name [" << name_pair.first << "]: " << name_pair.second;} +} + +FilamentColorCodes::FilamentColorCodes(const wxString& fila_id, const wxString& fila_type) + : m_fila_id(fila_id), m_fila_type(fila_type) +{ + m_fila_colors_map = new FilamentColor2CodeMap; +} + +FilamentColorCodes::~FilamentColorCodes() +{ + for (auto iter : *m_fila_colors_map) { delete iter.second; } + + m_fila_colors_map->clear(); + delete m_fila_colors_map; +} + +Slic3r::FilamentColorCode* FilamentColorCodes::GetColorCode(const FilamentColor& colors) const +{ + const auto& it = m_fila_colors_map->find(colors); + return (it != m_fila_colors_map->end()) ? it->second : nullptr; +} + +void FilamentColorCodes::AddColorCode(FilamentColorCode* code) +{ + m_fila_colors_map->emplace(code->GetFilaColor(), code); +} + +void FilamentColorCodes::Debug(const char* prefix) +{ + BOOST_LOG_TRIVIAL(debug) << prefix << "Fila ID: " << m_fila_id << ", Type: " << m_fila_type; + + auto iter = m_fila_colors_map->begin(); + while (iter != m_fila_colors_map->end()) + { + iter->second->Debug(prefix); + iter++; + } + + BOOST_LOG_TRIVIAL(debug) << prefix << "End"; +} + + + +} // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/EncodedFilament.hpp b/src/slic3r/GUI/EncodedFilament.hpp new file mode 100644 index 0000000..d65b000 --- /dev/null +++ b/src/slic3r/GUI/EncodedFilament.hpp @@ -0,0 +1,227 @@ +#ifndef slic3r_ENCODED_FILAMENT_hpp_ +#define slic3r_ENCODED_FILAMENT_hpp_ + +#include +#include +#include +#include + +#include +#include + +#include + +namespace Slic3r +{ + +//Previous definitions +class FilamentColorCode; +class FilamentColorCodes; +class FilamentColorCodeQuery; + +// Represents a color in HSV format +struct ColourHSV +{ + double h, s, v; +}; + +inline ColourHSV wxColourToHSV(const wxColour& c) +{ + double r = c.Red() / 255.0; + double g = c.Green() / 255.0; + double b = c.Blue() / 255.0; + + double maxc = std::max({ r, g, b }); + double minc = std::min({ r, g, b }); + double delta = maxc - minc; + + double h = 0, s = 0, v = maxc; + if (delta > 0.00001) { + if (maxc == r) { + h = 60.0 * (fmod(((g - b) / delta), 6.0)); + } else if (maxc == g) { + h = 60.0 * (((b - r) / delta) + 2.0); + } else { + h = 60.0 * (((r - g) / delta) + 4.0); + } + + if (h < 0) h += 360.0; + s = delta / maxc; + } else { + h = 0; + s = 0; + } + return { h, s, v }; +} + +// FilamentColorSorter +struct wxColorSorter +{ + std::size_t operator()(const wxColour& lhs_it, const wxColour& rhs_it) const noexcept { + ColourHSV ha = wxColourToHSV(lhs_it); + ColourHSV hb = wxColourToHSV(rhs_it); + if (ha.h != hb.h) return ha.h < hb.h; + if (ha.s != hb.s) return ha.s < hb.s; + if (ha.v != hb.v) return ha.v < hb.v; + if (lhs_it.Alpha() != rhs_it.Alpha()) return lhs_it.Alpha() < rhs_it.Alpha(); + return false; + } +}; + +struct FilamentColor +{ + enum class ColorType : char + { + SINGLE_CLR = 0, // single color filament + MULTI_CLR, // multi-color filament + GRADIENT_CLR, // gradient filament + }; + + ColorType m_color_type = ColorType::SINGLE_CLR; // default to single color + std::set m_colors; + +public: + size_t ColorCount() const noexcept { return m_colors.size(); } + + void EndSet(int ctype) + { + if (m_colors.size() < 2) + { + m_color_type = ColorType::SINGLE_CLR; + } + else + { + if (ctype == 0) + { + m_color_type = ColorType::GRADIENT_CLR; + } + else + { + m_color_type = ColorType::MULTI_CLR; + } + } + } + +public: + bool operator<(const FilamentColor& other) const { + if (ColorCount() != other.ColorCount()) { return ColorCount() < other.ColorCount(); }; + if (m_color_type != other.m_color_type) { return m_color_type < other.m_color_type; } + if (m_colors == other.m_colors) { return false;} + + // Compare colors in HSV format + auto lhs_it = m_colors.begin(); + auto rhs_it = other.m_colors.begin(); + while ((lhs_it != m_colors.end())) + { + ColourHSV ha = wxColourToHSV(*lhs_it); + ColourHSV hb = wxColourToHSV(*rhs_it); + if (ha.h != hb.h) return ha.h < hb.h; + if (ha.s != hb.s) return ha.s < hb.s; + if (ha.v != hb.v) return ha.v < hb.v; + + int lhs_alpha = lhs_it->Alpha(); + int rhs_alpha = rhs_it->Alpha(); + if (lhs_alpha != rhs_alpha) return lhs_alpha < rhs_alpha; + + lhs_it++; + rhs_it++; + } + + return false; + } +}; + +// Compare function for EncodedFilaColor +struct EncodedFilaColorEqual +{ + bool operator()(const FilamentColor& lhs, const FilamentColor& rhs) const noexcept { return lhs < rhs; } +}; +using FilamentColor2CodeMap = std::map; + + +// FilamentColorCodeQuery class is used to query filament color codes and their associated information +class FilamentColorCodeQuery +{ +public: + FilamentColorCodeQuery(); + virtual ~FilamentColorCodeQuery(); + +public: + FilamentColorCodes* GetFilaInfoMap(const wxString& fila_id) const; + wxString GetFilaColorName(const wxString& fila_id, const FilamentColor& colors) const; + +private: + FilamentColorCode* GetFilaInfo(const wxString& fila_id, const FilamentColor& colors) const; + +protected: + void LoadFromLocal(); + +public: + void CreateFilaCode(const wxString& fila_id, + const wxString& fila_type, + const wxString& fila_color_code, + FilamentColor&& fila_color, + std::unordered_map&& fila_color_names); + +private: + /* loaded info*/ + std::string m_fila_path; + + std::unordered_map* m_fila_id2colors_map; // +}; + +// EncodedFilaColorsInfo class holds a mapping of filament codes to specific filamet type +class FilamentColorCodes +{ +public: + FilamentColorCodes(const wxString& fila_id, const wxString& fila_type); + virtual ~FilamentColorCodes(); + +public: + wxString GetFilaCode() const { return m_fila_id; } + wxString GetFilaType() const { return m_fila_type; } + + FilamentColor2CodeMap* GetFilamentColor2CodeMap() const { return m_fila_colors_map; } + FilamentColorCode* GetColorCode(const FilamentColor& colors) const; + + void Debug(const char* prefix); + +public: + void AddColorCode(FilamentColorCode* code); + +private: + wxString m_fila_id;//eg. 54600 + wxString m_fila_type;//eg. PEBA 90A + FilamentColor2CodeMap* m_fila_colors_map; // key is the color set, value is the info +}; + +// The EncodedFilaColorInfo class holds information about a specific filament color +class FilamentColorCode +{ +public: + FilamentColorCode() = delete; + FilamentColorCode(const wxString& color_code, FilamentColorCodes* owner, FilamentColor&& color, std::unordered_map&& name_map); + ~FilamentColorCode() {}; + +public: + wxString GetFilaCode() const { return m_owner->GetFilaCode(); } + wxString GetFilaType() const { return m_owner->GetFilaType(); } + + + wxString GetFilaColorCode() const { return m_fila_color_code; } // eg. Q01B00 + FilamentColor GetFilaColor() const { return m_fila_color; } + wxString GetFilaColorName() const; + + void Debug(const char* prefix); + +private: + FilamentColorCodes* m_owner; + + /* color info*/ + wxString m_fila_color_code; // eg. Q01B00 + FilamentColor m_fila_color; + std::unordered_map m_fila_color_names; // eg. en -> Red +}; + +} +#endif \ No newline at end of file diff --git a/src/slic3r/GUI/Field.cpp b/src/slic3r/GUI/Field.cpp index 35a13b0..f8a41f6 100644 --- a/src/slic3r/GUI/Field.cpp +++ b/src/slic3r/GUI/Field.cpp @@ -1215,7 +1215,9 @@ void Choice::register_dynamic_list(std::string const &optname, DynamicList *list void DynamicList::update() { - for (auto c : m_choices) apply_on(c); + for (auto c : m_choices) { + apply_on(c); + } } void DynamicList::add_choice(Choice *choice) @@ -1520,7 +1522,8 @@ void Choice::set_value(const boost::any& value, bool change_event) if (m_opt_id.compare("host_type") == 0 && val != 0 && m_opt.enum_values.size() > field->GetCount()) // for case, when PrusaLink isn't used as a HostType val--; - if (m_opt_id == "top_surface_pattern" || m_opt_id == "bottom_surface_pattern" || m_opt_id == "internal_solid_infill_pattern" || m_opt_id == "sparse_infill_pattern" || m_opt_id == "support_style" || m_opt_id == "curr_bed_type") + if (m_opt_id == "top_surface_pattern" || m_opt_id == "bottom_surface_pattern" || m_opt_id == "internal_solid_infill_pattern" || m_opt_id == "sparse_infill_pattern" || + m_opt_id == "support_style" || m_opt_id == "curr_bed_type" || m_opt_id == "locked_skin_infill_pattern" || m_opt_id == "locked_skeleton_infill_pattern") { std::string key; const t_config_enum_values& map_names = *m_opt.enum_keys_map; @@ -1607,8 +1610,8 @@ boost::any& Choice::get_value() { if (m_opt.nullable && field->GetSelection() == -1) m_value = ConfigOptionEnumsGenericNullable::nil_value(); - else if (m_opt_id == "top_surface_pattern" || m_opt_id == "bottom_surface_pattern" || m_opt_id == "internal_solid_infill_pattern" || m_opt_id == "sparse_infill_pattern" || - m_opt_id == "support_style" || m_opt_id == "curr_bed_type") { + else if (m_opt_id == "top_surface_pattern" || m_opt_id == "bottom_surface_pattern" || m_opt_id == "internal_solid_infill_pattern" || m_opt_id == "sparse_infill_pattern" || m_opt_id == "support_style" || m_opt_id == "curr_bed_type" || m_opt_id == "locked_skin_infill_pattern" || + m_opt_id == "locked_skeleton_infill_pattern") { const std::string& key = m_opt.enum_values[field->GetSelection()]; m_value = int(m_opt.enum_keys_map->at(key)); } diff --git a/src/slic3r/GUI/FilamentBitmapUtils.cpp b/src/slic3r/GUI/FilamentBitmapUtils.cpp new file mode 100644 index 0000000..4f4b9f8 --- /dev/null +++ b/src/slic3r/GUI/FilamentBitmapUtils.cpp @@ -0,0 +1,184 @@ +#include +#include +#include +#include + +#include "EncodedFilament.hpp" + +namespace Slic3r { namespace GUI { + +// Helper struct to hold bitmap and DC +struct BitmapDC { + wxBitmap bitmap; + wxMemoryDC dc; + + BitmapDC(const wxSize& size) : bitmap(size), dc(bitmap) { + // Don't set white background - let the color patterns fill the entire area + dc.SetPen(*wxTRANSPARENT_PEN); + } +}; + +static BitmapDC init_bitmap_dc(const wxSize& size) { + return BitmapDC(size); +} + +// Sort colors by HSV values (primarily by hue, then saturation, then value) +static void sort_colors_by_hsv(std::vector& colors) { + if (colors.size() < 2) return; + std::sort(colors.begin(), colors.end(), + [](const wxColour& a, const wxColour& b) { + ColourHSV ha = wxColourToHSV(a); + ColourHSV hb = wxColourToHSV(b); + if (ha.h != hb.h) return ha.h < hb.h; + if (ha.s != hb.s) return ha.s < hb.s; + return ha.v < hb.v; + }); +} + +static wxBitmap create_single_filament_bitmap(const wxColour& color, const wxSize& size) +{ + BitmapDC bdc = init_bitmap_dc(size); + if (!bdc.dc.IsOk()) return wxNullBitmap; + + bdc.dc.SetBrush(wxBrush(color)); + bdc.dc.DrawRectangle(0, 0, size.GetWidth(), size.GetHeight()); + + // Add gray border for light colors (similar to wxExtensions.cpp logic) + if (color.Red() > 224 && color.Blue() > 224 && color.Green() > 224) { + bdc.dc.SetPen(*wxGREY_PEN); + bdc.dc.SetBrush(*wxTRANSPARENT_BRUSH); + bdc.dc.DrawRectangle(0, 0, size.GetWidth(), size.GetHeight()); + } + + bdc.dc.SelectObject(wxNullBitmap); + return bdc.bitmap; +} + +static wxBitmap create_dual_filament_bitmap(const wxColour& color1, const wxColour& color2, const wxSize& size) +{ + BitmapDC bdc = init_bitmap_dc(size); + + int half_width = size.GetWidth() / 2; + + bdc.dc.SetBrush(wxBrush(color1)); + bdc.dc.DrawRectangle(0, 0, half_width, size.GetHeight()); + + bdc.dc.SetBrush(wxBrush(color2)); + bdc.dc.DrawRectangle(half_width, 0, size.GetWidth() - half_width, size.GetHeight()); + + bdc.dc.SelectObject(wxNullBitmap); + return bdc.bitmap; +} + +static wxBitmap create_triple_filament_bitmap(const std::vector& colors, const wxSize& size) +{ + BitmapDC bdc = init_bitmap_dc(size); + + int third_width = size.GetWidth() / 3; + int remaining_width = size.GetWidth() - (third_width * 2); + + // Draw three vertical sections + bdc.dc.SetBrush(wxBrush(colors[0])); + bdc.dc.DrawRectangle(0, 0, third_width, size.GetHeight()); + + bdc.dc.SetBrush(wxBrush(colors[1])); + bdc.dc.DrawRectangle(third_width, 0, third_width, size.GetHeight()); + + bdc.dc.SetBrush(wxBrush(colors[2])); + bdc.dc.DrawRectangle(third_width * 2, 0, remaining_width, size.GetHeight()); + + bdc.dc.SelectObject(wxNullBitmap); + return bdc.bitmap; +} + +static wxBitmap create_quadruple_filament_bitmap(const std::vector& colors, const wxSize& size) +{ + BitmapDC bdc = init_bitmap_dc(size); + + int half_width = (size.GetWidth() + 1) / 2; + int half_height = (size.GetHeight() + 1) / 2; + + const int rects[4][4] = { + {0, 0, half_width, half_height}, // Top left + {half_width, 0, size.GetWidth() - half_width, half_height}, // Top right + {0, half_height, half_width, size.GetHeight() - half_height}, // Bottom left + {half_width, half_height, size.GetWidth() - half_width, size.GetHeight() - half_height} // Bottom right + }; + + for (int i = 0; i < 4; i++) { + bdc.dc.SetBrush(wxBrush(colors[i])); + bdc.dc.DrawRectangle(rects[i][0], rects[i][1], rects[i][2], rects[i][3]); + } + + bdc.dc.SelectObject(wxNullBitmap); + return bdc.bitmap; +} + +static wxBitmap create_gradient_filament_bitmap(const std::vector& colors, const wxSize& size) +{ + BitmapDC bdc = init_bitmap_dc(size); + + if (colors.size() == 1) { + return create_single_filament_bitmap(colors[0], size); + } + + // use segment gradient, make transition more natural + wxDC& dc = bdc.dc; + int total_width = size.GetWidth(); + int height = size.GetHeight(); + + // calculate segment count + int segment_count = colors.size() - 1; + double segment_width = (double)total_width / segment_count; + + int left = 0; + for (int i = 0; i < segment_count; i++) { + int current_width = (int)segment_width; + + // handle last segment, ensure fully filled + if (i == segment_count - 1) { + current_width = total_width - left; + } + + // avoid width exceed boundary + if (left + current_width > total_width) { + current_width = total_width - left; + } + + if (current_width > 0) { + auto rect = wxRect(left, 0, current_width, height); + dc.GradientFillLinear(rect, colors[i], colors[i + 1], wxEAST); + left += current_width; + } + } + + bdc.dc.SelectObject(wxNullBitmap); + return bdc.bitmap; +} + +wxBitmap create_filament_bitmap(const std::vector& colors, const wxSize& size, bool force_gradient) +{ + if (colors.empty()) return wxNullBitmap; + + // Make a copy to sort without modifying original + std::vector sorted_colors = colors; + + // Sort colors by HSV when there are 2 or more colors + if (sorted_colors.size() >= 2) { + sort_colors_by_hsv(sorted_colors); + } + + if (force_gradient && sorted_colors.size() >= 2) { + return create_gradient_filament_bitmap(sorted_colors, size); + } + + switch (sorted_colors.size()) { + case 1: return create_single_filament_bitmap(sorted_colors[0], size); + case 2: return create_dual_filament_bitmap(sorted_colors[0], sorted_colors[1], size); + case 3: return create_triple_filament_bitmap(sorted_colors, size); + case 4: return create_quadruple_filament_bitmap(sorted_colors, size); + default: return create_gradient_filament_bitmap(sorted_colors, size); + } +} + +}} // namespace Slic3r::GUI \ No newline at end of file diff --git a/src/slic3r/GUI/FilamentBitmapUtils.hpp b/src/slic3r/GUI/FilamentBitmapUtils.hpp new file mode 100644 index 0000000..b949522 --- /dev/null +++ b/src/slic3r/GUI/FilamentBitmapUtils.hpp @@ -0,0 +1,26 @@ +#ifndef slic3r_GUI_FilamentBitmapUtils_hpp_ +#define slic3r_GUI_FilamentBitmapUtils_hpp_ + +#include +#include +#include + +namespace Slic3r { namespace GUI { + +enum class FilamentRenderMode { + Single, + Dual, + Triple, + Quadruple, + Gradient +}; + +// Create a colour swatch bitmap. The render mode is chosen automatically from the +// number of colours unless force_gradient is true. +wxBitmap create_filament_bitmap(const std::vector& colors, + const wxSize& size, + bool force_gradient = false); + +}} // namespace Slic3r::GUI + +#endif // slic3r_GUI_FilamentBitmapUtils_hpp_ \ No newline at end of file diff --git a/src/slic3r/GUI/FilamentMapDialog.cpp b/src/slic3r/GUI/FilamentMapDialog.cpp index 9396296..2a28218 100644 --- a/src/slic3r/GUI/FilamentMapDialog.cpp +++ b/src/slic3r/GUI/FilamentMapDialog.cpp @@ -116,7 +116,7 @@ static const StateColor btn_bg_blue(std::pair(wxColour(40, 90, 22 static const StateColor btn_bd_blue(std::pair(wxColour(68, 121, 251), StateColor::Normal)); -static const StateColor btn_text_green(std::pair(wxColour(255, 255, 254), StateColor::Normal)); +static const StateColor btn_text_blue(std::pair(wxColour(255, 255, 254), StateColor::Normal)); static const StateColor btn_bg_white(std::pair(wxColour(206, 206, 206), StateColor::Pressed), std::pair(wxColour(238, 238, 238), StateColor::Hovered), @@ -222,7 +222,7 @@ FilamentMapDialog::FilamentMapDialog(wxWindow *parent, m_ok_btn->SetBackgroundColor(btn_bg_blue); m_ok_btn->SetBorderColor(btn_bd_blue); - m_ok_btn->SetTextColor(btn_text_green); + m_ok_btn->SetTextColor(btn_text_blue); m_cancel_btn->SetBackgroundColor(btn_bg_white); m_cancel_btn->SetBorderColor(btn_bd_white); m_cancel_btn->SetTextColor(btn_text_white); diff --git a/src/slic3r/GUI/FilamentPickerDialog.cpp b/src/slic3r/GUI/FilamentPickerDialog.cpp new file mode 100644 index 0000000..e8aeec4 --- /dev/null +++ b/src/slic3r/GUI/FilamentPickerDialog.cpp @@ -0,0 +1,733 @@ +#include "FilamentPickerDialog.hpp" +#include "GUI.hpp" +#include "I18N.hpp" +#include "GUI_App.hpp" +#include "MainFrame.hpp" +#include "EncodedFilament.hpp" +#include "Widgets/Label.hpp" +#include "Widgets/Button.hpp" +#include "Widgets/StateColor.hpp" +#include "wxExtensions.hpp" +#include +#include +#include +#include + +#define COLOR_DEMO_SIZE wxSize(FromDIP(50), FromDIP(50)) +#define COLOR_BTN_BITMAP_SIZE wxSize(FromDIP(24), FromDIP(24)) +#define COLOR_BTN_SIZE wxSize(FromDIP(30), FromDIP(30)) +#define GRID_GAP FromDIP(2) +#define COLS 9 // fixed column count +#define MAX_VISIBLE_ROWS 7 // max rows before scrollbar appears + +namespace Slic3r { namespace GUI { + +wxColour FilamentPickerDialog::GetSelectedColour() const +{ + if (!m_color_demo) return wxNullColour; + return m_color_demo->GetBackgroundColour(); +} + +void FilamentPickerDialog::on_dpi_changed(const wxRect &suggested_rect) +{ + // Handle DPI change + CreateShapedBitmap(); + SetWindowShape(); + Refresh(); + Layout(); +} + +FilamentPickerDialog::FilamentPickerDialog(wxWindow *parent, const wxString& fila_id, const FilamentColor& fila_color, const std::string& fila_type) + : DPIDialog(parent ? parent : wxGetApp().mainframe, + wxID_ANY, + _L("Select Filament"), + wxDefaultPosition, + wxDefaultSize, + wxBORDER_NONE | wxFRAME_NO_TASKBAR | wxFRAME_SHAPED) +{ + SetBackgroundColour(wxColour(255, 255, 255)); + + m_color_query = new FilamentColorCodeQuery(); + m_is_data_loaded = LoadFilamentData(fila_id); + m_cur_filament_color = fila_color; + wxString color_name = m_color_query->GetFilaColorName(fila_id, fila_color); + m_cur_color_name = new wxString(color_name); + + wxBoxSizer *container_sizer = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer *main_sizer = new wxBoxSizer(wxVERTICAL); + + // Preview panel (always present) + wxBoxSizer *preview_sizer = CreatePreviewPanel(fila_color, fila_type); + main_sizer->AddSpacer(FromDIP(4)); + main_sizer->Add(preview_sizer, 0, wxEXPAND, 0); + main_sizer->AddSpacer(FromDIP(12)); + + wxBoxSizer *line_sizer = CreateSeparatorLine(); + main_sizer->Add(line_sizer, 0, wxEXPAND, 0); + + // If caller passed an initial colour, reflect it in preview box. + if (m_is_data_loaded) { + // Colour grid with all filaments + wxScrolledWindow* color_grid = CreateColorGrid(); + main_sizer->Add(color_grid, 0, wxEXPAND | wxTOP | wxBOTTOM, FromDIP(8)); + } + + // "More colours" button (always present) + CreateMoreInfoButton(); + main_sizer->Add(m_more_btn, 0, wxEXPAND | wxTOP | wxBOTTOM, FromDIP(8)); + main_sizer->AddSpacer(FromDIP(8)); + + // OK / Cancel buttons + wxBoxSizer* btn_sizer = CreateButtonPanel(); + main_sizer->Add(btn_sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(10)); + container_sizer->Add(main_sizer, 1, wxEXPAND | wxALL, FromDIP(16)); + + SetSizer(container_sizer); + Layout(); + container_sizer->Fit(this); + + // Position the dialog relative to the parent window + if (GetParent()) { + // Align the dialog with the sidebar + auto& sidebar = wxGetApp().sidebar(); + wxPoint sidebar_pos = sidebar.GetScreenPosition(); + wxSize sidebar_size = sidebar.GetSize(); + + wxPoint new_pos( + sidebar_pos.x + sidebar_size.GetWidth() + FromDIP(10), + sidebar_pos.y + FromDIP(80) + ); + SetPosition(new_pos); + } else { + Centre(wxBOTH); // If no parent window, center the dialog + } + + // Create shaped window after sizing + CreateShapedBitmap(); +#ifndef __WXGTK__ + // Windows and macOS can set shape immediately + SetWindowShape(); +#endif +#ifdef __WXGTK__ + // GTK platform needs to wait for window creation + Bind(wxEVT_CREATE, &FilamentPickerDialog::OnWindowCreate, this); +#endif + + wxGetApp().UpdateDlgDarkUI(this); + Layout(); + // Set window transparency + SetTransparent(255); + BindEvents(); +} + +FilamentPickerDialog::~FilamentPickerDialog() +{ + delete m_color_query; + m_color_query = nullptr; + + delete m_cur_color_name; + m_cur_color_name = nullptr; +} + +void FilamentPickerDialog::CreateShapedBitmap() +{ + wxSize size = GetSize(); + if (size.GetWidth() <= 0 || size.GetHeight() <= 0) { + return; + } + + // Create a bitmap with alpha channel + m_shape_bmp.Create(size.GetWidth(), size.GetHeight(), 32); + + wxMemoryDC dc; + dc.SelectObject(m_shape_bmp); + + dc.SetBackground(wxBrush(wxColour(0, 0, 0))); + dc.Clear(); + + // Draw main white shape on top, positioned to let shadow show through + dc.SetBrush(wxBrush(wxColour(255, 255, 255, 255))); + dc.SetPen(*wxTRANSPARENT_PEN); + dc.DrawRoundedRectangle(0, 0, + size.GetWidth(), + size.GetHeight(), + FromDIP(m_corner_radius)); + + dc.SelectObject(wxNullBitmap); +} + +void FilamentPickerDialog::SetWindowShape() +{ + if (!m_shape_bmp.IsOk()) { + return; + } + + // Create a region from the bitmap using magenta as transparent mask color + wxRegion region(m_shape_bmp, wxColour(0, 0, 0)); + + if (region.IsOk()) { + SetShape(region); + } +} + +bool FilamentPickerDialog::LoadFilamentData(const wxString& fila_id) +{ + m_cur_color_codes = m_color_query->GetFilaInfoMap(fila_id); + + if (!m_cur_color_codes) { + BOOST_LOG_TRIVIAL(warning) << "No color codes found for filament ID: " << fila_id.ToStdString(); + return false; + } + + FilamentColor2CodeMap* color_map = m_cur_color_codes->GetFilamentColor2CodeMap(); + if (!color_map) { + BOOST_LOG_TRIVIAL(warning) << "No color map found for filament ID: " << fila_id.ToStdString(); + return false; + } + + BOOST_LOG_TRIVIAL(info) << "Successfully loaded " << color_map->size() << " color variants for filament " << fila_id.ToStdString(); + return !color_map->empty(); +} + +wxBoxSizer* FilamentPickerDialog::CreatePreviewPanel(const FilamentColor& fila_color, const std::string& fila_type) +{ + wxBoxSizer *preview_sizer = new wxBoxSizer(wxHORIZONTAL); + + // Create color preview bitmap + CreateColorBitmap(fila_color); + preview_sizer->Add(m_color_demo, 0, wxALIGN_CENTER_VERTICAL, 0); + preview_sizer->AddSpacer(FromDIP(12)); + + // Create info labels section + wxBoxSizer *label_sizer = CreateInfoSection(); + SetupLabelsContent(fila_color, fila_type); + preview_sizer->Add(label_sizer, 1, wxALIGN_CENTER_VERTICAL, 0); + + return preview_sizer; +} + +void FilamentPickerDialog::CreateColorBitmap(const FilamentColor &fila_color) +{ + m_color_demo = new wxStaticBitmap(this, wxID_ANY, wxNullBitmap, wxDefaultPosition, COLOR_DEMO_SIZE, 0); + + // Generate bitmap content + if (fila_color.ColorCount() > 0) { + std::vector wx_colors(fila_color.m_colors.begin(), fila_color.m_colors.end()); + wxBitmap init_bmp = create_filament_bitmap(wx_colors, COLOR_DEMO_SIZE, + fila_color.m_color_type == FilamentColor::ColorType::GRADIENT_CLR); + m_color_demo->SetBitmap(init_bmp); + } + else{ + std::vector wx_colors; + wx_colors.push_back(wxNullColour); + wxBitmap init_bmp = create_filament_bitmap(wx_colors, COLOR_DEMO_SIZE, false); + m_color_demo->SetBitmap(init_bmp); + } +} + +wxBoxSizer* FilamentPickerDialog::CreateInfoSection() +{ + wxBoxSizer *main_sizer = new wxBoxSizer(wxVERTICAL); + + // Create the container box + wxStaticBox *info_box = new wxStaticBox(this, wxID_ANY, wxEmptyString, wxDefaultPosition); + info_box->SetSize(wxSize(FromDIP(240), FromDIP(24))); + info_box->SetBackgroundColour(StateColor::darkModeColorFor(*wxWHITE)); + wxStaticBoxSizer *box_sizer = new wxStaticBoxSizer(info_box, wxHORIZONTAL); + + // Create labels with ellipsize style for text overflow + m_label_preview_color = new wxStaticText(this, wxID_ANY, _L("Custom Color"), + wxDefaultPosition, wxDefaultSize, + wxST_ELLIPSIZE_END); + m_label_preview_idx = new wxStaticText(this, wxID_ANY, _L(""), + wxDefaultPosition, wxDefaultSize); // No size limit, no ellipsis + m_label_preview_type = new wxStaticText(this, wxID_ANY, _L(""), + wxDefaultPosition, wxSize(FromDIP(220), FromDIP(16)), + wxST_ELLIPSIZE_END); + + // Set maximum width for color label to enable proper ellipsis behavior + m_label_preview_color->SetMaxSize(wxSize(FromDIP(160), -1)); + + // Setup fonts + wxFont bold_font = m_label_preview_color->GetFont(); + bold_font.SetWeight(wxFONTWEIGHT_BOLD); +#ifdef __WXMSW__ + bold_font.SetPointSize(FromDIP(8)); +#endif + m_label_preview_color->SetFont(bold_font); + m_label_preview_idx->SetFont(bold_font); + + m_label_preview_type->SetForegroundColour(wxColour(128, 128, 128)); + + // Layout with platform-specific spacing +#ifdef __WXMSW__ + int spacer = FromDIP(2), vPadding = FromDIP(0), gap1 = FromDIP(-6), gap2 = FromDIP(4); +#else + int spacer = FromDIP(0), vPadding = FromDIP(-1), gap1 = FromDIP(0), gap2 = FromDIP(2); +#endif + + box_sizer->AddSpacer(spacer); + box_sizer->Add(m_label_preview_color, 0, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, vPadding); + box_sizer->AddSpacer(FromDIP(2)); + box_sizer->Add(m_label_preview_idx, 0, wxALIGN_CENTER_VERTICAL | wxTOP | wxBOTTOM, vPadding); + box_sizer->AddSpacer(spacer); + + main_sizer->Add(box_sizer, 0, wxALIGN_CENTER_VERTICAL | wxTOP, gap1); + main_sizer->AddSpacer(gap2); + main_sizer->Add(m_label_preview_type, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(6)); + + return main_sizer; +} + +void FilamentPickerDialog::SetupLabelsContent(const FilamentColor &fila_color, const std::string &fila_type) +{ + m_label_preview_type->SetLabel(from_u8(fila_type)); + if (m_cur_color_name && !m_cur_color_name->IsEmpty()) { + m_label_preview_color->SetLabel(*m_cur_color_name); + + // Try to get additional color code information + if (m_cur_color_codes) { + FilamentColorCode *color_code = m_cur_color_codes->GetColorCode(fila_color); + if (color_code) { + m_label_preview_idx->SetLabel(wxString::Format("(%s)", color_code->GetFilaColorCode())); + } + } + } + else{ + if (fila_color.ColorCount() == 0){ + m_label_preview_color->SetLabel(_L("Null Color")); + } + else if (fila_color.ColorCount() == 1) { + m_label_preview_color->SetLabel(fila_color.m_colors.begin()->GetAsString(wxC2S_HTML_SYNTAX)); + } + else{ + m_label_preview_color->SetLabel(_L("Multiple Color")); + } + } +} + +wxBoxSizer* FilamentPickerDialog::CreateSeparatorLine() +{ + wxBoxSizer *line_sizer = new wxBoxSizer(wxHORIZONTAL); + wxPanel* separator_line = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, FromDIP(1))); + separator_line->SetBackgroundColour(wxColour(238,238,238)); + wxStaticText* line_text = new wxStaticText(this, wxID_ANY, _L("Official Filament"), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL); + line_text->SetForegroundColour(wxColour(128, 128, 128)); + line_sizer->Add(line_text, 0, wxEXPAND, 0); + line_sizer->AddSpacer(FromDIP(8)); + line_sizer->Add(separator_line, 1, wxALIGN_CENTER_VERTICAL, 0); + return line_sizer; +} + +wxScrolledWindow* FilamentPickerDialog::CreateColorGrid() +{ + if (!m_cur_color_codes) return nullptr; + + FilamentColor2CodeMap* color_map = m_cur_color_codes->GetFilamentColor2CodeMap(); + if (!color_map) return nullptr; + + // Calculate required row count + int total_colors = color_map->size(); + int needed_rows = (total_colors + COLS - 1) / COLS; // round-up division + bool need_scroll = needed_rows > MAX_VISIBLE_ROWS; + + // Create a vertical-only scrolled window + wxScrolledWindow* scroll_win = new wxScrolledWindow( + this, + wxID_ANY, + wxDefaultPosition, + wxDefaultSize, + wxVSCROLL | wxNO_BORDER + ); + + wxGridSizer* grid_sizer = new wxGridSizer(needed_rows, COLS, GRID_GAP, GRID_GAP); + + if (!color_map->empty()) { + for (const auto& color_pair : *color_map) { + const FilamentColor& fila_color = color_pair.first; // color info + FilamentColorCode* color_code = color_pair.second; // color code + + if (!color_code) continue; + std::vector wx_colors(fila_color.m_colors.begin(), fila_color.m_colors.end()); + wxBitmap btn_bmp = create_filament_bitmap( + wx_colors, + COLOR_BTN_BITMAP_SIZE, + fila_color.m_color_type == FilamentColor::ColorType::GRADIENT_CLR + ); + + if (!btn_bmp.IsOk()) { + BOOST_LOG_TRIVIAL(error) << "Failed to create bitmap for filament " << color_code->GetFilaColorCode().ToStdString(); + continue; + } + + wxBitmapButton* btn = new wxBitmapButton( + scroll_win, + wxID_ANY, + btn_bmp, + wxDefaultPosition, + COLOR_BTN_SIZE, + wxBU_EXACTFIT | wxNO_BORDER + ); + + if (btn) { + // Remove any default background and borders + btn->SetBackgroundColour(StateColor::darkModeColorFor(*wxWHITE)); + + // Set tooltip with filament information + wxString tooltip = wxString::Format("%s", color_code->GetFilaColorName()); + btn->SetToolTip(tooltip); + + // Check if this color matches the current color name and set as selected + bool is_matching_color = (m_cur_color_name && + !m_cur_color_name->IsEmpty() && + *m_cur_color_name == color_code->GetFilaColorName()); + + if (is_matching_color) { + m_cur_filament_color = color_code->GetFilaColor(); + m_cur_selected_btn = btn; + UpdatePreview(*color_code); + btn->Bind(wxEVT_PAINT, &FilamentPickerDialog::OnButtonPaint, this); + } + + // Bind click + btn->Bind(wxEVT_LEFT_DOWN, [this, btn, color_code](wxMouseEvent& evt) { + m_cur_filament_color = color_code->GetFilaColor(); + UpdatePreview(*color_code); + UpdateButtonStates(btn); + evt.Skip(); + }); + + // Hover highlight + btn->Bind(wxEVT_ENTER_WINDOW, [btn](wxMouseEvent& evt) { + evt.Skip(); + }); + + btn->Bind(wxEVT_LEAVE_WINDOW, [btn](wxMouseEvent& evt) { + evt.Skip(); + }); + + grid_sizer->Add(btn, 0, wxALL | wxALIGN_CENTER, FromDIP(1)); + } + } + } + + scroll_win->SetSizer(grid_sizer); + + if (need_scroll) { + int row_height = COLOR_BTN_SIZE.GetHeight() + FromDIP(2); + int col_width = COLOR_BTN_SIZE.GetWidth() + FromDIP(4); + + // Reserve space for vertical scrollbar so it doesn't overlay content + int scrollbar_width = wxSystemSettings::GetMetric(wxSYS_VSCROLL_X); + + // Set minimum visible area (including scrollbar width) + scroll_win->SetMinSize(wxSize(col_width * COLS + scrollbar_width, row_height * MAX_VISIBLE_ROWS)); + + // Let wxScrolledWindow calculate appropriate virtual size + scroll_win->FitInside(); + scroll_win->SetScrollRate(0, row_height); + } else { + scroll_win->FitInside(); + scroll_win->SetScrollRate(0, 0); + } + + return scroll_win; +} + +void FilamentPickerDialog::UpdatePreview(const FilamentColorCode& color_code) +{ + FilamentColor fila_color = color_code.GetFilaColor(); + + std::vector wx_colors(fila_color.m_colors.begin(), fila_color.m_colors.end()); + + // Update preview bitmap + wxBitmap bmp = create_filament_bitmap(wx_colors, COLOR_DEMO_SIZE, + fila_color.m_color_type == FilamentColor::ColorType::GRADIENT_CLR); + + if (bmp.IsOk()) { + BOOST_LOG_TRIVIAL(debug) << "Bitmap created successfully: " << bmp.GetWidth() << "x" << bmp.GetHeight(); + m_color_demo->SetBitmap(bmp); + if (!wx_colors.empty()) { + m_color_demo->SetBackgroundColour(wx_colors[0]); + } + m_color_demo->Refresh(); + } else { + BOOST_LOG_TRIVIAL(error) << "Failed to create bitmap"; + } + + // Update preview labels + m_label_preview_color->SetLabel(color_code.GetFilaColorName()); + m_label_preview_idx->SetLabel(wxString::Format("(%s)", color_code.GetFilaColorCode())); + Layout(); +} + +void FilamentPickerDialog::UpdateCustomColorPreview(const wxColour& custom_color) +{ + std::vector wx_colors = {custom_color}; + + // Update preview bitmap + wxBitmap bmp = create_filament_bitmap(wx_colors, COLOR_DEMO_SIZE, false); + + if (bmp.IsOk()) { + BOOST_LOG_TRIVIAL(debug) << "Custom color bitmap created successfully: " << bmp.GetWidth() << "x" << bmp.GetHeight(); + m_color_demo->SetBitmap(bmp); + m_color_demo->SetBackgroundColour(custom_color); + m_color_demo->Refresh(); + } else { + BOOST_LOG_TRIVIAL(error) << "Failed to create custom color bitmap"; + } + + // Update preview labels for custom color + m_label_preview_color->SetLabel(custom_color.GetAsString(wxC2S_HTML_SYNTAX)); + m_label_preview_idx->SetLabel(_L("")); + Layout(); +} + +void FilamentPickerDialog::UpdateButtonStates(wxBitmapButton* selected_btn) +{ + // Reset selected button appearance + if (m_cur_selected_btn) { + m_cur_selected_btn->SetBackgroundColour(StateColor::darkModeColorFor(*wxWHITE)); + m_cur_selected_btn->Unbind(wxEVT_PAINT, &FilamentPickerDialog::OnButtonPaint, this); + m_cur_selected_btn->Refresh(); + } + + if (selected_btn) { + // Bind paint event to draw custom green border + selected_btn->Bind(wxEVT_PAINT, &FilamentPickerDialog::OnButtonPaint, this); + selected_btn->Refresh(); + } + + m_cur_selected_btn = selected_btn; +} + +void FilamentPickerDialog::CreateMoreInfoButton() +{ + m_more_btn = new Button(this, "+ " + _L("More Colors")); + m_more_btn->SetMinSize(wxSize(-1, FromDIP(36))); + + StateColor btn_bg( + std::pair(wxColour(238, 238, 238), StateColor::Hovered), + std::pair(wxColour(248, 248, 248), StateColor::Normal) + ); + + + m_more_btn->SetBackgroundColor(btn_bg); + m_more_btn->SetBorderStyle(wxPENSTYLE_SHORT_DASH); + m_more_btn->SetCornerRadius(FromDIP(0)); +} + +wxBoxSizer* FilamentPickerDialog::CreateButtonPanel() +{ + wxBoxSizer* btn_sizer = new wxBoxSizer(wxHORIZONTAL); + + // Add spacer to push buttons to the right + btn_sizer->AddStretchSpacer(); + + // standard button color style + //y + StateColor btn_bg_blue(std::pair(wxColour(206, 206, 206), StateColor::Disabled), + std::pair(wxColour(40, 90, 220), StateColor::Pressed), + std::pair(wxColour(100, 150, 255), StateColor::Hovered), + std::pair(wxColour(68, 121, 251), StateColor::Normal)); + + StateColor btn_bd_green( + std::pair(wxColour(0, 174, 66), StateColor::Normal) + ); + StateColor btn_text_blue( + std::pair(wxColour(255, 255, 254), StateColor::Normal) + ); + + StateColor btn_bg_white( + std::pair(wxColour(206, 206, 206), StateColor::Pressed), + std::pair(wxColour(238, 238, 238), StateColor::Hovered), + std::pair(wxColour(255, 255, 255), StateColor::Normal) + ); + StateColor btn_bd_white( + std::pair(wxColour(38, 46, 48), StateColor::Normal) + ); + StateColor btn_text_white( + std::pair(wxColour(38, 46, 48), StateColor::Normal) + ); + + // Create Cancel button using project's Button class + m_cancel_btn = new Button(this, _L("Cancel"), "", 0, 0, wxID_CANCEL); + m_cancel_btn->SetMinSize(wxSize(FromDIP(55), FromDIP(24))); + m_cancel_btn->SetCornerRadius(FromDIP(12)); + m_cancel_btn->SetBackgroundColor(btn_bg_white); + m_cancel_btn->SetBorderColor(btn_bd_white); + m_cancel_btn->SetTextColor(btn_text_white); + btn_sizer->Add(m_cancel_btn, 0, wxEXPAND, 0); + btn_sizer->AddSpacer(FromDIP(10)); + + // Create OK button using project's Button class + m_ok_btn = new Button(this, _L("OK"), "", 0, 0, wxID_OK); + m_ok_btn->SetMinSize(wxSize(FromDIP(55), FromDIP(24))); + m_ok_btn->SetCornerRadius(FromDIP(12)); + m_ok_btn->SetBackgroundColor(btn_bg_blue); + m_ok_btn->SetBorderColor(btn_bd_green); + m_ok_btn->SetTextColor(btn_text_blue); + m_ok_btn->SetFocus(); + btn_sizer->Add(m_ok_btn, 0, wxEXPAND, 0); + + return btn_sizer; +} + +wxColourData FilamentPickerDialog::GetSingleColorData() +{ + wxColourData data; + data.SetChooseFull(true); + if (m_cur_filament_color.ColorCount() > 0) { + data.SetColour(*m_cur_filament_color.m_colors.begin()); + } + return data; +} + +void FilamentPickerDialog::BindEvents() +{ + // Bind mouse events + Bind(wxEVT_LEFT_DOWN, &FilamentPickerDialog::OnMouseLeftDown, this); + Bind(wxEVT_MOTION, &FilamentPickerDialog::OnMouseMove, this); + Bind(wxEVT_LEFT_UP, &FilamentPickerDialog::OnMouseLeftUp, this); + + // Add safety event handlers to ensure mouse capture is released + Bind(wxEVT_KILL_FOCUS, [this](wxFocusEvent& event) { + if (HasCapture()) { + ReleaseMouse(); + } + event.Skip(); + }); + + Bind(wxEVT_CLOSE_WINDOW, [this](wxCloseEvent& event) { + if (HasCapture()) { + ReleaseMouse(); + } + event.Skip(); + }); + + // Bind more colors button event + if (m_more_btn) { + m_more_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { + wxColourData original_data = GetSingleColorData(); + wxColourData result = show_sys_picker_dialog(this, original_data); + + // Check if user actually selected a different color + if (result.GetColour() != original_data.GetColour()) { + wxColour selected_color = result.GetColour(); + + // Update m_current_filament_color with the selected color + m_cur_filament_color.m_colors.clear(); + m_cur_filament_color.m_colors.insert(selected_color); + m_cur_filament_color.m_color_type = FilamentColor::ColorType::SINGLE_CLR; + + // Update preview + UpdateCustomColorPreview(selected_color); + + // Clear currently selected button since custom color selected + UpdateButtonStates(nullptr); + } + }); + } + + // Bind OK button event + if (m_ok_btn) { + m_ok_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { + EndModal(wxID_OK); + }); + } + + // Bind Cancel button event + if (m_cancel_btn) { + m_cancel_btn->Bind(wxEVT_BUTTON, [this](wxCommandEvent&) { + EndModal(wxID_CANCEL); + }); + } +} + +#ifdef __WXGTK__ +void FilamentPickerDialog::OnWindowCreate(wxWindowCreateEvent& event) +{ + // GTK platform needs to wait for window creation + SetWindowShape(); +} +#endif + +void FilamentPickerDialog::OnMouseLeftDown(wxMouseEvent& event) +{ + // Only allow dragging from empty areas (not from buttons or other controls) + wxWindow* hit_window = wxFindWindowAtPoint(ClientToScreen(event.GetPosition())); + if (hit_window && hit_window != this) { + // Click was on a child control, don't drag + event.Skip(); + return; + } + + // Release any existing capture first + if (HasCapture()) { + ReleaseMouse(); + } + + CaptureMouse(); + wxPoint pt = ClientToScreen(event.GetPosition()); + wxPoint origin = GetPosition(); + int dx = pt.x - origin.x; + int dy = pt.y - origin.y; + m_drag_delta = wxPoint(dx, dy); + + // Don't skip the event for dragging to work properly +} + +void FilamentPickerDialog::OnMouseMove(wxMouseEvent& event) +{ + wxPoint pt = event.GetPosition(); + + if (event.Dragging() && event.LeftIsDown() && HasCapture()) { + wxPoint pos = ClientToScreen(pt); + Move(wxPoint(pos.x - m_drag_delta.x, pos.y - m_drag_delta.y)); + } + + event.Skip(); +} + +void FilamentPickerDialog::OnMouseLeftUp(wxMouseEvent& event) +{ + if (HasCapture()) { + ReleaseMouse(); + } + + event.Skip(); +} + +void FilamentPickerDialog::OnButtonPaint(wxPaintEvent& event) +{ + wxWindow* button = dynamic_cast(event.GetEventObject()); + if (!button) { + event.Skip(); + return; + } + + // Create paint DC and let default painting happen first + wxPaintDC dc(button); + + //Clear the button with white background + dc.SetBrush(wxBrush(*wxTRANSPARENT_BRUSH)); + dc.SetPen(*wxTRANSPARENT_PEN); + dc.DrawRectangle(0, 0, COLOR_BTN_SIZE.GetWidth(), COLOR_BTN_SIZE.GetHeight()); + + // Draw the bitmap in the center + wxBitmapButton* bmpBtn = dynamic_cast(button); + if (bmpBtn && bmpBtn->GetBitmap().IsOk()) { + wxBitmap bmp = bmpBtn->GetBitmap(); + int x = (COLOR_BTN_SIZE.GetWidth() - COLOR_BTN_BITMAP_SIZE.GetWidth()) / 2; + int y = (COLOR_BTN_SIZE.GetHeight() - COLOR_BTN_BITMAP_SIZE.GetHeight()) / 2; + dc.DrawBitmap(bmp, x, y, true); + } + + // Draw the green border + dc.SetPen(wxPen(wxColour("#00AE42"), 2)); // Green pen, 2px thick + dc.SetBrush(*wxTRANSPARENT_BRUSH); + dc.DrawRectangle(1, 1, COLOR_BTN_SIZE.GetWidth() - 1, COLOR_BTN_SIZE.GetHeight() - 1); +} + +}} // namespace Slic3r::GUI + diff --git a/src/slic3r/GUI/FilamentPickerDialog.hpp b/src/slic3r/GUI/FilamentPickerDialog.hpp new file mode 100644 index 0000000..89c934e --- /dev/null +++ b/src/slic3r/GUI/FilamentPickerDialog.hpp @@ -0,0 +1,97 @@ +#ifndef slic3r_GUI_FilamentPickerDialog_hpp_ +#define slic3r_GUI_FilamentPickerDialog_hpp_ + +#include "GUI_App.hpp" +#include "GUI.hpp" +#include "GUI_Utils.hpp" +#include "FilamentBitmapUtils.hpp" +#include "Widgets/Button.hpp" +#include "EncodedFilament.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace Slic3r { namespace GUI { + +class FilamentPickerDialog : public DPIDialog +{ +public: + FilamentPickerDialog(wxWindow *parent, const wxString &fila_id, const FilamentColor &fila_color, const std::string &fila_type); + virtual ~FilamentPickerDialog(); + + // Public interface methods + bool IsDataLoaded() const { return m_is_data_loaded; } + wxColour GetSelectedColour() const; + const FilamentColor& GetSelectedFilamentColor() const { return m_cur_filament_color; } + +protected: + void on_dpi_changed(const wxRect &suggested_rect) override; + + // Event handlers +#ifdef __WXGTK__ + void OnWindowCreate(wxWindowCreateEvent& event); +#endif + void OnMouseLeftDown(wxMouseEvent& event); + void OnMouseMove(wxMouseEvent& event); + void OnMouseLeftUp(wxMouseEvent& event); + void OnButtonPaint(wxPaintEvent& event); + +private: + // UI creation methods + wxBoxSizer* CreatePreviewPanel(const FilamentColor& fila_color, const std::string& fila_type); + wxScrolledWindow* CreateColorGrid(); + wxBoxSizer* CreateSeparatorLine(); + void CreateMoreInfoButton(); + wxBoxSizer* CreateButtonPanel(); + void BindEvents(); + + // Preview panel helper methods + void CreateColorBitmap(const FilamentColor& fila_color); + wxBoxSizer* CreateInfoSection(); + void SetupLabelsContent(const FilamentColor& fila_color, const std::string& fila_type); + + // UI update methods + void UpdatePreview(const FilamentColorCode& filament); + void UpdateCustomColorPreview(const wxColour& custom_color); + void UpdateButtonStates(wxBitmapButton* selected_btn); + + // Shaped window methods + void SetWindowShape(); + void CreateShapedBitmap(); + + // Data loading + bool LoadFilamentData(const wxString& fila_id); + wxColourData GetSingleColorData(); + + // UI elements + wxStaticBitmap* m_color_demo{nullptr}; + wxStaticText* m_label_preview_color{nullptr}; + wxStaticText* m_label_preview_idx{nullptr}; + wxStaticText* m_label_preview_type{nullptr}; + Button* m_more_btn{nullptr}; + Button* m_ok_btn{nullptr}; + Button* m_cancel_btn{nullptr}; + + // Data members + bool m_is_data_loaded{false}; + wxString *m_cur_color_name{nullptr}; + FilamentColorCodeQuery* m_color_query{nullptr}; + FilamentColorCodes* m_cur_color_codes{nullptr}; + wxBitmapButton* m_cur_selected_btn{nullptr}; + FilamentColor m_cur_filament_color; + + // Shaped window members + wxBitmap m_shape_bmp; + int m_corner_radius{8}; + + // Mouse drag members + wxPoint m_drag_delta; +}; + +}} // namespace Slic3r::GUI + +#endif diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 13aa19a..234634f 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -547,11 +547,11 @@ void GCodeViewer::SequentialView::GCodeWindow::load_gcode(const std::string& fil try { m_file.open(boost::filesystem::path(m_filename)); - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": mapping file " << m_filename; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": mapping file " << PathSanitizer::sanitize(m_filename); } catch (...) { - BOOST_LOG_TRIVIAL(error) << "Unable to map file " << m_filename << ". Cannot show G-code window."; + BOOST_LOG_TRIVIAL(error) << "Unable to map file " << PathSanitizer::sanitize(m_filename) << ". Cannot show G-code window."; reset(); } } @@ -632,7 +632,7 @@ void GCodeViewer::SequentialView::GCodeWindow::render(float top, float bottom, f } catch (...) { - BOOST_LOG_TRIVIAL(error) << "Error while loading from file " << m_filename << ". Cannot show G-code window."; + BOOST_LOG_TRIVIAL(error) << "Error while loading from file " << PathSanitizer::sanitize(m_filename) << ". Cannot show G-code window."; return; } *const_cast(&m_selected_line_id) = curr_line_id; @@ -721,7 +721,7 @@ void GCodeViewer::SequentialView::GCodeWindow::stop_mapping_file() //QDS: add log to trace the gcode file issue if (m_file.is_open()) { m_file.close(); - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": finished mapping file " << m_filename; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": finished mapping file " << PathSanitizer::sanitize(m_filename); } } //QDS: GUI refactor: move to the right @@ -1007,7 +1007,9 @@ void GCodeViewer::load(const GCodeProcessorResult& gcode_result, const Print& pr } //QDS: add logs - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": gcode result %1%, new id %2%, gcode file %3% ") % (&gcode_result) % m_last_result_id % gcode_result.filename; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ + << boost::format(": gcode result %1%, new id %2%, gcode file %3% ") % (&gcode_result) % m_last_result_id % + PathSanitizer::sanitize(gcode_result.filename); // release gpu memory, if used reset(); @@ -1960,7 +1962,7 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const FILE* fp = boost::nowide::fopen(mat_filename.string().c_str(), "w"); if (fp == nullptr) { - BOOST_LOG_TRIVIAL(error) << "GCodeViewer::export_toolpaths_to_obj: Couldn't open " << mat_filename.string().c_str() << " for writing"; + BOOST_LOG_TRIVIAL(error) << "GCodeViewer::export_toolpaths_to_obj: Couldn't open " << PathSanitizer::sanitize(mat_filename) << " for writing"; return; } @@ -1980,7 +1982,7 @@ void GCodeViewer::export_toolpaths_to_obj(const char* filename) const // save geometry file fp = boost::nowide::fopen(filename, "w"); if (fp == nullptr) { - BOOST_LOG_TRIVIAL(error) << "GCodeViewer::export_toolpaths_to_obj: Couldn't open " << filename << " for writing"; + BOOST_LOG_TRIVIAL(error) << "GCodeViewer::export_toolpaths_to_obj: Couldn't open " << PathSanitizer::sanitize(filename) << " for writing"; return; } @@ -4268,7 +4270,8 @@ void GCodeViewer::render_all_plates_stats(const std::vectorget("use_inches") == "1"; float window_padding = 4.0f * m_scale; const float icon_size = ImGui::GetTextLineHeight() * 0.7; - std::map offsets; + std::map fil_table_offsets; + std::map time_est_table_offsets; std::map model_volume_of_extruders_all_plates; // map std::map flushed_volume_of_extruders_all_plates; // map std::map wipe_tower_volume_of_extruders_all_plates; // map @@ -4465,7 +4468,7 @@ void GCodeViewer::render_all_plates_stats(const std::vector> title_offsets; for (int i = 0; i < offsets_.size(); i++) { title_offsets.push_back({ title_columns[i].first, offsets_[i] }); - offsets[title_columns[i].first] = offsets_[i]; + fil_table_offsets[title_columns[i].first] = offsets_[i]; } append_headers(title_offsets); } @@ -4476,7 +4479,7 @@ void GCodeViewer::render_all_plates_stats(const std::vector> columns_offsets; - columns_offsets.push_back({ std::to_string(it->first + 1), offsets[_u8L("Filament")]}); + columns_offsets.push_back({ std::to_string(it->first + 1), fil_table_offsets[_u8L("Filament")] }); char buf[64]; @@ -4487,31 +4490,31 @@ void GCodeViewer::render_all_plates_stats(const std::vector 0) { ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", column_sum_m, column_sum_g / unit_conver); - columns_offsets.push_back({ buf, offsets[_u8L("Total")] }); + columns_offsets.push_back({ buf, fil_table_offsets[_u8L("Total")] }); } append_item(true, filament_colors[it->first], columns_offsets); @@ -4531,7 +4534,7 @@ void GCodeViewer::render_all_plates_stats(const std::vectorDrawList->AddLine(separator.Min, ImVec2(separator.Max.x, separator.Min.y), ImGui::GetColorU32(ImGuiCol_Separator)); std::vector> columns_offsets; - columns_offsets.push_back({_u8L("Total"), offsets[_u8L("Filament")]}); + columns_offsets.push_back({ _u8L("Total"), fil_table_offsets[_u8L("Filament")] }); double total_model_used_filament_m = 0; double total_model_used_filament_g = 0; double total_support_used_filament_m = 0; @@ -4551,7 +4554,7 @@ void GCodeViewer::render_all_plates_stats(const std::vector 0) { ::sprintf(buf, imperial_units ? "%.2f in\n%.2f oz" : "%.2f m\n%.2f g", total_model_used_filament_m + total_support_used_filament_m + total_flushed_filament_m + total_wipe_tower_used_filament_m, (total_model_used_filament_g + total_support_used_filament_g + total_flushed_filament_g + total_wipe_tower_used_filament_g) / unit_conver); - columns_offsets.push_back({buf, offsets[_u8L("Total")]}); + columns_offsets.push_back({ buf, fil_table_offsets[_u8L("Total")] }); } append_item(false, m_tools.m_tool_colors[0], columns_offsets); } @@ -4599,6 +4602,28 @@ void GCodeViewer::render_all_plates_stats(const std::vector time_labels; + std::vector time_values; + std::vector>> time_columns; + + for (auto it = plate_time.begin(); it != plate_time.end(); it++) { + time_labels.push_back(_u8L("Plate") + " " + std::to_string(it->first + 1)); + time_values.push_back(short_time(get_time_dhms(it->second))); + } + if (plate_time.size() > 1) { + time_labels.push_back(_u8L("Total")); + time_values.push_back(short_time(get_time_dhms(total_time_all_plates))); + } + + time_columns.push_back({_u8L("Plate"), time_labels}); + time_columns.push_back({_u8L("Time"), time_values}); + + auto time_offsets = calculate_offsets(time_columns); + for (int i = 0; i < time_offsets.size(); i++) { time_est_table_offsets[time_columns[i].first] = time_offsets[i]; } + } + ImGui::Dummy(ImVec2(0.0f, ImGui::GetFontSize() * 0.1)); ImGui::Dummy({ window_padding, window_padding }); ImGui::SameLine(); @@ -4606,8 +4631,8 @@ void GCodeViewer::render_all_plates_stats(const std::vector> columns_offsets; - columns_offsets.push_back({ _u8L("Plate") + " " + std::to_string(it->first + 1), offsets[_u8L("Filament")]}); - columns_offsets.push_back({ short_time(get_time_dhms(it->second)), offsets[_u8L("Model")] }); + columns_offsets.push_back({_u8L("Plate") + " " + std::to_string(it->first + 1), time_est_table_offsets[_u8L("Plate")]}); + columns_offsets.push_back({short_time(get_time_dhms(it->second)), time_est_table_offsets[_u8L("Time")]}); append_item(false, m_tools.m_tool_colors[0], columns_offsets); } @@ -4620,8 +4645,8 @@ void GCodeViewer::render_all_plates_stats(const std::vectorDrawList->AddLine(separator.Min, ImVec2(separator.Max.x, separator.Min.y), ImGui::GetColorU32(ImGuiCol_Separator)); std::vector> columns_offsets; - columns_offsets.push_back({ _u8L("Total"), offsets[_u8L("Filament")] }); - columns_offsets.push_back({ short_time(get_time_dhms(total_time_all_plates)), offsets[_u8L("Model")] }); + columns_offsets.push_back({_u8L("Total"), time_est_table_offsets[_u8L("Plate")]}); + columns_offsets.push_back({short_time(get_time_dhms(total_time_all_plates)), time_est_table_offsets[_u8L("Time")]}); append_item(false, m_tools.m_tool_colors[0], columns_offsets); } } @@ -6197,9 +6222,10 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv imgui.text(prepare_str + ":"); ImGui::SameLine(max_len); if (timelapse_time != 0.0f) - imgui.text(short_time(get_time_dhms(time_mode.prepare_time)) + " + " + short_time(get_time_dhms(timelapse_time))); + //y68 + imgui.text(short_time(get_time_dhms(time_mode.prepare_time + 600.0f)) + " + " + short_time(get_time_dhms(timelapse_time))); else - imgui.text(short_time(get_time_dhms(time_mode.prepare_time))); + imgui.text(short_time(get_time_dhms(time_mode.prepare_time + 600.0f))); } ImGui::Dummy({ window_padding, window_padding }); ImGui::SameLine(); @@ -6210,7 +6236,7 @@ void GCodeViewer::render_legend(float &legend_height, int canvas_width, int canv ImGui::SameLine(); imgui.text(total_str + ":"); ImGui::SameLine(max_len); - imgui.text(short_time(get_time_dhms(time_mode.time))); + imgui.text(short_time(get_time_dhms(time_mode.time + 600.0f))); //y68 auto show_mode_button = [this, &imgui, can_show_mode_button](const wxString& label, PrintEstimatedStatistics::ETimeMode mode) { if (can_show_mode_button(mode)) { diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index e6256dd..0277968 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -29,6 +29,7 @@ #include "slic3r/GUI/BitmapCache.hpp" #include "slic3r/Utils/MacDarkMode.hpp" +#include "GLToolbar.hpp" #include "GUI_App.hpp" #include "GUI_ObjectList.hpp" #include "GUI_Colors.hpp" @@ -157,6 +158,11 @@ std::string& get_right_extruder_unprintable_text() { return right_unprintable_text; } +std::string& get_nozzle_filament_incompatible_text() { + static std::string nozzle_filament_incompatible_text; + return nozzle_filament_incompatible_text; +} + static std::string format_number(float value) { std::ostringstream oss; @@ -284,7 +290,13 @@ void GLCanvas3D::LayersEditing::render_variable_layer_height_dialog(const GLCanv ImGuiWrapper& imgui = *wxGetApp().imgui(); const Size& cnv_size = canvas.get_canvas_size(); float zoom = (float) canvas.get_active_camera().get_zoom(); - float left_pos = canvas.m_main_toolbar.get_item("layersediting")->render_left_pos; + const auto& p_main_toolbar = canvas.get_main_toolbar(); + float left_pos = 0.0f; + float main_toolbar_height = 0.0f; + if (p_main_toolbar) { + left_pos = p_main_toolbar->get_item("layersediting")->render_rect[0]; + main_toolbar_height = p_main_toolbar->get_height(); + } float x = 0.5 * cnv_size.get_width() + left_pos * zoom; const auto canvas_width = cnv_size.get_width(); @@ -298,7 +310,7 @@ void GLCanvas3D::LayersEditing::render_variable_layer_height_dialog(const GLCanv } x = std::max(x, 0.0f); - imgui.set_next_window_pos(x, canvas.m_main_toolbar.get_height(), ImGuiCond_Always, 0.0f, 0.0f); + imgui.set_next_window_pos(x, main_toolbar_height, ImGuiCond_Always, 0.0f, 0.0f); imgui.push_toolbar_style(canvas.get_scale()); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f * canvas.get_scale(), 4.0f * canvas.get_scale())); @@ -380,7 +392,10 @@ void GLCanvas3D::LayersEditing::render_variable_layer_height_dialog(const GLCanv ImGui::Separator(); - float get_cur_y = ImGui::GetContentRegionMax().y + ImGui::GetFrameHeight() + canvas.m_main_toolbar.get_height(); + float get_cur_y = ImGui::GetContentRegionMax().y + ImGui::GetFrameHeight(); + if (p_main_toolbar) { + get_cur_y += p_main_toolbar->get_height(); + } std::map captions_texts = { {_L("Left mouse button:") ,_L("Add detail")}, {_L("Right mouse button:"), _L("Remove detail")}, @@ -1310,9 +1325,6 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed) , m_retina_helper(nullptr) #endif , m_in_render(false) - , m_main_toolbar(GLToolbar::Normal, "Main") - , m_separator_toolbar(GLToolbar::Normal, "Separator") - , m_assemble_view_toolbar(GLToolbar::Normal, "Assembly_View") , m_return_toolbar() , m_canvas_type(ECanvasType::CanvasView3D) , m_gizmos(*this) @@ -1433,7 +1445,7 @@ bool GLCanvas3D::init() glsafe(::glEnable(GL_MULTISAMPLE)); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": before m_layers_editing init"; - if (m_main_toolbar.is_enabled()) + if (m_canvas_type == ECanvasType::CanvasView3D) m_layers_editing.init(); // on linux the gl context is not valid until the canvas is not shown on screen @@ -1495,23 +1507,15 @@ void GLCanvas3D::on_change_color_mode(bool is_dark, bool reinit) { if (reinit) { // reset svg _switch_toolbars_icon_filename(); - m_gizmos.switch_gizmos_icon_filename(); - // set dirty to re-generate icon texture - m_separator_toolbar.set_icon_dirty(); - m_main_toolbar.set_icon_dirty(); + const auto& p_main_toolbar = get_main_toolbar(); + if (p_main_toolbar) { + p_main_toolbar->set_icon_dirty(); + } wxGetApp().plater()->get_collapse_toolbar().set_icon_dirty(); - m_assemble_view_toolbar.set_icon_dirty(); - m_gizmos.set_icon_dirty(); } } if (m_canvas_type == CanvasAssembleView) { m_gizmos.on_change_color_mode(is_dark); - if (reinit) { - // reset svg - m_gizmos.switch_gizmos_icon_filename(); - // set dirty to re-generate icon texture - m_gizmos.set_icon_dirty(); - } } } @@ -1800,10 +1804,8 @@ void GLCanvas3D::toggle_model_objects_visibility(bool visible, const ModelObject vol->force_native_color = false; vol->force_neutral_color = false; } else { - const GLGizmosManager& gm = get_gizmos_manager(); - auto gizmo_type = gm.get_current_type(); - if ( (gizmo_type == GLGizmosManager::FdmSupports - || gizmo_type == GLGizmosManager::Seam) + auto gizmo_type = m_gizmos.get_current_type(); + if (m_gizmos.is_paint_gizmo() && ! vol->is_modifier) vol->force_neutral_color = true; else if (gizmo_type == GLGizmosManager::BrimEars) @@ -1869,6 +1871,16 @@ void GLCanvas3D::set_model(Model* model) m_selection.set_model(m_model); } +const Selection& GLCanvas3D::get_selection() const +{ + return m_selection; +} + +Selection& GLCanvas3D::get_selection() +{ + return m_selection; +} + void GLCanvas3D::bed_shape_changed() { refresh_camera_scene_box(); @@ -1922,6 +1934,9 @@ BoundingBoxf3 GLCanvas3D::volumes_bounding_box(bool limit_to_expand_plate) const const auto v_bb = volume->transformed_bounding_box(); if (is_limit && !expand_part_plate_list_box.overlap(v_bb)) continue; + if (v_bb.max_size() > 100000) {//unit::mm more than 100m + continue; + } bb.merge(v_bb); } } @@ -2037,7 +2052,11 @@ void GLCanvas3D::enable_selection(bool enable) void GLCanvas3D::enable_main_toolbar(bool enable) { - m_main_toolbar.set_enabled(enable); + const auto& p_main_toolbar = get_main_toolbar(); + if (!p_main_toolbar) { + return; + } + p_main_toolbar->set_enabled(enable); } void GLCanvas3D::reset_select_plate_toolbar_selection() { @@ -2052,26 +2071,11 @@ void GLCanvas3D::enable_select_plate_toolbar(bool enable) m_sel_plate_toolbar.set_enabled(enable); } -void GLCanvas3D::clear_select_plate_toolbar_render_flag() -{ - m_sel_plate_toolbar.is_render_finish = false; -} - -void GLCanvas3D::enable_assemble_view_toolbar(bool enable) -{ - m_assemble_view_toolbar.set_enabled(enable); -} - void GLCanvas3D::enable_return_toolbar(bool enable) { m_return_toolbar.set_enabled(enable); } -void GLCanvas3D::enable_separator_toolbar(bool enable) -{ - m_separator_toolbar.set_enabled(enable); -} - void GLCanvas3D::enable_dynamic_background(bool enable) { m_dynamic_background_enabled = enable; @@ -2155,10 +2159,13 @@ int GLCanvas3D::get_main_toolbar_offset() const { const float cnv_width = get_canvas_size().get_width(); const float collapse_toolbar_width = get_collapse_toolbar_width() * 2; - const float gizmo_width = m_gizmos.get_scaled_total_width(); - const float assemble_width = m_assemble_view_toolbar.get_width(); - const float separator_width = m_separator_toolbar.get_width(); - const float toolbar_total_width = m_main_toolbar.get_width() + separator_width + gizmo_width + assemble_width + collapse_toolbar_width; + + float main_toolbar_width = 0.0f; + const auto& p_main_toolbar = get_main_toolbar(); + if (p_main_toolbar) { + main_toolbar_width = p_main_toolbar->get_width(); + } + const float toolbar_total_width = main_toolbar_width + collapse_toolbar_width; if (cnv_width < toolbar_total_width) { return is_collapse_toolbar_on_left() ? collapse_toolbar_width : 0; @@ -2172,6 +2179,33 @@ float GLCanvas3D::get_main_toolbar_left(int cnv_width,float inv_zoom) const { return (-0.5f * cnv_width + get_main_toolbar_offset()) * inv_zoom; } +int GLCanvas3D::get_main_toolbar_height() const +{ + const auto& p_main_toolbar = get_main_toolbar(); + if (!p_main_toolbar) { + return 0; + } + return p_main_toolbar->get_height(); +} + +int GLCanvas3D::get_main_toolbar_width() const +{ + const auto& p_main_toolbar = get_main_toolbar(); + if (!p_main_toolbar) { + return 0; + } + return p_main_toolbar->get_width(); +} + +float GLCanvas3D::get_main_toolbar_scale() const +{ + const auto& p_main_toolbar = get_main_toolbar(); + if (!p_main_toolbar) { + return 1.0f; + } + return p_main_toolbar->get_scale(); +} + bool GLCanvas3D::is_collapse_toolbar_on_left() const { auto state = wxGetApp().plater()->get_sidebar_docking_state(); @@ -2231,7 +2265,7 @@ void GLCanvas3D::render(bool only_init) if (m_canvas_type == ECanvasType::CanvasView3D && m_gizmos.get_current_type() == GLGizmosManager::Undefined) { enable_return_toolbar(false); } - if (!m_main_toolbar.is_enabled()) + if (m_canvas_type == ECanvasType::CanvasPreview) m_gcode_viewer.init(wxGetApp().get_mode(), wxGetApp().preset_bundle); if (! m_bed.build_volume().valid()) { @@ -2299,6 +2333,13 @@ void GLCanvas3D::render(bool only_init) return; } + if (m_main_toolbar) { + if (m_canvas_type == ECanvasType::CanvasView3D) { + const auto toolbar_style = p_ogl_manager->get_toolbar_rendering_style(); + m_main_toolbar->set_rendering_mode(static_cast(toolbar_style)); + } + } + auto& ogl_manager = *p_ogl_manager; ogl_manager.set_viewport_size(viewport[2], viewport[3]); @@ -2322,6 +2363,9 @@ void GLCanvas3D::render(bool only_init) if (!ogl_manager.are_framebuffers_supported()) { picking_effect = EPickingEffect::StencilOutline; // use stencil outline as framebuffer not supported yet. } + if (!m_gizmos.is_allow_show_volume_highlight_outline()) { + picking_effect = EPickingEffect::Disabled; + } } const bool off_screen_rendering_enabled = ogl_manager.is_fxaa_enabled(); @@ -2351,7 +2395,7 @@ void GLCanvas3D::render(bool only_init) bool only_current = false, only_body = false, show_axes = true, no_partplate = false; bool show_grid = true; GLGizmosManager::EType gizmo_type = m_gizmos.get_current_type(); - if (!m_main_toolbar.is_enabled() || m_gizmos.is_show_only_active_plate()) { + if (m_canvas_type != CanvasView3D || m_gizmos.is_show_only_active_plate()) { //only_body = true; if (m_gizmos.get_object_located_outside_plate()) { no_partplate = true; @@ -2359,8 +2403,13 @@ void GLCanvas3D::render(bool only_init) else { only_current = true; } - } - else if ((gizmo_type == GLGizmosManager::FdmSupports) || (gizmo_type == GLGizmosManager::Seam) || (gizmo_type == GLGizmosManager::MmuSegmentation)) + } else if (m_gizmos.get_current_type() == GLGizmosManager::EType::Text) { + if (m_gizmos.is_only_text_volume()) { + only_current = false; + } else { + only_current = true; + } + } else if (m_gizmos.is_paint_gizmo()) no_partplate = true; else if (gizmo_type == GLGizmosManager::BrimEars && !camera.is_looking_downward()) show_grid = false; @@ -2370,18 +2419,22 @@ void GLCanvas3D::render(bool only_init) bool b_with_stencil_outline = !m_gizmos.is_running() && (EPickingEffect::StencilOutline == picking_effect); if (m_canvas_type == ECanvasType::CanvasView3D) { //QDS: add outline logic - _render_objects(GLVolumeCollection::ERenderType::Opaque, b_with_stencil_outline); + _render_objects(m_volumes,GLVolumeCollection::ERenderType::Opaque, b_with_stencil_outline); + if (!m_paint_outline_volumes.empty()) { + _render_objects(m_paint_outline_volumes, GLVolumeCollection::ERenderType::Opaque, b_with_stencil_outline,true); + } _render_sla_slices(); _render_selection(); if (!no_partplate) _render_bed(!camera.is_looking_downward(), show_axes); if (!no_partplate) //QDS: add outline logic _render_platelist(!camera.is_looking_downward(), only_current, only_body, hover_id, true, show_grid); - _render_objects(GLVolumeCollection::ERenderType::Transparent, b_with_stencil_outline); + _render_objects(m_volumes, GLVolumeCollection::ERenderType::Transparent, b_with_stencil_outline); + _render_objects(m_paint_outline_volumes, GLVolumeCollection::ERenderType::Transparent, b_with_stencil_outline, true); } /* preview render */ else if (m_canvas_type == ECanvasType::CanvasPreview && m_render_preview) { - _render_objects(GLVolumeCollection::ERenderType::Opaque, b_with_stencil_outline); + _render_objects(m_volumes, GLVolumeCollection::ERenderType::Opaque, b_with_stencil_outline); _render_sla_slices(); _render_selection(); _render_bed(!camera.is_looking_downward(), show_axes); @@ -2395,13 +2448,13 @@ void GLCanvas3D::render(bool only_init) if (m_show_world_axes) { m_axes.render(); } - _render_objects(GLVolumeCollection::ERenderType::Opaque, b_with_stencil_outline); + _render_objects(m_volumes, GLVolumeCollection::ERenderType::Opaque, b_with_stencil_outline); //_render_bed(!camera.is_looking_downward(), show_axes); _render_plane(); //QDS: add outline logic insteadof selection under assemble view //_render_selection(); // QDS: add outline logic - _render_objects(GLVolumeCollection::ERenderType::Transparent, b_with_stencil_outline); + _render_objects(m_volumes, GLVolumeCollection::ERenderType::Transparent, b_with_stencil_outline); } if (m_picking_enabled && EPickingEffect::Silhouette == picking_effect) { @@ -2478,12 +2531,12 @@ void GLCanvas3D::render(bool only_init) if (tooltip.empty()) tooltip = m_gizmos.get_tooltip(); - if (tooltip.empty()) - tooltip = m_main_toolbar.get_tooltip(); - - //QDS: GUI refactor: GLToolbar - if (tooltip.empty()) - tooltip = m_assemble_view_toolbar.get_tooltip(); + if (tooltip.empty()) { + const auto& p_main_toolbar = get_main_toolbar(); + if (p_main_toolbar) { + tooltip = p_main_toolbar->get_tooltip(); + } + } if (tooltip.empty()) tooltip = wxGetApp().plater()->get_collapse_toolbar().get_tooltip(); @@ -2597,11 +2650,6 @@ void GLCanvas3D::remove_curr_plate_all() m_dirty = true; } -void GLCanvas3D::update_plate_thumbnails() -{ - _update_imgui_select_plate_toolbar(); -} - void GLCanvas3D::select_all() { if (!m_gizmos.is_allow_select_all()) { @@ -3209,6 +3257,30 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re /*!print->is_step_done(psWipeTower)*/ true, brim_width, m_initialized); int volume_idx_wipe_tower_old = volume_idxs_wipe_tower_old[plate_id]; if (volume_idx_wipe_tower_old != -1) map_glvolume_old_to_new[volume_idx_wipe_tower_old] = volume_idx_wipe_tower_new; + + //y68 + // update wipe_tower pos + { + bool need_update = false; + if (x + margin + wipe_tower_size(0) > plate_bbox_x_max_local_coord) { + x = plate_bbox_x_max_local_coord - wipe_tower_size(0) - margin; + } + else if (x < margin + plate_bbox_x_min_local_coord) { + x = margin + plate_bbox_x_min_local_coord; + } + + if (y + margin + wipe_tower_size(1) > plate_bbox_y_max_local_coord) { + y = plate_bbox_y_max_local_coord - wipe_tower_size(1) - margin; + } + else if (y < margin) { + y = margin; + } + } + ConfigOptionFloat wt_x_opt(x); + ConfigOptionFloat wt_y_opt(y); + + dynamic_cast(proj_cfg.option("wipe_tower_x"))->set_at(&wt_x_opt, plate_id, 0); + dynamic_cast(proj_cfg.option("wipe_tower_y"))->set_at(&wt_y_opt, plate_id, 0); } } else { auto tower_bottom = current_print->wipe_tower_data().wipe_tower_mesh_data->bottom; @@ -3281,15 +3353,19 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re //if (printer_technology != ptSLA || !contained_min_one) // _set_warning_notification(EWarning::SlaSupportsOutside, false); - bool tpu_valid = cur_plate->check_tpu_printable_status(wxGetApp().preset_bundle->full_config(), wxGetApp().preset_bundle->get_used_tpu_filaments(cur_plate->get_extruders(true))); + auto full_config_temp = wxGetApp().preset_bundle->full_config(); + bool tpu_valid = cur_plate->check_tpu_printable_status(full_config_temp, wxGetApp().preset_bundle->get_used_tpu_filaments(cur_plate->get_extruders(true))); _set_warning_notification(EWarning::TPUPrintableError, !tpu_valid); - bool filament_printable = cur_plate->check_filament_printable(wxGetApp().preset_bundle->full_config(), filament_printable_error_msg); + bool filament_printable = cur_plate->check_filament_printable(full_config_temp, filament_printable_error_msg); _set_warning_notification(EWarning::FilamentPrintableError, !filament_printable); - bool mix_pla_and_petg = cur_plate->check_mixture_of_pla_and_petg(wxGetApp().preset_bundle->full_config()); + bool mix_pla_and_petg = cur_plate->check_mixture_of_pla_and_petg(full_config_temp); _set_warning_notification(EWarning::MixUsePLAAndPETG, !mix_pla_and_petg); + bool filament_nozzle_compatible = cur_plate->check_compatible_of_nozzle_and_filament(full_config_temp, wxGetApp().preset_bundle->filament_presets, get_nozzle_filament_incompatible_text()); + _set_warning_notification(EWarning::NozzleFilamentIncompatible, !filament_nozzle_compatible); + bool model_fits = contained_min_one && !m_model->objects.empty() && !partlyOut && object_results.filaments.empty() && tpu_valid && filament_printable; post_event(Event(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, model_fits)); ppl.get_curr_plate()->update_slice_ready_status(model_fits); @@ -3307,6 +3383,8 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re _set_warning_notification(EWarning::PrimeTowerOutside, false); _set_warning_notification(EWarning::MultiExtruderPrintableError,false); _set_warning_notification(EWarning::MultiExtruderHeightOutside,false); + _set_warning_notification(EWarning::NozzleFilamentIncompatible,false); + post_event(Event(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, false)); } } @@ -3551,9 +3629,10 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) if (!m_initialized) return; - m_dirty |= m_main_toolbar.update_items_state(); - //QDS: GUI refactor: GLToolbar - m_dirty |= m_assemble_view_toolbar.update_items_state(); + const auto& p_main_toolbar = get_main_toolbar(); + if (p_main_toolbar) { + m_dirty |= p_main_toolbar->update_items_state(); + } // QDS //m_dirty |= wxGetApp().plater()->get_view_toolbar().update_items_state(); m_dirty |= wxGetApp().plater()->sidebar().get_update_3d_state(); @@ -3561,7 +3640,6 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt) wxGetApp().plater()->sidebar().cancel_update_3d_state(); } m_dirty |= wxGetApp().plater()->get_collapse_toolbar().update_items_state(); - _update_imgui_select_plate_toolbar(); bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(get_active_camera()); m_dirty |= mouse3d_controller_applied; m_dirty |= wxGetApp().plater()->get_notification_manager()->update_notifications(*this); @@ -3637,6 +3715,14 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) post_event(SimpleEvent(EVT_GLTOOLBAR_DELETE_ALL)); return; } + case 'z': + case 'Z': + case WXK_CONTROL_Z: { + if (m_canvas_type == CanvasView3D || m_canvas_type == CanvasAssembleView) { + post_event(SimpleEvent(EVT_GLCANVAS_REDO)); + } + return; + } } } // CTRL is pressed @@ -3745,6 +3831,16 @@ void GLCanvas3D::on_char(wxKeyEvent& evt) case WXK_CONTROL_E: #endif /* __APPLE__ */ { m_labels.show(!m_labels.is_shown()); m_dirty = true; break; } +#ifdef __APPLE__ + case 'W': + case 'w': +#else /* __APPLE__ */ + case WXK_CONTROL_W: +#endif /* __APPLE__ */ + { + wxGetApp().plater()->reset_window_layout(); + break; + } case '0': { select_view("plate"); zoom_to_bed(); @@ -4044,6 +4140,9 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) evt.ShiftDown() && evt.AltDown() && keyCode == WXK_RETURN) { wxGetApp().plater()->toggle_show_wireframe(); m_dirty = true; + } else if ((evt.ShiftDown() && evt.ControlDown() && keyCode == 'T')) { + wxGetApp().plater()->toggle_text_cs(); + m_dirty = true; } else if ((evt.ShiftDown() && evt.ControlDown() && keyCode == 'L')) { wxGetApp().plater()->toggle_non_manifold_edges(); m_dirty = true; @@ -4425,6 +4524,29 @@ void GLCanvas3D::schedule_extra_frame(int miliseconds) } } +int GLCanvas3D::get_main_toolbar_item_id(const std::string& name) const +{ + if (!m_main_toolbar) { + return -1; + } + return m_main_toolbar->get_item_id(name); +} + +void GLCanvas3D::force_main_toolbar_left_action(int item_id) +{ + if (!m_main_toolbar) { + return; + } + + m_dirty |= m_main_toolbar->update_items_state(); + m_main_toolbar->force_left_action(item_id, *this); +} + +void GLCanvas3D::force_main_toolbar_right_action(int item_id) +{ + m_main_toolbar->force_right_action(item_id, *this); +} + #ifndef NDEBUG // #define SLIC3R_DEBUG_MOUSE_EVENTS #endif @@ -4562,21 +4684,14 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) #endif /* SLIC3R_DEBUG_MOUSE_EVENTS */ } - if (m_main_toolbar.on_mouse(evt, *this)) { - if (m_main_toolbar.is_any_item_pressed()) - m_gizmos.reset_all_states(); - if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) - mouse_up_cleanup(); - m_mouse.set_start_position_3D_as_invalid(); - return; - } - - //QDS: GUI refactor: GLToolbar - if (m_assemble_view_toolbar.on_mouse(evt, *this)) { - if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) - mouse_up_cleanup(); - m_mouse.set_start_position_3D_as_invalid(); - return; + const auto& p_main_toolbar = get_main_toolbar(); + if (p_main_toolbar) { + if (p_main_toolbar->on_mouse(evt, *this)) { + if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) + mouse_up_cleanup(); + m_mouse.set_start_position_3D_as_invalid(); + return; + } } if (wxGetApp().plater()->get_collapse_toolbar().on_mouse(evt, *this)) { @@ -4623,7 +4738,10 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) wxMouseEvent evt2 = evt; evt2.SetEventType(wxEVT_MOTION); evt2.SetLeftDown(false); - m_main_toolbar.on_mouse(evt2, *this); + const auto& p_main_toolbar = get_main_toolbar(); + if (p_main_toolbar) { + p_main_toolbar->on_mouse(evt2, *this); + } } if (evt.LeftUp() || evt.MiddleUp() || evt.RightUp()) @@ -4733,9 +4851,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) m_selection.set_volume_selection_mode(evt.AltDown() ? Selection::Volume : Selection::Instance); if (evt.LeftDown() && evt.ShiftDown() && m_picking_enabled && m_layers_editing.state != LayersEditing::Editing) { if (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports - && m_gizmos.get_current_type() != GLGizmosManager::FdmSupports - && m_gizmos.get_current_type() != GLGizmosManager::Seam - && m_gizmos.get_current_type() != GLGizmosManager::MmuSegmentation) { + && !m_gizmos.is_paint_gizmo()) { m_rectangle_selection.start_dragging(m_mouse.position, evt.ShiftDown() ? GLSelectionRectangle::Select : GLSelectionRectangle::Deselect); m_dirty = true; } @@ -4814,7 +4930,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) } } else if (evt.Dragging() && evt.LeftIsDown() && m_mouse.drag.move_volume_idx != -1 && m_layers_editing.state == LayersEditing::Unknown) { - if (m_canvas_type != ECanvasType::CanvasAssembleView) { + if (m_canvas_type != ECanvasType::CanvasAssembleView && m_gizmos.is_allow_drag_volume()) { if (!m_mouse.drag.move_requires_threshold) { m_mouse.dragging = true; Vec3d cur_pos = m_mouse.drag.start_position_3D; @@ -4885,8 +5001,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) // if dragging over blank area with left button, rotate if ((any_gizmo_active || m_hover_volume_idxs.empty()) && m_mouse.is_start_position_3D_defined()) { const Vec3d rot = (Vec3d(pos.x(), pos.y(), 0.) - m_mouse.drag.start_position_3D) * (PI * TRACKBALLSIZE / 180.); - if (this->m_canvas_type == ECanvasType::CanvasAssembleView || m_gizmos.get_current_type() == GLGizmosManager::FdmSupports || - m_gizmos.get_current_type() == GLGizmosManager::Seam || m_gizmos.get_current_type() == GLGizmosManager::MmuSegmentation) { + if (this->m_canvas_type == ECanvasType::CanvasAssembleView || m_gizmos.is_paint_gizmo()) { //QDS rotate around target Camera& camera = get_active_camera(); Vec3d rotate_target = Vec3d::Zero(); @@ -4916,13 +5031,6 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) camera.recover_from_free_camera(); //QDS modify rotation - //if (m_gizmos.get_current_type() == GLGizmosManager::FdmSupports - // || m_gizmos.get_current_type() == GLGizmosManager::Seam - // || m_gizmos.get_current_type() == GLGizmosManager::MmuSegmentation) { - // //camera.rotate_local_with_target(Vec3d(rot.y(), rot.x(), 0.), rotate_target); - // //camera.rotate_on_sphere_with_target(rot.x(), rot.y(), rotate_limit, rotate_target); - //} - //else if (evt.ControlDown() || evt.CmdDown()) { if ((m_rotation_center.x() == 0.f) && (m_rotation_center.y() == 0.f) && (m_rotation_center.z() == 0.f)) { auto canvas_w = float(get_canvas_size().get_width()); @@ -5123,6 +5231,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) if (type == GLGizmosManager::EType::Text) m_gizmos.open_gizmo(GLGizmosManager::EType::Text); // close text wxGetApp().obj_list()->update_selections(); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Edit existed text by double click"); m_gizmos.open_gizmo(GLGizmosManager::EType::Text); return; } @@ -5134,10 +5243,13 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt) }*/ else if(hover_volume->emboss_shape.has_value()){ m_selection.add_volumes(Selection::EMode::Volume, {(unsigned) hover_volume_id}); - if (type == GLGizmosManager::EType::Svg) - m_gizmos.open_gizmo(GLGizmosManager::EType::Svg);// close svg wxGetApp().obj_list()->update_selections(); - m_gizmos.open_gizmo(GLGizmosManager::EType::Svg); + if (m_main_toolbar) { + const auto svg_item_name = GLGizmosManager::convert_gizmo_type_to_string(GLGizmosManager::EType::Svg); + if (!m_main_toolbar->is_item_pressed(svg_item_name)) { + force_main_toolbar_left_action(get_main_toolbar_item_id(svg_item_name)); + } + } return; } } @@ -5175,11 +5287,6 @@ void GLCanvas3D::force_set_focus() { void GLCanvas3D::on_set_focus(wxFocusEvent& evt) { m_tooltip_enabled = false; - if (m_canvas_type == ECanvasType::CanvasPreview) { - // update thumbnails and update plate toolbar - wxGetApp().plater()->update_all_plate_thumbnails(); - _update_imgui_select_plate_toolbar(); - } _refresh_if_shown_on_screen(); m_tooltip_enabled = true; } @@ -6080,9 +6187,19 @@ bool GLCanvas3D::_render_orient_menu(float left, float right, float bottom, floa #if QDS_TOOLBAR_ON_TOP const float x = left * float(get_active_camera().get_zoom()) + 0.5f * canvas_w; ImGuiWrapper::push_toolbar_style(get_scale()); - imgui->set_next_window_pos(x, m_main_toolbar.get_height(), ImGuiCond_Always, 0.5f, 0.0f); + float main_toolbar_height = 0.0f; + const auto& p_main_toolbar = get_main_toolbar(); + if (p_main_toolbar) { + main_toolbar_height = p_main_toolbar->get_height(); + } + imgui->set_next_window_pos(x, main_toolbar_height, ImGuiCond_Always, 0.5f, 0.0f); #else - const float x = canvas_w - m_main_toolbar.get_width(); + float main_toolbar_width = 0.0f; + const auto& p_main_toolbar = get_main_toolbar(); + if (p_main_toolbar) { + main_toolbar_width = p_main_toolbar->get_width(); + } + const float x = canvas_w - main_toolbar_width; const float y = 0.5f * canvas_h - top * float(get_active_camera().get_zoom()); imgui->set_next_window_pos(x, y, ImGuiCond_Always, 1.0f, 0.0f); #endif @@ -6153,7 +6270,7 @@ bool GLCanvas3D::_render_orient_menu(float left, float right, float bottom, floa } //QDS: GUI refactor: adjust main toolbar position -bool GLCanvas3D::_render_arrange_menu(float left, float right, float bottom, float top) +bool GLCanvas3D::_render_arrange_menu(float left, float toolbar_height) { ImGuiWrapper *imgui = wxGetApp().imgui(); @@ -6164,12 +6281,16 @@ bool GLCanvas3D::_render_arrange_menu(float left, float right, float bottom, flo //now change to left_up as {0,0}, and top is 0, bottom is canvas_h #if QDS_TOOLBAR_ON_TOP float zoom = (float)get_active_camera().get_zoom(); - float left_pos = m_main_toolbar.get_item("arrange")->render_left_pos; - const float x = 0.5 * canvas_w + left_pos * zoom; - imgui->set_next_window_pos(x, m_main_toolbar.get_height(), ImGuiCond_Always, 0.0f, 0.0f); + const float x = 0.5 * canvas_w + left * zoom; + imgui->set_next_window_pos(x, toolbar_height, ImGuiCond_Always, 0.0f, 0.0f); #else - const float x = canvas_w - m_main_toolbar.get_width(); + float main_toolbar_width = 0.0f; + const auto& p_main_toolbar = get_main_toolbar(); + if (p_main_toolbar) { + main_toolbar_width = p_main_toolbar->get_width(); + } + const float x = canvas_w - main_toolbar_width; const float y = 0.5f * canvas_h - top * float(get_active_camera().get_zoom()); imgui->set_next_window_pos(x, y, ImGuiCond_Always, 1.0f, 0.0f); #endif @@ -6509,61 +6630,24 @@ void GLCanvas3D::_switch_toolbars_icon_filename() background_data.top = 16; background_data.right = 16; background_data.bottom = 16; - m_main_toolbar.init(background_data); - m_assemble_view_toolbar.init(background_data); - m_separator_toolbar.init(background_data); + + const auto& p_main_toolbar = get_main_toolbar(); + if (p_main_toolbar) { + p_main_toolbar->init(background_data); + p_main_toolbar->set_dark_mode_enabled(m_is_dark); + } + wxGetApp().plater()->get_collapse_toolbar().init(background_data); - // main toolbar - { - GLToolbarItem* item; - item = m_main_toolbar.get_item("add"); - item->set_icon_filename(m_is_dark ? "toolbar_open_dark.svg" : "toolbar_open.svg"); - - item = m_main_toolbar.get_item("addplate"); - item->set_icon_filename(m_is_dark ? "toolbar_add_plate_dark.svg" : "toolbar_add_plate.svg"); - - item = m_main_toolbar.get_item("orient"); - item->set_icon_filename(m_is_dark ? "toolbar_orient_dark.svg" : "toolbar_orient.svg"); - - item = m_main_toolbar.get_item("addplate"); - item->set_icon_filename(m_is_dark ? "toolbar_add_plate_dark.svg" : "toolbar_add_plate.svg"); - - item = m_main_toolbar.get_item("arrange"); - item->set_icon_filename(m_is_dark ? "toolbar_arrange_dark.svg" : "toolbar_arrange.svg"); - - item = m_main_toolbar.get_item("splitobjects"); - item->set_icon_filename(m_is_dark ? "split_objects_dark.svg" : "split_objects.svg"); - - item = m_main_toolbar.get_item("splitvolumes"); - item->set_icon_filename(m_is_dark ? "split_parts_dark.svg" : "split_parts.svg"); - - item = m_main_toolbar.get_item("layersediting"); - item->set_icon_filename(m_is_dark ? "toolbar_variable_layer_height_dark.svg" : "toolbar_variable_layer_height.svg"); - } - - // assemble view toolbar - { - GLToolbarItem* item; - item = m_assemble_view_toolbar.get_item("assembly_view"); - item->set_icon_filename(m_is_dark ? "toolbar_assemble_dark.svg" : "toolbar_assemble.svg"); - } } bool GLCanvas3D::_init_toolbars() { if (!_init_main_toolbar()) return false; - //QDS: GUI refractor - if (!_init_assemble_view_toolbar()) - return false; - if (!_init_return_toolbar()) return false; - if (!_init_separator_toolbar()) - return false; - if (!_init_select_plate_toolbar()) return false; @@ -6581,9 +6665,21 @@ bool GLCanvas3D::_init_toolbars() //QDS: GUI refactor: GLToolbar bool GLCanvas3D::_init_main_toolbar() { - if (!m_main_toolbar.is_enabled()) + const auto& p_main_toolbar = get_main_toolbar(); + if (!p_main_toolbar) { + return false; + } + if (!p_main_toolbar->is_enabled()) return true; + if (m_canvas_type == ECanvasType::CanvasAssembleView) { + m_main_toolbar->set_position_mode(ToolbarLayout::EPositionMode::Custom); + } + else { + m_main_toolbar->set_position_mode(ToolbarLayout::EPositionMode::TopMiddle); + } + m_main_toolbar->set_collapsed_offset(8); // by design from UX + BackgroundTexture::Metadata background_data; background_data.filename = m_is_dark ? "toolbar_background_dark.png" : "toolbar_background.png"; background_data.left = 16; @@ -6591,12 +6687,15 @@ bool GLCanvas3D::_init_main_toolbar() background_data.right = 16; background_data.bottom = 16; - if (!m_main_toolbar.init(background_data)) + if (!p_main_toolbar->init(background_data)) { // unable to init the toolbar texture, disable it - m_main_toolbar.set_enabled(false); + p_main_toolbar->set_enabled(false); return true; } + + p_main_toolbar->set_dark_mode_enabled(m_is_dark); + // init arrow BackgroundTexture::Metadata arrow_data; arrow_data.filename = "toolbar_arrow.svg"; @@ -6604,166 +6703,278 @@ bool GLCanvas3D::_init_main_toolbar() arrow_data.top = 0; arrow_data.right = 0; arrow_data.bottom = 0; - if (!m_main_toolbar.init_arrow(arrow_data)) + if (!p_main_toolbar->init_arrow(arrow_data)) { BOOST_LOG_TRIVIAL(error) << "Main toolbar failed to load arrow texture."; } - // m_gizmos is created at constructor, thus we can init arrow here. - if (!m_gizmos.init_arrow(arrow_data)) - { - BOOST_LOG_TRIVIAL(error) << "Gizmos manager failed to load arrow texture."; - } - m_main_toolbar.set_layout_type(GLToolbar::Layout::Horizontal); + p_main_toolbar->set_layout_type(ToolbarLayout::EType::Horizontal); //QDS: main toolbar is at the top and left, we don't need the rounded-corner effect at the right side and the top side - m_main_toolbar.set_horizontal_orientation(GLToolbar::Layout::HO_Right); - m_main_toolbar.set_vertical_orientation(GLToolbar::Layout::VO_Top); - m_main_toolbar.set_border(5.0f); - m_main_toolbar.set_separator_size(5); - m_main_toolbar.set_gap_size(4); + p_main_toolbar->set_horizontal_orientation(ToolbarLayout::HO_Right); + p_main_toolbar->set_vertical_orientation(ToolbarLayout::VO_Top); + p_main_toolbar->set_border(5.0f); + p_main_toolbar->set_separator_size(5); + p_main_toolbar->set_gap_size(4); - m_main_toolbar.del_all_item(); + p_main_toolbar->del_all_item(); + uint8_t sprite_id = 0; GLToolbarItem::Data item; - item.name = "add"; - item.icon_filename = m_is_dark ? "toolbar_open_dark.svg" : "toolbar_open.svg"; - item.tooltip = _utf8(L("Add")) + " [" + GUI::shortkey_ctrl_prefix() + "I]"; - item.sprite_id = 0; - item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ADD)); }; - item.enabling_callback = []()->bool {return wxGetApp().plater()->can_add_model(); }; - if (!m_main_toolbar.add_item(item)) - return false; + if (m_canvas_type == ECanvasType::CanvasView3D) { + item.name = "add"; + item.icon_filename_callback = [](bool is_dark_mode)->std::string { + return is_dark_mode ? "toolbar_open_dark.svg" : "toolbar_open.svg"; + }; + item.tooltip = _utf8(L("Add")) + " [" + GUI::shortkey_ctrl_prefix() + "I]"; + item.sprite_id = sprite_id++; + item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ADD)); }; + item.enabling_callback = []()->bool {return wxGetApp().plater()->can_add_model(); }; + if (!p_main_toolbar->add_item(item)) + return false; - item.name = "addplate"; - item.icon_filename = m_is_dark ? "toolbar_add_plate_dark.svg" : "toolbar_add_plate.svg"; - item.tooltip = _utf8(L("Add plate")); - item.sprite_id++; - item.continuous_click = true; - item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ADD_PLATE)); }; - item.enabling_callback = []()->bool {return wxGetApp().plater()->can_add_plate(); }; - if (!m_main_toolbar.add_item(item)) - return false; + item.name = "addplate"; + item.icon_filename_callback = [](bool is_dark_mode)->std::string { + return is_dark_mode ? "toolbar_add_plate_dark.svg" : "toolbar_add_plate.svg"; + }; + item.tooltip = _utf8(L("Add plate")); + item.sprite_id = sprite_id++; + item.continuous_click = true; + item.left.action_callback = [this]() { if (m_canvas != nullptr) wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_ADD_PLATE)); }; + item.enabling_callback = []()->bool {return wxGetApp().plater()->can_add_plate(); }; + if (!p_main_toolbar->add_item(item)) + return false; + + item.name = "orient"; + item.icon_filename_callback = [](bool is_dark_mode)->std::string { + return is_dark_mode ? "toolbar_orient_dark.svg" : "toolbar_orient.svg"; + }; + item.tooltip = _utf8(L("Auto orient")); + item.sprite_id = sprite_id++; + item.left.render_callback = nullptr; + item.enabling_callback = []()->bool { return wxGetApp().plater()->can_do_ui_job(); }; + item.left.toggable = false; // allow right mouse click + //QDS: GUI refactor: adjust the main toolbar position + item.left.action_callback = [this]() { + if (m_canvas != nullptr) + { + wxGetApp().plater()->set_prepare_state(Job::PREPARE_STATE_DEFAULT); + wxGetApp().plater()->orient(); + //QDS do not show orient menu + //_render_orient_menu(left, right, bottom, top); + NetworkAgent* agent = GUI::wxGetApp().getAgent(); + if (agent) agent->track_update_property("auto_orient", std::to_string(++auto_orient_count)); + } + }; + if (!p_main_toolbar->add_item(item)) + return false; + + item.name = "arrange"; + item.icon_filename_callback = [](bool is_dark_mode)->std::string { + return is_dark_mode ? "toolbar_arrange_dark.svg" : "toolbar_arrange.svg"; + }; + item.tooltip = _utf8(L("Arrange all objects")) + " [A]\n" + _utf8(L("Arrange objects on selected plates")) + " [Shift+A]"; + item.sprite_id = sprite_id++; + item.left.action_callback = [this]() { + if (m_canvas != nullptr) { + NetworkAgent* agent = GUI::wxGetApp().getAgent(); + if (agent) agent->track_update_property("auto_arrange", std::to_string(++auto_arrange_count)); + } + }; + item.enabling_callback = []()->bool { return wxGetApp().plater()->can_do_ui_job(); }; + item.left.toggable = true; + //QDS: GUI refactor: adjust the main toolbar position + item.left.render_callback = [this](float left, float right, float bottom, float top, float toolbar_height) { + if (m_canvas != nullptr) + { + _render_arrange_menu(left, toolbar_height); + //_render_arrange_menu(0.5f * (left + right)); + } + }; + if (!p_main_toolbar->add_item(item)) + return false; + + GLToolbarItem::Data layers_editing_item; + layers_editing_item.name = "layersediting"; + layers_editing_item.icon_filename_callback = [](bool is_dark_mode)->std::string { + return is_dark_mode ? "toolbar_variable_layer_height_dark.svg" : "toolbar_variable_layer_height.svg"; + }; + layers_editing_item.tooltip = _utf8(L("Variable layer height")); + layers_editing_item.additional_tooltip = _u8L("Please select single object."); + layers_editing_item.sprite_id = sprite_id++; + layers_editing_item.left.action_callback = [this]()->void{ + if (m_canvas != nullptr) { + wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); + NetworkAgent* agent = GUI::wxGetApp().getAgent(); + if (agent) agent->track_update_property("custom_height", std::to_string(++custom_height_count)); + } + }; + layers_editing_item.right.action_callback = [this]()->void { + enable_layers_editing(false); + }; + layers_editing_item.visibility_callback = [this, &p_main_toolbar]()->bool { + bool res = current_printer_technology() == ptFFF; + // turns off if changing printer technology + if (!res && p_main_toolbar->is_item_visible("layersediting") && p_main_toolbar->is_item_pressed("layersediting")) + force_main_toolbar_left_action(get_main_toolbar_item_id("layersediting")); + + return res; + }; + layers_editing_item.enabling_callback = []()->bool { return wxGetApp().plater()->can_layers_editing(); }; + layers_editing_item.left.toggable = true; + if (!p_main_toolbar->add_item(layers_editing_item)) + return false; - item.name = "orient"; - item.icon_filename = m_is_dark ? "toolbar_orient_dark.svg" : "toolbar_orient.svg"; - item.tooltip = _utf8(L("Auto orient")); - item.sprite_id++; - item.left.render_callback = nullptr; - item.enabling_callback = []()->bool { return wxGetApp().plater()->can_do_ui_job(); }; - item.left.toggable = false; // allow right mouse click - //QDS: GUI refactor: adjust the main toolbar position - item.left.action_callback = [this]() { - if (m_canvas != nullptr) { - wxGetApp().plater()->set_prepare_state(Job::PREPARE_STATE_DEFAULT); - wxGetApp().plater()->orient(); - //QDS do not show orient menu - //_render_orient_menu(left, right, bottom, top); - NetworkAgent* agent = GUI::wxGetApp().getAgent(); - if (agent) agent->track_update_property("auto_orient", std::to_string(++auto_orient_count)); + GLToolbarItem::Data sperate_item; + sperate_item.name = "seperator2"; + sperate_item.icon_filename_callback = [](bool is_dark_mode)->std::string { + return "seperator.svg"; + }; + sperate_item.sprite_id = sprite_id++; + sperate_item.left.action_callback = [this]() {}; + sperate_item.visibility_callback = []()->bool { return true; }; + sperate_item.enabling_callback = []()->bool { return true; }; + if (!p_main_toolbar->add_item(sperate_item, GLToolbarItem::EType::SeparatorLine)) + return false; } - }; - if (!m_main_toolbar.add_item(item)) - return false; + } - item.name = "arrange"; - item.icon_filename = m_is_dark ? "toolbar_arrange_dark.svg" : "toolbar_arrange.svg"; - item.tooltip = _utf8(L("Arrange all objects")) + " [A]\n" + _utf8(L("Arrange objects on selected plates")) + " [Shift+A]"; - item.sprite_id++; - item.left.action_callback = [this]() { - if (m_canvas != nullptr) { - NetworkAgent* agent = GUI::wxGetApp().getAgent(); - if (agent) agent->track_update_property("auto_arrange", std::to_string(++auto_arrange_count)); + const auto do_add_other_items = [this](uint8_t& sprite_id)->void { + if (m_canvas_type == ECanvasType::CanvasView3D) { + const auto& p_main_toolbar = get_main_toolbar(); + if (!p_main_toolbar) { + return; + } + GLToolbarItem::Data item; + item.name = "splitobjects"; + item.icon_filename_callback = [](bool is_dark_mode)->std::string { + return is_dark_mode ? "split_objects_dark.svg" : "split_objects.svg"; + }; + item.tooltip = _utf8(L("Split to objects")); + item.additional_tooltip = _u8L("Please select single object.") + "\n" + + _u8L("And it is valid when there are at least two parts in object or stl has at least two meshes."); + item.sprite_id = sprite_id++; + item.left.render_callback = nullptr; + item.left.action_callback = [this]() { + if (m_canvas != nullptr) { + wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_OBJECTS)); + NetworkAgent* agent = GUI::wxGetApp().getAgent(); + if (agent) agent->track_update_property("split_to_objects", std::to_string(++split_to_objects_count)); + } + }; + item.visibility_callback = GLToolbarItem::Default_Visibility_Callback; + item.left.toggable = false; + item.enabling_callback = []()->bool { return wxGetApp().plater()->can_split_to_objects(); }; + p_main_toolbar->add_item(item); + + item.name = "splitvolumes"; + item.icon_filename_callback = [](bool is_dark_mode)->std::string { + return is_dark_mode ? "split_parts_dark.svg" : "split_parts.svg"; + }; + item.tooltip = _utf8(L("Split to parts")); + item.additional_tooltip = _u8L("Please select single object.") + "\n" + + _u8L("And it is valid when importing an stl with at least two meshes."); + item.sprite_id = sprite_id++; + item.left.action_callback = [this]() { + if (m_canvas != nullptr) { + wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_VOLUMES)); + NetworkAgent* agent = GUI::wxGetApp().getAgent(); + if (agent) agent->track_update_property("split_to_part", std::to_string(++split_to_part_count)); + } + }; + item.visibility_callback = GLToolbarItem::Default_Visibility_Callback; + item.enabling_callback = []()->bool { return wxGetApp().plater()->can_split_to_volumes(); }; + p_main_toolbar->add_item(item); } }; - item.enabling_callback = []()->bool { return wxGetApp().plater()->can_do_ui_job(); }; - item.left.toggable = true; - //QDS: GUI refactor: adjust the main toolbar position - item.left.render_callback = [this](float left, float right, float bottom, float top) { - if (m_canvas != nullptr) + + if (m_gizmos.is_enabled()) { + m_gizmos.add_toolbar_items(p_main_toolbar, sprite_id, do_add_other_items); + } + else if (m_canvas_type == ECanvasType::CanvasView3D) { + do_add_other_items(sprite_id); + } + + if (m_canvas_type == ECanvasType::CanvasView3D) { + + GLToolbarItem::Data more_item; + more_item.name = "More"; + more_item.icon_filename_callback = [](bool is_dark_mode)->std::string { + return is_dark_mode ? "more_dark.svg" : "more.svg"; + }; + more_item.tooltip = _utf8(L("Click to Extend")); + more_item.on_hover = [this]()->std::string { + if (m_main_toolbar) { + if (m_main_toolbar->is_collapsed()) { + return _utf8(L("Click to Extend")); + } + else { + return _utf8(L("Click to Collapse")); + } + } + return ""; + }; + more_item.sprite_id = sprite_id++; + more_item.visible = false; + more_item.left.action_callback = [this]()->void{}; + more_item.visibility_callback = [this]()->bool { + if (m_main_toolbar) { + return m_main_toolbar->needs_collapsed(); + } + return false; + }; + more_item.enabling_callback = []()->bool { return true; }; + more_item.b_collapsible = false; + more_item.b_collapse_button = true; + more_item.continuous_click = true; + if (!p_main_toolbar->add_item(more_item)) + return false; + { - _render_arrange_menu(left, right, bottom, top); - //_render_arrange_menu(0.5f * (left + right)); + GLToolbarItem::Data sperate_item; + sperate_item.name = "seperator1"; + sperate_item.icon_filename_callback = [](bool is_dark_mode)->std::string { + return "seperator.svg"; + }; + sperate_item.sprite_id = sprite_id++; + sperate_item.left.action_callback = [this]() {}; + sperate_item.visibility_callback = []()->bool { return true; }; + sperate_item.enabling_callback = []()->bool { return true; }; + sperate_item.b_collapsible = false; + if (!p_main_toolbar->add_item(sperate_item, GLToolbarItem::EType::SeparatorLine)) + return false; } - }; - if (!m_main_toolbar.add_item(item)) - return false; - item.right.toggable = false; - item.right.render_callback = GLToolbarItem::Default_Render_Callback; - - if (!m_main_toolbar.add_separator()) - return false; - - item.name = "splitobjects"; - item.icon_filename = m_is_dark ? "split_objects_dark.svg" : "split_objects.svg"; - item.tooltip = _utf8(L("Split to objects")); - item.additional_tooltip = _u8L("Please select single object.") + "\n"+ - _u8L("And it is valid when there are at least two parts in object or stl has at least two meshes."); - item.sprite_id++; - item.left.render_callback = nullptr; - item.left.action_callback = [this]() { - if (m_canvas != nullptr) { - wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_OBJECTS)); - NetworkAgent* agent = GUI::wxGetApp().getAgent(); - if (agent) agent->track_update_property("split_to_objects", std::to_string(++split_to_objects_count)); + { + GLToolbarItem::Data item; + item.name = "assembly_view"; + item.icon_filename_callback = [](bool is_dark_mode)->std::string { + return is_dark_mode ? "toolbar_assemble_dark.svg" : "toolbar_assemble.svg"; + }; + item.tooltip = _utf8(L("Assembly View")); + item.sprite_id = sprite_id++; + item.left.toggable = false; + item.left.action_callback = [this]() { + if (m_canvas != nullptr) { + wxPostEvent(m_canvas, SimpleEvent(EVT_GLVIEWTOOLBAR_ASSEMBLE)); m_gizmos.reset_all_states(); wxGetApp().plater()->get_assmeble_canvas3D()->get_gizmos_manager().reset_all_states(); + NetworkAgent* agent = GUI::wxGetApp().getAgent(); + if (agent) agent->track_update_property("assembly_view", std::to_string(++assembly_view_count)); + } + }; + item.left.render_callback = GLToolbarItem::Default_Render_Callback; + item.visible = true; + item.visibility_callback = [this]()->bool { return true; }; + item.enabling_callback = [this]()->bool { + return wxGetApp().plater()->has_assmeble_view(); + }; + item.b_collapsible = false; + if (!p_main_toolbar->add_item(item)) + return false; } - }; - item.visibility_callback = GLToolbarItem::Default_Visibility_Callback; - item.left.toggable = false; - item.enabling_callback = []()->bool { return wxGetApp().plater()->can_split_to_objects(); }; - if (!m_main_toolbar.add_item(item)) - return false; - - item.name = "splitvolumes"; - item.icon_filename = m_is_dark ? "split_parts_dark.svg" : "split_parts.svg"; - item.tooltip = _utf8(L("Split to parts")); - item.additional_tooltip = _u8L("Please select single object.")+ "\n" + - _u8L("And it is valid when importing an stl with at least two meshes."); - item.sprite_id++; - item.left.action_callback = [this]() { - if (m_canvas != nullptr) { - wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_SPLIT_VOLUMES)); - NetworkAgent* agent = GUI::wxGetApp().getAgent(); - if (agent) agent->track_update_property("split_to_part", std::to_string(++split_to_part_count)); - } - }; - item.visibility_callback = GLToolbarItem::Default_Visibility_Callback; - item.enabling_callback = []()->bool { return wxGetApp().plater()->can_split_to_volumes(); }; - if (!m_main_toolbar.add_item(item)) - return false; - - item.name = "layersediting"; - item.icon_filename = m_is_dark ? "toolbar_variable_layer_height_dark.svg" : "toolbar_variable_layer_height.svg"; - item.tooltip = _utf8(L("Variable layer height")); - item.additional_tooltip = _u8L("Please select single object."); - item.sprite_id++; - item.left.action_callback = [this]() { - if (m_canvas != nullptr) { - wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); - NetworkAgent* agent = GUI::wxGetApp().getAgent(); - if (agent) agent->track_update_property("custom_height", std::to_string(++custom_height_count)); - } - }; - item.right.action_callback = [this]() { - if (m_canvas != nullptr) { - wxPostEvent(m_canvas, SimpleEvent(EVT_GLTOOLBAR_LAYERSEDITING)); - } - }; - item.visibility_callback = [this]()->bool { - bool res = current_printer_technology() == ptFFF; - // turns off if changing printer technology - if (!res && m_main_toolbar.is_item_visible("layersediting") && m_main_toolbar.is_item_pressed("layersediting")) - force_main_toolbar_left_action(get_main_toolbar_item_id("layersediting")); - - return res; - }; - item.enabling_callback = []()->bool { return wxGetApp().plater()->can_layers_editing(); }; - item.left.toggable = true; - if (!m_main_toolbar.add_item(item)) - return false; + } + p_main_toolbar->update_items_state(); return true; } @@ -6792,13 +7003,28 @@ void GLCanvas3D::_update_select_plate_toolbar_stats_item(bool force_selected) { bool GLCanvas3D::_update_imgui_select_plate_toolbar() { bool result = true; - if (!m_sel_plate_toolbar.is_enabled() || m_sel_plate_toolbar.is_render_finish) return false; + if (!m_sel_plate_toolbar.is_enabled()) { + return false; + } + + const auto& p_plater = wxGetApp().plater(); + if (!p_plater) { + return false; + } + + if (!p_plater->is_plate_toolbar_image_dirty()) { + return false; + } + + if (!p_plater->is_gcode_3mf()) { + p_plater->update_all_plate_thumbnails(true); + } _update_select_plate_toolbar_stats_item(); m_sel_plate_toolbar.del_all_item(); - PartPlateList& plate_list = wxGetApp().plater()->get_partplate_list(); + PartPlateList& plate_list = p_plater->get_partplate_list(); for (int i = 0; i < plate_list.get_plate_count(); i++) { IMToolbarItem* item = new IMToolbarItem(); PartPlate* plate = plate_list.get_plate(i); @@ -6811,69 +7037,11 @@ bool GLCanvas3D::_update_imgui_select_plate_toolbar() } m_sel_plate_toolbar.m_items.push_back(item); } - + p_plater->clear_plate_toolbar_image_dirty(); m_sel_plate_toolbar.is_display_scrollbar = false; return result; } -//QDS: GUI refactor -//init the assemble view toolbar on the top -bool GLCanvas3D::_init_assemble_view_toolbar() -{ - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": enter, m_assemble_view_toolbar.is_enabled=" << m_assemble_view_toolbar.is_enabled() << "\n"; - if (!m_assemble_view_toolbar.is_enabled()) - return true; - - BackgroundTexture::Metadata background_data; - background_data.filename = m_is_dark ? "toolbar_background_dark.png" : "toolbar_background.png"; - background_data.left = 16; - background_data.top = 16; - background_data.right = 16; - background_data.bottom = 16; - - if (!m_assemble_view_toolbar.init(background_data)) - { - // unable to init the toolbar texture, disable it - m_assemble_view_toolbar.set_enabled(false); - return true; - } - - m_assemble_view_toolbar.set_layout_type(GLToolbar::Layout::Horizontal); - //QDS: assemble toolbar is at the top and right, we don't need the rounded-corner effect at the left side and the top side - m_assemble_view_toolbar.set_horizontal_orientation(GLToolbar::Layout::HO_Left); - m_assemble_view_toolbar.set_vertical_orientation(GLToolbar::Layout::VO_Top); - m_assemble_view_toolbar.set_border(5.0f); - m_assemble_view_toolbar.set_separator_size(10); - m_assemble_view_toolbar.set_gap_size(4); - - m_assemble_view_toolbar.del_all_item(); - - GLToolbarItem::Data item; - item.name = "assembly_view"; - item.icon_filename = m_is_dark ? "toolbar_assemble_dark.svg" : "toolbar_assemble.svg"; - item.tooltip = _utf8(L("Assembly View")); - item.sprite_id = 1; - item.left.toggable = false; - item.left.action_callback = [this]() { - if (m_canvas != nullptr) { - wxPostEvent(m_canvas, SimpleEvent(EVT_GLVIEWTOOLBAR_ASSEMBLE)); m_gizmos.reset_all_states(); wxGetApp().plater()->get_assmeble_canvas3D()->get_gizmos_manager().reset_all_states(); - NetworkAgent* agent = GUI::wxGetApp().getAgent(); - if (agent) agent->track_update_property("assembly_view", std::to_string(++assembly_view_count)); - } - }; - item.left.render_callback = GLToolbarItem::Default_Render_Callback; - item.visible = true; - item.visibility_callback = [this]()->bool { return true; }; - item.enabling_callback = [this]()->bool { - return wxGetApp().plater()->has_assmeble_view(); - }; - if (!m_assemble_view_toolbar.add_item(item)) - return false; - - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": Finished Successfully\n"; - return true; -} - bool GLCanvas3D::_init_return_toolbar() { if (!m_return_toolbar.is_enabled()) @@ -6882,47 +7050,6 @@ bool GLCanvas3D::_init_return_toolbar() return m_return_toolbar.init(); } -bool GLCanvas3D::_init_separator_toolbar() -{ - if (!m_separator_toolbar.is_enabled()) - return true; - - BackgroundTexture::Metadata background_data; - background_data.filename = m_is_dark ? "toolbar_background_dark.png" : "toolbar_background.png"; - background_data.left = 0; - background_data.top = 0; - background_data.right = 0; - background_data.bottom = 0; - - if (!m_separator_toolbar.init(background_data)) - { - // unable to init the toolbar texture, disable it - m_separator_toolbar.set_enabled(false); - return true; - } - - m_separator_toolbar.set_layout_type(GLToolbar::Layout::Horizontal); - //QDS: assemble toolbar is at the top and right, we don't need the rounded-corner effect at the left side and the top side - m_separator_toolbar.set_horizontal_orientation(GLToolbar::Layout::HO_Left); - m_separator_toolbar.set_vertical_orientation(GLToolbar::Layout::VO_Top); - m_separator_toolbar.set_border(5.0f); - - m_separator_toolbar.del_all_item(); - - GLToolbarItem::Data sperate_item; - sperate_item.name = "start_seperator"; - sperate_item.icon_filename = "seperator.svg"; - sperate_item.sprite_id = 0; - sperate_item.left.action_callback = [this]() {}; - sperate_item.visibility_callback = []()->bool { return true; }; - sperate_item.enabling_callback = []()->bool { return false; }; - if (!m_separator_toolbar.add_item(sperate_item)) - return false; - - return true; -} - - // QDS #if 0 bool GLCanvas3D::_init_view_toolbar() @@ -7013,7 +7140,7 @@ BoundingBoxf3 GLCanvas3D::_max_bounding_box(bool include_gizmos, bool include_be bb.merge(wxGetApp().plater()->get_partplate_list().get_bounding_box()); } - if (!m_main_toolbar.is_enabled()) { + if (m_canvas_type == CanvasPreview) { const BoundingBoxf3& toolpath_bb = m_gcode_viewer.get_max_bounding_box(); if (toolpath_bb.max_size() > 0.f) bb.merge(toolpath_bb); @@ -7107,10 +7234,7 @@ void GLCanvas3D::_picking_pass() glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); //QDS: only render plate in view 3D - bool is_paint_gizmo=(m_gizmos.get_current_type() == GLGizmosManager::EType::FdmSupports || - m_gizmos.get_current_type() == GLGizmosManager::EType::MmuSegmentation || - m_gizmos.get_current_type() == GLGizmosManager::EType::Seam); - if (m_canvas_type == ECanvasType::CanvasView3D && !is_paint_gizmo) { + if (m_canvas_type == ECanvasType::CanvasView3D && !m_gizmos.is_paint_gizmo()) { _render_plates_for_picking(); } @@ -7366,17 +7490,11 @@ void GLCanvas3D::_render_bed(bool bottom, bool show_axes) scale_factor = m_retina_helper->get_scale_factor(); #endif // ENABLE_RETINA_GL - /* - bool show_texture = ! bottom || - (m_gizmos.get_current_type() != GLGizmosManager::FdmSupports - && m_gizmos.get_current_type() != GLGizmosManager::SlaSupports - && m_gizmos.get_current_type() != GLGizmosManager::Hollow - && m_gizmos.get_current_type() != GLGizmosManager::Seam - && m_gizmos.get_current_type() != GLGizmosManager::MmuSegmentation); - */ //bool show_texture = true; //QDS set axes mode - m_bed.set_axes_mode(m_main_toolbar.is_enabled() && !m_gizmos.is_show_only_active_plate()); + const auto& p_main_toolbar = get_main_toolbar(); + bool b_main_toolbar_enabled = p_main_toolbar && p_main_toolbar->is_enabled(); + m_bed.set_axes_mode(b_main_toolbar_enabled && !m_gizmos.is_show_only_active_plate()); m_bed.render(*this, bottom, scale_factor, show_axes); } @@ -7407,16 +7525,16 @@ void GLCanvas3D::_render_plane() const } //QDS: add outline drawing logic -void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with_outline) +void GLCanvas3D::_render_objects(GLVolumeCollection &cur_volumes, GLVolumeCollection::ERenderType type, bool with_outline, bool in_paint_gizmo) { - if (m_volumes.empty()) + if (cur_volumes.empty()) return; glsafe(::glEnable(GL_DEPTH_TEST)); m_camera_clipping_plane = m_gizmos.get_clipping_plane(); - if (m_picking_enabled) + if (m_picking_enabled && !in_paint_gizmo) // Update the layer editing selection to the first object selected, update the current object maximum Z. m_layers_editing.select_object(*m_model, this->is_layers_editing_enabled() ? m_selection.get_object_idx() : -1); @@ -7424,13 +7542,13 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with switch (build_volume.type()) { case BuildVolume::Type::Rectangle: { const BoundingBox3Base bed_bb = build_volume.bounding_volume().inflated(BuildVolume::SceneEpsilon); - m_volumes.set_print_volume({ 0, // Rectangle + cur_volumes.set_print_volume({0, // Rectangle { float(bed_bb.min.x()), float(bed_bb.min.y()), float(bed_bb.max.x()), float(bed_bb.max.y()) }, { 0.0f, float(build_volume.printable_height()) } }); break; } case BuildVolume::Type::Circle: { - m_volumes.set_print_volume({ 1, // Circle + cur_volumes.set_print_volume({1, // Circle { unscaled(build_volume.circle().center.x()), unscaled(build_volume.circle().center.y()), unscaled(build_volume.circle().radius + BuildVolume::SceneEpsilon), 0.0f }, { 0.0f, float(build_volume.printable_height() + BuildVolume::SceneEpsilon) } }); break; @@ -7438,38 +7556,38 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with default: case BuildVolume::Type::Convex: case BuildVolume::Type::Custom: { - m_volumes.set_print_volume({ static_cast(type), + cur_volumes.set_print_volume({static_cast(type), { -FLT_MAX, -FLT_MAX, FLT_MAX, FLT_MAX }, { -FLT_MAX, FLT_MAX } } ); } } if (m_requires_check_outside_state) { - m_volumes.check_outside_state(build_volume, nullptr, nullptr, *m_model); + cur_volumes.check_outside_state(build_volume, nullptr, nullptr, *m_model); m_requires_check_outside_state = false; } } if (m_use_clipping_planes) - m_volumes.set_z_range(-m_clipping_planes[0].get_data()[3], m_clipping_planes[1].get_data()[3]); + cur_volumes.set_z_range(-m_clipping_planes[0].get_data()[3], m_clipping_planes[1].get_data()[3]); else - m_volumes.set_z_range(-FLT_MAX, FLT_MAX); + cur_volumes.set_z_range(-FLT_MAX, FLT_MAX); GLGizmosManager& gm = get_gizmos_manager(); GLGizmoBase* current_gizmo = gm.get_current(); if (m_canvas_type == CanvasAssembleView) { - m_volumes.set_clipping_plane(m_gizmos.get_assemble_view_clipping_plane().get_data()); + cur_volumes.set_clipping_plane(m_gizmos.get_assemble_view_clipping_plane().get_data()); } else if (current_gizmo && !current_gizmo->apply_clipping_plane()) { - m_volumes.set_clipping_plane(ClippingPlane::ClipsNothing().get_data()); + cur_volumes.set_clipping_plane(ClippingPlane::ClipsNothing().get_data()); } else { - m_volumes.set_clipping_plane(m_camera_clipping_plane.get_data()); + cur_volumes.set_clipping_plane(m_camera_clipping_plane.get_data()); } if (m_canvas_type == CanvasAssembleView) - m_volumes.set_show_sinking_contours(false); + cur_volumes.set_show_sinking_contours(false); else - m_volumes.set_show_sinking_contours(!m_gizmos.is_hiding_instances()); + cur_volumes.set_show_sinking_contours(!m_gizmos.is_hiding_instances()); const auto& shader = wxGetApp().get_shader("gouraud"); ECanvasType canvas_type = this->m_canvas_type; @@ -7492,16 +7610,24 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with case GLVolumeCollection::ERenderType::Opaque: { const GLGizmosManager& gm = get_gizmos_manager(); - if (dynamic_cast(gm.get_current()) == nullptr) - { - if (m_picking_enabled && m_layers_editing.is_enabled() && (m_layers_editing.last_object_id != -1) && (m_layers_editing.object_max_z() > 0.0f) && GUI::ERenderPipelineStage::Silhouette != render_pipeline_stage) { + if (in_paint_gizmo) { + cur_volumes.render( + render_pipeline_stage, type, m_picking_enabled, camera, colors, *m_model, + [this](const GLVolume &volume) { + return true; + }, + with_outline, body_color, partly_inside_enable, nullptr); + } + if (dynamic_cast(gm.get_current()) == nullptr){ + if (m_picking_enabled && m_layers_editing.is_enabled() && (m_layers_editing.last_object_id != -1) && (m_layers_editing.object_max_z() > 0.0f) && + GUI::ERenderPipelineStage::Silhouette != render_pipeline_stage) { int object_id = m_layers_editing.last_object_id; - m_volumes.render( + cur_volumes.render( render_pipeline_stage, type, false, camera, colors, *m_model,[object_id](const GLVolume &volume) { // Which volume to paint without the layer height profile shader? return volume.is_active && (volume.is_modifier || volume.composite_id.object_id != object_id); }); - m_layers_editing.render_volumes(*this, m_volumes); + m_layers_editing.render_volumes(*this, cur_volumes); } else { /*if (wxGetApp().plater()->is_wireframe_enabled()) { @@ -7512,7 +7638,7 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with }*/ //QDS:add assemble view related logic // do not cull backfaces to show broken geometry, if any - m_volumes.render( + cur_volumes.render( render_pipeline_stage, type, m_picking_enabled, camera, colors, *m_model,[this, canvas_type](const GLVolume &volume) { if (canvas_type == ECanvasType::CanvasAssembleView) { @@ -7548,17 +7674,25 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with shader->set_uniform("show_wireframe", false); }*/ //QDS:add assemble view related logic - m_volumes.render( - render_pipeline_stage, type, false, camera, colors,*m_model, - [this, canvas_type](const GLVolume &volume) { - if (canvas_type == ECanvasType::CanvasAssembleView) { - return !volume.is_modifier; - } - else { - return true; - } - }, - with_outline, body_color, partly_inside_enable, printable_height_option ? &printable_height_option->values : nullptr); + if (in_paint_gizmo) { + cur_volumes.render( + render_pipeline_stage, type, false, camera, colors, *m_model, + [this, canvas_type](const GLVolume &volume) { + return true; + }, + with_outline, body_color, partly_inside_enable, nullptr); + } else { + cur_volumes.render( + render_pipeline_stage, type, false, camera, colors, *m_model, + [this, canvas_type](const GLVolume &volume) { + if (canvas_type == ECanvasType::CanvasAssembleView) { + return !volume.is_modifier; + } else { + return true; + } + }, + with_outline, body_color, partly_inside_enable, printable_height_option ? &printable_height_option->values : nullptr); + } if (m_canvas_type == CanvasAssembleView && m_gizmos.m_assemble_view_data->model_objects_clipper()->get_position() > 0 && GUI::ERenderPipelineStage::Silhouette != render_pipeline_stage) { const GLGizmosManager& gm = get_gizmos_manager(); wxGetApp().unbind_shader(); @@ -7566,7 +7700,7 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with wxGetApp().bind_shader(shader); } if (m_canvas_type == CanvasView3D && m_gizmos.is_paint_gizmo()) { - m_volumes.only_render_sinking( + cur_volumes.only_render_sinking( render_pipeline_stage, type, false, camera, colors, *m_model, [this, canvas_type](const GLVolume &volume) { if (canvas_type == ECanvasType::CanvasAssembleView) { @@ -7675,7 +7809,13 @@ void GLCanvas3D::_check_and_update_toolbar_icon_scale() return; } - float scale = wxGetApp().toolbar_icon_scale(); + float scale = 1.0f; + + const auto& p_main_toolbar = get_main_toolbar(); + if (p_main_toolbar) { + const bool auto_scale = p_main_toolbar->get_rendering_mode() == GLToolbar::EToolbarRenderingMode::Auto; + scale = wxGetApp().toolbar_icon_scale(auto_scale); + } Size cnv_size = get_canvas_size(); //QDS: GUI refactor: GLToolbar @@ -7687,9 +7827,9 @@ void GLCanvas3D::_check_and_update_toolbar_icon_scale() #if ENABLE_RETINA_GL const float sc = m_retina_helper->get_scale_factor() * scale; //QDS: GUI refactor: GLToolbar - m_main_toolbar.set_scale(sc); - m_assemble_view_toolbar.set_scale(sc); - m_separator_toolbar.set_scale(sc); + if (p_main_toolbar) { + p_main_toolbar->set_scale(sc); + } collapse_toolbar.set_scale(sc); size *= m_retina_helper->get_scale_factor(); @@ -7697,9 +7837,9 @@ void GLCanvas3D::_check_and_update_toolbar_icon_scale() m_notification->set_scale(sc); #else //QDS: GUI refactor: GLToolbar - m_main_toolbar.set_icons_size(GLGizmosManager::Default_Icons_Size * scale); - m_assemble_view_toolbar.set_icons_size(size); - m_separator_toolbar.set_icons_size(size); + if (p_main_toolbar) { + p_main_toolbar->set_icons_size(size); + } collapse_toolbar.set_icons_size(wxGetApp().plater()->get_collapse_toolbar_size()); #endif // ENABLE_RETINA_GL // Update collapse toolbar @@ -7709,13 +7849,18 @@ void GLCanvas3D::_check_and_update_toolbar_icon_scale() #if QDS_TOOLBAR_ON_TOP float collapse_toolbar_width = collapse_toolbar.is_enabled() ? collapse_toolbar.get_width() : GLToolbar::Default_Icons_Size; - float top_tb_width = m_main_toolbar.get_width() + m_gizmos.get_scaled_total_width() + m_assemble_view_toolbar.get_width() + m_separator_toolbar.get_width() + collapse_toolbar_width; - int items_cnt = m_main_toolbar.get_visible_items_cnt() + m_gizmos.get_selectable_icons_cnt() + m_assemble_view_toolbar.get_visible_items_cnt() + m_separator_toolbar.get_visible_items_cnt() + collapse_toolbar.get_visible_items_cnt(); + float top_tb_width = collapse_toolbar_width; + int items_cnt = collapse_toolbar.get_visible_items_cnt(); + if (p_main_toolbar) { + top_tb_width += p_main_toolbar->get_width(); + items_cnt += p_main_toolbar->get_visible_items_cnt(); + } + float noitems_width = top_tb_width - size * items_cnt; // width of separators and borders in top toolbars // calculate scale needed for items in all top toolbars #ifdef __WINDOWS__ - cnv_size.set_width(cnv_size.get_width() + m_separator_toolbar.get_width() + collapse_toolbar_width); + cnv_size.set_width(cnv_size.get_width() + collapse_toolbar_width); #endif float new_h_scale = (cnv_size.get_width() - noitems_width) / (items_cnt * GLToolbar::Default_Icons_Size); @@ -7737,7 +7882,9 @@ void GLCanvas3D::_check_and_update_toolbar_icon_scale() //items_cnt = m_main_toolbar.get_visible_items_cnt() + m_gizmos.get_selectable_icons_cnt() + 3; // +3 means a place for top and view toolbars and separators in gizmos toolbar // calculate scale needed for items in the gizmos toolbar - items_cnt = m_main_toolbar.get_visible_items_cnt() + m_gizmos.get_selectable_icons_cnt() + m_assemble_view_toolbar.get_visible_items_cnt(); + if (p_main_toolbar) { + items_cnt += p_main_toolbar->get_visible_items_cnt(); + } float new_v_scale = cnv_size.get_height() / (items_cnt * GLGizmosManager::Default_Icons_Size); #endif @@ -7752,52 +7899,13 @@ void GLCanvas3D::_check_and_update_toolbar_icon_scale() void GLCanvas3D::_render_overlays() { - glsafe(::glDisable(GL_DEPTH_TEST)); + glsafe(::glDisable(GL_DEPTH_TEST)); _check_and_update_toolbar_icon_scale(); _render_assemble_control(); - // main toolbar and undoredo toolbar need to be both updated before rendering because both their sizes are needed - // to correctly place them -#if ENABLE_RETINA_GL - const float scale = m_retina_helper->get_scale_factor() * wxGetApp().toolbar_icon_scale(/*true*/); - //QDS: GUI refactor: GLToolbar - m_main_toolbar.set_scale(scale); - m_assemble_view_toolbar.set_scale(scale); - m_separator_toolbar.set_scale(scale); - wxGetApp().plater()->get_collapse_toolbar().set_scale(scale); - m_gizmos.set_overlay_scale(scale); -#else - // QDS adjust display scale - const float size = int(GLToolbar::Default_Icons_Size * wxGetApp().toolbar_icon_scale(/*true*/)); - const float gizmo_size = int(GLGizmosManager::Default_Icons_Size * wxGetApp().toolbar_icon_scale()); - //const float size = int(GLToolbar::Default_Icons_Size); - //const float gizmo_size = int(GLGizmosManager::Default_Icons_Size); - - //QDS: GUI refactor: GLToolbar - m_main_toolbar.set_icons_size(gizmo_size); - m_assemble_view_toolbar.set_icons_size(gizmo_size); - m_separator_toolbar.set_icons_size(gizmo_size); - wxGetApp().plater()->get_collapse_toolbar().set_icons_size(wxGetApp().plater()->get_collapse_toolbar_size()); - m_gizmos.set_overlay_icon_size(gizmo_size); -#endif // ENABLE_RETINA_GL - - _render_separator_toolbar_right(); - _render_separator_toolbar_left(); - _render_main_toolbar(); - _render_collapse_toolbar(); - _render_assemble_view_toolbar(); - //QDS: GUI refactor: GLToolbar - _render_imgui_select_plate_toolbar(); - _render_return_toolbar(); - // QDS - //_render_view_toolbar(); - _render_paint_toolbar(); - - //QDS: GUI refactor: GLToolbar - //move gizmos behind of main - _render_gizmos_overlay(); + _render_toolbar(); if (m_layers_editing.last_object_id >= 0 && m_layers_editing.object_max_z() > 0.0f) m_layers_editing.render_overlay(*this); @@ -7976,54 +8084,40 @@ void GLCanvas3D::_render_current_gizmo() const m_gizmos.render_current_gizmo(); } -//QDS: GUI refactor: GLToolbar adjust -//move the size calc to GLCanvas -void GLCanvas3D::_render_gizmos_overlay() -{ -/*#if ENABLE_RETINA_GL -// m_gizmos.set_overlay_scale(m_retina_helper->get_scale_factor()); - const float scale = m_retina_helper->get_scale_factor()*wxGetApp().toolbar_icon_scale(); - m_gizmos.set_overlay_scale(scale); //! #ys_FIXME_experiment -#else -// m_gizmos.set_overlay_scale(m_canvas->GetContentScaleFactor()); -// m_gizmos.set_overlay_scale(wxGetApp().em_unit()*0.1f); - const float size = int(GLGizmosManager::Default_Icons_Size * wxGetApp().toolbar_icon_scale()); - m_gizmos.set_overlay_icon_size(size); //! #ys_FIXME_experiment -#endif /* __WXMSW__ */ - m_gizmos.render_overlay(); - - if (m_gizmo_highlighter.m_render_arrow) - { - m_gizmos.render_arrow(*this, m_gizmo_highlighter.m_gizmo_type); - } -} - //QDS: GUI refactor: GLToolbar adjust //when rendering, {0, 0} is at the center, left-up is -0.5, 0.5, right-up is 0.5, -0.5 void GLCanvas3D::_render_main_toolbar() { - if (!m_main_toolbar.is_enabled()) + const auto& p_main_toolbar = get_main_toolbar(); + if (!p_main_toolbar) { + return; + } + if (!p_main_toolbar->is_enabled()) return; - Size cnv_size = get_canvas_size(); - float inv_zoom = (float)get_active_camera().get_inv_zoom(); + const auto& t_camera = get_active_camera(); -#if QDS_TOOLBAR_ON_TOP - float top = 0.5f * (float)cnv_size.get_height() * inv_zoom; - float left = get_main_toolbar_left(cnv_size.get_width(),inv_zoom); -#else - float gizmo_height = m_gizmos.get_scaled_total_height(); - float space_height = GLGizmosManager::Default_Icons_Size * wxGetApp().toolbar_icon_scale(); - float main_toolbar_height = (float)m_main_toolbar.get_height(); - float assemble_height = m_assemble_view_toolbar.get_height(); - float top = 0.5f * (main_toolbar_height + gizmo_height + assemble_height) * inv_zoom; - float left = (0.5f * (float)cnv_size.get_width() - m_main_toolbar.get_width()) * inv_zoom; - //BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": top %1%, main_toolbar_height %2%, space_height %3% gizmo_height %4%") % top % main_toolbar_height % space_height % gizmo_height; -#endif - m_main_toolbar.set_position(top, left); - m_main_toolbar.render(*this); + if (m_canvas_type == ECanvasType::CanvasAssembleView) { + float inv_zoom = (float)t_camera.get_inv_zoom(); + const auto& t_viewport = t_camera.get_viewport(); + float top = 0.5f * (float)t_viewport[3] * inv_zoom; + float left = 0.5f * m_paint_toolbar_width * inv_zoom; + p_main_toolbar->set_position(top, left); + } + else { + float inv_zoom = (float)t_camera.get_inv_zoom(); + const auto& t_viewport = t_camera.get_viewport(); + float left = get_main_toolbar_left(t_viewport[2], inv_zoom); + if (is_collapse_toolbar_on_left()) { + p_main_toolbar->set_offset(get_collapse_toolbar_width() * 2.0f); + } + else { + p_main_toolbar->set_offset(0.0f); + } + } + p_main_toolbar->render(t_camera); if (m_toolbar_highlighter.m_render_arrow){ - m_main_toolbar.render_arrow(*this, m_toolbar_highlighter.m_toolbar_item); + p_main_toolbar->render_arrow(m_toolbar_highlighter.m_toolbar_item); } } @@ -8037,6 +8131,8 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar() return; } + _update_imgui_select_plate_toolbar(); + IMToolbarItem* all_plates_stats_item = m_sel_plate_toolbar.m_all_plates_stats_item; PartPlateList& plate_list = wxGetApp().plater()->get_partplate_list(); @@ -8373,36 +8469,6 @@ void GLCanvas3D::_render_imgui_select_plate_toolbar() } imgui.end(); - m_sel_plate_toolbar.is_render_finish = true; -} - -//QDS: GUI refactor: GLToolbar adjust -//when rendering, {0, 0} is at the center, {-0.5, 0.5} at the left-up -void GLCanvas3D::_render_assemble_view_toolbar() const -{ - if (!m_assemble_view_toolbar.is_enabled()) - return; - - Size cnv_size = get_canvas_size(); - float inv_zoom = (float)get_active_camera().get_inv_zoom(); - -#if QDS_TOOLBAR_ON_TOP - const float separator_width = m_separator_toolbar.get_width(); - float gizmo_width = m_gizmos.get_scaled_total_width(); - float top = 0.5f * (float)cnv_size.get_height() * inv_zoom; - float main_toolbar_left = get_main_toolbar_left(cnv_size.get_width(), inv_zoom); - float left = main_toolbar_left + (m_main_toolbar.get_width() + gizmo_width + separator_width) * inv_zoom; -#else - float gizmo_height = m_gizmos.get_scaled_total_height(); - //float space_height = GLGizmosManager::Default_Icons_Size * wxGetApp().toolbar_icon_scale(); - float main_toolbar_height = (float)m_main_toolbar.get_height(); - float assemble_height = (float)m_assemble_view_toolbar.get_height(); - float top = 0.5f * (assemble_height - main_toolbar_height - gizmo_height) * inv_zoom; - float left = (0.5f * (float)cnv_size.get_width() - m_assemble_view_toolbar.get_width()) * inv_zoom; - //BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": top %1%, main_toolbar_height %2%, space_height %3% gizmo_height %4%") % top % main_toolbar_height % space_height % gizmo_height; -#endif - m_assemble_view_toolbar.set_position(top, left); - m_assemble_view_toolbar.render(*this); } void GLCanvas3D::_render_return_toolbar() @@ -8425,12 +8491,19 @@ void GLCanvas3D::_render_return_toolbar() {//solve ui overlap issue if (m_canvas_type == ECanvasType::CanvasView3D) { float zoom = (float) get_active_camera().get_zoom(); - float left_pos = m_main_toolbar.get_item("add")->render_left_pos; + float left_pos = 0.0f; + const auto& p_main_toolbar = get_main_toolbar(); + if (p_main_toolbar) { + left_pos = p_main_toolbar->get_item("add")->render_rect[0]; + } const float toolbar_x = 0.5 * canvas_w + left_pos * zoom; const float margin = 5; if (toolbar_x < window_width + margin * 3) { window_pos_x = 5.0f; - window_pos_y = m_main_toolbar.get_height() + 2.0f; + window_pos_y = 2.0f; + if (p_main_toolbar) { + window_pos_y += p_main_toolbar->get_height(); + } } } } @@ -8471,7 +8544,11 @@ void GLCanvas3D::_render_return_toolbar() wxGetApp().plater()->get_view3D_canvas3D()->reload_scene(true); { GLCanvas3D * view_3d = wxGetApp().plater()->get_view3D_canvas3D(); - GLToolbarItem * assembly_item = view_3d->m_assemble_view_toolbar.get_item("assembly_view"); + const auto& p_main_toolbar = view_3d->get_main_toolbar(); + if (!p_main_toolbar) { + return; + } + std::shared_ptr assembly_item = p_main_toolbar->get_item("assembly_view"); std::chrono::system_clock::time_point end = std::chrono::system_clock::now(); std::chrono::duration duration = std::chrono::duration_cast>(end - assembly_item->get_start_time_point()); int times = duration.count(); @@ -8549,43 +8626,6 @@ void GLCanvas3D::_render_fit_camera_toolbar() imgui.end(); } -void GLCanvas3D::_render_separator_toolbar_right() const -{ - if (!m_separator_toolbar.is_enabled()) - return; - - Size cnv_size = get_canvas_size(); - float inv_zoom = (float)get_active_camera().get_inv_zoom(); - - GLToolbar& collapse_toolbar = wxGetApp().plater()->get_collapse_toolbar(); - float collapse_toolbar_width = collapse_toolbar.is_enabled() ? collapse_toolbar.get_width() : 0.0f; - float gizmo_width = m_gizmos.get_scaled_total_width(); - float assemble_width = m_assemble_view_toolbar.get_width(); - float separator_width = m_separator_toolbar.get_width(); - float top = 0.5f * (float)cnv_size.get_height() * inv_zoom; - float main_toolbar_left = get_main_toolbar_left(cnv_size.get_width(), inv_zoom); - float left = main_toolbar_left + (m_main_toolbar.get_width() + gizmo_width + 0.5 * separator_width) * inv_zoom; - - m_separator_toolbar.set_position(top, left); - m_separator_toolbar.render(*this,GLToolbarItem::SeparatorLine); -} - -void GLCanvas3D::_render_separator_toolbar_left() const -{ - if (!m_separator_toolbar.is_enabled()) - return; - - Size cnv_size = get_canvas_size(); - float inv_zoom = (float)get_active_camera().get_inv_zoom(); - float separator_width = m_separator_toolbar.get_width(); - float top = 0.5f * (float)cnv_size.get_height() * inv_zoom; - float main_toolbar_left = get_main_toolbar_left(cnv_size.get_width(), inv_zoom); - float left = main_toolbar_left + (m_main_toolbar.get_width()) * inv_zoom; - - m_separator_toolbar.set_position(top, left); - m_separator_toolbar.render(*this,GLToolbarItem::SeparatorLine); -} - void GLCanvas3D::_render_collapse_toolbar() const { auto & plater = *wxGetApp().plater(); @@ -8595,15 +8635,8 @@ void GLCanvas3D::_render_collapse_toolbar() const } GLToolbar& collapse_toolbar = wxGetApp().plater()->get_collapse_toolbar(); - Size cnv_size = get_canvas_size(); - float inv_zoom = (float)get_active_camera().get_inv_zoom(); - - float top = 0.5f * (float)cnv_size.get_height() * inv_zoom; - //float left = (0.5f * (float)cnv_size.get_width() - (float)collapse_toolbar.get_width() - band) * inv_zoom; - float left = -0.5f * (float)cnv_size.get_width() * inv_zoom; - - collapse_toolbar.set_position(top, left); - collapse_toolbar.render(*this); + const auto& t_camera = get_active_camera(); + collapse_toolbar.render(t_camera); } //QDS reander assemble toolbar @@ -10012,9 +10045,10 @@ void GLCanvas3D::_render_silhouette_effect() const Transform3d& projection_matrix = camera.get_projection_matrix(); const Matrix4d view_proj = projection_matrix.matrix() * view_matrix.matrix(); p_silhouette_shader->set_uniform("u_view_projection_matrix", view_proj); - _render_objects(GLVolumeCollection::ERenderType::Opaque, false); - _render_objects(GLVolumeCollection::ERenderType::Transparent, false); - + _render_objects(m_volumes,GLVolumeCollection::ERenderType::Opaque, false); + _render_objects(m_volumes, GLVolumeCollection::ERenderType::Transparent, false); + _render_objects(m_paint_outline_volumes, GLVolumeCollection::ERenderType::Opaque, false,true); + _render_objects(m_paint_outline_volumes, GLVolumeCollection::ERenderType::Transparent, false, true); wxGetApp().unbind_shader(); // QDS: end render silhouette @@ -10442,6 +10476,27 @@ void GLCanvas3D::_append_to_frame_callback(const FrameCallback& cb) m_frame_callback_list.emplace_back(cb); } +void GLCanvas3D::_render_toolbar() +{ + _render_main_toolbar(); + _render_collapse_toolbar(); + + //QDS: GUI refactor: GLToolbar + _render_imgui_select_plate_toolbar(); + _render_return_toolbar(); + // QDS + //_render_view_toolbar(); + _render_paint_toolbar(); +} + +const std::shared_ptr& GLCanvas3D::get_main_toolbar() const +{ + if (!m_main_toolbar) { + m_main_toolbar = std::make_shared(GLToolbar::EType::Normal, "Main"); + } + return m_main_toolbar; +} + void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector>& extruder_colors, const std::shared_ptr& shader, @@ -10852,6 +10907,10 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state) error = ErrorType::ASSEMBLY_WARNNING; break; } + case EWarning::NozzleFilamentIncompatible: { + text = _u8L(get_nozzle_filament_incompatible_text()); + break; + } } //QDS: this may happened when exit the app, plater is null if (!wxGetApp().plater()) @@ -10869,13 +10928,22 @@ void GLCanvas3D::_set_warning_notification(EWarning warning, bool state) wxString region = L"en"; if (language.find("zh") == 0) region = L"zh"; - wxGetApp().open_browser_with_warning_dialog(wxString::Format(L"https://wiki.qiditech.com/%s/filament-acc/filament/h2d-pla-and-petg-mutual-support", region)); + //y68 + wxGetApp().open_browser_with_warning_dialog(wxString::Format(L"https://wiki.qidi3d.com/%s/Memo/Filament-Guide", region)); return false; }); } else notification_manager.close_slicing_customize_error_notification(NotificationType::QDTMixUsePLAAndPETG, NotificationLevel::WarningNotificationLevel); } + else if (warning == EWarning::NozzleFilamentIncompatible){ + if(state){ + notification_manager.push_slicing_customize_error_notification(NotificationType::QDTNozzleFilamentIncompatible, NotificationLevel::WarningNotificationLevel, text); + } + else{ + notification_manager.close_slicing_customize_error_notification(NotificationType::QDTNozzleFilamentIncompatible, NotificationLevel::WarningNotificationLevel); + } + } else { if (state) notification_manager.push_plater_warning_notification(text); @@ -11076,8 +11144,12 @@ void GLCanvas3D::_update_selection_from_hover() bool GLCanvas3D::_deactivate_arrange_menu() { - if (m_main_toolbar.is_item_pressed("arrange")) { - m_main_toolbar.force_right_action(m_main_toolbar.get_item_id("arrange"), *this); + const auto& p_main_toolbar = get_main_toolbar(); + if (!p_main_toolbar) { + return false; + } + if (p_main_toolbar->is_item_pressed("arrange")) { + p_main_toolbar->force_right_action(p_main_toolbar->get_item_id("arrange"), *this); return true; } @@ -11087,8 +11159,12 @@ bool GLCanvas3D::_deactivate_arrange_menu() //QDS: add deactivate orient menu bool GLCanvas3D::_deactivate_orient_menu() { - if (m_main_toolbar.is_item_pressed("orient")) { - m_main_toolbar.force_right_action(m_main_toolbar.get_item_id("orient"), *this); + const auto& p_main_toolbar = get_main_toolbar(); + if (!p_main_toolbar) { + return false; + } + if (p_main_toolbar->is_item_pressed("orient")) { + p_main_toolbar->force_right_action(p_main_toolbar->get_item_id("orient"), *this); return true; } @@ -11098,8 +11174,12 @@ bool GLCanvas3D::_deactivate_orient_menu() //QDS: add deactivate layersediting menu bool GLCanvas3D::_deactivate_layersediting_menu() { - if (m_main_toolbar.is_item_pressed("layersediting")) { - m_main_toolbar.force_right_action(m_main_toolbar.get_item_id("layersediting"), *this); + const auto& p_main_toolbar = get_main_toolbar(); + if (!p_main_toolbar) { + return false; + } + if (p_main_toolbar->is_item_pressed("layersediting")) { + p_main_toolbar->force_right_action(p_main_toolbar->get_item_id("layersediting"), *this); return true; } @@ -11119,7 +11199,11 @@ bool GLCanvas3D::_deactivate_collapse_toolbar_items() void GLCanvas3D::highlight_toolbar_item(const std::string& item_name) { - GLToolbarItem* item = m_main_toolbar.get_item(item_name); + const auto& p_main_toolbar = get_main_toolbar(); + if (!p_main_toolbar) { + return; + } + std::shared_ptr item = p_main_toolbar->get_item(item_name); if (!item || !item->is_visible()) return; m_toolbar_highlighter.init(item, this); @@ -11194,7 +11278,7 @@ void GLCanvas3D::ToolbarHighlighter::set_timer_owner(wxEvtHandler* owner, int ti m_timer.SetOwner(owner, timerid); } -void GLCanvas3D::ToolbarHighlighter::init(GLToolbarItem* toolbar_item, GLCanvas3D* canvas) +void GLCanvas3D::ToolbarHighlighter::init(const std::shared_ptr& toolbar_item, GLCanvas3D* canvas) { if (m_timer.IsRunning()) invalidate(); @@ -11211,22 +11295,23 @@ void GLCanvas3D::ToolbarHighlighter::invalidate() { m_timer.Stop(); - if (m_toolbar_item) { - m_toolbar_item->set_highlight(GLToolbarItem::EHighlightState::NotHighlighted); + if (const auto& p_toolbar_item = m_toolbar_item.lock()) { + p_toolbar_item->set_highlight(GLToolbarItem::EHighlightState::NotHighlighted); } - m_toolbar_item = nullptr; + m_toolbar_item.reset(); m_blink_counter = 0; m_render_arrow = false; } void GLCanvas3D::ToolbarHighlighter::blink() { - if (m_toolbar_item) { - char state = m_toolbar_item->get_highlight(); + const auto& p_toolbar_item = m_toolbar_item.lock(); + if (p_toolbar_item) { + char state = p_toolbar_item->get_highlight(); if (state != (char)GLToolbarItem::EHighlightState::HighlightedShown) - m_toolbar_item->set_highlight(GLToolbarItem::EHighlightState::HighlightedShown); + p_toolbar_item->set_highlight(GLToolbarItem::EHighlightState::HighlightedShown); else - m_toolbar_item->set_highlight(GLToolbarItem::EHighlightState::HighlightedHidden); + p_toolbar_item->set_highlight(GLToolbarItem::EHighlightState::HighlightedHidden); m_render_arrow = !m_render_arrow; m_canvas->set_as_dirty(); @@ -11321,6 +11406,15 @@ ModelVolume *get_model_volume(const GLVolume &v, const ModelObject& object) { return object.volumes[volume_idx]; } +ModelVolume *get_selected_model_volume(const GLCanvas3D &canvas) +{ + auto gl_volume = get_selected_gl_volume(canvas); + if (gl_volume) { + return get_model_volume(*gl_volume, canvas.get_model()->objects); + } + return nullptr; +} + ModelVolume *get_model_volume(const GLVolume &v, const ModelObjectPtrs &objects) { if (v.object_idx() < 0) diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index a46128d..ca7ce00 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -8,7 +8,6 @@ #include #include -#include "GLToolbar.hpp" #include "Event.hpp" #include "Selection.hpp" #include "Gizmos/GLGizmosManager.hpp" @@ -59,6 +58,9 @@ namespace CustomGCode { struct Item; } namespace GUI { class PartPlateList; +class OpenGLManager; +class GLToolbar; +class GLToolbarItem; #if ENABLE_RETINA_GL class RetinaHelper; @@ -407,6 +409,7 @@ class GLCanvas3D FilamentUnPrintableOnFirstLayer, MixUsePLAAndPETG, PrimeTowerOutside, + NozzleFilamentIncompatible, AsemblyInvalid // for asembly view only }; @@ -564,6 +567,7 @@ public: int GetHoverId(); void set_ignore_left_up() { m_mouse.ignore_left_up = true; } + GLVolumeCollection &get_paint_outline_volumes() { return m_paint_outline_volumes; } private: bool m_is_dark = false; @@ -583,10 +587,8 @@ private: Mouse m_mouse; GLGizmosManager m_gizmos; //QDS: GUI refactor: GLToolbar - mutable GLToolbar m_main_toolbar; - mutable GLToolbar m_separator_toolbar; + mutable std::shared_ptr m_main_toolbar{ nullptr }; mutable IMToolbar m_sel_plate_toolbar; - mutable GLToolbar m_assemble_view_toolbar; mutable IMReturnToolbar m_return_toolbar; mutable Vec2i m_fit_camrea_button_pos = {128, 5}; mutable float m_sc{1}; @@ -604,6 +606,7 @@ private: bool m_extra_frame_requested; bool m_event_handlers_bound{ false }; + GLVolumeCollection m_paint_outline_volumes; GLVolumeCollection m_volumes; GCodeViewer m_gcode_viewer; @@ -747,11 +750,11 @@ public: struct ToolbarHighlighter { void set_timer_owner(wxEvtHandler* owner, int timerid = wxID_ANY); - void init(GLToolbarItem* toolbar_item, GLCanvas3D* canvas); + void init(const std::shared_ptr& toolbar_item, GLCanvas3D* canvas); void blink(); void invalidate(); bool m_render_arrow{ false }; - GLToolbarItem* m_toolbar_item{ nullptr }; + std::weak_ptr m_toolbar_item; private: GLCanvas3D* m_canvas{ nullptr }; int m_blink_counter{ 0 }; @@ -830,8 +833,8 @@ public: const Model* get_model() const { return m_model; } Model& get_ref_model() const { return *m_model; } - const Selection& get_selection() const { return m_selection; } - Selection& get_selection() { return m_selection; } + const Selection& get_selection() const; + Selection& get_selection(); const GLGizmosManager& get_gizmos_manager() const { return m_gizmos; } GLGizmosManager& get_gizmos_manager() { return m_gizmos; } @@ -854,6 +857,9 @@ public: bool get_use_clipping_planes() const { return m_use_clipping_planes; } const std::array &get_clipping_planes() const { return m_clipping_planes; }; + void set_use_color_clip_plane(bool use) { m_volumes.set_use_color_clip_plane(use); } + void set_color_clip_plane(const Vec3d &cp_normal, double offset) { m_volumes.set_color_clip_plane(cp_normal, offset); } + void set_color_clip_plane_colors(const std::array &colors) { m_volumes.set_color_clip_plane_colors(colors); } void set_color_by(const std::string& value); @@ -886,10 +892,7 @@ public: void _update_select_plate_toolbar_stats_item(bool force_selected = false); void reset_select_plate_toolbar_selection(); void enable_select_plate_toolbar(bool enable); - void clear_select_plate_toolbar_render_flag(); - void enable_assemble_view_toolbar(bool enable); void enable_return_toolbar(bool enable); - void enable_separator_toolbar(bool enable); void enable_dynamic_background(bool enable); void enable_labels(bool enable) { m_labels.enable(enable); } void enable_slope(bool enable) { m_slope.enable(enable); } @@ -907,13 +910,10 @@ public: //QDS: GUI refactor: GLToolbar&&gizmo int get_main_toolbar_offset() const; float get_main_toolbar_left(int cnv_width,float inv_zoom) const; - int get_main_toolbar_height() const { return m_main_toolbar.get_height(); } - int get_main_toolbar_width() const { return m_main_toolbar.get_width(); } - float get_assemble_view_toolbar_width() const { return m_assemble_view_toolbar.get_width(); } - float get_assemble_view_toolbar_height() const { return m_assemble_view_toolbar.get_height(); } + int get_main_toolbar_height() const; + int get_main_toolbar_width() const; + float get_main_toolbar_scale() const; float get_assembly_paint_toolbar_width() const { return m_paint_toolbar_width; } - float get_separator_toolbar_width() const { return m_separator_toolbar.get_width(); } - float get_separator_toolbar_height() const { return m_separator_toolbar.get_height(); } bool is_collapse_toolbar_on_left() const; float get_collapse_toolbar_width() const; float get_collapse_toolbar_height() const; @@ -967,7 +967,6 @@ public: void select_curr_plate_all(); void select_object_from_idx(std::vector& object_idxs); void remove_curr_plate_all(); - void update_plate_thumbnails(); void select_all(); void deselect_all(); @@ -1101,9 +1100,9 @@ public: void schedule_extra_frame(int miliseconds); - int get_main_toolbar_item_id(const std::string& name) const { return m_main_toolbar.get_item_id(name); } - void force_main_toolbar_left_action(int item_id) { m_main_toolbar.force_left_action(item_id, *this); } - void force_main_toolbar_right_action(int item_id) { m_main_toolbar.force_right_action(item_id, *this); } + int get_main_toolbar_item_id(const std::string& name) const; + void force_main_toolbar_left_action(int item_id); + void force_main_toolbar_right_action(int item_id); bool has_toolpaths_to_export() const; void export_toolpaths_to_obj(const char* filename) const; @@ -1188,9 +1187,7 @@ private: bool _init_main_toolbar(); bool _init_select_plate_toolbar(); bool _update_imgui_select_plate_toolbar(); - bool _init_assemble_view_toolbar(); bool _init_return_toolbar(); - bool _init_separator_toolbar(); // QDS //bool _init_view_toolbar(); bool _init_collapse_toolbar(); @@ -1215,7 +1212,7 @@ private: void _render_platelist(bool bottom, bool only_current, bool only_body = false, int hover_id = -1, bool render_cali = false, bool show_grid = true) const; void _render_plates_for_picking() const; //QDS: add outline drawing logic - void _render_objects(GLVolumeCollection::ERenderType type, bool with_outline = true); + void _render_objects(GLVolumeCollection& cur_volumes, GLVolumeCollection::ERenderType type, bool with_outline = true,bool in_paint_gizmo = false); //QDS: GUI refactor: add canvas size as parameters void _render_gcode(int canvas_width, int canvas_height); //QDS: render a plane for assemble @@ -1230,14 +1227,10 @@ private: void _render_style_editor(); void _render_volumes_for_picking() const; void _render_current_gizmo() const; - void _render_gizmos_overlay(); void _render_main_toolbar(); void _render_imgui_select_plate_toolbar(); - void _render_assemble_view_toolbar() const; void _render_return_toolbar(); void _render_fit_camera_toolbar(); - void _render_separator_toolbar_right() const; - void _render_separator_toolbar_left() const; void _render_collapse_toolbar() const; // QDS //void _render_view_toolbar() const; @@ -1252,7 +1245,7 @@ private: void _render_selection_sidebar_hints() const; //QDS: GUI refactor: adjust main toolbar position bool _render_orient_menu(float left, float right, float bottom, float top); - bool _render_arrange_menu(float left, float right, float bottom, float top); + bool _render_arrange_menu(float left, float toolbar_height); void _render_3d_navigator(); bool can_show_3d_navigator(); void _update_volumes_hover_state(); @@ -1316,6 +1309,11 @@ private: void _init_unit_cube(); void _append_to_frame_callback(const FrameCallback& cb); + + void _render_toolbar(); + + const std::shared_ptr& get_main_toolbar() const; + static bool is_volume_in_plate_boundingbox(const GLVolume &v, int plate_idx, const BoundingBoxf3 &plate_build_volume); static void _init_fullscreen_mesh(); @@ -1334,6 +1332,7 @@ const ModelVolume *get_model_volume(const GLVolume &v, const Model &model); ModelVolume *get_model_volume(const ObjectID &volume_id, const ModelObjectPtrs &objects); ModelVolume *get_model_volume(const GLVolume &v, const ModelObjectPtrs &objects); ModelVolume *get_model_volume(const GLVolume &v, const ModelObject &object); +ModelVolume *get_selected_model_volume(const GLCanvas3D &canvas); GLVolume *get_first_hovered_gl_volume(const GLCanvas3D &canvas); GLVolume *get_selected_gl_volume(const GLCanvas3D &canvas); diff --git a/src/slic3r/GUI/GLShader.cpp b/src/slic3r/GUI/GLShader.cpp index a0c16b0..2c1ecf8 100644 --- a/src/slic3r/GUI/GLShader.cpp +++ b/src/slic3r/GUI/GLShader.cpp @@ -383,6 +383,16 @@ bool GLShaderProgram::set_uniform(const char* name, const ColorRGB& value) const return false; } +bool GLShaderProgram::set_uniform(const char *name, const ColorRGBA &value) const +{ + int id = get_uniform_location(name); + if (id >= 0) { + glsafe(::glUniform4fv(id, 1, static_cast(value.data()))); + return true; + } + return false; +} + int GLShaderProgram::get_attrib_location(const char* name) const { assert(m_id > 0); diff --git a/src/slic3r/GUI/GLShader.hpp b/src/slic3r/GUI/GLShader.hpp index d48eba8..71eddea 100644 --- a/src/slic3r/GUI/GLShader.hpp +++ b/src/slic3r/GUI/GLShader.hpp @@ -9,7 +9,7 @@ namespace Slic3r { class ColorRGB; - +class ColorRGBA; class GLShaderProgram { friend class GLShadersManager; @@ -63,7 +63,7 @@ public: bool set_uniform(const char* name, const Vec3f& value) const; bool set_uniform(const char* name, const Vec3d& value) const; bool set_uniform(const char* name, const ColorRGB& value) const; - + bool set_uniform(const char *name, const ColorRGBA& value) const; // returns -1 if not found int get_attrib_location(const char* name) const; // returns -1 if not found diff --git a/src/slic3r/GUI/GLTexture.cpp b/src/slic3r/GUI/GLTexture.cpp index 400e39c..8a86aae 100644 --- a/src/slic3r/GUI/GLTexture.cpp +++ b/src/slic3r/GUI/GLTexture.cpp @@ -18,7 +18,7 @@ #include #include #include - +#include "FileHelp.hpp" #define STB_DXT_IMPLEMENTATION #include "stb_dxt/stb_dxt.h" @@ -171,12 +171,13 @@ bool GLTexture::load_from_file(const std::string& filename, bool use_mipmaps, EC bool GLTexture::load_from_svg_file(const std::string& filename, bool use_mipmaps, bool compress, bool apply_anisotropy, unsigned int max_size_px) { reset(); - - if (!boost::filesystem::exists(filename)) + auto svg_file = filename; + Utils::slash_to_back_slash(svg_file); + if (!boost::filesystem::exists(svg_file)) return false; - if (boost::algorithm::iends_with(filename, ".svg")) - return load_from_svg(filename, use_mipmaps, compress, apply_anisotropy, max_size_px); + if (boost::algorithm::iends_with(svg_file, ".svg")) + return load_from_svg(svg_file, use_mipmaps, compress, apply_anisotropy, max_size_px); else return false; } @@ -1074,5 +1075,14 @@ GLModel& GLTexture::get_model_for_render_image() return s_model_for_render_image; } +BackgroundTexture::Metadata::Metadata() + : filename("") + , left(0) + , right(0) + , top(0) + , bottom(0) +{ +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GLTexture.hpp b/src/slic3r/GUI/GLTexture.hpp index ff9f3c4..b49f575 100644 --- a/src/slic3r/GUI/GLTexture.hpp +++ b/src/slic3r/GUI/GLTexture.hpp @@ -152,6 +152,28 @@ namespace GUI { friend class Compressor; }; + struct BackgroundTexture + { + struct Metadata + { + // path of the file containing the background texture + std::string filename; + // size of the left edge, in pixels + unsigned int left; + // size of the right edge, in pixels + unsigned int right; + // size of the top edge, in pixels + unsigned int top; + // size of the bottom edge, in pixels + unsigned int bottom; + + Metadata(); + }; + + GLTexture texture; + Metadata metadata; + }; + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GLToolbar.cpp b/src/slic3r/GUI/GLToolbar.cpp index fdcb61c..c7d49bb 100644 --- a/src/slic3r/GUI/GLToolbar.cpp +++ b/src/slic3r/GUI/GLToolbar.cpp @@ -35,6 +35,7 @@ wxDEFINE_EVENT(EVT_GLTOOLBAR_SEND_TO_PRINTER_ALL, SimpleEvent); wxDEFINE_EVENT(EVT_GLTOOLBAR_PRINT_MULTI_MACHINE, SimpleEvent); wxDEFINE_EVENT(EVT_GLTOOLBAR_SEND_MULTI_APP, SimpleEvent); + wxDEFINE_EVENT(EVT_GLTOOLBAR_ADD, SimpleEvent); wxDEFINE_EVENT(EVT_GLTOOLBAR_DELETE, SimpleEvent); wxDEFINE_EVENT(EVT_GLTOOLBAR_DELETE_ALL, SimpleEvent); @@ -62,7 +63,7 @@ wxDEFINE_EVENT(EVT_GLVIEWTOOLBAR_ASSEMBLE, SimpleEvent); const GLToolbarItem::ActionCallback GLToolbarItem::Default_Action_Callback = [](){}; const GLToolbarItem::VisibilityCallback GLToolbarItem::Default_Visibility_Callback = []()->bool { return true; }; const GLToolbarItem::EnablingCallback GLToolbarItem::Default_Enabling_Callback = []()->bool { return true; }; -const GLToolbarItem::RenderCallback GLToolbarItem::Default_Render_Callback = [](float, float, float, float){}; +const GLToolbarItem::RenderCallback GLToolbarItem::Default_Render_Callback = [](float, float, float, float, float){}; GLToolbarItem::Data::Option::Option() : toggable(false) @@ -73,7 +74,6 @@ GLToolbarItem::Data::Option::Option() GLToolbarItem::Data::Data() : name("") - , icon_filename("") , tooltip("") , additional_tooltip("") , sprite_id(-1) @@ -89,6 +89,66 @@ GLToolbarItem::Data::Data() { } +const GLToolbarItem::Data& GLToolbarItem::get_data() const +{ + return m_data; +} + +void GLToolbarItem::set_visible(bool visible) +{ + m_data.visible = visible; +} + +GLToolbarItem::EType GLToolbarItem::get_type() const +{ + return m_type; +} + +bool GLToolbarItem::is_inside(const Vec2d& scaled_mouse_pos) const +{ + bool inside = (render_rect[0] <= (float)scaled_mouse_pos(0)) + && ((float)scaled_mouse_pos(0) <= render_rect[1]) + && (render_rect[2] <= (float)scaled_mouse_pos(1)) + && ((float)scaled_mouse_pos(1) <= render_rect[3]); + return inside; +} + +bool GLToolbarItem::is_collapsible() const +{ + return m_data.b_collapsible; +} + +bool GLToolbarItem::is_collapse_button() const +{ + return m_data.b_collapse_button; +} + +void GLToolbarItem::set_collapsed(bool value) +{ + if (!is_collapsible()) { + return; + } + m_data.b_collapsed = value; +} + +bool GLToolbarItem::is_collapsed() const +{ + if (!is_collapsible()) { + return false; + } + return m_data.b_collapsed; +} + +bool GLToolbarItem::recheck_pressed() const +{ + bool rt = false; + if (m_data.pressed_recheck_callback) { + const bool recheck_rt = m_data.pressed_recheck_callback(); + rt = (is_pressed() != recheck_rt); + } + return rt; +} + GLToolbarItem::GLToolbarItem(GLToolbarItem::EType type, const GLToolbarItem::Data& data) : m_type(type) , m_state(Normal) @@ -96,7 +156,6 @@ GLToolbarItem::GLToolbarItem(GLToolbarItem::EType type, const GLToolbarItem::Dat , m_last_action_type(Undefined) , m_highlight_state(NotHighlighted) { - render_left_pos = 0.0f; } void GLToolbarItem::set_state(EState state) @@ -136,6 +195,47 @@ void GLToolbarItem::set_state(EState state) m_state = state; } +std::string GLToolbarItem::get_icon_filename(bool is_dark_mode) const +{ + if (m_data.icon_filename_callback) { + return m_data.icon_filename_callback(is_dark_mode); + } + return ""; +} + +void GLToolbarItem::set_last_action_type(GLToolbarItem::EActionType type) +{ + m_last_action_type = type; +} + +void GLToolbarItem::do_left_action() +{ + m_last_action_type = Left; + m_data.left.action_callback(); +} + +void GLToolbarItem::do_right_action() +{ + m_last_action_type = Right; + m_data.right.action_callback(); +} + +bool GLToolbarItem::is_visible() const +{ + bool rt = m_data.visible; + return rt; +} + +bool GLToolbarItem::toggle_disable_others() const +{ + return m_data.b_toggle_disable_others; +} + +bool GLToolbarItem::toggle_affectable() const +{ + return m_data.b_toggle_affectable; +} + bool GLToolbarItem::update_visibility() { bool visible = m_data.visibility_callback(); @@ -157,8 +257,11 @@ bool GLToolbarItem::update_enabled_state() } //QDS: GUI refactor: GLToolbar -void GLToolbarItem::render_text(float left, float right, float bottom, float top) const +void GLToolbarItem::render_text() const { + if (is_collapsed()) { + return; + } float tex_width = (float)m_data.text_texture.get_width(); float tex_height = (float)m_data.text_texture.get_height(); //float inv_tex_width = (tex_width != 0.0f) ? 1.0f / tex_width : 0.0f; @@ -169,7 +272,7 @@ void GLToolbarItem::render_text(float left, float right, float bottom, float top float internal_top_uv = 0.0f; float internal_bottom_uv = (float)m_data.text_texture.m_original_height / tex_height; - GLTexture::render_sub_texture(m_data.text_texture.get_id(), left, right, bottom, top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); + GLTexture::render_sub_texture(m_data.text_texture.get_id(), render_rect[0], render_rect[1], render_rect[2], render_rect[3], {{internal_left_uv, internal_bottom_uv}, {internal_right_uv, internal_bottom_uv}, {internal_right_uv, internal_top_uv}, {internal_left_uv, internal_top_uv}}); } //QDS: GUI refactor: GLToolbar @@ -209,9 +312,9 @@ int GLToolbarItem::generate_image_texture() return ret; } -void GLToolbarItem::render(unsigned int tex_id, float left, float right, float bottom, float top, unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const +void GLToolbarItem::render(unsigned int tex_id, unsigned int tex_width, unsigned int tex_height, unsigned int icon_size, float toolbar_height, bool b_flip_v) const { - auto uvs = [this](unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) -> GLTexture::Quad_UVs + auto uvs = [this](unsigned int tex_width, unsigned int tex_height, unsigned int icon_size, bool b_flip_v) -> GLTexture::Quad_UVs { assert((tex_width != 0) && (tex_height != 0)); GLTexture::Quad_UVs ret; @@ -229,6 +332,10 @@ void GLToolbarItem::render(unsigned int tex_id, float left, float right, float b float right = left + du - u_offset; float top = v_offset + (float)m_data.sprite_id * dv; float bottom = top + dv - v_offset; + + if (b_flip_v) { + std::swap(top, bottom); + } ret.left_top = { left, top }; ret.left_bottom = { left, bottom }; ret.right_bottom = { right, bottom }; @@ -236,14 +343,22 @@ void GLToolbarItem::render(unsigned int tex_id, float left, float right, float b return ret; }; - GLTexture::render_sub_texture(tex_id, left, right, bottom, top, uvs(tex_width, tex_height, icon_size)); + float* t_render_rect = render_rect; + if (is_visible()) { + if (!is_collapsed()) { + GLTexture::render_sub_texture(tex_id, render_rect[0], render_rect[1], render_rect[2], render_rect[3], uvs(tex_width, tex_height, icon_size, b_flip_v)); + } + else if (override_render_rect) { + t_render_rect = override_render_rect; + } + } if (is_pressed()) { if ((m_last_action_type == Left) && m_data.left.can_render()) - m_data.left.render_callback(left, right, bottom, top); + m_data.left.render_callback(t_render_rect[0], t_render_rect[1], t_render_rect[2], t_render_rect[3], toolbar_height); else if ((m_last_action_type == Right) && m_data.right.can_render()) - m_data.right.render_callback(left, right, bottom, top); + m_data.right.render_callback(t_render_rect[0], t_render_rect[1], t_render_rect[2], t_render_rect[3], toolbar_height); } } @@ -252,40 +367,36 @@ void GLToolbarItem::render_image(unsigned int tex_id, float left, float right, f GLTexture::Quad_UVs image_uvs = { { 0.0f, 0.0f }, { 1.0f, 0.0f }, { 1.0f, 1.0f }, { 0.0f, 1.0f } }; //GLTexture::Quad_UVs image_uvs = { { 0.0f, 1.0f }, { 1.0f, 1.0f }, { 1.0f, 0.0f }, { 0.0f, 0.0f } }; - GLTexture::render_sub_texture(tex_id, left, right, bottom, top, image_uvs); + if (is_visible()) { + GLTexture::render_sub_texture(tex_id, left, right, bottom, top, image_uvs); + } if (is_pressed()) { if ((m_last_action_type == Left) && m_data.left.can_render()) - m_data.left.render_callback(left, right, bottom, top); + m_data.left.render_callback(left, right, bottom, top, 0.0f); else if ((m_last_action_type == Right) && m_data.right.can_render()) - m_data.right.render_callback(left, right, bottom, top); + m_data.right.render_callback(left, right, bottom, top, 0.0f); } } -BackgroundTexture::Metadata::Metadata() - : filename("") - , left(0) - , right(0) - , top(0) - , bottom(0) -{ -} - const float GLToolbar::Default_Icons_Size = 40.0f; -GLToolbar::Layout::Layout() +ToolbarLayout::ToolbarLayout() : type(Horizontal) , horizontal_orientation(HO_Center) , vertical_orientation(VO_Center) + , position_mode(EPositionMode::TopMiddle) + , offset(0.0f) , top(0.0f) , left(0.0f) , border(0.0f) , separator_size(0.0f) , gap_size(0.0f) - , icons_size(Default_Icons_Size) + , icons_size(GLToolbar::Default_Icons_Size) , scale(1.0f) , width(0.0f) , height(0.0f) + , collapsed_offset(0.0f) , dirty(true) { } @@ -293,18 +404,12 @@ GLToolbar::Layout::Layout() GLToolbar::GLToolbar(GLToolbar::EType type, const std::string& name) : m_type(type) , m_name(name) - , m_enabled(false) - , m_icons_texture_dirty(true) , m_pressed_toggable_id(-1) { } GLToolbar::~GLToolbar() { - for (GLToolbarItem* item : m_items) - { - delete item; - } } bool GLToolbar::init(const BackgroundTexture::Metadata& background_texture) @@ -338,17 +443,53 @@ bool GLToolbar::init_arrow(const BackgroundTexture::Metadata& arrow_texture) return res; } -GLToolbar::Layout::EType GLToolbar::get_layout_type() const +const ToolbarLayout& GLToolbar::get_layout() const { - return m_layout.type; + if (m_layout.dirty) { + calc_layout(); + } + return m_layout; } -void GLToolbar::set_layout_type(GLToolbar::Layout::EType type) +void GLToolbar::set_layout_type(ToolbarLayout::EType type) { + if (m_layout.type == type) { + return; + } m_layout.type = type; m_layout.dirty = true; } +ToolbarLayout::EHorizontalOrientation GLToolbar::get_horizontal_orientation() const +{ + return m_layout.horizontal_orientation; +} + +void GLToolbar::set_horizontal_orientation(ToolbarLayout::EHorizontalOrientation orientation) +{ + m_layout.horizontal_orientation = orientation; +} + +ToolbarLayout::EVerticalOrientation GLToolbar::get_vertical_orientation() const +{ + return m_layout.vertical_orientation; +} + +void GLToolbar::set_vertical_orientation(ToolbarLayout::EVerticalOrientation orientation) +{ + m_layout.vertical_orientation = orientation; +} + +void GLToolbar::set_position_mode(ToolbarLayout::EPositionMode t_position_mode) +{ + m_layout.position_mode = t_position_mode; +} + +void GLToolbar::set_offset(float offset) +{ + m_layout.offset = offset; +} + void GLToolbar::set_position(float top, float left) { m_layout.top = top; @@ -375,12 +516,12 @@ void GLToolbar::set_gap_size(float size) void GLToolbar::set_icons_size(float size) { - if (m_layout.icons_size != size) - { - m_layout.icons_size = size; - m_layout.dirty = true; - m_icons_texture_dirty = true; + if (abs(m_layout.icons_size - size) < 1e-6f) { + return; } + m_layout.icons_size = size; + m_layout.dirty = true; + m_icons_texture_dirty = true; } void GLToolbar::set_text_size(float size) @@ -401,42 +542,32 @@ void GLToolbar::set_scale(float scale) } } -//QDS: GUI refactor: GLToolbar -bool GLToolbar::add_item(const GLToolbarItem::Data& data, GLToolbarItem::EType type) +float GLToolbar::get_scale() const { - GLToolbarItem* item = new GLToolbarItem(type, data); - if (item == nullptr) - return false; - - m_items.push_back(item); - m_layout.dirty = true; - return true; + return m_layout.scale; } -bool GLToolbar::del_all_item() +void GLToolbar::set_icon_dirty() { - for (int i = 0; i < m_items.size(); i++) { - delete m_items[i]; - m_items[i] = nullptr; - } - m_items.clear(); - m_layout.dirty = true; - return true; + m_icons_texture_dirty = true; } -bool GLToolbar::add_separator() +bool GLToolbar::is_enabled() const { - GLToolbarItem::Data data; - GLToolbarItem* item = new GLToolbarItem(GLToolbarItem::Separator, data); - if (item == nullptr) - return false; - - m_items.push_back(item); - m_layout.dirty = true; - return true; + return m_enabled; } -float GLToolbar::get_width() +void GLToolbar::set_enabled(bool enable) +{ + m_enabled = enable; +} + +float GLToolbar::get_icons_size() const +{ + return m_layout.icons_size; +} + +float GLToolbar::get_width() const { if (m_layout.dirty) calc_layout(); @@ -444,7 +575,7 @@ float GLToolbar::get_width() return m_layout.width; } -float GLToolbar::get_height() +float GLToolbar::get_height() const { if (m_layout.dirty) calc_layout(); @@ -452,12 +583,36 @@ float GLToolbar::get_height() return m_layout.height; } +bool GLToolbar::add_item(const GLToolbarItem::Data& data, GLToolbarItem::EType type) +{ + const auto item = std::make_shared(type, data); + m_items.emplace_back(item); + m_layout.dirty = true; + return true; +} + +bool GLToolbar::add_separator() +{ + GLToolbarItem::Data data; + const auto item = std::make_shared(GLToolbarItem::Separator, data); + m_items.push_back(item); + m_layout.dirty = true; + return true; +} + +bool GLToolbar::del_all_item() +{ + m_items.clear(); + m_layout.dirty = true; + return true; +} + void GLToolbar::select_item(const std::string& name) { if (is_item_disabled(name)) return; - for (GLToolbarItem* item : m_items) + for (const auto& item : m_items) { if (!item->is_disabled()) { @@ -469,7 +624,7 @@ void GLToolbar::select_item(const std::string& name) bool GLToolbar::is_item_pressed(const std::string& name) const { - for (const GLToolbarItem* item : m_items) + for (const auto& item : m_items) { if (item->get_name() == name) return item->is_pressed(); @@ -480,7 +635,7 @@ bool GLToolbar::is_item_pressed(const std::string& name) const bool GLToolbar::is_item_disabled(const std::string& name) const { - for (const GLToolbarItem* item : m_items) + for (const auto& item : m_items) { if (item->get_name() == name) return item->is_disabled(); @@ -491,7 +646,7 @@ bool GLToolbar::is_item_disabled(const std::string& name) const bool GLToolbar::is_item_visible(const std::string& name) const { - for (const GLToolbarItem* item : m_items) + for (const auto& item : m_items) { if (item->get_name() == name) return item->is_visible(); @@ -502,7 +657,7 @@ bool GLToolbar::is_item_visible(const std::string& name) const bool GLToolbar::is_any_item_pressed() const { - for (const GLToolbarItem* item : m_items) + for (const auto& item : m_items) { if (item->is_pressed()) return true; @@ -526,25 +681,41 @@ std::string GLToolbar::get_tooltip() const { std::string tooltip; - for (GLToolbarItem* item : m_items) + for (const auto& item : m_items) { + if (item->is_collapsed()) { + continue; + } if (item->is_hovered()) { - tooltip = item->get_tooltip(); - if (!item->is_enabled()) - { - const std::string& additional_tooltip = item->get_additional_tooltip(); - if (!additional_tooltip.empty()) - tooltip += ":\n" + additional_tooltip; - + const auto t_item_data = item->get_data(); + if (t_item_data.on_hover) { + tooltip = t_item_data.on_hover(); break; } + else { + tooltip = item->get_tooltip(); + if (!item->is_enabled()) + { + const std::string& additional_tooltip = item->get_additional_tooltip(); + if (!additional_tooltip.empty()) + tooltip += ":\n" + additional_tooltip; + + break; + } + } } } return tooltip; } +void GLToolbar::set_tooltip(int item_id, const std::string& text) +{ + if (0 <= item_id && item_id < (int)m_items.size()) + m_items[item_id]->set_tooltip(text); +} + void GLToolbar::get_additional_tooltip(int item_id, std::string& text) { if (0 <= item_id && item_id < (int)m_items.size()) @@ -562,10 +733,290 @@ void GLToolbar::set_additional_tooltip(int item_id, const std::string& text) m_items[item_id]->set_additional_tooltip(text); } -void GLToolbar::set_tooltip(int item_id, const std::string& text) +int GLToolbar::get_visible_items_cnt() const { - if (0 <= item_id && item_id < (int)m_items.size()) - m_items[item_id]->set_tooltip(text); + int cnt = 0; + for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i) + if (m_items[i]->is_visible() && !m_items[i]->is_separator()) + cnt++; + + return cnt; +} + +const std::shared_ptr& GLToolbar::get_item(const std::string& item_name) const +{ + if (m_enabled) + { + for (const auto& item : m_items) + { + if (item->get_name() == item_name) + { + return item; + } + } + } + static std::shared_ptr s_empty{ nullptr }; + return s_empty; +} + +void GLToolbar::calc_layout() const +{ + switch (m_layout.type) + { + default: + case ToolbarLayout::EType::Horizontal: + { + m_layout.width = get_width_horizontal(); + m_layout.height = get_height_horizontal(); + break; + } + case ToolbarLayout::EType::Vertical: + { + m_layout.width = get_width_vertical(); + m_layout.height = get_height_vertical(); + break; + } + } + + m_layout.dirty = false; +} + +const std::shared_ptr& GLToolbar::get_renderer() const +{ + if (!m_p_renderer || m_p_renderer->get_mode() != m_rendering_mode) { + switch (m_rendering_mode) { + case EToolbarRenderingMode::KeepSize: + m_p_renderer = std::make_shared(); + break; + case EToolbarRenderingMode::Auto: + default: + m_p_renderer = std::make_shared(); + break; + } + for (const auto& p_item : m_items) { + p_item->set_collapsed(false); + } + } + return m_p_renderer; +} + +float GLToolbar::get_width_horizontal() const +{ + float size = 2.0f * m_layout.border; + for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i) + { + if (!m_items[i]->is_visible()) + continue; + + if (m_items[i]->is_separator()) + size += m_layout.separator_size; + else if (m_items[i]->get_type() == GLToolbarItem::EType::SeparatorLine) { + size += ((float)m_layout.icons_size * 0.5f); + } + else + { + size += (float)m_layout.icons_size; + if (m_items[i]->is_action_with_text()) + size += m_items[i]->get_extra_size_ratio() * m_layout.icons_size; + if (m_items[i]->is_action_with_text_image()) + size += m_layout.text_size; + } + + if (i < m_items.size() - 1) { + size += m_layout.gap_size; + } + } + + return size * m_layout.scale; +} + +float GLToolbar::get_width_vertical() const +{ + float max_extra_text_size = 0.0; + for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i) + { + if (m_items[i]->is_action_with_text()) + { + float temp_size = m_items[i]->get_extra_size_ratio() * m_layout.icons_size; + + max_extra_text_size = (temp_size > max_extra_text_size) ? temp_size : max_extra_text_size; + } + + if (m_items[i]->is_action_with_text_image()) + { + max_extra_text_size = m_layout.text_size; + } + } + + return (2.0f * m_layout.border + m_layout.icons_size + max_extra_text_size) * m_layout.scale; +} + +float GLToolbar::get_height_horizontal() const +{ + return (2.0f * m_layout.border + m_layout.icons_size) * m_layout.scale; +} + +float GLToolbar::get_height_vertical() const +{ + return get_main_size(); +} + +float GLToolbar::get_main_size() const +{ + float size = 2.0f * m_layout.border; + for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i) + { + if (!m_items[i]->is_visible()) + continue; + + if (m_items[i]->is_separator()) + size += m_layout.separator_size; + else + size += (float)m_layout.icons_size; + } + + if (m_items.size() > 1) + size += ((float)m_items.size() - 1.0f) * m_layout.gap_size; + + return size * m_layout.scale; +} + +bool GLToolbar::generate_icons_texture() const +{ + std::string path = resources_dir() + "/images/"; + std::vector filenames; + for (const auto& item : m_items) { + const std::string icon_filename = item->get_icon_filename(m_b_dark_mode_enabled); + if (!icon_filename.empty()) + filenames.push_back(path + icon_filename); + } + + std::vector> states; + //1: white only, 2: gray only, 0 : normal + //true/false: apply background or not + if (m_type == Normal) { + states.push_back({ 1, false }); // Normal + states.push_back({ 0, false }); // Pressed + states.push_back({ 2, false }); // Disabled + states.push_back({ 0, false }); // Hover + states.push_back({ 0, false }); // HoverPressed + states.push_back({ 2, false }); // HoverDisabled + states.push_back({ 0, false }); // HighlightedShown + states.push_back({ 2, false }); // HighlightedHidden + } + else { + states.push_back({ 1, false }); // Normal + states.push_back({ 0, true }); // Pressed + states.push_back({ 2, false }); // Disabled + states.push_back({ 0, false }); // Hover + states.push_back({ 1, true }); // HoverPressed + states.push_back({ 1, false }); // HoverDisabled + states.push_back({ 0, false }); // HighlightedShown + states.push_back({ 1, false }); // HighlightedHidden + } + + unsigned int sprite_size_px = (unsigned int)(m_layout.icons_size * m_layout.scale); + // // force even size + // if (sprite_size_px % 2 != 0) + // sprite_size_px += 1; + + bool res = m_icons_texture.load_from_svg_files_as_sprites_array(filenames, states, sprite_size_px, false); + if (res) + m_icons_texture_dirty = false; + + return res; +} + +bool GLToolbar::update_items_visibility() +{ + bool ret = false; + + for (const auto& item : m_items) { + ret |= item->update_visibility(); + } + + if (ret) + m_layout.dirty = true; + + // updates separators visibility to avoid having two of them consecutive + bool any_item_visible = false; + for (const auto& item : m_items) { + if (!item->is_separator()) + any_item_visible |= item->is_visible(); + else { + item->set_visible(any_item_visible); + any_item_visible = false; + } + } + + return ret; +} + +bool GLToolbar::update_items_enabled_state() +{ + bool ret = false; + + for (int i = 0; i < (int)m_items.size(); ++i) + { + const auto& item = m_items[i]; + ret |= item->update_enabled_state(); + if ((m_pressed_toggable_id != -1) && (m_pressed_toggable_id != i)) { + const auto& pressed_item = m_items[m_pressed_toggable_id]; + if (pressed_item->toggle_disable_others()) { + if (item->is_enabled() && item->toggle_affectable()) { + ret = true; + item->set_state(GLToolbarItem::Disabled); + } + } + } + } + + if (ret) + m_layout.dirty = true; + + return ret; +} + +bool GLToolbar::update_items_pressed_state() +{ + bool ret = false; + + for (int i = 0; i < (int)m_items.size(); ++i) + { + const auto& item = m_items[i]; + if (!item) { + continue; + } + + if (!item->recheck_pressed()) { + continue; + } + ret = true; + if (item->is_pressed()) { + item->set_state(GLToolbarItem::EState::Normal); + } + else { + item->set_state(GLToolbarItem::EState::Pressed); + m_pressed_toggable_id = i; + item->set_last_action_type(GLToolbarItem::EActionType::Left); + set_collapsed(); + } + } + + return ret; +} + +void GLToolbar::render(const Camera& t_camera) +{ + if (!m_enabled || m_items.empty()) + return; + + const auto& p_renderer = get_renderer(); + + if (!p_renderer) { + return; + } + p_renderer->render(*this, t_camera); } bool GLToolbar::update_items_state() @@ -573,29 +1024,102 @@ bool GLToolbar::update_items_state() bool ret = false; ret |= update_items_visibility(); ret |= update_items_enabled_state(); + ret |= update_items_pressed_state(); if (!is_any_item_pressed()) m_pressed_toggable_id = -1; return ret; } -void GLToolbar::render(const GLCanvas3D& parent,GLToolbarItem::EType type) +void GLToolbar::set_dark_mode_enabled(bool is_enabled) { - if (!m_enabled || m_items.empty()) + if (m_b_dark_mode_enabled == is_enabled) { return; - - if (m_icons_texture_dirty) - generate_icons_texture(); - - switch (m_layout.type) - { - default: - case Layout::Horizontal: { render_horizontal(parent,type); break; } - case Layout::Vertical: { render_vertical(parent); break; } } + + m_b_dark_mode_enabled = is_enabled; + set_icon_dirty(); } +const std::vector>& GLToolbar::get_items() const +{ + return m_items; +} +const GLTexture& GLToolbar::get_icon_texture() const +{ + if (m_icons_texture_dirty) { + generate_icons_texture(); + } + return m_icons_texture; +} + +const BackgroundTexture& GLToolbar::get_background_texture() const +{ + return m_background_texture; +} + +const BackgroundTexture& GLToolbar::get_arrow_texture() const +{ + return m_arrow_texture; +} + +bool GLToolbar::needs_collapsed() const +{ + const auto& p_renderer = get_renderer(); + if (!p_renderer) { + return false; + } + return p_renderer->needs_collapsed(); +} + +void GLToolbar::toggle_collapsed() +{ + m_b_collapsed = !m_b_collapsed; +} + +void GLToolbar::set_collapsed() +{ + m_b_collapsed = true; +} + +bool GLToolbar::is_collapsed() const +{ + return m_b_collapsed; +} + +void GLToolbar::set_collapsed_offset(uint32_t offset_in_pixel) +{ + m_layout.collapsed_offset = offset_in_pixel; + m_layout.dirty = true; +} + +uint32_t GLToolbar::get_collapsed_offset() +{ + return m_layout.collapsed_offset; +} + +GLToolbar::EToolbarRenderingMode GLToolbar::get_rendering_mode() +{ + return m_rendering_mode; +} + +void GLToolbar::set_rendering_mode(EToolbarRenderingMode mode) +{ + m_rendering_mode = mode; +} + +void GLToolbar::render_arrow(const std::weak_ptr& highlighted_item) +{ + if (!m_enabled || m_items.empty()) { + return; + } + const auto& p_renderer = get_renderer(); + if (!p_renderer) { + return; + } + p_renderer->render_arrow(*this, highlighted_item); +} bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent) { @@ -657,11 +1181,25 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent) m_mouse_capture.left = true; m_mouse_capture.parent = &parent; processed = true; - if (item_id != -2 && !m_items[item_id]->is_separator() && !m_items[item_id]->is_disabled() && - (m_pressed_toggable_id == -1 || m_items[item_id]->get_last_action_type() == GLToolbarItem::Left)) { + bool rt = item_id != -2 && !m_items[item_id]->is_separator() && !m_items[item_id]->is_disabled() && + (m_pressed_toggable_id == -1 + || m_items[item_id]->get_last_action_type() == GLToolbarItem::Left); + if (!rt) { + if (item_id >= 0 && item_id < m_items.size()) { + rt = !m_items[item_id]->toggle_affectable(); + } + } + if (!rt) { + if (m_pressed_toggable_id >= 0 && m_pressed_toggable_id < m_items.size()) { + rt = !m_items[m_pressed_toggable_id]->toggle_disable_others(); + } + } + if (rt) { // mouse is inside an icon do_action(GLToolbarItem::Left, item_id, parent, true); parent.set_as_dirty(); + evt.StopPropagation(); + processed = true; } } else if (evt.MiddleDown()) { @@ -677,6 +1215,8 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent) // mouse is inside an icon do_action(GLToolbarItem::Right, item_id, parent, true); parent.set_as_dirty(); + evt.StopPropagation(); + processed = true; } } } @@ -684,179 +1224,80 @@ bool GLToolbar::on_mouse(wxMouseEvent& evt, GLCanvas3D& parent) return processed; } -void GLToolbar::calc_layout() -{ - switch (m_layout.type) - { - default: - case Layout::Horizontal: - { - m_layout.width = get_width_horizontal(); - m_layout.height = get_height_horizontal(); - break; - } - case Layout::Vertical: - { - m_layout.width = get_width_vertical(); - m_layout.height = get_height_vertical(); - break; - } - } - - m_layout.dirty = false; -} - -//QDS: GUI refactor: GLToolbar -float GLToolbar::get_width_horizontal() const -{ - float size = 2.0f * m_layout.border; - for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i) - { - if (!m_items[i]->is_visible()) - continue; - - if (m_items[i]->is_separator()) - size += m_layout.separator_size; - else - { - size += (float)m_layout.icons_size; - if (m_items[i]->is_action_with_text()) - size += m_items[i]->get_extra_size_ratio() * m_layout.icons_size; - if (m_items[i]->is_action_with_text_image()) - size += m_layout.text_size; - } - } - - if (m_items.size() > 1) - size += ((float)m_items.size() - 1.0f) * m_layout.gap_size; - - return size * m_layout.scale; -} - -//QDS: GUI refactor: GLToolbar -float GLToolbar::get_width_vertical() const -{ - float max_extra_text_size = 0.0; - for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i) - { - if (m_items[i]->is_action_with_text()) - { - float temp_size = m_items[i]->get_extra_size_ratio() * m_layout.icons_size; - - max_extra_text_size = (temp_size > max_extra_text_size) ? temp_size : max_extra_text_size; - } - - if (m_items[i]->is_action_with_text_image()) - { - max_extra_text_size = m_layout.text_size; - } - } - - return (2.0f * m_layout.border + m_layout.icons_size + max_extra_text_size) * m_layout.scale; -} - -float GLToolbar::get_height_horizontal() const -{ - return (2.0f * m_layout.border + m_layout.icons_size) * m_layout.scale; -} - -float GLToolbar::get_height_vertical() const -{ - return get_main_size(); -} - -float GLToolbar::get_main_size() const -{ - float size = 2.0f * m_layout.border; - for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i) - { - if (!m_items[i]->is_visible()) - continue; - - if (m_items[i]->is_separator()) - size += m_layout.separator_size; - else - size += (float)m_layout.icons_size; - } - - if (m_items.size() > 1) - size += ((float)m_items.size() - 1.0f) * m_layout.gap_size; - - return size * m_layout.scale; -} - -int GLToolbar::get_visible_items_cnt() const -{ - int cnt = 0; - for (unsigned int i = 0; i < (unsigned int)m_items.size(); ++i) - if (m_items[i]->is_visible() && !m_items[i]->is_separator()) - cnt++; - - return cnt; -} - void GLToolbar::do_action(GLToolbarItem::EActionType type, int item_id, GLCanvas3D& parent, bool check_hover) { - if ((m_pressed_toggable_id == -1) || (m_pressed_toggable_id == item_id)) - { - if ((0 <= item_id) && (item_id < (int)m_items.size())) - { - GLToolbarItem* item = m_items[item_id]; - if ((item != nullptr) && !item->is_separator() && !item->is_disabled() && (!check_hover || item->is_hovered())) - { - if (((type == GLToolbarItem::Right) && item->is_right_toggable()) || - ((type == GLToolbarItem::Left) && item->is_left_toggable())) - { - GLToolbarItem::EState state = item->get_state(); - if (state == GLToolbarItem::Hover) - item->set_state(GLToolbarItem::HoverPressed); - else if (state == GLToolbarItem::HoverPressed) - item->set_state(GLToolbarItem::Hover); - else if (state == GLToolbarItem::Pressed) - item->set_state(GLToolbarItem::Normal); - else if (state == GLToolbarItem::Normal) - item->set_state(GLToolbarItem::Pressed); - - m_pressed_toggable_id = item->is_pressed() ? item_id : -1; - item->reset_last_action_type(); - - parent.render(); - switch (type) - { - default: - case GLToolbarItem::Left: { item->do_left_action(); break; } - case GLToolbarItem::Right: { item->do_right_action(); break; } - } - } - else - { - if (m_type == Radio) - select_item(item->get_name()); - else - item->set_state(item->is_hovered() ? GLToolbarItem::HoverPressed : GLToolbarItem::Pressed); - - item->reset_last_action_type(); - parent.render(); - switch (type) - { - default: - case GLToolbarItem::Left: { item->do_left_action(); break; } - case GLToolbarItem::Right: { item->do_right_action(); break; } - } - if (item->get_continuous_click_flag()) { - item->set_state(GLToolbarItem::Hover); - parent.render(); - } - else if ((m_type == Normal) && (item->get_state() != GLToolbarItem::Disabled) && !item->get_continuous_click_flag()) - { - // the item may get disabled during the action, if not, set it back to normal state - item->set_state(GLToolbarItem::Normal); - parent.render(); - } - } - } - } + if (item_id < 0 || item_id >= (int)m_items.size()) { + return; } + const auto& item = m_items[item_id]; + if (!item || item->is_separator() || item->is_disabled() || (check_hover && !item->is_hovered())) { + return; + } + + auto do_item_action = [this](GLToolbarItem::EActionType type, const std::shared_ptr& item, int item_id, GLCanvas3D& parent)->void { + if (((type == GLToolbarItem::Right) && item->is_right_toggable()) || + ((type == GLToolbarItem::Left) && item->is_left_toggable())) + { + GLToolbarItem::EState state = item->get_state(); + if (state == GLToolbarItem::Hover) + item->set_state(GLToolbarItem::HoverPressed); + else if (state == GLToolbarItem::HoverPressed) + item->set_state(GLToolbarItem::Hover); + else if (state == GLToolbarItem::Pressed) + item->set_state(GLToolbarItem::Normal); + else if (state == GLToolbarItem::Normal) + item->set_state(GLToolbarItem::Pressed); + + m_pressed_toggable_id = item->is_pressed() ? item_id : -1; + item->reset_last_action_type(); + + switch (type) + { + default: + case GLToolbarItem::Left: { item->do_left_action(); break; } + case GLToolbarItem::Right: { item->do_right_action(); break; } + } + + parent.set_as_dirty(); + } + else + { + if (m_type == Radio) + select_item(item->get_name()); + else + item->set_state(item->is_hovered() ? GLToolbarItem::HoverPressed : GLToolbarItem::Pressed); + + item->reset_last_action_type(); + switch (type) + { + default: + case GLToolbarItem::Left: { item->do_left_action(); break; } + case GLToolbarItem::Right: { item->do_right_action(); break; } + } + if (item->get_continuous_click_flag()) { + item->set_state(GLToolbarItem::Hover); + } + else if ((m_type == Normal) && (item->get_state() != GLToolbarItem::Disabled) && !item->get_continuous_click_flag()) + { + // the item may get disabled during the action, if not, set it back to normal state + item->set_state(GLToolbarItem::Normal); + } + + parent.set_as_dirty(); + } + + if (item->is_collapse_button()) { + toggle_collapsed(); + } + else { + set_collapsed(); + } + }; + if ((m_pressed_toggable_id != -1) && (m_pressed_toggable_id != item_id)) + { + do_item_action(type, m_items[m_pressed_toggable_id], m_pressed_toggable_id, parent); + } + do_item_action(type, item, item_id, parent); } void GLToolbar::update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent) @@ -864,793 +1305,111 @@ void GLToolbar::update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent) if (!m_enabled) return; - switch (m_layout.type) - { - default: - case Layout::Horizontal: { update_hover_state_horizontal(mouse_pos, parent); break; } - case Layout::Vertical: { update_hover_state_vertical(mouse_pos, parent); break; } - } -} - -void GLToolbar::update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent) -{ - // NB: mouse_pos is already scaled appropriately - float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); - float factor = m_layout.scale * inv_zoom; - Size cnv_size = parent.get_canvas_size(); Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); - float scaled_icons_size = m_layout.icons_size * factor; - float scaled_separator_size = m_layout.separator_size * factor; - float scaled_gap_size = m_layout.gap_size * factor; - float scaled_border = m_layout.border * factor; - - float separator_stride = scaled_separator_size + scaled_gap_size; - float icon_stride = scaled_icons_size + scaled_gap_size; - - float left = m_layout.left + scaled_border; - float top = m_layout.top - scaled_border; - - for (GLToolbarItem* item : m_items) + for (const auto& item : m_items) { if (!item->is_visible()) continue; - if (item->is_separator()) - left += separator_stride; - else - { - float right = left + scaled_icons_size; - float bottom = top - scaled_icons_size; - - //QDS: GUI refactor: GLToolbar - if (item->is_action_with_text()) - right += scaled_icons_size * item->get_extra_size_ratio(); - GLToolbarItem::EState state = item->get_state(); - bool inside = (left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top); - - switch (state) - { - case GLToolbarItem::Normal: - { - if (inside) - { - item->set_state(GLToolbarItem::Hover); - parent.set_as_dirty(); - } - - break; - } - case GLToolbarItem::Hover: - { - if (!inside) - { - item->set_state(GLToolbarItem::Normal); - parent.set_as_dirty(); - } - - break; - } - case GLToolbarItem::Pressed: - { - if (inside) - { - item->set_state(GLToolbarItem::HoverPressed); - parent.set_as_dirty(); - } - - break; - } - case GLToolbarItem::HoverPressed: - { - if (!inside) - { - item->set_state(GLToolbarItem::Pressed); - parent.set_as_dirty(); - } - - break; - } - case GLToolbarItem::Disabled: - { - if (inside) - { - item->set_state(GLToolbarItem::HoverDisabled); - parent.set_as_dirty(); - } - - break; - } - case GLToolbarItem::HoverDisabled: - { - if (!inside) - { - item->set_state(GLToolbarItem::Disabled); - parent.set_as_dirty(); - } - - break; - } - default: - { - break; - } - } - - left += icon_stride; - //QDS: GUI refactor: GLToolbar - if (item->is_action_with_text()) - left += scaled_icons_size * item->get_extra_size_ratio(); - } - } -} - -void GLToolbar::update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent) -{ - // NB: mouse_pos is already scaled appropriately - - float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); - float factor = m_layout.scale * inv_zoom; - - Size cnv_size = parent.get_canvas_size(); - Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); - - float scaled_icons_size = m_layout.icons_size * factor; - float scaled_separator_size = m_layout.separator_size * factor; - float scaled_gap_size = m_layout.gap_size * factor; - float scaled_border = m_layout.border * factor; - float separator_stride = scaled_separator_size + scaled_gap_size; - float icon_stride = scaled_icons_size + scaled_gap_size; - - float left = m_layout.left + scaled_border; - float top = m_layout.top - scaled_border; - - for (GLToolbarItem* item : m_items) - { - if (!item->is_visible()) + if (item->is_separator()) { continue; + } - if (item->is_separator()) - top -= separator_stride; - else + if (item->get_type() == GLToolbarItem::EType::SeparatorLine) { + continue; + } + bool inside = item->is_inside(scaled_mouse_pos); + GLToolbarItem::EState state = item->get_state(); + switch (state) { - float right = left + scaled_icons_size; - float bottom = top - scaled_icons_size; - - if (item->is_action_with_text_image()) - right += m_layout.text_size * factor; - - //QDS: GUI refactor: GLToolbar - if (item->is_action_with_text()) - right += scaled_icons_size * item->get_extra_size_ratio(); - - GLToolbarItem::EState state = item->get_state(); - bool inside = (left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top); - - switch (state) + case GLToolbarItem::Normal: + { + if (inside) { - case GLToolbarItem::Normal: - { - if (inside) - { - item->set_state(GLToolbarItem::Hover); - parent.set_as_dirty(); - } - - break; - } - case GLToolbarItem::Hover: - { - if (!inside) - { - item->set_state(GLToolbarItem::Normal); - parent.set_as_dirty(); - } - - break; - } - case GLToolbarItem::Pressed: - { - if (inside) - { - item->set_state(GLToolbarItem::HoverPressed); - parent.set_as_dirty(); - } - - break; - } - case GLToolbarItem::HoverPressed: - { - if (!inside) - { - item->set_state(GLToolbarItem::Pressed); - parent.set_as_dirty(); - } - - break; - } - case GLToolbarItem::Disabled: - { - if (inside) - { - item->set_state(GLToolbarItem::HoverDisabled); - parent.set_as_dirty(); - } - - break; - } - case GLToolbarItem::HoverDisabled: - { - if (!inside) - { - item->set_state(GLToolbarItem::Disabled); - parent.set_as_dirty(); - } - - break; - } - default: - { - break; - } + item->set_state(GLToolbarItem::Hover); + parent.set_as_dirty(); } - top -= icon_stride; + break; + } + case GLToolbarItem::Hover: + { + if (!inside) + { + item->set_state(GLToolbarItem::Normal); + parent.set_as_dirty(); + } + + break; + } + case GLToolbarItem::Pressed: + { + if (inside) + { + item->set_state(GLToolbarItem::HoverPressed); + parent.set_as_dirty(); + } + + break; + } + case GLToolbarItem::HoverPressed: + { + if (!inside) + { + item->set_state(GLToolbarItem::Pressed); + parent.set_as_dirty(); + } + + break; + } + case GLToolbarItem::Disabled: + { + if (inside) + { + item->set_state(GLToolbarItem::HoverDisabled); + parent.set_as_dirty(); + } + + break; + } + case GLToolbarItem::HoverDisabled: + { + if (!inside) + { + item->set_state(GLToolbarItem::Disabled); + parent.set_as_dirty(); + } + + break; + } + default: + { + break; + } } } } -GLToolbarItem* GLToolbar::get_item(const std::string& item_name) -{ - if (!m_enabled) - return nullptr; - - for (GLToolbarItem* item : m_items) - { - if (item->get_name() == item_name) - { - return item; - } - } - return nullptr; -} - int GLToolbar::contains_mouse(const Vec2d& mouse_pos, const GLCanvas3D& parent) const { if (!m_enabled) return -1; - switch (m_layout.type) - { - default: - case Layout::Horizontal: { return contains_mouse_horizontal(mouse_pos, parent); } - case Layout::Vertical: { return contains_mouse_vertical(mouse_pos, parent); } - } -} - -int GLToolbar::contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3D& parent) const -{ - // NB: mouse_pos is already scaled appropriately - - float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); - float factor = m_layout.scale * inv_zoom; - - Size cnv_size = parent.get_canvas_size(); - Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); - - float scaled_icons_size = m_layout.icons_size * factor; - float scaled_separator_size = m_layout.separator_size * factor; - float scaled_gap_size = m_layout.gap_size * factor; - float scaled_border = m_layout.border * factor; - - float left = m_layout.left + scaled_border; - float top = m_layout.top - scaled_border; - - - for (size_t id=0; idis_visible()) + for (size_t i = 0; i < m_items.size(); ++i) { + if (m_items[i]->is_collapsed()) { continue; - - if (item->is_separator()) - { - float right = left + scaled_separator_size; - float bottom = top - scaled_icons_size; - - // mouse inside the separator - if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top)) - return id; - - left = right; - right += scaled_gap_size; - - if (id < m_items.size() - 1) - { - // mouse inside the gap - if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top)) - return -2; - } - - left = right; } - else - { - float right = left + scaled_icons_size; - float bottom = top - scaled_icons_size; - - //QDS: GUI refactor: GLToolbar - if (item->is_action_with_text()) - right += scaled_icons_size * item->get_extra_size_ratio(); - - // mouse inside the icon - if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top)) - return id; - - left = right; - right += scaled_gap_size; - - if (id < m_items.size() - 1) - { - // mouse inside the gap - if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top)) - return -2; - } - - left = right; + if (m_items[i]->is_hovered()) { + return i; } } return -1; } -int GLToolbar::contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D& parent) const -{ - // NB: mouse_pos is already scaled appropriately - - float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); - float factor = m_layout.scale * inv_zoom; - - Size cnv_size = parent.get_canvas_size(); - Vec2d scaled_mouse_pos((mouse_pos(0) - 0.5 * (double)cnv_size.get_width()) * inv_zoom, (0.5 * (double)cnv_size.get_height() - mouse_pos(1)) * inv_zoom); - - float scaled_icons_size = m_layout.icons_size * factor; - float scaled_separator_size = m_layout.separator_size * factor; - float scaled_gap_size = m_layout.gap_size * factor; - float scaled_border = m_layout.border * factor; - - float left = m_layout.left + scaled_border; - float top = m_layout.top - scaled_border; - - for (size_t id=0; idis_visible()) - continue; - - if (item->is_separator()) - { - float right = left + scaled_icons_size; - float bottom = top - scaled_separator_size; - - // mouse inside the separator - if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top)) - return id; - - top = bottom; - bottom -= scaled_gap_size; - - if (id < m_items.size() - 1) - { - // mouse inside the gap - if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top)) - return -2; - } - - top = bottom; - } - else - { - float right = left + scaled_icons_size; - float bottom = top - scaled_icons_size; - - if (item->is_action_with_text_image()) - right += m_layout.text_size * factor; - - //QDS: GUI refactor: GLToolbar - if (item->is_action_with_text()) - right += scaled_icons_size * item->get_extra_size_ratio(); - - // mouse inside the icon - if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top)) - return id; - - top = bottom; - bottom -= scaled_gap_size; - - if (id < m_items.size() - 1) - { - // mouse inside the gap - if ((left <= (float)scaled_mouse_pos(0)) && ((float)scaled_mouse_pos(0) <= right) && (bottom <= (float)scaled_mouse_pos(1)) && ((float)scaled_mouse_pos(1) <= top)) - return -2; - } - - top = bottom; - } - } - - return -1; -} - -void GLToolbar::render_background(float left, float top, float right, float bottom, float border) const -{ - unsigned int tex_id = m_background_texture.texture.get_id(); - float tex_width = (float)m_background_texture.texture.get_width(); - float tex_height = (float)m_background_texture.texture.get_height(); - if ((tex_id != 0) && (tex_width > 0) && (tex_height > 0)) - { - float inv_tex_width = (tex_width != 0.0f) ? 1.0f / tex_width : 0.0f; - float inv_tex_height = (tex_height != 0.0f) ? 1.0f / tex_height : 0.0f; - - float internal_left = left + border; - float internal_right = right - border; - float internal_top = top - border; - float internal_bottom = bottom + border; - - float left_uv = 0.0f; - float right_uv = 1.0f; - float top_uv = 1.0f; - float bottom_uv = 0.0f; - - float internal_left_uv = (float)m_background_texture.metadata.left * inv_tex_width; - float internal_right_uv = 1.0f - (float)m_background_texture.metadata.right * inv_tex_width; - float internal_top_uv = 1.0f - (float)m_background_texture.metadata.top * inv_tex_height; - float internal_bottom_uv = (float)m_background_texture.metadata.bottom * inv_tex_height; - - // top-left corner - if ((m_layout.horizontal_orientation == Layout::HO_Left) || (m_layout.vertical_orientation == Layout::VO_Top)) - GLTexture::render_sub_texture(tex_id, left, internal_left, internal_top, top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); - else - GLTexture::render_sub_texture(tex_id, left, internal_left, internal_top, top, { { left_uv, internal_top_uv }, { internal_left_uv, internal_top_uv }, { internal_left_uv, top_uv }, { left_uv, top_uv } }); - - // top edge - if (m_layout.vertical_orientation == Layout::VO_Top) - GLTexture::render_sub_texture(tex_id, internal_left, internal_right, internal_top, top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); - else - GLTexture::render_sub_texture(tex_id, internal_left, internal_right, internal_top, top, { { internal_left_uv, internal_top_uv }, { internal_right_uv, internal_top_uv }, { internal_right_uv, top_uv }, { internal_left_uv, top_uv } }); - - // top-right corner - if ((m_layout.horizontal_orientation == Layout::HO_Right) || (m_layout.vertical_orientation == Layout::VO_Top)) - GLTexture::render_sub_texture(tex_id, internal_right, right, internal_top, top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); - else - GLTexture::render_sub_texture(tex_id, internal_right, right, internal_top, top, { { internal_right_uv, internal_top_uv }, { right_uv, internal_top_uv }, { right_uv, top_uv }, { internal_right_uv, top_uv } }); - - // center-left edge - if (m_layout.horizontal_orientation == Layout::HO_Left) - GLTexture::render_sub_texture(tex_id, left, internal_left, internal_bottom, internal_top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); - else - GLTexture::render_sub_texture(tex_id, left, internal_left, internal_bottom, internal_top, { { left_uv, internal_bottom_uv }, { internal_left_uv, internal_bottom_uv }, { internal_left_uv, internal_top_uv }, { left_uv, internal_top_uv } }); - - // center - GLTexture::render_sub_texture(tex_id, internal_left, internal_right, internal_bottom, internal_top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); - - // center-right edge - if (m_layout.horizontal_orientation == Layout::HO_Right) - GLTexture::render_sub_texture(tex_id, internal_right, right, internal_bottom, internal_top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); - else - GLTexture::render_sub_texture(tex_id, internal_right, right, internal_bottom, internal_top, { { internal_right_uv, internal_bottom_uv }, { right_uv, internal_bottom_uv }, { right_uv, internal_top_uv }, { internal_right_uv, internal_top_uv } }); - - // bottom-left corner - if ((m_layout.horizontal_orientation == Layout::HO_Left) || (m_layout.vertical_orientation == Layout::VO_Bottom)) - GLTexture::render_sub_texture(tex_id, left, internal_left, bottom, internal_bottom, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); - else - GLTexture::render_sub_texture(tex_id, left, internal_left, bottom, internal_bottom, { { left_uv, bottom_uv }, { internal_left_uv, bottom_uv }, { internal_left_uv, internal_bottom_uv }, { left_uv, internal_bottom_uv } }); - - // bottom edge - if (m_layout.vertical_orientation == Layout::VO_Bottom) - GLTexture::render_sub_texture(tex_id, internal_left, internal_right, bottom, internal_bottom, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); - else - GLTexture::render_sub_texture(tex_id, internal_left, internal_right, bottom, internal_bottom, { { internal_left_uv, bottom_uv }, { internal_right_uv, bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_left_uv, internal_bottom_uv } }); - - // bottom-right corner - if ((m_layout.horizontal_orientation == Layout::HO_Right) || (m_layout.vertical_orientation == Layout::VO_Bottom)) - GLTexture::render_sub_texture(tex_id, internal_right, right, bottom, internal_bottom, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); - else - GLTexture::render_sub_texture(tex_id, internal_right, right, bottom, internal_bottom, { { internal_right_uv, bottom_uv }, { right_uv, bottom_uv }, { right_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv } }); - } -} - -void GLToolbar::render_arrow(const GLCanvas3D& parent, GLToolbarItem* highlighted_item) -{ - // arrow texture not initialized - if (m_arrow_texture.texture.get_id() == 0) - return; - - float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); - float factor = inv_zoom * m_layout.scale; - - float scaled_icons_size = m_layout.icons_size * factor; - float scaled_separator_size = m_layout.separator_size * factor; - float scaled_gap_size = m_layout.gap_size * factor; - float border = m_layout.border * factor; - - float separator_stride = scaled_separator_size + scaled_gap_size; - float icon_stride = scaled_icons_size + scaled_gap_size; - - float left = m_layout.left; - float top = m_layout.top - icon_stride; - - bool found = false; - for (const GLToolbarItem* item : m_items) { - if (!item->is_visible()) - continue; - - if (item->is_separator()) - left += separator_stride; - else { - if (item->get_name() == highlighted_item->get_name()) { - found = true; - break; - } - left += icon_stride; - } - } - if (!found) - return; - - left += border; - top -= separator_stride; - float right = left + scaled_icons_size; - - unsigned int tex_id = m_arrow_texture.texture.get_id(); - // arrow width and height - float arr_tex_width = (float)m_arrow_texture.texture.get_width(); - float arr_tex_height = (float)m_arrow_texture.texture.get_height(); - if ((tex_id != 0) && (arr_tex_width > 0) && (arr_tex_height > 0)) { - float inv_tex_width = (arr_tex_width != 0.0f) ? 1.0f / arr_tex_width : 0.0f; - float inv_tex_height = (arr_tex_height != 0.0f) ? 1.0f / arr_tex_height : 0.0f; - - float internal_left = left + border - scaled_icons_size * 1.5f; // add scaled_icons_size for huge arrow - float internal_right = right - border + scaled_icons_size * 1.5f; - float internal_top = top - border; - // bottom is not moving and should be calculated from arrow texture sides ratio - float arrow_sides_ratio = (float)m_arrow_texture.texture.get_height() / (float)m_arrow_texture.texture.get_width(); - float internal_bottom = internal_top - (internal_right - internal_left) * arrow_sides_ratio ; - - float internal_left_uv = (float)m_arrow_texture.metadata.left * inv_tex_width; - float internal_right_uv = 1.0f - (float)m_arrow_texture.metadata.right * inv_tex_width; - float internal_top_uv = 1.0f - (float)m_arrow_texture.metadata.top * inv_tex_height; - float internal_bottom_uv = (float)m_arrow_texture.metadata.bottom * inv_tex_height; - - GLTexture::render_sub_texture(tex_id, internal_left, internal_right, internal_bottom, internal_top, { { internal_left_uv, internal_top_uv }, { internal_right_uv, internal_top_uv }, { internal_right_uv, internal_bottom_uv }, { internal_left_uv, internal_bottom_uv } }); - } -} - -void GLToolbar::render_horizontal(const GLCanvas3D& parent,GLToolbarItem::EType type) -{ - float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); - float factor = inv_zoom * m_layout.scale; - - float scaled_icons_size = m_layout.icons_size * factor; - float scaled_separator_size = m_layout.separator_size * factor; - float scaled_gap_size = m_layout.gap_size * factor; - float scaled_border = m_layout.border * factor; - float scaled_width = get_width() * inv_zoom; - float scaled_height = get_height() * inv_zoom; - - float separator_stride = scaled_separator_size + scaled_gap_size; - float icon_stride = scaled_icons_size + scaled_gap_size; - - float left = m_layout.left; - float top = m_layout.top; - float right = left + scaled_width; - if (type == GLToolbarItem::SeparatorLine) - right = left + scaled_width * 0.5; - float bottom = top - scaled_height; - - render_background(left, top, right, bottom, scaled_border); - - left += scaled_border; - top -= scaled_border; - - // renders icons - for (const GLToolbarItem* item : m_items) - { - if (!item->is_visible()) - continue; - - if (item->is_separator()) - left += separator_stride; - else - { - //QDS GUI refactor - item->render_left_pos = left; - if (!item->is_action_with_text_image()) { - unsigned int tex_id = m_icons_texture.get_id(); - int tex_width = m_icons_texture.get_width(); - int tex_height = m_icons_texture.get_height(); - if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0)) - return; - item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_width, (unsigned int)tex_height, (unsigned int)(m_layout.icons_size * m_layout.scale)); - } - //QDS: GUI refactor: GLToolbar - if (item->is_action_with_text()) - { - float scaled_text_size = item->get_extra_size_ratio() * scaled_icons_size; - item->render_text(left + scaled_icons_size, left + scaled_icons_size + scaled_text_size, top - scaled_icons_size, top); - left += scaled_text_size; - } - left += icon_stride; - } - } -} - -void GLToolbar::render_vertical(const GLCanvas3D& parent) -{ - float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); - float factor = inv_zoom * m_layout.scale; - - float scaled_icons_size = m_layout.icons_size * factor; - float scaled_separator_size = m_layout.separator_size * factor; - float scaled_gap_size = m_layout.gap_size * factor; - float scaled_border = m_layout.border * factor; - float scaled_width = get_width() * inv_zoom; - float scaled_height = get_height() * inv_zoom; - - float separator_stride = scaled_separator_size + scaled_gap_size; - float icon_stride = scaled_icons_size + scaled_gap_size; - - float left = m_layout.left; - float top = m_layout.top; - float right = left + scaled_width; - float bottom = top - scaled_height; - - render_background(left, top, right, bottom, scaled_border); - - left += scaled_border; - top -= scaled_border; - - // renders icons - for (const GLToolbarItem* item : m_items) { - if (!item->is_visible()) - continue; - - if (item->is_separator()) - top -= separator_stride; - else { - unsigned int tex_id; - int tex_width, tex_height; - if (item->is_action_with_text_image()) { - float scaled_text_size = m_layout.text_size * factor; - float scaled_text_width = item->get_extra_size_ratio() * scaled_icons_size; - float scaled_text_border = 2.5 * factor; - float scaled_text_height = scaled_icons_size / 2.0f; - item->render_text(left, left + scaled_text_size, top - scaled_text_border - scaled_text_height, top - scaled_text_border); - - float image_left = left + scaled_text_size; - tex_id = item->m_data.image_texture.get_id(); - tex_width = item->m_data.image_texture.get_width(); - tex_height = item->m_data.image_texture.get_height(); - if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0)) - return; - item->render_image(tex_id, image_left, image_left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_width, (unsigned int)tex_height, (unsigned int)(m_layout.icons_size * m_layout.scale)); - } - else { - tex_id = m_icons_texture.get_id(); - tex_width = m_icons_texture.get_width(); - tex_height = m_icons_texture.get_height(); - if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0)) - return; - item->render(tex_id, left, left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_width, (unsigned int)tex_height, (unsigned int)(m_layout.icons_size * m_layout.scale)); - //QDS: GUI refactor: GLToolbar - } - if (item->is_action_with_text()) - { - float scaled_text_width = item->get_extra_size_ratio() * scaled_icons_size; - float scaled_text_height = scaled_icons_size; - item->render_text(left + scaled_icons_size, left + scaled_icons_size + scaled_text_width, top - scaled_text_height, top); - } - top -= icon_stride; - } - } -} - -bool GLToolbar::generate_icons_texture() -{ - std::string path = resources_dir() + "/images/"; - std::vector filenames; - for (GLToolbarItem* item : m_items) { - const std::string& icon_filename = item->get_icon_filename(); - if (!icon_filename.empty()) - filenames.push_back(path + icon_filename); - } - - std::vector> states; - //1: white only, 2: gray only, 0 : normal - //true/false: apply background or not - if (m_type == Normal) { - states.push_back({ 1, false }); // Normal - states.push_back({ 0, false }); // Pressed - states.push_back({ 2, false }); // Disabled - states.push_back({ 0, false }); // Hover - states.push_back({ 0, false }); // HoverPressed - states.push_back({ 2, false }); // HoverDisabled - states.push_back({ 0, false }); // HighlightedShown - states.push_back({ 2, false }); // HighlightedHidden - } - else { - states.push_back({ 1, false }); // Normal - states.push_back({ 0, true }); // Pressed - states.push_back({ 2, false }); // Disabled - states.push_back({ 0, false }); // Hover - states.push_back({ 1, true }); // HoverPressed - states.push_back({ 1, false }); // HoverDisabled - states.push_back({ 0, false }); // HighlightedShown - states.push_back({ 1, false }); // HighlightedHidden - } - - unsigned int sprite_size_px = (unsigned int)(m_layout.icons_size * m_layout.scale); -// // force even size -// if (sprite_size_px % 2 != 0) -// sprite_size_px += 1; - - bool res = m_icons_texture.load_from_svg_files_as_sprites_array(filenames, states, sprite_size_px, false); - if (res) - m_icons_texture_dirty = false; - - return res; -} - -bool GLToolbar::update_items_visibility() -{ - bool ret = false; - - for (GLToolbarItem* item : m_items) { - ret |= item->update_visibility(); - } - - if (ret) - m_layout.dirty = true; - - // updates separators visibility to avoid having two of them consecutive - bool any_item_visible = false; - for (GLToolbarItem* item : m_items) { - if (!item->is_separator()) - any_item_visible |= item->is_visible(); - else { - item->set_visible(any_item_visible); - any_item_visible = false; - } - } - - return ret; -} - -bool GLToolbar::update_items_enabled_state() -{ - bool ret = false; - - for (int i = 0; i < (int)m_items.size(); ++i) - { - GLToolbarItem* item = m_items[i]; - ret |= item->update_enabled_state(); - if (item->is_enabled() && (m_pressed_toggable_id != -1) && (m_pressed_toggable_id != i)) - { - ret = true; - item->set_state(GLToolbarItem::Disabled); - } - } - - if (ret) - m_layout.dirty = true; - - return ret; -} - //QDS: GUI refactor: GLToolbar int GLToolbar::generate_button_text_textures(wxFont& font) { @@ -1658,7 +1417,7 @@ int GLToolbar::generate_button_text_textures(wxFont& font) for (int i = 0; i < (int)m_items.size(); ++i) { - GLToolbarItem* item = m_items[i]; + const auto& item = m_items[i]; if (item->is_action_with_text()) { @@ -1679,7 +1438,7 @@ int GLToolbar::generate_image_textures() int ret = 0; for (int i = 0; i < (int)m_items.size(); ++i) { - GLToolbarItem* item = m_items[i]; + const auto& item = m_items[i]; if (item->is_action_with_text_image()) { ret |= item->generate_image_texture(); } @@ -1693,5 +1452,930 @@ float GLToolbar::get_scaled_icon_size() return m_layout.icons_size * m_layout.scale; } +ToolbarRenderer::ToolbarRenderer() +{ +} + +ToolbarRenderer::~ToolbarRenderer() +{ +} + +bool ToolbarRenderer::needs_collapsed() const +{ + return false; +} + +ToolbarAutoSizeRenderer::ToolbarAutoSizeRenderer() +{ +} + +ToolbarAutoSizeRenderer::~ToolbarAutoSizeRenderer() +{ +} + +void ToolbarAutoSizeRenderer::render(const GLToolbar& t_toolbar, const Camera& t_camera) +{ + const auto& t_layout = t_toolbar.get_layout(); + switch (t_layout.type) + { + default: + case ToolbarLayout::EType::Horizontal: { render_horizontal(t_toolbar, t_camera); break; } + case ToolbarLayout::EType::Vertical: { render_vertical(t_toolbar, t_camera); break; } + } +} + +GLToolbar::EToolbarRenderingMode ToolbarAutoSizeRenderer::get_mode() const +{ + return GLToolbar::EToolbarRenderingMode::Auto; +} + +void ToolbarAutoSizeRenderer::render_horizontal(const GLToolbar& t_toolbar, const Camera& t_camera) +{ + const auto& t_layout = t_toolbar.get_layout(); + float inv_zoom = (float)t_camera.get_inv_zoom(); + float factor = inv_zoom * t_layout.scale; + + float scaled_icons_size = t_layout.icons_size * factor; + float scaled_separator_size = t_layout.separator_size * factor; + float scaled_gap_size = t_layout.gap_size * factor; + float scaled_border = t_layout.border * factor; + float scaled_width = t_toolbar.get_width() * inv_zoom; + float scaled_height = t_toolbar.get_height() * inv_zoom; + + float separator_stride = scaled_separator_size + scaled_gap_size; + float icon_stride = scaled_icons_size + scaled_gap_size; + + float left = 0.0f; + float top = 0.0f; + calculate_position(t_toolbar, t_camera, left, top); + float right = left + scaled_width; + float bottom = top - scaled_height; + + render_background(t_toolbar, left, top, right, bottom, scaled_border); + + left += scaled_border; + top -= scaled_border; + + // renders icons + const auto& t_items = t_toolbar.get_items(); + const auto& t_icon_texture = t_toolbar.get_icon_texture(); + for (const auto& item : t_items) + { + if (!item->is_visible()) + continue; + + if (item->is_separator()) + left += separator_stride; + else + { + if (!item->is_action_with_text_image()) { + unsigned int tex_id = t_icon_texture.get_id(); + int tex_width = t_icon_texture.get_width(); + int tex_height = t_icon_texture.get_height(); + if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0)) + return; + item->render_rect[0] = left; + item->render_rect[1] = left + scaled_icons_size; + item->render_rect[2] = top - scaled_icons_size; + item->render_rect[3] = top; + item->render(tex_id, (unsigned int)tex_width, (unsigned int)tex_height, (unsigned int)(t_layout.icons_size * t_layout.scale), t_toolbar.get_height()); + } + //QDS: GUI refactor: GLToolbar + if (item->is_action_with_text()) + { + float scaled_text_size = item->get_extra_size_ratio() * scaled_icons_size; + item->render_rect[0] = left + scaled_icons_size; + item->render_rect[1] = left + scaled_icons_size + scaled_text_size; + item->render_rect[2] = top - scaled_icons_size; + item->render_rect[3] = top; + item->render_text(); + left += scaled_text_size; + } + if (item->get_type() == GLToolbarItem::EType::SeparatorLine) { + left += (icon_stride - 0.5f * scaled_icons_size); + } + else { + left += icon_stride; + } + } + } +} + +void ToolbarAutoSizeRenderer::render_vertical(const GLToolbar& t_toolbar, const Camera& t_camera) +{ + const auto& t_layout = t_toolbar.get_layout(); + float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); + float factor = inv_zoom * t_layout.scale; + + float scaled_icons_size = t_layout.icons_size * factor; + float scaled_separator_size = t_layout.separator_size * factor; + float scaled_gap_size = t_layout.gap_size * factor; + float scaled_border = t_layout.border * factor; + float scaled_width = t_toolbar.get_width() * inv_zoom; + float scaled_height = t_toolbar.get_height() * inv_zoom; + + float separator_stride = scaled_separator_size + scaled_gap_size; + float icon_stride = scaled_icons_size + scaled_gap_size; + + float left = 0.0f; + float top = 0.0f; + calculate_position(t_toolbar, t_camera, left, top); + float right = left + scaled_width; + float bottom = top - scaled_height; + + render_background(t_toolbar, left, top, right, bottom, scaled_border); + + left += scaled_border; + top -= scaled_border; + + // renders icons + const auto& t_items = t_toolbar.get_items(); + const auto& t_icon_texture = t_toolbar.get_icon_texture(); + for (const auto& item : t_items) { + if (!item->is_visible()) + continue; + + if (item->is_separator()) + top -= separator_stride; + else { + unsigned int tex_id; + int tex_width, tex_height; + if (item->is_action_with_text_image()) { + float scaled_text_size = t_layout.text_size * factor; + float scaled_text_width = item->get_extra_size_ratio() * scaled_icons_size; + float scaled_text_border = 2.5 * factor; + float scaled_text_height = scaled_icons_size / 2.0f; + item->render_rect[0] = left; + item->render_rect[1] = left + scaled_text_size; + item->render_rect[2] = top - scaled_text_border - scaled_text_height; + item->render_rect[3] = top - scaled_text_border; + item->render_text(); + + float image_left = left + scaled_text_size; + const auto& item_data = item->get_data(); + tex_id = item_data.image_texture.get_id(); + tex_width = item_data.image_texture.get_width(); + tex_height = item_data.image_texture.get_height(); + if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0)) + return; + item->render_image(tex_id, image_left, image_left + scaled_icons_size, top - scaled_icons_size, top, (unsigned int)tex_width, (unsigned int)tex_height, (unsigned int)(t_layout.icons_size * t_layout.scale)); + } + else { + tex_id = t_icon_texture.get_id(); + tex_width = t_icon_texture.get_width(); + tex_height = t_icon_texture.get_height(); + if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0)) + return; + item->render_rect[0] = left; + item->render_rect[1] = left + scaled_icons_size; + item->render_rect[2] = top - scaled_icons_size; + item->render_rect[3] = top; + item->render(tex_id, (unsigned int)tex_width, (unsigned int)tex_height, (unsigned int)(t_layout.icons_size * t_layout.scale), t_toolbar.get_width()); + //QDS: GUI refactor: GLToolbar + } + if (item->is_action_with_text()) + { + float scaled_text_width = item->get_extra_size_ratio() * scaled_icons_size; + float scaled_text_height = scaled_icons_size; + + item->render_rect[0] = left + scaled_icons_size; + item->render_rect[1] = left + scaled_icons_size + scaled_text_width; + item->render_rect[2] = top - scaled_text_height; + item->render_rect[3] = top; + item->render_text(); + } + top -= icon_stride; + if (item->get_type() == GLToolbarItem::EType::SeparatorLine) { + top -= (icon_stride - 0.5f * scaled_icons_size); + } + else { + top -= icon_stride; + } + } + } +} + +void ToolbarAutoSizeRenderer::render_background(const GLToolbar& t_toolbar, float left, float top, float right, float bottom, float border) const +{ + const auto& t_background_texture = t_toolbar.get_background_texture(); + unsigned int tex_id = t_background_texture.texture.get_id(); + float tex_width = (float)t_background_texture.texture.get_width(); + float tex_height = (float)t_background_texture.texture.get_height(); + if ((tex_id != 0) && (tex_width > 0) && (tex_height > 0)) + { + float inv_tex_width = (tex_width != 0.0f) ? 1.0f / tex_width : 0.0f; + float inv_tex_height = (tex_height != 0.0f) ? 1.0f / tex_height : 0.0f; + + float internal_left = left + border; + float internal_right = right - border; + float internal_top = top - border; + float internal_bottom = bottom + border; + + float left_uv = 0.0f; + float right_uv = 1.0f; + float top_uv = 1.0f; + float bottom_uv = 0.0f; + + float internal_left_uv = (float)t_background_texture.metadata.left * inv_tex_width; + float internal_right_uv = 1.0f - (float)t_background_texture.metadata.right * inv_tex_width; + float internal_top_uv = 1.0f - (float)t_background_texture.metadata.top * inv_tex_height; + float internal_bottom_uv = (float)t_background_texture.metadata.bottom * inv_tex_height; + + const auto& t_layout = t_toolbar.get_layout(); + // top-left corner + if ((t_layout.horizontal_orientation == ToolbarLayout::HO_Left) || (t_layout.vertical_orientation == ToolbarLayout::VO_Top)) + GLTexture::render_sub_texture(tex_id, left, internal_left, internal_top, top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); + else + GLTexture::render_sub_texture(tex_id, left, internal_left, internal_top, top, { { left_uv, internal_top_uv }, { internal_left_uv, internal_top_uv }, { internal_left_uv, top_uv }, { left_uv, top_uv } }); + + // top edge + if (t_layout.vertical_orientation == ToolbarLayout::VO_Top) + GLTexture::render_sub_texture(tex_id, internal_left, internal_right, internal_top, top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); + else + GLTexture::render_sub_texture(tex_id, internal_left, internal_right, internal_top, top, { { internal_left_uv, internal_top_uv }, { internal_right_uv, internal_top_uv }, { internal_right_uv, top_uv }, { internal_left_uv, top_uv } }); + + // top-right corner + if ((t_layout.horizontal_orientation == ToolbarLayout::HO_Right) || (t_layout.vertical_orientation == ToolbarLayout::VO_Top)) + GLTexture::render_sub_texture(tex_id, internal_right, right, internal_top, top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); + else + GLTexture::render_sub_texture(tex_id, internal_right, right, internal_top, top, { { internal_right_uv, internal_top_uv }, { right_uv, internal_top_uv }, { right_uv, top_uv }, { internal_right_uv, top_uv } }); + + // center-left edge + if (t_layout.horizontal_orientation == ToolbarLayout::HO_Left) + GLTexture::render_sub_texture(tex_id, left, internal_left, internal_bottom, internal_top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); + else + GLTexture::render_sub_texture(tex_id, left, internal_left, internal_bottom, internal_top, { { left_uv, internal_bottom_uv }, { internal_left_uv, internal_bottom_uv }, { internal_left_uv, internal_top_uv }, { left_uv, internal_top_uv } }); + + // center + GLTexture::render_sub_texture(tex_id, internal_left, internal_right, internal_bottom, internal_top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); + + // center-right edge + if (t_layout.horizontal_orientation == ToolbarLayout::HO_Right) + GLTexture::render_sub_texture(tex_id, internal_right, right, internal_bottom, internal_top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); + else + GLTexture::render_sub_texture(tex_id, internal_right, right, internal_bottom, internal_top, { { internal_right_uv, internal_bottom_uv }, { right_uv, internal_bottom_uv }, { right_uv, internal_top_uv }, { internal_right_uv, internal_top_uv } }); + + // bottom-left corner + if ((t_layout.horizontal_orientation == ToolbarLayout::HO_Left) || (t_layout.vertical_orientation == ToolbarLayout::VO_Bottom)) + GLTexture::render_sub_texture(tex_id, left, internal_left, bottom, internal_bottom, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); + else + GLTexture::render_sub_texture(tex_id, left, internal_left, bottom, internal_bottom, { { left_uv, bottom_uv }, { internal_left_uv, bottom_uv }, { internal_left_uv, internal_bottom_uv }, { left_uv, internal_bottom_uv } }); + + // bottom edge + if (t_layout.vertical_orientation == ToolbarLayout::VO_Bottom) + GLTexture::render_sub_texture(tex_id, internal_left, internal_right, bottom, internal_bottom, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); + else + GLTexture::render_sub_texture(tex_id, internal_left, internal_right, bottom, internal_bottom, { { internal_left_uv, bottom_uv }, { internal_right_uv, bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_left_uv, internal_bottom_uv } }); + + // bottom-right corner + if ((t_layout.horizontal_orientation == ToolbarLayout::HO_Right) || (t_layout.vertical_orientation == ToolbarLayout::VO_Bottom)) + GLTexture::render_sub_texture(tex_id, internal_right, right, bottom, internal_bottom, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); + else + GLTexture::render_sub_texture(tex_id, internal_right, right, bottom, internal_bottom, { { internal_right_uv, bottom_uv }, { right_uv, bottom_uv }, { right_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv } }); + } +} + +void ToolbarAutoSizeRenderer::calculate_position(const GLToolbar& t_toolbar, const Camera& t_camera, float& left, float& top) +{ + const auto& t_layout = t_toolbar.get_layout(); + switch (t_layout.position_mode) { + case ToolbarLayout::EPositionMode::TopLeft: + { + float inv_zoom = (float)t_camera.get_inv_zoom(); + const auto& t_viewport = t_camera.get_viewport(); + top = 0.5f * (float)t_viewport[3] * inv_zoom; + left = -0.5f * (float)t_viewport[2] * inv_zoom; + + break; + } + case ToolbarLayout::EPositionMode::TopMiddle: + { + float inv_zoom = (float)t_camera.get_inv_zoom(); + const auto& t_viewport = t_camera.get_viewport(); + const auto cnv_width = t_viewport[2]; + const auto cnv_height = t_viewport[3]; + top = 0.5f * (float)cnv_height * inv_zoom; + left = -0.5f * (float)cnv_width * inv_zoom; + const auto final_width = t_toolbar.get_width() + t_layout.offset; + if (cnv_width < final_width) { + left += (t_layout.offset * inv_zoom); + } + else { + const float offset = (cnv_width - final_width) / 2.f; + left += (offset + t_layout.offset) * inv_zoom; + } + + break; + } + case ToolbarLayout::EPositionMode::Custom: + default: + { + left = t_layout.left; + top = t_layout.top; + break; + } + } +} + +void ToolbarAutoSizeRenderer::render_arrow(const GLToolbar& t_toolbar, const std::weak_ptr& highlighted_item) +{ + const auto p_item = highlighted_item.lock(); + if (!p_item) { + return; + } + const auto& t_arrow_texture = t_toolbar.get_arrow_texture(); + // arrow texture not initialized + if (t_arrow_texture.texture.get_id() == 0) + return; + + const auto& t_layout = t_toolbar.get_layout(); + float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); + float factor = inv_zoom * t_layout.scale; + + float scaled_icons_size = t_layout.icons_size * factor; + float scaled_separator_size = t_layout.separator_size * factor; + float scaled_gap_size = t_layout.gap_size * factor; + float border = t_layout.border * factor; + + float separator_stride = scaled_separator_size + scaled_gap_size; + float icon_stride = scaled_icons_size + scaled_gap_size; + + float left = t_layout.left; + float top = t_layout.top - icon_stride; + + bool found = false; + const auto& t_items = t_toolbar.get_items(); + for (const auto& item : t_items) { + if (!item->is_visible()) + continue; + + if (item->is_separator()) + left += separator_stride; + else { + if (item->get_name() == p_item->get_name()) { + found = true; + break; + } + if (item->get_type() == GLToolbarItem::EType::SeparatorLine) { + left += (icon_stride - 0.5f * scaled_icons_size); + } + else { + left += icon_stride; + } + } + } + if (!found) + return; + + left += border; + top -= separator_stride; + float right = left + scaled_icons_size; + + unsigned int tex_id = t_arrow_texture.texture.get_id(); + // arrow width and height + float arr_tex_width = (float)t_arrow_texture.texture.get_width(); + float arr_tex_height = (float)t_arrow_texture.texture.get_height(); + if ((tex_id != 0) && (arr_tex_width > 0) && (arr_tex_height > 0)) { + float inv_tex_width = (arr_tex_width != 0.0f) ? 1.0f / arr_tex_width : 0.0f; + float inv_tex_height = (arr_tex_height != 0.0f) ? 1.0f / arr_tex_height : 0.0f; + + float internal_left = left + border - scaled_icons_size * 1.5f; // add scaled_icons_size for huge arrow + float internal_right = right - border + scaled_icons_size * 1.5f; + float internal_top = top - border; + // bottom is not moving and should be calculated from arrow texture sides ratio + float arrow_sides_ratio = (float)t_arrow_texture.texture.get_height() / (float)t_arrow_texture.texture.get_width(); + float internal_bottom = internal_top - (internal_right - internal_left) * arrow_sides_ratio; + + float internal_left_uv = (float)t_arrow_texture.metadata.left * inv_tex_width; + float internal_right_uv = 1.0f - (float)t_arrow_texture.metadata.right * inv_tex_width; + float internal_top_uv = 1.0f - (float)t_arrow_texture.metadata.top * inv_tex_height; + float internal_bottom_uv = (float)t_arrow_texture.metadata.bottom * inv_tex_height; + + GLTexture::render_sub_texture(tex_id, internal_left, internal_right, internal_bottom, internal_top, { { internal_left_uv, internal_top_uv }, { internal_right_uv, internal_top_uv }, { internal_right_uv, internal_bottom_uv }, { internal_left_uv, internal_bottom_uv } }); + } +} + +ToolbarKeepSizeRenderer::ToolbarKeepSizeRenderer() +{ +} + +ToolbarKeepSizeRenderer::~ToolbarKeepSizeRenderer() +{ +} + +void ToolbarKeepSizeRenderer::render(const GLToolbar& t_toolbar, const Camera& t_camera) +{ + const auto& t_layout = t_toolbar.get_layout(); + + const auto& t_viewport = t_camera.get_viewport(); + const auto canvas_width = t_viewport[2]; + const auto canvas_height = t_viewport[3]; + + const auto toolbar_width = t_toolbar.get_width(); + const auto toolbar_height = t_toolbar.get_height(); + const auto toolbar_offset = t_layout.offset >= canvas_width ? 0.0f : t_layout.offset; + + float final_toolbar_width = toolbar_width; + float final_toolbar_height = toolbar_height; + + const auto final_canvas_width = canvas_width - toolbar_offset; + + float collapse_width = 0.0f; + recalculate_item_pos(t_toolbar, t_camera, final_toolbar_width, final_toolbar_height, collapse_width); + + if (t_toolbar.is_collapsed()) { + final_toolbar_height = toolbar_height; + } + float inv_zoom = (float)t_camera.get_inv_zoom(); + float factor = inv_zoom * t_layout.scale; + float scaled_border = t_layout.border * factor; + + float left = 0.0f; + float top = 0.0f; + calculate_position(t_toolbar, t_camera, final_toolbar_width, final_toolbar_height, left, top); + + if (t_layout.collapsed_offset < 1e-6f || t_toolbar.is_collapsed()) { + float scaled_width = final_toolbar_width * inv_zoom; + float scaled_height = final_toolbar_height * inv_zoom; + float right = left + scaled_width; + float bottom = top - scaled_height; + + render_background(t_toolbar, left, top, right, bottom, scaled_border); + } + else { + float scaled_width = final_toolbar_width * inv_zoom; + float scaled_height = toolbar_height * inv_zoom; + float right = left + scaled_width; + float bottom = top - scaled_height; + + render_background(t_toolbar, left, top, right, bottom, scaled_border); + + const auto others_height = final_toolbar_height - toolbar_height; + if (others_height > 1e-6f) { + scaled_width = collapse_width * inv_zoom; + right = left + scaled_width; + top = bottom - t_layout.collapsed_offset * factor; + scaled_height = others_height * inv_zoom; + bottom = top - scaled_height; + render_background(t_toolbar, left, top, right, bottom, scaled_border); + } + } + + // renders icons + const auto& t_icon_texture = t_toolbar.get_icon_texture(); + unsigned int tex_id = t_icon_texture.get_id(); + int tex_width = t_icon_texture.get_width(); + int tex_height = t_icon_texture.get_height(); + if ((tex_id == 0) || (tex_width <= 0) || (tex_height <= 0)) + return; + + const auto& t_items = t_toolbar.get_items(); + for (size_t i = 0; i < t_items.size(); ++i) { + const auto& current_item = t_items[i]; + current_item->override_render_rect = m_p_override_render_rect; + if (current_item->is_action() || current_item->get_type() == GLToolbarItem::EType::SeparatorLine) { + const bool b_filp_v = !t_toolbar.is_collapsed() && current_item->is_collapse_button(); + current_item->render(tex_id, (unsigned int)tex_width, (unsigned int)tex_height, (unsigned int)(t_layout.icons_size * t_layout.scale), t_toolbar.get_height(), b_filp_v); + } + //QDS: GUI refactor: GLToolbar + if (current_item->is_action_with_text()) + { + current_item->render_text(); + } + } +} + +GLToolbar::EToolbarRenderingMode ToolbarKeepSizeRenderer::get_mode() const +{ + return GLToolbar::EToolbarRenderingMode::KeepSize; +} + +void ToolbarKeepSizeRenderer::render_arrow(const GLToolbar& t_toolbar, const std::weak_ptr& highlighted_item) +{ +} + +bool ToolbarKeepSizeRenderer::needs_collapsed() const +{ + return m_b_needs_collapsed; +} + +void ToolbarKeepSizeRenderer::render_horizontal(const GLToolbar& t_toolbar) +{ +} + +void ToolbarKeepSizeRenderer::render_vertical(const GLToolbar& t_toolbar) +{ +} + +void ToolbarKeepSizeRenderer::render_background(const GLToolbar& t_toolbar, float left, float top, float right, float bottom, float border) const +{ + const auto& t_background_texture = t_toolbar.get_background_texture(); + unsigned int tex_id = t_background_texture.texture.get_id(); + if (tex_id < 0) { + return; + } + + float tex_width = (float)t_background_texture.texture.get_width(); + if (tex_width <= 0) { + return; + } + + float tex_height = (float)t_background_texture.texture.get_height(); + if (tex_height <= 0) { + return; + } + + float inv_tex_width = (tex_width != 0.0f) ? 1.0f / tex_width : 0.0f; + float inv_tex_height = (tex_height != 0.0f) ? 1.0f / tex_height : 0.0f; + + float internal_left = left + border; + float internal_right = right - border; + float internal_top = top - border; + float internal_bottom = bottom + border; + + float left_uv = 0.0f; + float right_uv = 1.0f; + float top_uv = 1.0f; + float bottom_uv = 0.0f; + + float internal_left_uv = (float)t_background_texture.metadata.left * inv_tex_width; + float internal_right_uv = 1.0f - (float)t_background_texture.metadata.right * inv_tex_width; + float internal_top_uv = 1.0f - (float)t_background_texture.metadata.top * inv_tex_height; + float internal_bottom_uv = (float)t_background_texture.metadata.bottom * inv_tex_height; + + const auto& t_layout = t_toolbar.get_layout(); + GLTexture::render_sub_texture(tex_id, left, right, bottom, top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); +} + +void ToolbarKeepSizeRenderer::calculate_position(const GLToolbar& t_toolbar, const Camera& t_camera, float toolbar_width, float toolbar_height, float& left, float& top) +{ + const auto& t_layout = t_toolbar.get_layout(); + switch (t_layout.position_mode) { + case ToolbarLayout::EPositionMode::TopLeft: + { + float inv_zoom = (float)t_camera.get_inv_zoom(); + const auto& t_viewport = t_camera.get_viewport(); + top = 0.5f * (float)t_viewport[3] * inv_zoom; + left = -0.5f * (float)t_viewport[2] * inv_zoom; + + break; + } + case ToolbarLayout::EPositionMode::TopMiddle: + { + float inv_zoom = (float)t_camera.get_inv_zoom(); + const auto& t_viewport = t_camera.get_viewport(); + const auto cnv_width = t_viewport[2]; + const auto cnv_height = t_viewport[3]; + top = 0.5f * (float)cnv_height * inv_zoom; + left = -0.5f * (float)cnv_width * inv_zoom; + const auto final_width = toolbar_width + t_layout.offset; + if (cnv_width < final_width) { + left += (t_layout.offset * inv_zoom); + } + else { + const float offset = (cnv_width - final_width) / 2.f; + left += (offset + t_layout.offset) * inv_zoom; + } + + break; + } + case ToolbarLayout::EPositionMode::Custom: + default: + { + left = t_layout.left; + top = t_layout.top; + break; + } + } +} + +void ToolbarKeepSizeRenderer::recalculate_item_pos(const GLToolbar& t_toolbar, const Camera& t_camera, float& final_toolbar_width, float& final_toolbar_height, float& collapse_width) +{ + const auto& t_layout = t_toolbar.get_layout(); + + const auto& t_viewport = t_camera.get_viewport(); + const auto canvas_width = t_viewport[2]; + const auto canvas_height = t_viewport[3]; + + const auto toolbar_width = t_toolbar.get_width(); + const auto toolbar_height = t_toolbar.get_height(); + const auto toolbar_offset = t_layout.offset >= canvas_width ? 0.0f : t_layout.offset; + + const auto final_canvas_width = canvas_width - toolbar_offset; + + m_b_needs_collapsed = false; + const auto& t_items = t_toolbar.get_items(); + m_indices_to_draw.clear(); + + float total_collapse_item_width = 2.0f * t_layout.border; + for (size_t i = 0; i < t_items.size(); ++i) + { + const auto& current_item = t_items[i]; + + if (!current_item) { + continue; + } + + if (!current_item->is_visible()) + continue; + + current_item->set_collapsed(false); + m_indices_to_draw.emplace_back(i); + } + + float collapse_button_width = 0.0f; + + if (final_canvas_width - toolbar_width < 1e-6f) { + float current_width = 2.0f * t_layout.border; + float uncollapsible_width = 2.0f * t_layout.border; + std::vector t_other_visible_indices; + std::vector t_uncollapsible_indices; + t_other_visible_indices.reserve(10); + t_uncollapsible_indices.reserve(10); + for (size_t i = 0; i < m_indices_to_draw.size(); ++i) + { + const auto& current_index = m_indices_to_draw[i]; + const auto& current_item = t_items[current_index]; + + if (!current_item) { + continue; + } + + if (!current_item->is_visible()) + continue; + + if (current_item->is_collapsible()) { + t_other_visible_indices.emplace_back(i); + } + else { + t_uncollapsible_indices.emplace_back(i); + float item_width = 0.0f; + if (current_item->is_separator()) + item_width += t_layout.separator_size; + else if (current_item->get_type() == GLToolbarItem::EType::SeparatorLine) { + item_width += ((float)t_layout.icons_size * 0.5f); + } + else + { + item_width += (float)t_layout.icons_size; + if (current_item->is_action_with_text()) + item_width += current_item->get_extra_size_ratio() * t_layout.icons_size; + if (current_item->is_action_with_text_image()) + item_width += t_layout.text_size; + } + + if (i < m_indices_to_draw.size() - 1) { + item_width += t_layout.gap_size; + } + + if (current_item->is_collapse_button()) { + collapse_button_width = item_width; + continue; + } + + uncollapsible_width += item_width; + } + } + + current_width = uncollapsible_width; + size_t final_index = 0; + + if (current_width * t_layout.scale - final_canvas_width < 1e-6f) { + for (; final_index < t_other_visible_indices.size(); ++final_index) { + const auto& item_index = m_indices_to_draw[t_other_visible_indices[final_index]]; + const auto& current_item = t_items[item_index]; + float item_width = 0.0f; + if (current_item->is_separator()) + item_width += t_layout.separator_size; + else if (current_item->get_type() == GLToolbarItem::EType::SeparatorLine) { + item_width += ((float)t_layout.icons_size * 0.5f); + } + else + { + item_width += (float)t_layout.icons_size; + if (current_item->is_action_with_text()) + item_width += current_item->get_extra_size_ratio() * t_layout.icons_size; + if (current_item->is_action_with_text_image()) + item_width += t_layout.text_size; + } + + if (item_index < m_indices_to_draw.size() - 1) { + item_width += t_layout.gap_size; + } + + if ((current_width + item_width) * t_layout.scale - final_canvas_width > 1e-6f) { + break; + } + current_width += item_width; + } + } + + if (final_index < t_other_visible_indices.size()) { + current_width = std::max(GLToolbar::Default_Icons_Size + 2.0f * t_layout.border, current_width + collapse_button_width); + + final_toolbar_width = current_width * t_layout.scale; + while (final_canvas_width - final_toolbar_width < 1e-6f) { + if (final_index < 1) { + break; + } + const auto item_index = m_indices_to_draw[t_other_visible_indices[final_index]]; + float item_width = 0.0f; + const auto& current_item = t_items[item_index]; + if (current_item->get_type() == GLToolbarItem::EType::SeparatorLine) { + item_width += ((float)t_layout.icons_size * 0.5f); + item_width += t_layout.gap_size; + } + else if (current_item->is_separator()) { + item_width += t_layout.separator_size; + item_width += t_layout.gap_size; + } + else + { + item_width += (float)t_layout.icons_size; + if (current_item->is_action_with_text()) + item_width += current_item->get_extra_size_ratio() * t_layout.icons_size; + if (current_item->is_action_with_text_image()) + item_width += t_layout.text_size; + item_width += t_layout.gap_size; + } + + final_toolbar_width = final_toolbar_width - (item_width * t_layout.scale); + + --final_index; + } + m_b_needs_collapsed = true; + } + + if (m_b_needs_collapsed) { + if (final_index < t_other_visible_indices.size()) { + std::vector temp_indices; + temp_indices.reserve(m_indices_to_draw.size()); + for (size_t i = 0; i < final_index; ++i) { + const auto item_index = m_indices_to_draw[t_other_visible_indices[i]]; + temp_indices.emplace_back(item_index); + } + for (size_t i = 0; i < t_uncollapsible_indices.size(); ++i) { + const auto item_index = m_indices_to_draw[t_uncollapsible_indices[i]]; + temp_indices.emplace_back(item_index); + } + for (size_t i = final_index; i < t_other_visible_indices.size(); ++i) { + const auto item_index = m_indices_to_draw[t_other_visible_indices[i]]; + const auto& p_item = t_items[item_index]; + temp_indices.emplace_back(item_index); + p_item->set_collapsed(t_toolbar.is_collapsed()); + + if (p_item->is_separator()) { + total_collapse_item_width += t_layout.separator_size; + total_collapse_item_width += t_layout.gap_size; + } + else if (p_item->is_action() || p_item->get_type() == GLToolbarItem::EType::SeparatorLine) { + if (p_item->is_action()) { + total_collapse_item_width += (float)t_layout.icons_size; + total_collapse_item_width += t_layout.gap_size; + } + else { + total_collapse_item_width += ((float)t_layout.icons_size * 0.5f); + total_collapse_item_width += t_layout.gap_size; + } + } + else if (p_item->is_action_with_text()) + { + total_collapse_item_width += p_item->get_extra_size_ratio() * t_layout.icons_size; + total_collapse_item_width += t_layout.gap_size; + } + } + m_indices_to_draw = std::move(temp_indices); + } + } + } + + float inv_zoom = (float)t_camera.get_inv_zoom(); + float factor = inv_zoom * t_layout.scale; + float scaled_icons_size = t_layout.icons_size * factor; + float scaled_separator_size = t_layout.separator_size * factor; + float scaled_gap_size = t_layout.gap_size * factor; + float scaled_border = t_layout.border * factor; + + float separator_stride = scaled_separator_size + scaled_gap_size; + float icon_stride = scaled_icons_size + scaled_gap_size; + + float left = 0.0f; + float top = 0.0f; + calculate_position(t_toolbar, t_camera, final_toolbar_width, final_toolbar_height, left, top); + + left += scaled_border; + top -= scaled_border; + + float temp_left = left; + float temp_top = top; + float temp_width = 2.0f * t_layout.border; + bool line_start = true; + final_toolbar_height = 0; + + bool offset_flag = true; + collapse_width = 0.0f; + bool b_needs_to_double_check_collapse_width = true; + m_p_override_render_rect = nullptr; + std::vector temp_indices; + temp_indices.reserve(m_indices_to_draw.size()); + for (size_t i = 0; i < m_indices_to_draw.size(); ++i) { + const auto& current_item = t_items[m_indices_to_draw[i]]; + + if (line_start && (current_item->get_type() == GLToolbarItem::EType::SeparatorLine || current_item->is_separator())) { + continue; + } + + if (current_item->is_separator()) { + temp_left += separator_stride; + temp_width += t_layout.separator_size; + temp_width += t_layout.gap_size; + continue; + } + + if (current_item->is_action() || current_item->get_type() == GLToolbarItem::EType::SeparatorLine) { + if (current_item->is_action()) { + temp_width += (float)t_layout.icons_size; + temp_width += t_layout.gap_size; + } + else { + temp_width += ((float)t_layout.icons_size * 0.5f); + temp_width += t_layout.gap_size; + } + current_item->render_rect[0] = temp_left; + current_item->render_rect[1] = temp_left + scaled_icons_size; + current_item->render_rect[2] = temp_top - scaled_icons_size; + current_item->render_rect[3] = temp_top; + } + //QDS: GUI refactor: GLToolbar + if (current_item->is_action_with_text()) + { + float scaled_text_size = current_item->get_extra_size_ratio() * scaled_icons_size; + current_item->render_rect[0] = temp_left + scaled_icons_size; + current_item->render_rect[1] = temp_left + scaled_icons_size + scaled_text_size; + current_item->render_rect[2] = temp_top - scaled_icons_size; + current_item->render_rect[3] = temp_top; + temp_left += scaled_text_size; + temp_width += current_item->get_extra_size_ratio() * t_layout.icons_size; + temp_width += t_layout.gap_size; + } + if (current_item->get_type() == GLToolbarItem::EType::SeparatorLine) { + temp_left += (icon_stride - 0.5f * scaled_icons_size); + } + else { + temp_left += icon_stride; + } + + if (line_start) { + final_toolbar_height += toolbar_height; + } + + if (current_item->is_collapse_button()) { + collapse_width = temp_width; + if (total_collapse_item_width < collapse_width) { + collapse_width = total_collapse_item_width; + } + else { + collapse_width = (total_collapse_item_width) / 2.0f + GLToolbar::Default_Icons_Size; + } + collapse_width = std::min(collapse_width * t_layout.scale, final_toolbar_width); + } + + temp_indices.emplace_back(m_indices_to_draw[i]); + + line_start = false; + + bool new_line = false; + if (offset_flag) { + new_line = final_toolbar_width - temp_width * t_layout.scale < GLToolbar::Default_Icons_Size * t_layout.scale; + } + else { + new_line = collapse_width - temp_width * t_layout.scale < GLToolbar::Default_Icons_Size * t_layout.scale; + } + if (new_line) { + temp_left = left; + temp_top -= toolbar_height * inv_zoom; + if (offset_flag) { + temp_top -= t_layout.collapsed_offset * factor; + offset_flag = false; + } + else { + if (b_needs_to_double_check_collapse_width) { + b_needs_to_double_check_collapse_width = false; + collapse_width = temp_width * t_layout.scale; + } + } + temp_width = 2.0f * t_layout.border; + line_start = true; + } + + if (current_item->is_collapse_button()) { + m_p_override_render_rect = current_item->render_rect; + } + } + m_indices_to_draw.clear(); + m_indices_to_draw = std::move(temp_indices); + +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GLToolbar.hpp b/src/slic3r/GUI/GLToolbar.hpp index a58ceaa..bda6f14 100644 --- a/src/slic3r/GUI/GLToolbar.hpp +++ b/src/slic3r/GUI/GLToolbar.hpp @@ -16,7 +16,7 @@ namespace Slic3r { namespace GUI { class GLCanvas3D; - +struct Camera; //QDS: GUI refactor: GLToolbar wxDECLARE_EVENT(EVT_GLTOOLBAR_OPEN_PROJECT, SimpleEvent); @@ -36,6 +36,7 @@ wxDECLARE_EVENT(EVT_GLTOOLBAR_SEND_TO_PRINTER_ALL, SimpleEvent); wxDECLARE_EVENT(EVT_GLTOOLBAR_PRINT_MULTI_MACHINE, SimpleEvent); wxDECLARE_EVENT(EVT_GLTOOLBAR_SEND_MULTI_APP, SimpleEvent); + wxDECLARE_EVENT(EVT_GLTOOLBAR_ADD, SimpleEvent); wxDECLARE_EVENT(EVT_GLTOOLBAR_DELETE, SimpleEvent); wxDECLARE_EVENT(EVT_GLTOOLBAR_DELETE_ALL, SimpleEvent); @@ -67,7 +68,10 @@ public: typedef std::function ActionCallback; typedef std::function VisibilityCallback; typedef std::function EnablingCallback; - typedef std::function RenderCallback; + typedef std::function RenderCallback; + using OnHoverCallback = std::function; + using IconFilenameCallback = std::function; + using PressedRecheckCallback = std::function; enum EType : unsigned char { @@ -121,7 +125,6 @@ public: }; std::string name; - std::string icon_filename; std::string tooltip; std::string additional_tooltip; //QDS: GUI refactor: GLToolbar @@ -142,13 +145,19 @@ public: bool continuous_click{false}; VisibilityCallback visibility_callback; EnablingCallback enabling_callback; - + OnHoverCallback on_hover = nullptr; + IconFilenameCallback icon_filename_callback = nullptr; + PressedRecheckCallback pressed_recheck_callback = nullptr; + bool b_toggle_disable_others{ true }; + bool b_toggle_affectable{ true }; + bool b_collapsible{ true }; + bool b_collapse_button{ false }; + bool b_collapsed{ false }; Data(); //QDS: GUI refactor: GLToolbar Data(const GLToolbarItem::Data& data) { name = data.name; - icon_filename = data.icon_filename; tooltip = data.tooltip; additional_tooltip = data.additional_tooltip; button_text = data.button_text; @@ -163,6 +172,14 @@ public: image_data = data.image_data; image_width = data.image_width; image_height = data.image_height; + on_hover = data.on_hover; + icon_filename_callback = data.icon_filename_callback; + pressed_recheck_callback = data.pressed_recheck_callback; + b_toggle_disable_others = data.b_toggle_disable_others; + b_toggle_affectable = data.b_toggle_affectable; + b_collapsible = data.b_collapsible; + b_collapse_button = data.b_collapse_button; + b_collapsed = data.b_collapsed; } }; @@ -181,8 +198,8 @@ private: public: - // remember left position for rendering menu - mutable float render_left_pos; + mutable float render_rect[4]{ 0.0f }; // left, right, bottom, top + mutable float* override_render_rect{ nullptr }; std::chrono::system_clock::time_point get_start_time_point() const { return start; } @@ -195,22 +212,23 @@ public: void set_highlight(EHighlightState state) { m_highlight_state = state; } const std::string& get_name() const { return m_data.name; } - const std::string& get_icon_filename() const { return m_data.icon_filename; } - void set_icon_filename(const std::string& filename) { m_data.icon_filename = filename; } + std::string get_icon_filename(bool is_dark_mode) const; const std::string& get_tooltip() const { return m_data.tooltip; } const std::string& get_additional_tooltip() const { return m_data.additional_tooltip; } void set_additional_tooltip(const std::string& text) { m_data.additional_tooltip = text; } void set_tooltip(const std::string& text) { m_data.tooltip = text; } - - void do_left_action() { m_last_action_type = Left; m_data.left.action_callback(); } - void do_right_action() { m_last_action_type = Right; m_data.right.action_callback(); } + void set_last_action_type(GLToolbarItem::EActionType type); + void do_left_action(); + void do_right_action(); bool is_enabled() const { return (m_state != Disabled) && (m_state != HoverDisabled); } bool is_disabled() const { return (m_state == Disabled) || (m_state == HoverDisabled); } bool is_hovered() const { return (m_state == Hover) || (m_state == HoverPressed) || (m_state == HoverDisabled); } bool is_pressed() const { return (m_state == Pressed) || (m_state == HoverPressed); } - bool is_visible() const { return m_data.visible; } + bool is_visible() const; bool is_separator() const { return m_type == Separator; } + bool toggle_disable_others() const; + bool toggle_affectable() const; bool is_left_toggable() const { return m_data.left.toggable; } bool is_right_toggable() const { return m_data.right.toggable; } @@ -235,45 +253,95 @@ public: void set_button_text(const std::string& text) { m_data.button_text = text; } float get_extra_size_ratio() const { return m_data.extra_size_ratio; } void set_extra_size_ratio(const float ratio) { m_data.extra_size_ratio = ratio; } - void render_text(float left, float right, float bottom, float top) const; + void render_text() const; int generate_texture(wxFont& font); int generate_image_texture(); - void render(unsigned int tex_id, float left, float right, float bottom, float top, unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const; + void render(unsigned int tex_id, unsigned int tex_width, unsigned int tex_height, unsigned int icon_size, float toolbar_height, bool b_flip_v = false) const; void render_image(unsigned int tex_id, float left, float right, float bottom, float top, unsigned int tex_width, unsigned int tex_height, unsigned int icon_size) const; -private: - void set_visible(bool visible) { m_data.visible = visible; } - friend class GLToolbar; + const GLToolbarItem::Data& get_data() const; + + void set_visible(bool visible); + + GLToolbarItem::EType get_type() const; + + bool is_inside(const Vec2d& mouse_pos) const; + + bool is_collapsible() const; + + bool is_collapse_button() const; + + void set_collapsed(bool value); + + bool is_collapsed() const; + + bool recheck_pressed() const; }; -struct BackgroundTexture +struct ToolbarLayout { - struct Metadata + enum EType : unsigned char { - // path of the file containing the background texture - std::string filename; - // size of the left edge, in pixels - unsigned int left; - // size of the right edge, in pixels - unsigned int right; - // size of the top edge, in pixels - unsigned int top; - // size of the bottom edge, in pixels - unsigned int bottom; - - Metadata(); + Horizontal, + Vertical, + Num_Types }; - GLTexture texture; - Metadata metadata; + enum EHorizontalOrientation : unsigned char + { + HO_Left, + HO_Center, + HO_Right, + Num_Horizontal_Orientations + }; + + enum EVerticalOrientation : unsigned char + { + VO_Top, + VO_Center, + VO_Bottom, + Num_Vertical_Orientations + }; + + enum EPositionMode : uint8_t + { + TopLeft, + TopMiddle, + Custom + }; + + EType type; + EHorizontalOrientation horizontal_orientation; + EVerticalOrientation vertical_orientation; + EPositionMode position_mode; + float offset; + float top; + float left; + float border; + float separator_size; + float gap_size; + float icons_size; + float text_size; + float image_width; + float image_height; + float scale; + float collapsed_offset; + + float width; + float height; + bool dirty; + + ToolbarLayout(); }; +class ToolbarRenderer; + class GLToolbar { public: static const float Default_Icons_Size; - +public: enum EType : unsigned char { Normal, @@ -281,68 +349,160 @@ public: Num_Types }; - struct Layout + enum EToolbarRenderingMode : uint8_t { - enum EType : unsigned char - { - Horizontal, - Vertical, - Num_Types - }; - - enum EHorizontalOrientation : unsigned char - { - HO_Left, - HO_Center, - HO_Right, - Num_Horizontal_Orientations - }; - - enum EVerticalOrientation : unsigned char - { - VO_Top, - VO_Center, - VO_Bottom, - Num_Vertical_Orientations - }; - - EType type; - EHorizontalOrientation horizontal_orientation; - EVerticalOrientation vertical_orientation; - float top; - float left; - float border; - float separator_size; - float gap_size; - float icons_size; - float text_size; - float image_width; - float image_height; - float scale; - - float width; - float height; - bool dirty; - - Layout(); + KeepSize, + Auto }; -private: - typedef std::vector ItemsList; + GLToolbar(GLToolbar::EType type, const std::string& name); + virtual ~GLToolbar(); + + bool init(const BackgroundTexture::Metadata& background_texture); + + bool init_arrow(const BackgroundTexture::Metadata& arrow_texture); + + const ToolbarLayout& get_layout() const; + + void set_layout_type(ToolbarLayout::EType type); + + ToolbarLayout::EHorizontalOrientation get_horizontal_orientation() const; + void set_horizontal_orientation(ToolbarLayout::EHorizontalOrientation orientation); + + ToolbarLayout::EVerticalOrientation get_vertical_orientation() const; + void set_vertical_orientation(ToolbarLayout::EVerticalOrientation orientation); + + void set_position_mode(ToolbarLayout::EPositionMode t_position_mode); + void set_offset(float offset); + void set_position(float top, float left); + void set_border(float border); + void set_separator_size(float size); + void set_gap_size(float size); + void set_icons_size(float size); + void set_text_size(float size); + void set_scale(float scale); + float get_scale() const; + + void set_icon_dirty(); + + bool is_enabled() const; + void set_enabled(bool enable); + + float get_icons_size() const; + float get_width() const; + float get_height() const; + + //QDS: GUI refactor: GLToolbar + bool add_item(const GLToolbarItem::Data& data, GLToolbarItem::EType type = GLToolbarItem::Action); + bool add_separator(); + bool del_all_item(); + + void select_item(const std::string& name); + + bool is_item_pressed(const std::string& name) const; + bool is_item_disabled(const std::string& name) const; + bool is_item_visible(const std::string& name) const; + + bool is_any_item_pressed() const; + + unsigned int get_items_count() const { return (unsigned int)m_items.size(); } + int get_item_id(const std::string& name) const; + + std::string get_tooltip() const; + void set_tooltip(int item_id, const std::string& text); + + void get_additional_tooltip(int item_id, std::string& text); + void set_additional_tooltip(int item_id, const std::string& text); + int get_visible_items_cnt() const; + + // get item pointer for highlighter timer + const std::shared_ptr& get_item(const std::string& item_name) const; + + void render(const Camera& t_camera); + + // returns true if any item changed its state + bool update_items_state(); + + void set_dark_mode_enabled(bool is_enabled); + + const std::vector>& get_items() const; + + const GLTexture& get_icon_texture() const; + const BackgroundTexture& get_background_texture() const; + const BackgroundTexture& get_arrow_texture() const; + + bool needs_collapsed() const; + + void toggle_collapsed(); + void set_collapsed(); + + bool is_collapsed() const; + + void set_collapsed_offset(uint32_t offset_in_pixel); + uint32_t get_collapsed_offset(); + + GLToolbar::EToolbarRenderingMode get_rendering_mode(); + void set_rendering_mode(GLToolbar::EToolbarRenderingMode mode); + + void force_left_action(int item_id, GLCanvas3D& parent) { do_action(GLToolbarItem::Left, item_id, parent, false); } + void force_right_action(int item_id, GLCanvas3D& parent) { do_action(GLToolbarItem::Right, item_id, parent, false); } + + void render_arrow(const std::weak_ptr& highlighted_item); + + bool on_mouse(wxMouseEvent& evt, GLCanvas3D& parent); + + //QDS: GUI refactor: GLToolbar + int generate_button_text_textures(wxFont& font); + int generate_image_textures(); + float get_scaled_icon_size(); + +protected: + void calc_layout() const; + const std::shared_ptr& get_renderer() const; + +private: + float get_width_horizontal() const; + float get_width_vertical() const; + float get_height_horizontal() const; + float get_height_vertical() const; + float get_main_size() const; + bool generate_icons_texture() const; + + // returns true if any item changed its state + bool update_items_visibility(); + // returns true if any item changed its state + bool update_items_enabled_state(); + + bool update_items_pressed_state(); + + void do_action(GLToolbarItem::EActionType type, int item_id, GLCanvas3D& parent, bool check_hover); + void update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent); + // returns the id of the item under the given mouse position or -1 if none + int contains_mouse(const Vec2d& mouse_pos, const GLCanvas3D& parent) const; + +private: + mutable ToolbarLayout m_layout; + mutable bool m_icons_texture_dirty{ true }; + mutable GLTexture m_icons_texture; + bool m_enabled{ false }; + GLToolbar::EType m_type{ GLToolbar::EType::Normal }; + std::string m_name{}; + std::vector> m_items; - EType m_type; - std::string m_name; - bool m_enabled; - GLTexture m_icons_texture; - bool m_icons_texture_dirty; - mutable GLTexture m_images_texture; - mutable bool m_images_texture_dirty; BackgroundTexture m_background_texture; BackgroundTexture m_arrow_texture; - Layout m_layout; - ItemsList m_items; + int m_pressed_toggable_id{ -1 }; + bool m_b_dark_mode_enabled{ false }; + mutable std::shared_ptr m_p_renderer{ nullptr }; + + bool m_b_collapsed{ true }; + + EToolbarRenderingMode m_rendering_mode{ EToolbarRenderingMode::Auto }; + + mutable GLTexture m_images_texture; + mutable bool m_images_texture_dirty; struct MouseCapture { bool left; @@ -357,106 +517,67 @@ private: }; MouseCapture m_mouse_capture; - int m_pressed_toggable_id; +}; +class ToolbarRenderer +{ public: - GLToolbar(EType type, const std::string& name); - ~GLToolbar(); + explicit ToolbarRenderer(); + ~ToolbarRenderer(); - bool init(const BackgroundTexture::Metadata& background_texture); + virtual void render(const GLToolbar& t_toolbar, const Camera& t_camera) = 0; - bool init_arrow(const BackgroundTexture::Metadata& arrow_texture); + virtual GLToolbar::EToolbarRenderingMode get_mode() const = 0; - Layout::EType get_layout_type() const; - void set_layout_type(Layout::EType type); - void set_icon_dirty() { m_icons_texture_dirty = true; } - Layout::EHorizontalOrientation get_horizontal_orientation() const { return m_layout.horizontal_orientation; } - void set_horizontal_orientation(Layout::EHorizontalOrientation orientation) { m_layout.horizontal_orientation = orientation; } - Layout::EVerticalOrientation get_vertical_orientation() const { return m_layout.vertical_orientation; } - void set_vertical_orientation(Layout::EVerticalOrientation orientation) { m_layout.vertical_orientation = orientation; } + virtual void render_arrow(const GLToolbar& t_toolbar, const std::weak_ptr& highlighted_item) = 0; - void set_position(float top, float left); - void set_border(float border); - void set_separator_size(float size); - void set_gap_size(float size); - void set_icons_size(float size); - void set_text_size(float size); - void set_scale(float scale); + virtual bool needs_collapsed() const; +}; - bool is_enabled() const { return m_enabled; } - void set_enabled(bool enable) { m_enabled = enable; } +class ToolbarAutoSizeRenderer : public ToolbarRenderer +{ +public: + explicit ToolbarAutoSizeRenderer(); + ~ToolbarAutoSizeRenderer(); - //QDS: GUI refactor: GLToolbar - bool add_item(const GLToolbarItem::Data& data, GLToolbarItem::EType type = GLToolbarItem::Action); - bool add_separator(); - bool del_all_item(); + void render(const GLToolbar& t_toolbar, const Camera& t_camera) override; - float get_icons_size() { return m_layout.icons_size; } - float get_width(); - float get_height(); + GLToolbar::EToolbarRenderingMode get_mode() const override; - void select_item(const std::string& name); - - bool is_item_pressed(const std::string& name) const; - bool is_item_disabled(const std::string& name) const; - bool is_item_visible(const std::string& name) const; - - bool is_any_item_pressed() const; - - unsigned int get_items_count() const { return (unsigned int)m_items.size(); } - int get_item_id(const std::string& name) const; - - void force_left_action(int item_id, GLCanvas3D& parent) { do_action(GLToolbarItem::Left, item_id, parent, false); } - void force_right_action(int item_id, GLCanvas3D& parent) { do_action(GLToolbarItem::Right, item_id, parent, false); } - - std::string get_tooltip() const; - - void get_additional_tooltip(int item_id, std::string& text); - void set_additional_tooltip(int item_id, const std::string& text); - void set_tooltip(int item_id, const std::string& text); - int get_visible_items_cnt() const; - - // returns true if any item changed its state - bool update_items_state(); - - void render(const GLCanvas3D& parent,GLToolbarItem::EType type = GLToolbarItem::Action); - void render_arrow(const GLCanvas3D& parent, GLToolbarItem* highlighted_item); - - bool on_mouse(wxMouseEvent& evt, GLCanvas3D& parent); - // get item pointer for highlighter timer - GLToolbarItem* get_item(const std::string& item_name); - - //QDS: GUI refactor: GLToolbar - int generate_button_text_textures(wxFont& font); - int generate_image_textures(); - float get_scaled_icon_size(); + void render_arrow(const GLToolbar& t_toolbar, const std::weak_ptr& highlighted_item) override; private: - void calc_layout(); - float get_width_horizontal() const; - float get_width_vertical() const; - float get_height_horizontal() const; - float get_height_vertical() const; - float get_main_size() const; - void do_action(GLToolbarItem::EActionType type, int item_id, GLCanvas3D& parent, bool check_hover); - void update_hover_state(const Vec2d& mouse_pos, GLCanvas3D& parent); - void update_hover_state_horizontal(const Vec2d& mouse_pos, GLCanvas3D& parent); - void update_hover_state_vertical(const Vec2d& mouse_pos, GLCanvas3D& parent); - // returns the id of the item under the given mouse position or -1 if none - int contains_mouse(const Vec2d& mouse_pos, const GLCanvas3D& parent) const; - int contains_mouse_horizontal(const Vec2d& mouse_pos, const GLCanvas3D& parent) const; - int contains_mouse_vertical(const Vec2d& mouse_pos, const GLCanvas3D& parent) const; + void render_horizontal(const GLToolbar& t_toolbar, const Camera& t_camera); + void render_vertical(const GLToolbar& t_toolbar, const Camera& t_camera); + void render_background(const GLToolbar& t_toolbar, float left, float top, float right, float bottom, float border) const; + void calculate_position(const GLToolbar& t_toolbar, const Camera& t_camera, float& left, float& top); +}; - void render_background(float left, float top, float right, float bottom, float border) const; - void render_horizontal(const GLCanvas3D& parent,GLToolbarItem::EType type); - void render_vertical(const GLCanvas3D& parent); +class ToolbarKeepSizeRenderer : public ToolbarRenderer +{ +public: + explicit ToolbarKeepSizeRenderer(); + ~ToolbarKeepSizeRenderer(); - bool generate_icons_texture(); + void render(const GLToolbar& t_toolbar, const Camera& t_camera) override; - // returns true if any item changed its state - bool update_items_visibility(); - // returns true if any item changed its state - bool update_items_enabled_state(); + GLToolbar::EToolbarRenderingMode get_mode() const override; + + void render_arrow(const GLToolbar& t_toolbar, const std::weak_ptr& highlighted_item) override; + + bool needs_collapsed() const override; + +private: + void render_horizontal(const GLToolbar& t_toolbar); + void render_vertical(const GLToolbar& t_toolbar); + void render_background(const GLToolbar& t_toolbar, float left, float top, float right, float bottom, float border) const; + void calculate_position(const GLToolbar& t_toolbar, const Camera& t_camera, float toolbar_width, float toolbar_height, float& left, float& top); + void recalculate_item_pos(const GLToolbar& t_toolbar, const Camera& t_camera, float& final_toolbar_width, float& final_toolbar_height, float& collapse_width); + +private: + bool m_b_needs_collapsed{ false }; + std::vector m_indices_to_draw; + float* m_p_override_render_rect{ nullptr }; }; } // namespace GUI diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 5164db6..14626a7 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -68,6 +68,7 @@ #include "MainFrame.hpp" #include "Plater.hpp" #include "GLCanvas3D.hpp" +#include "EncodedFilament.hpp" #include "../Utils/PresetUpdater.hpp" #include "../Utils/PrintHost.hpp" @@ -105,6 +106,7 @@ #include "PrivacyUpdateDialog.hpp" #include "ModelMall.hpp" #include "HintNotification.hpp" +#include "QDTUtil.hpp" //#ifdef WIN32 //#include "BaseException.h" @@ -1343,7 +1345,7 @@ void GUI_App::post_init() if(!m_networking_need_update && m_agent) { m_agent->set_on_ssdp_msg_fn( [this](std::string json_str) { - if (m_is_closing) { + if (is_closing()) { return; } GUI::wxGetApp().CallAfter([this, json_str] { @@ -1451,7 +1453,7 @@ void GUI_App::shutdown() } if (m_is_recreating_gui) return; - m_is_closing = true; + set_closing(true); BOOST_LOG_TRIVIAL(info) << "GUI_App::shutdown exit"; } @@ -1763,11 +1765,11 @@ int GUI_App::install_plugin(std::string name, std::string package_name, InstallP //auto plugin_folder = boost::filesystem::path(wxStandardPaths::Get().GetUserDataDir().ToUTF8().data()) / "plugins"; auto backup_folder = plugin_folder/"backup"; if (!boost::filesystem::exists(plugin_folder)) { - BOOST_LOG_TRIVIAL(info) << "[install_plugin] will create directory "<status())) { ++file_count; } } for (fs::directory_iterator it(dir_path); it != fs::directory_iterator(); ++it) { - BOOST_LOG_TRIVIAL(info) << " current path:" << it->path().string(); + BOOST_LOG_TRIVIAL(trace) << " current path:" << PathSanitizer::sanitize(it->path().string()); if (it->path().string() == backup_folder) { continue; } auto dest_path = backup_folder.string() + "/" + it->path().filename().string(); if (fs::is_regular_file(it->status())) { - BOOST_LOG_TRIVIAL(info) << " copy file:" << it->path().string() << "," << it->path().filename(); + BOOST_LOG_TRIVIAL(trace) << " copy file:" << PathSanitizer::sanitize(it->path().string()) << "," << it->path().filename(); try { if (pro_fn) { pro_fn(InstallStatusNormal, 50 + file_index / file_count, cancel); } file_index++; @@ -1887,7 +1889,7 @@ int GUI_App::install_plugin(std::string name, std::string package_name, InstallP BOOST_LOG_TRIVIAL(error) << "Copying to backup failed: " << e.what(); } } else { - BOOST_LOG_TRIVIAL(info) << " copy framework:" << it->path().string() << "," << it->path().filename(); + BOOST_LOG_TRIVIAL(trace) << " copy framework:" << it->path().string() << "," << it->path().filename(); copy_framework(it->path().string(), dest_path); } } @@ -1910,7 +1912,7 @@ void GUI_App::restart_networking() init_networking_callbacks(); m_agent->set_on_ssdp_msg_fn( [this](std::string json_str) { - if (m_is_closing) { + if (is_closing()) { return; } GUI::wxGetApp().CallAfter([this, json_str] { @@ -1954,11 +1956,11 @@ void GUI_App::remove_old_networking_plugins() auto plugin_folder = data_dir_path / "plugins"; //auto plugin_folder = boost::filesystem::path(wxStandardPaths::Get().GetUserDataDir().ToUTF8().data()) / "plugins"; if (boost::filesystem::exists(plugin_folder)) { - BOOST_LOG_TRIVIAL(info) << "[remove_old_networking_plugins] remove the directory "<set_on_server_connected_fn([this](int return_code, int reason_code) { - if (m_is_closing) { + if (is_closing()) { return; } if (return_code == 5) { @@ -2051,7 +2053,7 @@ void GUI_App::init_networking_callbacks() return; } GUI::wxGetApp().CallAfter([this] { - if (m_is_closing) + if (is_closing()) return; BOOST_LOG_TRIVIAL(trace) << "static: server connected"; m_agent->set_user_selected_machine(m_agent->get_user_selected_machine()); @@ -2062,7 +2064,6 @@ void GUI_App::init_networking_callbacks() m_agent->set_user_selected_machine(m_agent->get_user_selected_machine()); //subscribe device if (m_agent->is_user_login()) { - m_agent->start_device_subscribe(); /*disconnect lan*/ DeviceManager* dev = this->getDeviceManager(); @@ -2090,11 +2091,11 @@ void GUI_App::init_networking_callbacks() }); m_agent->set_on_printer_connected_fn([this](std::string dev_id) { - if (m_is_closing) { + if (is_closing()) { return; } GUI::wxGetApp().CallAfter([this, dev_id] { - if (m_is_closing) + if (is_closing()) return; bool tunnel = boost::algorithm::starts_with(dev_id, "tunnel/"); /* request_pushing */ @@ -2133,11 +2134,11 @@ void GUI_App::init_networking_callbacks() m_agent->set_on_local_connect_fn( [this](int state, std::string dev_id, std::string msg) { - if (m_is_closing) { + if (is_closing()) { return; } CallAfter([this, state, dev_id, msg] { - if (m_is_closing) { + if (is_closing()) { return; } /* request_pushing */ @@ -2204,11 +2205,11 @@ void GUI_App::init_networking_callbacks() ); auto message_arrive_fn = [this](std::string dev_id, std::string msg) { - if (m_is_closing) { + if (is_closing()) { return; } CallAfter([this, dev_id, msg] { - if (m_is_closing) + if (is_closing()) return; this->process_network_msg(dev_id, msg); @@ -2238,11 +2239,11 @@ void GUI_App::init_networking_callbacks() m_agent->set_on_message_fn(message_arrive_fn); auto user_message_arrive_fn = [this](std::string user_id, std::string msg) { - if (m_is_closing) { + if (is_closing()) { return; } CallAfter([this, user_id, msg] { - if (m_is_closing) + if (is_closing()) return; //check user @@ -2257,11 +2258,11 @@ void GUI_App::init_networking_callbacks() auto lan_message_arrive_fn = [this](std::string dev_id, std::string msg) { - if (m_is_closing) { + if (is_closing()) { return; } CallAfter([this, dev_id, msg] { - if (m_is_closing) + if (is_closing()) return; this->process_network_msg(dev_id, msg); @@ -2562,7 +2563,7 @@ void GUI_App::on_start_subscribe_again(std::string dev_id) if ( (dev_id == obj->dev_id) && obj->is_connecting() && obj->subscribe_counter > 0) { obj->subscribe_counter--; if(wxGetApp().getAgent()) wxGetApp().getAgent()->set_user_selected_machine(dev_id); - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": dev_id=" << obj->dev_id; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": dev_id=" << QDTCrossTalk::Crosstalk_DevId(obj->dev_id); } }); start_subscribe_timer->Start(5000, wxTIMER_ONE_SHOT); @@ -2589,7 +2590,7 @@ std::string GUI_App::get_local_models_path() void GUI_App::init_single_instance_checker(const std::string &name, const std::string &path) { - BOOST_LOG_TRIVIAL(debug) << "init wx instance checker " << name << " "<< path; + BOOST_LOG_TRIVIAL(debug) << "init wx instance checker " << name << " " << PathSanitizer::sanitize(path); m_single_instance_checker = std::make_unique(boost::nowide::widen(name), boost::nowide::widen(path)); } @@ -2798,7 +2799,11 @@ bool GUI_App::on_init_inner() } #endif - BOOST_LOG_TRIVIAL(info) << boost::format("gui mode, Current QIDIStudio Version %1%")%SLIC3R_VERSION; + BOOST_LOG_TRIVIAL(info) << boost::format("gui mode, Current QIDIStudio Version %1%")%SLIC3R_VERSION << ", BuildTime " << SLIC3R_BUILD_TIME; + +#if !QDT_RELEASE_TO_PUBLIC + BOOST_LOG_TRIVIAL(info) << boost::format("Build Version %1%")%SLIC3R_COMPILE_VERSION; +#endif BOOST_LOG_TRIVIAL(info) << get_system_info(); #if defined(__WINDOWS__) @@ -3288,11 +3293,11 @@ void GUI_App::copy_network_if_available() BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": checking network_library from ota directory"; if (!boost::filesystem::exists(plugin_folder)) { - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": create directory " << plugin_folder.string(); + //BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": create directory " << plugin_folder.string(); boost::filesystem::create_directory(plugin_folder); } if (!boost::filesystem::exists(cache_folder)) { - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": can not found ota plugins directory " << cache_folder.string(); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": can not found ota plugins directory "; app_config->set("update_network_plugin", "false"); return; } @@ -3315,7 +3320,7 @@ void GUI_App::copy_network_if_available() static constexpr const auto perms = fs::owner_read | fs::owner_write | fs::group_read | fs::others_read; fs::permissions(dest_path, perms); - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": Copying network library from" << file_path << " to " << dest_path << " successfully."; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": Copying network library successfully."; } } @@ -3832,7 +3837,7 @@ wxSize GUI_App::get_min_size() const return wxSize(std::max(1000, 76*m_em_unit), std::max(600, 49 * m_em_unit)); } -float GUI_App::toolbar_icon_scale(const bool is_limited/* = false*/) const +float GUI_App::toolbar_icon_scale(bool auto_scale, const bool is_limited/* = false*/) const { #ifdef __APPLE__ const float icon_sc = 1.0f; // for Retina display will be used its own scale @@ -3840,7 +3845,9 @@ float GUI_App::toolbar_icon_scale(const bool is_limited/* = false*/) const const float icon_sc = m_em_unit * 0.1f; #endif // __APPLE__ - //return icon_sc; + if (!auto_scale) { + return icon_sc; + } const std::string& auto_val = app_config->get("toolkit_size"); @@ -4404,7 +4411,7 @@ int GUI_App::request_user_unbind(std::string dev_id) int result = -1; if (m_agent) { result = m_agent->unbind(dev_id); - BOOST_LOG_TRIVIAL(info) << "request_user_unbind, dev_id = " << dev_id << ", result = " << result; + BOOST_LOG_TRIVIAL(info) << "request_user_unbind, dev_id = " << QDTCrossTalk::Crosstalk_DevId(dev_id) << ", result = " << result; return result; } return result; @@ -4638,9 +4645,28 @@ std::string GUI_App::handle_web_request(std::string cmd) } else if (command_str.compare("homepage_makerlab_open") == 0) { if (root.get_child_optional("url") != boost::none) { - std::string strUrl = root.get_optional("url").value(); + if(wxGetApp().is_user_login()) { + std::string strUrl = root.get_optional("url").value(); - if (mainframe && mainframe->m_webview) { mainframe->m_webview->OpenOneMakerlab(strUrl); } + if (mainframe && mainframe->m_webview) { mainframe->m_webview->OpenOneMakerlab(strUrl); } + }else { + //Check Plugin + bool bValid = is_compatibility_version(); + if (!bValid) { + CallAfter([this] { handle_web_request("{\"sequence_id\":1,\"command\":\"homepage_need_networkplugin\"}"); + }); + return ""; + } + CallAfter([this] { + this->request_login(true); + }); + } + + } + } + else if (command_str.compare("update_plugin_installtip") == 0) { + if (mainframe) { + mainframe->refresh_plugin_tips(); } } //y @@ -5679,7 +5705,7 @@ void GUI_App::start_sync_user_preset(bool with_progress_dlg) // }); // }; // cancelFn = [this, dlg]() { - // return m_is_closing || dlg->WasCanceled(); + // return is_closing() || dlg->WasCanceled(); // }; // finishFn = [this, userid = m_agent->get_user_id(), dlg, t = std::weak_ptr(m_user_sync_token)](bool ok) { // CallAfter([=]{ @@ -5694,12 +5720,16 @@ void GUI_App::start_sync_user_preset(bool with_progress_dlg) // if (ok && m_agent && t.lock() == m_user_sync_token && userid == m_agent->get_user_id()) reload_settings(); // }); // }; + // cancelFn = [this]() { + // return is_closing(); + // }; // } // m_sync_update_thread = Slic3r::create_thread( // [this, progressFn, cancelFn, finishFn, t = std::weak_ptr(m_user_sync_token)] { // // get setting list, update setting list // std::string version = preset_bundle->get_vendor_profile_version(PresetBundle::QDT_BUNDLE).to_string(); + // BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << __LINE__ << " start sync user preset, m_is_closing = " << m_is_closing; // int ret = m_agent->get_setting_list2(version, [this](auto info) { // auto type = info[QDT_JSON_KEY_TYPE]; // auto name = info[QDT_JSON_KEY_NAME]; @@ -5718,6 +5748,7 @@ void GUI_App::start_sync_user_preset(bool with_progress_dlg) // return true; // } // }, progressFn, cancelFn); + // BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << __LINE__ << " get_setting_list2 ret = " << ret << " m_is_closing = " << m_is_closing; // finishFn(ret == 0); // int count = 0, sync_count = 0; @@ -5762,7 +5793,7 @@ void GUI_App::start_sync_user_preset(bool with_progress_dlg) // if (total_count == 0) { // CallAfter([this] { - // if (!m_is_closing) + // if (!is_closing()) // plater()->get_notification_manager()->close_notification_of_type(NotificationType::QDTUserPresetExceedLimit); // }); // } @@ -5801,7 +5832,7 @@ void GUI_App::stop_sync_user_preset() m_user_sync_token.reset(); if (m_sync_update_thread.joinable()) { - if (m_is_closing) + if (is_closing()) m_sync_update_thread.join(); else m_sync_update_thread.detach(); @@ -6029,13 +6060,27 @@ bool GUI_App::load_language(wxString language, bool initial) // There is a static list of lookup path prefixes in wxWidgets. Add ours. wxFileTranslationsLoader::AddCatalogLookupPathPrefix(from_u8(localization_dir())); // Get the active language from PrusaSlicer.ini, or empty string if the key does not exist. + language = app_config->get("language"); + + /* erase the unsupported language in config files*/ + { + wxLanguage cur_lang = wxLANGUAGE_UNKNOWN; + auto cur_lang_info = wxLocale::FindLanguageInfo(language); + if (cur_lang_info) { cur_lang = static_cast (cur_lang_info->Language);} + if (std::find(s_supported_languages.begin(), s_supported_languages.end(), cur_lang) == s_supported_languages.end()) + { + app_config->set("language", ""); + language = app_config->get("language"); + } + } + if (! language.empty()) BOOST_LOG_TRIVIAL(info) << boost::format("language provided by QIDIStudio.conf: %1%") % language; else { // Get the system language. const wxLanguage lang_system = wxLanguage(wxLocale::GetSystemLanguage()); - if (lang_system != wxLANGUAGE_UNKNOWN) { + if (std::find(s_supported_languages.begin(), s_supported_languages.end(), lang_system) != s_supported_languages.end()) { m_language_info_system = wxLocale::GetLanguageInfo(lang_system); #ifdef __WXMSW__ WCHAR wszLanguagesBuffer[LOCALE_NAME_MAX_LENGTH]; @@ -6263,6 +6308,7 @@ void GUI_App::update_mode() sidebar().update_mode(); //QDS: GUI refactor + mainframe->update_calibration_button_status(); if (mainframe->m_param_panel) mainframe->m_param_panel->update_mode(); if (mainframe->m_param_dialog) @@ -6895,7 +6941,9 @@ void GUI_App::OSXStoreOpenFiles(const wxArrayString &fileNames) void GUI_App::MacOpenURL(const wxString& url) { +#if !QDT_RELEASE_TO_PUBLIC BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << "get mac url " << url; +#endif if (!url.empty() && boost::starts_with(url, "qidistudioopen://")) { auto input_str_arr = split_str(url.ToStdString(), "qidistudioopen://"); @@ -6907,7 +6955,11 @@ void GUI_App::MacOpenURL(const wxString& url) } std::string download_file_url = url_decode(download_origin_url); + +#if !QDT_RELEASE_TO_PUBLIC BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << download_file_url; +#endif + if (!download_file_url.empty() && (boost::starts_with(download_file_url, "http://") || boost::starts_with(download_file_url, "https://"))) { if (m_post_initialized) { @@ -7582,6 +7634,17 @@ const ColorRGB& GUI_App::get_picking_color() const return m_picking_color; } + +FilamentColorCodeQuery* GUI_App::get_filament_color_code_query() +{ + if (!m_filament_color_code_query) + { + m_filament_color_code_query = new FilamentColorCodeQuery(); + } + + return m_filament_color_code_query; +} + bool GUI_App::open_browser_with_warning_dialog(const wxString& url, int flags/* = 0*/) { return wxLaunchDefaultBrowser(url, flags); diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 0ff693e..dbb42ff 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -68,6 +69,7 @@ struct wxLanguageInfo; namespace Slic3r { class AppConfig; +class FilamentColorCodeQuery; class PresetBundle; class PresetUpdater; class ModelObject; @@ -300,7 +302,7 @@ private: size_t m_instance_hash_int; //QDS - bool m_is_closing {false}; + std::atomic m_is_closing {false}; Slic3r::DeviceManager* m_device_manager { nullptr }; Slic3r::UserManager* m_user_manager { nullptr }; Slic3r::TaskManager* m_task_manager { nullptr }; @@ -319,6 +321,7 @@ private: VersionInfo privacy_version_info; static std::string version_display; HMSQuery *hms_query { nullptr }; + FilamentColorCodeQuery* m_filament_color_code_query{ nullptr }; boost::thread m_sync_update_thread; std::shared_ptr m_user_sync_token; @@ -359,11 +362,15 @@ public: Slic3r::TaskManager* getTaskManager() { return m_task_manager; } HMSQuery* get_hms_query() { return hms_query; } NetworkAgent* getAgent() { return m_agent; } + FilamentColorCodeQuery* get_filament_color_code_query(); bool is_editor() const { return m_app_mode == EAppMode::Editor; } bool is_gcode_viewer() const { return m_app_mode == EAppMode::GCodeViewer; } bool is_recreating_gui() const { return m_is_recreating_gui; } std::string logo_name() const { return is_editor() ? "QIDIStudio" : "QIDIStudio-gcodeviewer"; } + bool is_closing() const { return m_is_closing.load(std::memory_order_acquire); } + void set_closing(bool closing) { m_is_closing.store(closing, std::memory_order_release); } + bool show_3d_navigator() const { return app_config->get_bool("show_3d_navigator"); } void toggle_show_3d_navigator() const { app_config->set_bool("show_3d_navigator", !show_3d_navigator()); } @@ -443,7 +450,7 @@ public: int em_unit() const { return m_em_unit; } bool tabs_as_menu() const; wxSize get_min_size() const; - float toolbar_icon_scale(const bool is_limited = false) const; + float toolbar_icon_scale(bool auto_scale, const bool is_limited = false) const; void set_auto_toolbar_icon_scale(float scale) const; void check_printer_presets(); @@ -769,6 +776,26 @@ bool is_support_filament(int extruder_id); bool is_soluble_filament(int extruder_id); // check if the filament for model is in the list bool has_filaments(const std::vector& model_filaments); + +static std::vector s_supported_languages = { + wxLANGUAGE_ENGLISH, + wxLANGUAGE_CHINESE_SIMPLIFIED, + wxLANGUAGE_GERMAN, + wxLANGUAGE_FRENCH, + wxLANGUAGE_SPANISH, + wxLANGUAGE_SWEDISH, + wxLANGUAGE_DUTCH, + wxLANGUAGE_HUNGARIAN, + wxLANGUAGE_JAPANESE, + wxLANGUAGE_ITALIAN, + wxLANGUAGE_KOREAN, + wxLANGUAGE_RUSSIAN, + wxLANGUAGE_CZECH, + wxLANGUAGE_UKRAINIAN, + wxLANGUAGE_PORTUGUESE_BRAZILIAN, + wxLANGUAGE_TURKISH, + wxLANGUAGE_POLISH +}; } // namespace GUI } // Slic3r diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index ee015a8..441f834 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -107,7 +107,8 @@ std::map> SettingsFactory::PART_CAT }}, { L("Speed"), {{"outer_wall_speed", "",1},{"inner_wall_speed", "",2},{"sparse_infill_speed", "",3},{"top_surface_speed", "",4}, {"internal_solid_infill_speed", "",5}, {"enable_overhang_speed", "",6}, {"overhang_1_4_speed", "",7}, {"overhang_2_4_speed", "",8}, {"overhang_3_4_speed", "",9}, {"overhang_4_4_speed", "",10}, {"overhang_totally_speed", "",11}, - {"bridge_speed", "",12}, {"gap_infill_speed", "",13} + {"bridge_speed", "",12}, {"gap_infill_speed", "",13}, {"enable_height_slowdown", "", 14}, {"slowdown_start_height", "", 15}, {"slowdown_start_speed", "", 16}, {"slowdown_start_acc", "", 17}, + {"slowdown_end_height", "", 18}, {"slowdown_end_speed", "", 19}, {"slowdown_end_acc", "", 20} }} }; @@ -255,7 +256,7 @@ std::map SettingsFactory::CATEGORY_ICON = { L("Shell") , "blank_14" }, { L("Infill") , "blank_14" }, { L("Ironing") , "blank_14" }, - { L("Fuzzy Skin") , "menu_fuzzy_skin" }, + { L("Fuzzy Skin") , "fuzzy_skin" }, { L("Support") , "support" }, { L("Speed") , "blank_14" }, { L("Extruders") , "blank_14" }, @@ -501,15 +502,19 @@ void MenuFactory::append_menu_item_delete_all_cutter(wxMenu *menu) void MenuFactory::append_menu_item_edit_text(wxMenu *menu) { -#ifdef __WINDOWS__ + wxString name = _L("Edit Text"); + if (menu != &m_text_part_menu) { + const int menu_item_id = menu->FindItem(name); + if (menu_item_id != wxNOT_FOUND) + menu->Destroy(menu_item_id); + if (plater() == nullptr) + return; + if (!plater()->can_edit_text()) + return; + } append_menu_item( menu, wxID_ANY, _L("Edit Text"), "", [](wxCommandEvent &) { plater()->edit_text(); }, "", nullptr, []() { return plater()->can_edit_text(); }, m_parent); -#else - append_menu_item( - menu, wxID_ANY, _L("Edit Text"), "", [](wxCommandEvent &) { plater()->edit_text(); }, "", nullptr, - []() { return plater()->can_edit_text(); }, m_parent); -#endif } void MenuFactory::append_menu_item_edit_svg(wxMenu *menu) @@ -528,15 +533,26 @@ void MenuFactory::append_menu_item_edit_svg(wxMenu *menu) if (menu != &m_svg_part_menu) { const int menu_item_id = menu->FindItem(name); - if (menu_item_id != wxNOT_FOUND) menu->Destroy(menu_item_id); + if (menu_item_id != wxNOT_FOUND) + menu->Destroy(menu_item_id); if (!can_edit_svg()) return; } wxString description = _L("Change SVG source file, projection, size, ..."); std::string icon = "svg_part"; auto open_svg = [](const wxCommandEvent &) { - GLGizmosManager &mng = plater()->get_view3D_canvas3D()->get_gizmos_manager(); - if (mng.get_current_type() == GLGizmosManager::Svg) mng.open_gizmo(GLGizmosManager::Svg); // close() and reopen - move to be visible + const auto& p_plater = plater(); + if (!p_plater) { + return; + } + const auto& p_canvas = p_plater->get_current_canvas3D(); + if (!p_canvas) { + return; + } + GLGizmosManager &mng = p_canvas->get_gizmos_manager(); + if (mng.get_current_type() == GLGizmosManager::Svg) { + mng.open_gizmo(GLGizmosManager::Svg); // close() and reopen - move to be visible + } mng.open_gizmo(GLGizmosManager::Svg); }; append_menu_item(menu, wxID_ANY, name, description, open_svg, icon, nullptr, can_edit_svg, m_parent); @@ -1498,6 +1514,7 @@ void MenuFactory::init(wxWindow* parent) //create_object_menu(); create_sla_object_menu(); //create_part_menu(); + create_text_part_menu(); create_svg_part_menu(); create_qdt_object_menu(); create_qdt_part_menu(); @@ -1533,6 +1550,7 @@ wxMenu* MenuFactory::object_menu() append_menu_items_convert_unit(&m_object_menu); append_menu_items_flush_options(&m_object_menu); append_menu_item_invalidate_cut_info(&m_object_menu); + append_menu_item_edit_text(&m_object_menu); append_menu_item_edit_svg(&m_object_menu); append_menu_item_change_filament(&m_object_menu); { @@ -1546,7 +1564,7 @@ wxMenu* MenuFactory::sla_object_menu() { append_menu_items_convert_unit(&m_sla_object_menu); append_menu_item_settings(&m_sla_object_menu); - //append_menu_item_edit_text(&m_sla_object_menu); + append_menu_item_edit_text(&m_sla_object_menu); append_menu_item_edit_svg(&m_object_menu); //update_menu_items_instance_manipulation(mtObjectSLA); return &m_sla_object_menu; diff --git a/src/slic3r/GUI/GUI_ObjectList.cpp b/src/slic3r/GUI/GUI_ObjectList.cpp index b3a6c5c..258542d 100644 --- a/src/slic3r/GUI/GUI_ObjectList.cpp +++ b/src/slic3r/GUI/GUI_ObjectList.cpp @@ -4,6 +4,7 @@ #include "GUI_Factories.hpp" //#include "GUI_ObjectLayers.hpp" #include "GUI_App.hpp" +#include "GLToolbar.hpp" #include "I18N.hpp" #include "Plater.hpp" #include "BitmapComboBox.hpp" @@ -211,7 +212,7 @@ ObjectList::ObjectList(wxWindow* parent) : { // Accelerators // wxAcceleratorEntry entries[25]; - wxAcceleratorEntry entries[26]; + wxAcceleratorEntry entries[27]; int index = 0; entries[index++].Set(wxACCEL_CTRL, (int)'C', wxID_COPY); entries[index++].Set(wxACCEL_CTRL, (int)'X', wxID_CUT); @@ -220,6 +221,7 @@ ObjectList::ObjectList(wxWindow* parent) : entries[index++].Set(wxACCEL_CTRL, (int)'A', wxID_SELECTALL); entries[index++].Set(wxACCEL_CTRL, (int)'Z', wxID_UNDO); entries[index++].Set(wxACCEL_CTRL, (int)'Y', wxID_REDO); + entries[index++].Set(wxACCEL_CTRL | wxACCEL_SHIFT, (int)'Z', wxID_REDO); entries[index++].Set(wxACCEL_NORMAL, WXK_BACK, wxID_DELETE); //entries[index++].Set(wxACCEL_NORMAL, int('+'), wxID_ADD); //entries[index++].Set(wxACCEL_NORMAL, WXK_NUMPAD_ADD, wxID_ADD); @@ -234,7 +236,7 @@ ObjectList::ObjectList(wxWindow* parent) : numbers_cnt++; // index++; } - wxAcceleratorTable accel(26, entries); + wxAcceleratorTable accel(27, entries); SetAcceleratorTable(accel); this->Bind(wxEVT_MENU, [this](wxCommandEvent &evt) { this->copy(); }, wxID_COPY); @@ -374,6 +376,7 @@ void ObjectList::create_objects_ctrl() m_columns_width[colPrint] = 3; m_columns_width[colFilament] = 5; m_columns_width[colSupportPaint] = 3; + m_columns_width[colFuzzySkin] = 3; m_columns_width[colSinking] = 3; m_columns_width[colColorPaint] = 3; m_columns_width[colEditing] = 3; @@ -418,6 +421,7 @@ void ObjectList::create_objects_ctrl() // QDS AppendBitmapColumn(" ", colSupportPaint, wxOSX ? wxDATAVIEW_CELL_EDITABLE : wxDATAVIEW_CELL_INERT, m_columns_width[colSupportPaint] * em, wxALIGN_CENTER_HORIZONTAL, 0); + AppendBitmapColumn(" ", colFuzzySkin, wxOSX ? wxDATAVIEW_CELL_EDITABLE : wxDATAVIEW_CELL_INERT, m_columns_width[colFuzzySkin] * em, wxALIGN_CENTER_HORIZONTAL, 0); AppendBitmapColumn(" ", colColorPaint, wxOSX ? wxDATAVIEW_CELL_EDITABLE : wxDATAVIEW_CELL_INERT, m_columns_width[colColorPaint] * em, wxALIGN_CENTER_HORIZONTAL, 0); AppendBitmapColumn(" ", colSinking, wxOSX ? wxDATAVIEW_CELL_EDITABLE : wxDATAVIEW_CELL_INERT, m_columns_width[colSinking] * em, @@ -600,6 +604,10 @@ void ObjectList::set_tooltip_for_item(const wxPoint& pt) if (node->HasSupportPainting()) tooltip = _(L("Click the icon to edit support painting of the object")); + } else if (col->GetModelColumn() == (unsigned int) colFuzzySkin) { + if (node->HasFuzzySkinPainting()) + tooltip = _(L("Click the icon to edit fuzzy skin painting of the object")); + } else if (col->GetModelColumn() == (unsigned int)colColorPaint) { if (node->HasColorPainting()) @@ -1016,6 +1024,12 @@ void ObjectList::set_support_paint_hidden(const bool hide) const update_name_column_width(); } +void GUI::ObjectList::set_fuzzy_skin_paint_hidden(const bool hide) const +{ + GetColumn(colFuzzySkin)->SetHidden(hide); + update_name_column_width(); +} + void GUI::ObjectList::set_sinking_hidden(const bool hide) const { GetColumn(colSinking)->SetHidden(hide); @@ -1447,6 +1461,17 @@ void ObjectList::list_manipulation(const wxPoint& mouse_pos, bool evt_context_me else gizmos_mgr.reset_all_states(); } + } else if (col_num == colFuzzySkin) { + if (wxGetApp().plater()->get_current_canvas3D()->get_canvas_type() != GLCanvas3D::CanvasAssembleView) { + ObjectDataViewModelNode *node = (ObjectDataViewModelNode *) item.GetID(); + if (node && node->HasFuzzySkinPainting()) { + GLGizmosManager &gizmos_mgr = wxGetApp().plater()->get_view3D_canvas3D()->get_gizmos_manager(); + if (gizmos_mgr.get_current_type() != GLGizmosManager::EType::FuzzySkin) + gizmos_mgr.open_gizmo(GLGizmosManager::EType::FuzzySkin); + else + gizmos_mgr.reset_all_states(); + } + } } else if (col_num == colColorPaint) { if (wxGetApp().plater()->get_current_canvas3D()->get_canvas_type() != GLCanvas3D::CanvasAssembleView) { @@ -1543,9 +1568,13 @@ void ObjectList::show_context_menu(const bool evt_context_menu) get_selected_item_indexes(obj_idx, vol_idx, item); if (obj_idx < 0 || vol_idx < 0) return; const ModelVolume *volume = object(obj_idx)->volumes[vol_idx]; - - menu = volume->is_svg() ? plater->svg_part_menu() : // ORCA fixes missing "Edit SVG" item for Add/Negative/Modifier SVG objects in object list - plater->part_menu(); + if (volume->is_text()) { + menu = plater->text_part_menu(); + } else if (volume->is_svg()) { + menu = plater->svg_part_menu(); + } else { + menu = plater->part_menu(); + } } else { menu = type & itPlate ? plater->plate_menu() : @@ -2364,8 +2393,8 @@ void ObjectList::load_generic_subobject(const std::string& type_name, const Mode // First (any) GLVolume of the selected instance. They all share the same instance matrix. const GLVolume* v = selection.get_volume(*selection.get_volume_idxs().begin()); // Transform the new modifier to be aligned with the print bed. - const BoundingBoxf3 mesh_bb = new_volume->mesh().bounding_box(); - new_volume->set_transformation(Geometry::Transformation::volume_to_bed_transformation(v->get_instance_transformation(), mesh_bb)); + new_volume->set_transformation(v->get_instance_transformation().get_matrix_no_offset().inverse()); + const BoundingBoxf3 mesh_bb = new_volume->mesh().bounding_box(); // Set the modifier position. auto offset = (type_name == "Slab") ? // Slab: Lift to print bed @@ -2486,10 +2515,10 @@ void GUI::ObjectList::add_new_model_object_from_old_object() { new_object->config.set_key_value("extruder", new ConfigOptionInt(min_extruder)); new_object->invalidate_bounding_box(); new_object->instances[0]->set_transformation(mo->instances[0]->get_transformation()); - // BBS: backup + // QDS: backup Slic3r::save_object_mesh(*new_object); new_object->ensure_on_bed(); - // BBS init assmeble transformation + // QDS init assmeble transformation new_object->get_model()->set_assembly_pos(new_object); object_idxs.push_back(model.objects.size() - 1); paste_objects_into_list(object_idxs); @@ -2739,7 +2768,12 @@ void ObjectList::del_info_item(const int obj_idx, InfoItemType type) for (ModelVolume* mv : (*m_objects)[obj_idx]->volumes) mv->supported_facets.reset(); break; - + case InfoItemType::FuzzySkin: + cnv->get_gizmos_manager().reset_all_states(); + Plater::TakeSnapshot(plater, "Remove fuzzy skin painting"); + for (ModelVolume *mv : (*m_objects)[obj_idx]->volumes) + mv->fuzzy_skin_facets.reset(); + break; // QDS: remove CustomSeam case InfoItemType::MmuSegmentation: cnv->get_gizmos_manager().reset_all_states(); @@ -3154,7 +3188,9 @@ void ObjectList::merge(bool to_multipart_object) auto option = new_volume->config.option("extruder"); if (!option) { auto opt = object->config.option("extruder"); - if (opt) { new_volume->config.set_key_value("extruder", new ConfigOptionInt(opt->getInt())); } + if (opt) { + new_volume->config.set_key_value("extruder", new ConfigOptionInt(opt->getInt())); + } } new_volume->mmu_segmentation_facets.assign(std::move(volume->mmu_segmentation_facets)); } @@ -3402,6 +3438,9 @@ bool ObjectList::get_volume_by_item(const wxDataViewItem& item, ModelVolume*& vo auto obj_idx = get_selected_obj_idx(); if (!item || obj_idx < 0) return false; + if (m_objects->size() <= obj_idx) { + return false; + } const auto volume_id = m_objects_model->GetVolumeIdByItem(item); const bool split_part = m_objects_model->GetItemType(item) == itVolume; @@ -3432,6 +3471,9 @@ bool ObjectList::is_splittable(bool to_objects) auto obj_idx = get_selected_obj_idx(); if (obj_idx < 0) return false; + if (m_objects->size() <= obj_idx) { + return false; + } if ((*m_objects)[obj_idx]->volumes.size() > 1) return true; return (*m_objects)[obj_idx]->volumes[0]->is_splittable(); @@ -3707,24 +3749,25 @@ void ObjectList::part_selection_changed() if (type == itInfo) { InfoItemType info_type = m_objects_model->GetInfoItemType(item); - switch (info_type) - { - case InfoItemType::CustomSupports: - // QDS: remove CustomSeam - //case InfoItemType::CustomSeam: - case InfoItemType::MmuSegmentation: - { - GLGizmosManager::EType gizmo_type = info_type == InfoItemType::CustomSupports ? GLGizmosManager::EType::FdmSupports : - /*info_type == InfoItemType::CustomSeam ? GLGizmosManager::EType::Seam :*/ - GLGizmosManager::EType::MmuSegmentation; - GLGizmosManager& gizmos_mgr = wxGetApp().plater()->get_view3D_canvas3D()->get_gizmos_manager(); - if (gizmos_mgr.get_current_type() != gizmo_type) - gizmos_mgr.open_gizmo(gizmo_type); - break; + GLGizmosManager::EType gizmo_type = GLGizmosManager::EType::Undefined; + switch (info_type){ + case InfoItemType::CustomSupports: { + gizmo_type = GLGizmosManager::EType::FdmSupports; + break; + } + case InfoItemType::FuzzySkin: { + gizmo_type = GLGizmosManager::EType::FuzzySkin; + break; + } + case InfoItemType::MmuSegmentation:{ + gizmo_type = GLGizmosManager::EType::MmuSegmentation; + break; + } + default: { break; } } - // QDS: remove Sinking - //case InfoItemType::Sinking: { break; } - default: { break; } + GLGizmosManager &gizmos_mgr = wxGetApp().plater()->get_view3D_canvas3D()->get_gizmos_manager(); + if (gizmos_mgr.get_current_type() != gizmo_type) { + gizmos_mgr.open_gizmo(gizmo_type); } } else { // QDS: select object to edit config @@ -3990,7 +4033,19 @@ void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray *selectio m_objects_model->SetSupportPaintState(true, item_obj,true); } } - + { + bool shows = m_objects_model->IsFuzzySkinPainted(item_obj); + bool should_show = printer_technology() == ptFFF && + std::any_of(model_object->volumes.begin(), model_object->volumes.end(), [](const ModelVolume *mv) { return !mv->fuzzy_skin_facets.empty(); }); + if (shows && !should_show) { + m_objects_model->SetFuzzySkinPaintState(false, item_obj); + } else if (!shows && should_show) { + m_objects_model->SetFuzzySkinPaintState(true, item_obj); + } + if (color_mode_changed && shows) { + m_objects_model->SetFuzzySkinPaintState(true, item_obj, true); + } + } { bool shows = m_objects_model->IsColorPainted(item_obj); bool should_show = printer_technology() == ptFFF @@ -4045,7 +4100,26 @@ void ObjectList::update_info_items(size_t obj_idx, wxDataViewItemArray *selectio this->set_support_paint_hidden(false); } } + { + bool shows = this->GetColumn(colFuzzySkin)->IsShown(); + bool should_show = false; + for (ModelObject *mo : *m_objects) { + for (ModelVolume *mv : mo->volumes) { + if (!mv->fuzzy_skin_facets.empty()) { + should_show = true; + break; + } + } + if (should_show) + break; + } + if (shows && !should_show) { + this->set_fuzzy_skin_paint_hidden(true); + } else if (!shows && should_show) { + this->set_fuzzy_skin_paint_hidden(false); + } + } { bool shows = this->GetColumn(colColorPaint)->IsShown(); bool should_show = false; @@ -5941,6 +6015,9 @@ void ObjectList::fix_through_netfabb() void ObjectList::simplify() { auto plater = wxGetApp().plater(); + if (!plater) { + return; + } GLGizmosManager& gizmos_mgr = plater->get_view3D_canvas3D()->get_gizmos_manager(); // Do not simplify when a gizmo is open. There might be issues with updates @@ -5984,6 +6061,7 @@ void ObjectList::msw_rescale() GetColumn(colFilament)->SetWidth( 5 * em); // QDS GetColumn(colSupportPaint)->SetWidth(3 * em); + GetColumn(colFuzzySkin)->SetWidth(3 * em); GetColumn(colColorPaint)->SetWidth(3 * em); GetColumn(colSinking)->SetWidth(3 * em); GetColumn(colEditing )->SetWidth( 3 * em); @@ -6041,12 +6119,17 @@ void ObjectList::OnEditingStarted(wxDataViewEvent &event) event.Veto(); // Not edit with NSTableView's text auto col = event.GetColumn(); auto item = event.GetItem(); - if (col == colPrint) { + const auto p_plater = wxGetApp().plater(); + if (col == colHeight) { + enable_layers_editing(); + return; + } + else if (col == colPrint) { toggle_printable_state(); return; } else if (col == colSupportPaint) { ObjectDataViewModelNode* node = (ObjectDataViewModelNode*)item.GetID(); - if (node->HasSupportPainting()) { + if (node && node->HasSupportPainting()) { GLGizmosManager& gizmos_mgr = wxGetApp().plater()->get_view3D_canvas3D()->get_gizmos_manager(); if (gizmos_mgr.get_current_type() != GLGizmosManager::EType::FdmSupports) gizmos_mgr.open_gizmo(GLGizmosManager::EType::FdmSupports); @@ -6054,10 +6137,20 @@ void ObjectList::OnEditingStarted(wxDataViewEvent &event) gizmos_mgr.reset_all_states(); } return; + } else if (col == colFuzzySkin) { + ObjectDataViewModelNode *node = (ObjectDataViewModelNode *) item.GetID(); + if (node && node->HasFuzzySkinPainting()) { + GLGizmosManager &gizmos_mgr = wxGetApp().plater()->get_view3D_canvas3D()->get_gizmos_manager(); + if (gizmos_mgr.get_current_type() != GLGizmosManager::EType::FuzzySkin) + gizmos_mgr.open_gizmo(GLGizmosManager::EType::FuzzySkin); + else + gizmos_mgr.reset_all_states(); + } + return; } else if (col == colColorPaint) { ObjectDataViewModelNode* node = (ObjectDataViewModelNode*)item.GetID(); - if (node->HasColorPainting()) { + if (node && node->HasColorPainting()) { GLGizmosManager& gizmos_mgr = wxGetApp().plater()->get_view3D_canvas3D()->get_gizmos_manager(); if (gizmos_mgr.get_current_type() != GLGizmosManager::EType::MmuSegmentation) gizmos_mgr.open_gizmo(GLGizmosManager::EType::MmuSegmentation); @@ -6439,11 +6532,6 @@ ModelObject* ObjectList::object(const int obj_idx) const return (*m_objects)[obj_idx]; } -bool ObjectList::has_paint_on_segmentation() -{ - return m_objects_model->HasInfoItem(InfoItemType::MmuSegmentation); -} - void ObjectList::apply_object_instance_transfrom_to_all_volumes(ModelObject *model_object, bool need_update_assemble_matrix) { const Geometry::Transformation &instance_transformation = model_object->instances[0]->get_transformation(); diff --git a/src/slic3r/GUI/GUI_ObjectList.hpp b/src/slic3r/GUI/GUI_ObjectList.hpp index 4ce78d6..c89c8f1 100644 --- a/src/slic3r/GUI/GUI_ObjectList.hpp +++ b/src/slic3r/GUI/GUI_ObjectList.hpp @@ -231,6 +231,7 @@ public: // QDS void set_color_paint_hidden(const bool hide) const; void set_support_paint_hidden(const bool hide) const; + void set_fuzzy_skin_paint_hidden(const bool hide) const; void set_sinking_hidden(const bool hide) const; // update extruder in current config @@ -455,8 +456,6 @@ public: void set_extruder_for_selected_items(const int extruder); wxDataViewItemArray reorder_volumes_and_get_selection(int obj_idx, std::function add_to_selection = nullptr); void apply_volumes_order(); - bool has_paint_on_segmentation(); - // QDS void on_plate_added(PartPlate* part_plate); void on_plate_deleted(int plate_index); diff --git a/src/slic3r/GUI/GUI_Preview.cpp b/src/slic3r/GUI/GUI_Preview.cpp index bedc0d9..4e6876d 100644 --- a/src/slic3r/GUI/GUI_Preview.cpp +++ b/src/slic3r/GUI/GUI_Preview.cpp @@ -109,8 +109,6 @@ bool View3D::init(wxWindow* parent, Bed3D& bed, Model* model, DynamicPrintConfig m_canvas->enable_return_toolbar(true); //QDS: GUI refactor: GLToolbar m_canvas->enable_select_plate_toolbar(false); - m_canvas->enable_assemble_view_toolbar(true); - m_canvas->enable_separator_toolbar(true); m_canvas->enable_labels(true); m_canvas->enable_slope(true); @@ -303,7 +301,6 @@ bool Preview::init(wxWindow* parent, Bed3D& bed, Model* model) if (wxGetApp().is_editor()) { m_canvas->enable_select_plate_toolbar(true); } - m_canvas->enable_assemble_view_toolbar(false); // sizer, m_canvas_widget m_canvas_widget->Bind(wxEVT_KEY_DOWN, &Preview::update_layers_slider_from_canvas, this); @@ -855,13 +852,11 @@ bool AssembleView::init(wxWindow* parent, Bed3D& bed, Model* model, DynamicPrint m_canvas->set_config(config); m_canvas->enable_gizmos(true); m_canvas->enable_selection(true); - m_canvas->enable_main_toolbar(false); + m_canvas->enable_main_toolbar(true); m_canvas->enable_labels(false); m_canvas->enable_slope(false); //QDS: GUI refactor: GLToolbar - m_canvas->enable_assemble_view_toolbar(false); m_canvas->enable_return_toolbar(true); - m_canvas->enable_separator_toolbar(false); //m_canvas->set_show_world_axes(true);//wait for GitHub users to see if they have this requirement // QDS: set volume_selection_mode to Volume //same to 3d //m_canvas->get_selection().set_volume_selection_mode(Selection::Instance); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.cpp b/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.cpp index 5580722..3a2e935 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.cpp @@ -100,8 +100,8 @@ const double GLGizmoAdvancedCut::Margin = 20.0; const std::array GLGizmoAdvancedCut::GrabberColor = { 1.0, 1.0, 0.0, 1.0 }; const std::array GLGizmoAdvancedCut::GrabberHoverColor = { 0.7, 0.7, 0.0, 1.0}; -GLGizmoAdvancedCut::GLGizmoAdvancedCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) - : GLGizmoRotate3D(parent, icon_filename, sprite_id, nullptr) +GLGizmoAdvancedCut::GLGizmoAdvancedCut(GLCanvas3D& parent, unsigned int sprite_id) + : GLGizmoRotate3D(parent, sprite_id, nullptr) , m_movement(0.0) , m_buffered_movement(0.0) , m_keep_upper(true) @@ -171,10 +171,11 @@ bool GLGizmoAdvancedCut::gizmo_event(SLAGizmoEventType action, const Vec2d &mous else if (action == SLAGizmoEventType::RightDown) { if (!m_connectors_editing && m_cut_mode == CutMode::cutPlanar) { //&& control_down // Check the internal part raycasters. - if (m_part_selection && m_part_selection->valid()) { - m_part_selection->toggle_selection(mouse_position); - check_and_update_connectors_state(); // after a contour is deactivated, its connectors are inside the object - } + if (m_part_selection && !m_part_selection->valid()) + process_contours(); + m_part_selection->toggle_selection(mouse_position); + check_and_update_connectors_state(); // after a contour is deactivated, its connectors are inside the object + return true; } if (m_hover_id < c_connectors_group_id) @@ -438,6 +439,11 @@ BoundingBoxf3 GLGizmoAdvancedCut::get_bounding_box() const return t_aabb; } +std::string GLGizmoAdvancedCut::get_icon_filename(bool b_dark_mode) const +{ + return b_dark_mode ? "toolbar_cut_dark.svg" : "toolbar_cut.svg"; +} + bool GLGizmoAdvancedCut::on_init() { if (!GLGizmoRotate3D::on_init()) @@ -475,6 +481,14 @@ std::string GLGizmoAdvancedCut::on_get_name() const } } +void GLGizmoAdvancedCut::apply_color_clip_plane_colors() +{ + if (CutMode(m_cut_mode) == CutMode::cutTongueAndGroove) + m_parent.set_color_clip_plane_colors({CUT_PLANE_DEF_COLOR, CUT_PLANE_DEF_COLOR}); + else + m_parent.set_color_clip_plane_colors({UPPER_PART_COLOR, LOWER_PART_COLOR}); +} + void GLGizmoAdvancedCut::on_load(cereal::BinaryInputArchive &ar) { size_t mode; @@ -530,7 +544,6 @@ void GLGizmoAdvancedCut::data_changed(bool is_serializing) update_bb(); if (auto oc = m_c->object_clipper()) { oc->set_behaviour(m_connectors_editing, m_connectors_editing, double(m_contour_width)); - reset_cut_by_contours(); } } } @@ -541,6 +554,7 @@ void GLGizmoAdvancedCut::on_set_state() // Reset m_cut_z on gizmo activation if (get_state() == On) { + m_parent.set_use_color_clip_plane(true); const Selection &selection = m_parent.get_selection(); if (selection.is_empty()) {//check selection again close(); @@ -568,6 +582,7 @@ void GLGizmoAdvancedCut::on_set_state() oc->release(); } m_selected.clear(); + m_parent.set_use_color_clip_plane(false); m_c->selection_info()->set_use_shift(false); // Make sure that the part selection data are released when the gizmo is closed. @@ -611,7 +626,6 @@ CommonGizmosDataID GLGizmoAdvancedCut::on_get_requirements() const { return CommonGizmosDataID(int(CommonGizmosDataID::SelectionInfo) | int(CommonGizmosDataID::InstancesHider) - | int(CommonGizmosDataID::Raycaster) | int(CommonGizmosDataID::ObjectClipper)); } @@ -757,7 +771,9 @@ void GLGizmoAdvancedCut::on_render() if (m_part_selection) { if (!m_connectors_editing) { - if (m_is_dragging == false) { m_part_selection->part_render(nullptr,nullptr); } + if (m_is_dragging == false) { + m_part_selection->part_render(nullptr,nullptr); + } } else { m_part_selection->part_render(&m_plane_center, &m_plane_normal); } @@ -776,8 +792,8 @@ void GLGizmoAdvancedCut::on_render_for_picking() const Camera& camera = wxGetApp().plater()->get_picking_camera(); const auto& view_matrix = camera.get_view_matrix(); const auto& projection_matrix = camera.get_projection_matrix(); + glsafe(::glDisable(GL_DEPTH_TEST)); if (!m_connectors_editing) { - glsafe(::glDisable(GL_DEPTH_TEST)); std::array color; // pick plane { @@ -810,7 +826,6 @@ void GLGizmoAdvancedCut::on_render_for_picking() } } else { - glsafe(::glEnable(GL_DEPTH_TEST)); auto inst_id = m_c->selection_info()->get_active_instance(); if (inst_id < 0) return; @@ -955,7 +970,7 @@ static void check_objects_after_cut(const ModelObjectPtrs &objects) } } -void synchronize_model_after_cut(Model &model, const CutObjectBase &cut_id) + void synchronize_model_after_cut(Model &model, const CutObjectBase &cut_id) { for (ModelObject *obj : model.objects) if (obj->is_cut() && obj->cut_id.has_same_id(cut_id) && !obj->cut_id.is_equal(cut_id)) obj->cut_id.copy(cut_id); @@ -1202,6 +1217,7 @@ void GLGizmoAdvancedCut::update_clipper() double offset = normal.dot(m_plane_center); double dist = normal.dot(beg); + m_parent.set_color_clip_plane(normal, offset); if (!is_looking_forward()) { // recalculate normal and offset for clipping plane, if camera is looking downward to cut plane normal = m_rotate_matrix * (-1. * Vec3d::UnitZ()); @@ -1325,7 +1341,7 @@ void GLGizmoAdvancedCut::render_cut_plane_and_grabbers() m_grabber_model.render_geometry(); } - m_move_x_grabber.center = m_plane_center + m_plane_x_direction * Offset; + m_move_x_grabber.center = m_plane_center + m_plane_x_direction * radius; if (is_render_x_grabber) { Transform3d model_matrix{ Transform3d::Identity() }; @@ -1341,23 +1357,28 @@ void GLGizmoAdvancedCut::render_cut_plane_and_grabbers() } } - bool hover = (m_hover_id == get_group_id()); - std::array render_color; - if (hover) { - render_color = GrabberHoverColor; - } else - render_color = GrabberColor; - // QDS set to fixed size grabber // float fullsize = 2 * (dragging ? get_dragging_half_size(size) : get_half_size(size)); GLModel &cube_z = m_move_z_grabber.get_cube(); + const auto get_render_color = [this](unsigned int id)->std::array { + bool hover = (m_hover_id == id); + std::array render_color; + if (hover) { + render_color = GrabberHoverColor; + } + else + render_color = GrabberColor; + return render_color; + }; + if (is_render_z_grabber) { Vec3d t_z_dir = m_move_z_grabber.center - m_plane_center; const auto scale_z = t_z_dir.stableNorm(); const Vec3d target_z = m_plane_center + scale_z * t_z_dir.normalized(); Transform3d cube_mat_z = Geometry::translation_transform(target_z) * m_rotate_matrix * Geometry::scale_transform(fullsize); // m_move_z_grabber.m_matrix = cube_mat_z; + std::array render_color = get_render_color(c_cube_z_move_id); render_glmodel(cube_z, render_color, view_matrix * m_move_z_grabber.m_matrix, projection_matrix); } @@ -1368,6 +1389,8 @@ void GLGizmoAdvancedCut::render_cut_plane_and_grabbers() const Vec3d target_x = m_plane_center + scale_x * t_x_dir.normalized(); Transform3d cube_mat_x = Geometry::translation_transform(target_x) * m_rotate_matrix * Geometry::scale_transform(fullsize); // m_move_x_grabber.m_matrix = cube_mat_x; + + std::array render_color = get_render_color(c_cube_x_move_id); render_glmodel(cube_x, render_color, view_matrix * m_move_x_grabber.m_matrix, projection_matrix); } // Should be placed at last, because GLGizmoRotate3D clears depth buffer @@ -1530,6 +1553,7 @@ void GLGizmoAdvancedCut::render_cut_line() { glsafe(::glEnable(GL_LINE_STIPPLE)); } + glsafe(::glEnable(GL_DEPTH_TEST)); glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); @@ -1778,6 +1802,7 @@ void GLGizmoAdvancedCut::switch_to_mode(CutMode new_mode) { if (m_cut_mode == CutMode::cutTongueAndGroove) { m_cut_to_parts = false;//into Groove function,cancel m_cut_to_parts } + apply_color_clip_plane_colors(); if (auto oc = m_c->object_clipper()) { m_contour_width = m_cut_mode == CutMode::cutTongueAndGroove ? 0.f : 0.4f; oc->set_behaviour(m_connectors_editing, m_connectors_editing, double(m_contour_width)); // for debug @@ -1842,6 +1867,13 @@ bool GLGizmoAdvancedCut::has_valid_groove() const return false; const Transform3d cp_matrix = Geometry::translation_transform(m_plane_center) * m_rotate_matrix; + if (!m_c->raycaster()) { + if (!m_c->selection_info()) { + m_c->update(get_requirements()); + } + wxBusyCursor wait; + m_c->raycaster_ptr()->update(); + } for (size_t id = 0; id < m_groove_vertices.size(); id += 2) { const Vec3d beg = cp_matrix * m_groove_vertices[id]; @@ -1880,7 +1912,7 @@ void GLGizmoAdvancedCut::reset_cut_by_contours() return; process_contours(); } else { - process_contours(); + toggle_model_objects_visibility(); } } @@ -1929,6 +1961,7 @@ void GLGizmoAdvancedCut::render_flip_plane_button(bool disable_pred /*=false*/) ImGui::PopStyleColor(); } + void GLGizmoAdvancedCut::toggle_model_objects_visibility(bool show_in_3d) { if (m_part_selection && m_part_selection->valid() && show_in_3d == false && (m_is_dragging == false || m_connectors_editing)) // QDT @@ -1941,6 +1974,9 @@ void GLGizmoAdvancedCut::toggle_model_objects_visibility(bool show_in_3d) if (idx < 0) { return; } + if (idx >= model_objects.size()) { + return; + } m_parent.toggle_model_objects_visibility(true, model_objects[idx], selection.get_instance_idx()); } } @@ -1985,7 +2021,7 @@ void GLGizmoAdvancedCut::update_bb() else set_center_pos(m_bb_center); reset_cut_by_contours(); - + apply_color_clip_plane_colors(); m_contour_width = m_cut_mode == CutMode::cutTongueAndGroove ? 0.f : 0.4f; m_radius = box.radius(); @@ -2160,8 +2196,17 @@ void GLGizmoAdvancedCut::render_cut_plane_input_window(float x, float y, float b unit_size = vec_max + ImGui::GetStyle().FramePadding.x * 2.0f; } - CutConnectors &connectors = m_c->selection_info()->model_object()->cut_connectors; - const bool has_connectors = !connectors.empty(); + bool has_connectors = false; + if (m_c) { + const auto& p_selection_info = m_c->selection_info(); + if (p_selection_info) { + const auto& p_model_object = p_selection_info->model_object(); + if (p_model_object) { + const auto& t_commectors = p_model_object->cut_connectors; + has_connectors = !t_commectors.empty(); + } + } + } m_imgui->disabled_begin(has_connectors); if (render_cut_mode_combo(caption_size + 1 * space_size, 4 * unit_size + 0.5 * space_size)) { @@ -2352,7 +2397,7 @@ void GLGizmoAdvancedCut::render_cut_plane_input_window(float x, float y, float b float get_cur_y = ImGui::GetContentRegionMax().y + ImGui::GetFrameHeight() + y; show_tooltip_information(x, get_cur_y); - float f_scale = m_parent.get_gizmos_manager().get_layout_scale(); + float f_scale = m_parent.get_main_toolbar_scale(); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale)); ImGui::SameLine(); @@ -2370,7 +2415,19 @@ void GLGizmoAdvancedCut::render_cut_plane_input_window(float x, float y, float b void GLGizmoAdvancedCut::init_connectors_input_window_data() { - CutConnectors &connectors = m_c->selection_info()->model_object()->cut_connectors; + if (!m_c) { + return; + } + const auto p_selection_info = m_c->selection_info(); + if (!p_selection_info) { + return; + } + const auto& p_model_object = p_selection_info->model_object(); + if (!p_model_object) { + return; + } + + CutConnectors &connectors = p_model_object->cut_connectors; float connectors_cap = m_imgui->calc_text_size(_L("Connectors")).x; float type_cap = m_imgui->calc_text_size(_L("Type")).x; @@ -2438,7 +2495,18 @@ void GLGizmoAdvancedCut::init_connectors_input_window_data() void GLGizmoAdvancedCut::render_connectors_input_window(float x, float y, float bottom_limit) { - CutConnectors &connectors = m_c->selection_info()->model_object()->cut_connectors; + if (!m_c) { + return; + } + const auto p_selection_info = m_c->selection_info(); + if (!p_selection_info) { + return; + } + const auto& p_model_object = p_selection_info->model_object(); + if (!p_model_object) { + return; + } + CutConnectors &connectors = p_model_object->cut_connectors; // update when change input window m_imgui->set_requires_extra_frame(); @@ -2525,7 +2593,7 @@ void GLGizmoAdvancedCut::render_connectors_input_window(float x, float y, float float get_cur_y = ImGui::GetContentRegionMax().y + ImGui::GetFrameHeight() + y; show_tooltip_information(x, get_cur_y); - float f_scale = m_parent.get_gizmos_manager().get_layout_scale(); + float f_scale = m_parent.get_main_toolbar_scale(); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale)); ImGui::SameLine(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.hpp b/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.hpp index 7e04c76..d70ece0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoAdvancedCut.hpp @@ -208,9 +208,9 @@ private: //GLSelectionRectangle m_selection_rectangle; public: - GLGizmoAdvancedCut(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + GLGizmoAdvancedCut(GLCanvas3D& parent, unsigned int sprite_id); - bool gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_position, bool shift_down, bool alt_down, bool control_down); + bool gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_position, bool shift_down, bool alt_down, bool control_down) override; bool on_key(const wxKeyEvent &evt) override; double get_movement() const { return m_movement; } @@ -228,6 +228,9 @@ public: BoundingBoxf3 get_bounding_box() const override; + std::string get_icon_filename(bool b_dark_mode) const override; + bool wants_enter_leave_snapshots() const override { return true; } + protected: virtual bool on_init(); virtual void on_load(cereal::BinaryInputArchive &ar) override; @@ -235,6 +238,7 @@ protected: virtual void data_changed(bool is_serializing) override; virtual std::string on_get_name() const; virtual std::string on_get_name_str() override { return "Cut"; } + void apply_color_clip_plane_colors(); virtual void on_set_state(); void close(); virtual bool on_is_activable() const; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoAssembly.cpp b/src/slic3r/GUI/Gizmos/GLGizmoAssembly.cpp index a955a12..e371628 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoAssembly.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoAssembly.cpp @@ -21,12 +21,17 @@ namespace Slic3r { namespace GUI { -GLGizmoAssembly::GLGizmoAssembly(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) : - GLGizmoMeasure(parent, icon_filename, sprite_id) +GLGizmoAssembly::GLGizmoAssembly(GLCanvas3D& parent, unsigned int sprite_id) : + GLGizmoMeasure(parent, sprite_id) { m_measure_mode = EMeasureMode::ONLY_ASSEMBLY; } +std::string GLGizmoAssembly::get_icon_filename(bool is_dark_mode) const +{ + return is_dark_mode ? "toolbar_assembly_dark.svg" : "toolbar_assembly.svg"; +} + bool GLGizmoAssembly::on_init() { GLGizmoMeasure::on_init(); m_shortcut_key = WXK_CONTROL_Y; @@ -50,7 +55,6 @@ std::string GLGizmoAssembly::on_get_name() const bool GLGizmoAssembly::on_is_activable() const { const Selection& selection = m_parent.get_selection(); - //1.9.5 if (selection.is_wipe_tower()) { return false; } @@ -118,7 +122,7 @@ void GLGizmoAssembly::on_render_input_window(float x, float y, float bottom_limi } show_tooltip_information(caption_max, x, get_cur_y); - float f_scale =m_parent.get_gizmos_manager().get_layout_scale(); + float f_scale = m_parent.get_main_toolbar_scale(); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale)); ImGui::PopStyleVar(2); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoAssembly.hpp b/src/slic3r/GUI/Gizmos/GLGizmoAssembly.hpp index 4804af9..9c3d75d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoAssembly.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoAssembly.hpp @@ -10,7 +10,7 @@ class GLGizmoAssembly : public GLGizmoMeasure { public: - GLGizmoAssembly(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + GLGizmoAssembly(GLCanvas3D& parent, unsigned int sprite_id); /// /// Apply rotation on select plane /// @@ -23,9 +23,12 @@ public: bool wants_enter_leave_snapshots() const override { return true; } std::string get_gizmo_entering_text() const override { return _u8L("Entering Assembly gizmo"); } std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Assembly gizmo"); } + + std::string get_icon_filename(bool is_dark_mode) const override; protected: bool on_init() override; std::string on_get_name() const override; + virtual std::string on_get_name_str() override { return "Assembly"; } bool on_is_activable() const override; //void on_render() override; //void on_set_state() override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp index 6739734..516cf5d 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.cpp @@ -138,7 +138,6 @@ void GLGizmoBase::Grabber::render(const std::array& render_color, bool //float fullsize = 2 * (dragging ? get_dragging_half_size(size) : get_half_size(size)); //float fullsize = get_grabber_size(); - const_cast(&cube)->set_color(-1, render_color); const Camera& camera = picking ? wxGetApp().plater()->get_picking_camera() : wxGetApp().plater()->get_camera(); @@ -227,7 +226,7 @@ bool GLGizmoBase::render_combo(const std::string &label, const std::vectorset_uniform("view_model_matrix", view_model_matrix * model_matrix); m_cross_mark.set_color({ 0.0f, 0.0f, 1.0f, 1.0f }); m_cross_mark.render_geometry(); + glsafe(::glEnable(GL_DEPTH_TEST)); + wxGetApp().unbind_shader(); +} +void GLGizmoBase::render_lines(const std::vector &points) +{ + if (!m_lines_mark.is_initialized()) { + GLModel::Geometry geo; + geo.format.type = GLModel::PrimitiveType::Lines; + geo.format.vertex_layout = GLModel::Geometry::EVertexLayout::P3; + + for (int i = 1; i < points.size(); i++) { + Vec3f p0 = points[i - 1].cast(); + Vec3f p1 = points[i].cast(); + geo.add_vertex(p0); + geo.add_vertex(p1); + geo.add_line(i - 1, i); + } + m_lines_mark.init_from(std::move(geo)); + } + const auto &p_flat_shader = wxGetApp().get_shader("flat"); + if (!p_flat_shader) return; + + wxGetApp().bind_shader(p_flat_shader); + + const Camera &camera = wxGetApp().plater()->get_camera(); + const auto & view_matrix = camera.get_view_matrix(); + const auto & proj_matrix = camera.get_projection_matrix(); + + const auto view_model_matrix = view_matrix; + glsafe(::glDisable(GL_DEPTH_TEST)); + + const auto &ogl_manager = wxGetApp().get_opengl_manager(); + if (ogl_manager) { ogl_manager->set_line_width(2.0f); } + + Transform3d model_matrix{Transform3d::Identity()}; + + p_flat_shader->set_uniform("view_model_matrix", view_model_matrix); + p_flat_shader->set_uniform("projection_matrix", proj_matrix); + m_lines_mark.set_color({1.0f, 1.0f, 0.0f, 1.0f}); + m_lines_mark.render_geometry(); + glsafe(::glEnable(GL_DEPTH_TEST)); wxGetApp().unbind_shader(); } @@ -288,12 +331,11 @@ float GLGizmoBase::get_grabber_size() return grabber_size; } -GLGizmoBase::GLGizmoBase(GLCanvas3D &parent, const std::string &icon_filename, unsigned int sprite_id) +GLGizmoBase::GLGizmoBase(GLCanvas3D &parent, unsigned int sprite_id) : m_parent(parent) , m_group_id(-1) , m_state(Off) , m_shortcut_key(0) - , m_icon_filename(icon_filename) , m_sprite_id(sprite_id) , m_hover_id(-1) , m_dragging(false) @@ -347,10 +389,6 @@ void GLGizmoBase::set_state(EState state) on_set_state(); } -void GLGizmoBase::set_icon_filename(const std::string &filename) { - m_icon_filename = filename; -} - bool GLGizmoBase::on_key(const wxKeyEvent& key_event) { return false; @@ -686,7 +724,12 @@ Transform3d GLGizmoBase::get_corss_mask_model_matrix(ECrossMaskType type, const void GLGizmoBase::render_input_window(float x, float y, float bottom_limit) { - on_render_input_window(x, y, bottom_limit); + auto canvas_w = float(m_parent.get_canvas_size().get_width()); + auto canvas_h = float(m_parent.get_canvas_size().get_height()); + float zoom = (float)m_parent.get_active_camera().get_zoom(); + const float final_x = 0.5 * canvas_w + x * zoom; + + on_render_input_window(final_x, y, bottom_limit); if (m_first_input_window_render) { // for some reason, the imgui dialogs are not shown on screen in the 1st frame where they are rendered, but show up only with the 2nd rendered frame // so, we forces another frame rendering the first time the imgui window is shown @@ -702,6 +745,11 @@ BoundingBoxf3 GLGizmoBase::get_bounding_box() const return t_aabb; } +bool GLGizmoBase::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) +{ + return false; +} + void GLGizmoBase::render_glmodel(GLModel &model, const std::array &color, Transform3d view_model_matrix, const Transform3d& projection_matrix, bool for_picking, float emission_factor) { const auto& shader = wxGetApp().get_shader(for_picking ? "flat" : "gouraud_light"); @@ -735,7 +783,10 @@ std::string GLGizmoBase::get_name(bool include_shortcut) const return out; } - +void GLGizmoBase::set_serializing(bool is_serializing) +{ + m_is_serializing = is_serializing; +} // Produce an alpha channel checksum for the red green blue components. The alpha channel may then be used to verify, whether the rgb components // were not interpolated by alpha blending or multi sampling. diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp index fec5131..bd797a0 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBase.hpp @@ -6,6 +6,7 @@ #include "slic3r/GUI/I18N.hpp" #include "slic3r/GUI/3DScene.hpp" +#include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp" #include @@ -13,6 +14,7 @@ #include #include +#include #define ENABLE_FIXED_GRABBER 1 @@ -28,7 +30,6 @@ namespace GUI { #define MAX_NUM 9999.99 #define MAX_SIZE "9999.99" - class ImGuiWrapper; class GLCanvas3D; enum class CommonGizmosDataID; @@ -61,6 +62,7 @@ public: static void update_render_colors(); static void load_render_colors(); + struct Grabber { static const float SizeFactor; @@ -119,7 +121,6 @@ protected: int m_group_id; EState m_state; int m_shortcut_key; - std::string m_icon_filename; unsigned int m_sprite_id; int m_hover_id; enum GripperType { @@ -150,8 +151,10 @@ protected: mutable GLModel m_cylinder; GLModel m_sphere; GLModel m_cross_mark; + GLModel m_lines_mark; bool m_is_dark_mode = false; + bool m_is_serializing = false; std::chrono::system_clock::time_point start; enum DoubleShowType { @@ -175,12 +178,12 @@ protected: DoubleShowType show_type = DoubleShowType::Normal); bool render_combo(const std::string &label, const std::vector &lines, size_t &selection_idx, float label_width, float item_width); - void render_cross_mark(const Transform3d& matrix, const Vec3f& target); + void render_cross_mark(const Transform3d& matrix, const Vec3f& target,bool single =false); + void render_lines(const std::vector &points); static float get_grabber_size(); public: GLGizmoBase(GLCanvas3D& parent, - const std::string& icon_filename, unsigned int sprite_id); virtual ~GLGizmoBase() {} @@ -196,11 +199,10 @@ public: EState get_state() const { return m_state; } void set_state(EState state); + void set_serializing(bool is_serializing); int get_shortcut_key() const { return m_shortcut_key; } - const std::string& get_icon_filename() const { return m_icon_filename; } - - void set_icon_filename(const std::string& filename); + virtual std::string get_icon_filename(bool b_dark_mode) const = 0; bool is_activable() const { return on_is_activable(); } bool is_selectable() const { return on_is_selectable(); } @@ -249,6 +251,8 @@ public: virtual BoundingBoxf3 get_bounding_box() const; + virtual bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); + static void render_glmodel(GLModel &model, const std::array &color, Transform3d view_model_matrix, const Transform3d& projection_matrix, bool for_picking = false, float emission_factor = 0.0f); protected: float last_input_window_width = 0; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBrimEars.cpp b/src/slic3r/GUI/Gizmos/GLGizmoBrimEars.cpp index 20aac31..a40d501 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBrimEars.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBrimEars.cpp @@ -31,7 +31,7 @@ static ModelVolume *get_model_volume(const Selection &selection, Model &model) return obj->volumes[cid.volume_id]; } -GLGizmoBrimEars::GLGizmoBrimEars(GLCanvas3D &parent, const std::string &icon_filename, unsigned int sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id) {} +GLGizmoBrimEars::GLGizmoBrimEars(GLCanvas3D &parent, unsigned int sprite_id) : GLGizmoBase(parent, sprite_id) {} bool GLGizmoBrimEars::on_init() { @@ -286,6 +286,11 @@ void GLGizmoBrimEars::data_changed(bool is_serializing) set_brim_data(); } +std::string GLGizmoBrimEars::get_icon_filename(bool is_dark_mode) const +{ + return is_dark_mode ? "toolbar_brimears_dark.svg" : "toolbar_brimears.svg"; +} + void GLGizmoBrimEars::set_brim_data() { if (!m_c->selection_info()) return; @@ -531,7 +536,15 @@ void GLGizmoBrimEars::on_render_input_window(float x, float y, float bottom_limi static float last_y = 0.0f; static float last_h = 0.0f; - ModelObject *mo = m_c->selection_info()->model_object(); + if (!m_c) { + return; + } + const auto& p_selection_info = m_c->selection_info(); + if (!p_selection_info) { + return; + } + + ModelObject *mo = p_selection_info->model_object(); if (!mo) return; @@ -640,7 +653,7 @@ void GLGizmoBrimEars::on_render_input_window(float x, float y, float bottom_limi // ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6.0f, 10.0f)); - float f_scale = m_parent.get_gizmos_manager().get_layout_scale(); + float f_scale = m_parent.get_main_toolbar_scale(); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale)); if (m_imgui->button(m_desc["auto_generate"])) { auto_generate(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoBrimEars.hpp b/src/slic3r/GUI/Gizmos/GLGizmoBrimEars.hpp index abe4037..4f8481b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoBrimEars.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoBrimEars.hpp @@ -72,10 +72,10 @@ private: }; public: - GLGizmoBrimEars(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + GLGizmoBrimEars(GLCanvas3D& parent, unsigned int sprite_id); virtual ~GLGizmoBrimEars() = default; void data_changed(bool is_serializing) override; - bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); + bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) override; void delete_selected_points(); void update_model_object(); //ClippingPlane get_sla_clipping_plane() const; @@ -86,6 +86,8 @@ public: std::string get_gizmo_entering_text() const override { return "Entering Brim Ears"; } std::string get_gizmo_leaving_text() const override { return "Leaving Brim Ears"; } + std::string get_icon_filename(bool is_dark_mode) const override; + private: void set_brim_data(); bool on_init() override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFaceDetector.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFaceDetector.cpp index cb63ff4..8914d55 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFaceDetector.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFaceDetector.cpp @@ -30,6 +30,11 @@ std::string GLGizmoFaceDetector::on_get_name() const return (_L("Face recognition") + " [P]").ToUTF8().data(); } +std::string GLGizmoFaceDetector::get_icon_filename(bool is_dark_mode) const +{ + return "face recognition.svg"; +} + void GLGizmoFaceDetector::on_render() { if (m_model.is_initialized()) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFaceDetector.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFaceDetector.hpp index 7906f30..b43b92c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFaceDetector.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFaceDetector.hpp @@ -11,9 +11,10 @@ namespace GUI { class GLGizmoFaceDetector : public GLGizmoBase { public: - GLGizmoFaceDetector(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) - : GLGizmoBase(parent, icon_filename, sprite_id) {} + GLGizmoFaceDetector(GLCanvas3D& parent, unsigned int sprite_id) + : GLGizmoBase(parent, sprite_id) {} + std::string get_icon_filename(bool is_dark_mode) const override; protected: void on_render() override; void on_render_for_picking() override {} diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp index 51dd57f..8bde014 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp @@ -22,8 +22,8 @@ namespace Slic3r::GUI { -GLGizmoFdmSupports::GLGizmoFdmSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) - : GLGizmoPainterBase(parent, icon_filename, sprite_id), m_current_tool(ImGui::CircleButtonIcon) +GLGizmoFdmSupports::GLGizmoFdmSupports(GLCanvas3D& parent, unsigned int sprite_id) + : GLGizmoPainterBase(parent, sprite_id), m_current_tool(ImGui::CircleButtonIcon) { m_tool_type = ToolType::BRUSH; m_cursor_type = TriangleSelector::CursorType::CIRCLE; @@ -163,6 +163,11 @@ bool GLGizmoFdmSupports::on_key_down_select_tool_type(int keyCode) { return true; } +std::string GLGizmoFdmSupports::get_icon_filename(bool b_dark_mode) const +{ + return b_dark_mode ? "toolbar_support_dark.svg" : "toolbar_support.svg"; +} + // QDS void GLGizmoFdmSupports::render_triangles(const Selection& selection) const { @@ -226,7 +231,15 @@ void GLGizmoFdmSupports::on_set_state() void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_limit) { init_print_instance(); - if (! m_c->selection_info()->model_object()) + if (!m_c) { + return; + } + const auto& p_selection_info = m_c->selection_info(); + if (!p_selection_info) { + return; + } + const auto& p_model_object = p_selection_info->model_object(); + if (!p_model_object) return; m_imgui_start_pos[0] = x; m_imgui_start_pos[1] = y; @@ -471,7 +484,7 @@ void GLGizmoFdmSupports::on_render_input_window(float x, float y, float bottom_l float get_cur_y = ImGui::GetContentRegionMax().y + ImGui::GetFrameHeight() + y; show_tooltip_information(caption_max, x, get_cur_y); - float f_scale =m_parent.get_gizmos_manager().get_layout_scale(); + float f_scale = m_parent.get_main_toolbar_scale(); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale)); ImGui::SameLine(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp index 881bc60..92c4801 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp @@ -13,7 +13,7 @@ namespace Slic3r::GUI { class GLGizmoFdmSupports : public GLGizmoPainterBase { public: - GLGizmoFdmSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + GLGizmoFdmSupports(GLCanvas3D& parent, unsigned int sprite_id); void data_changed(bool is_serializing) override; void render_painter_gizmo() const override; @@ -27,6 +27,8 @@ public: //QDS bool on_key_down_select_tool_type(int keyCode); + std::string get_icon_filename(bool b_dark_mode) const override; + protected: void on_render_input_window(float x, float y, float bottom_limit) override; std::string on_get_name() const override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp index 29790ca..20f0e08 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp @@ -17,8 +17,8 @@ namespace Slic3r { namespace GUI { -GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) - : GLGizmoBase(parent, icon_filename, sprite_id) +GLGizmoFlatten::GLGizmoFlatten(GLCanvas3D& parent, unsigned int sprite_id) + : GLGizmoBase(parent, sprite_id) , m_normal(Vec3d::Zero()) , m_starting_center(Vec3d::Zero()) { @@ -92,7 +92,6 @@ void GLGizmoFlatten::on_render_input_window(float x, float y, float bottom_limit ImGuiWrapper::pop_toolbar_style(); } - std::string GLGizmoFlatten::on_get_name() const { if (!on_is_activable() && m_state == EState::Off) { @@ -216,19 +215,20 @@ void GLGizmoFlatten::on_render() if (m_last_hit_facet != m_hit_facet) { m_last_hit_facet = m_hit_facet; m_one_tri_model.reset(); - auto mv = mo->volumes[m_rr.mesh_id]; - auto world_tran = (mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix()).cast(); - auto &vertices = mv->mesh().its.vertices; - auto &cur_faces = mv->mesh().its.indices; + const auto& mv = mo->volumes[m_rr.mesh_id]; + const auto world_tran = (mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix()).cast(); + const auto& vertices = mv->mesh().its.vertices; + const auto& cur_faces = mv->mesh().its.indices; if (m_hit_facet < cur_faces.size()) { - auto v0 = world_tran * vertices[cur_faces[m_hit_facet][0]] + m_rr.normal * 0.05; - auto v1 = world_tran * vertices[cur_faces[m_hit_facet][1]] + m_rr.normal * 0.05; - auto v2 = world_tran * vertices[cur_faces[m_hit_facet][2]] + m_rr.normal * 0.05; indexed_triangle_set temp_its; - temp_its.indices.push_back({0, 1, 2}); - temp_its.vertices.push_back(v0); - temp_its.vertices.push_back(v1); - temp_its.vertices.push_back(v2); + const auto normal_bias = m_rr.normal * 0.05f; + for (int i = 0; i < 3; ++i) { + const auto& mesh_v = vertices[cur_faces[m_hit_facet][i]]; + auto v = world_tran * Vec4f(mesh_v[0], mesh_v[1], mesh_v[2], 1.0f); + v /= (abs(v[3]) > 1e-6f ? v[3] : 1e-6f); + temp_its.vertices.push_back({ v[0] + normal_bias[0], v[1] + normal_bias[1], v[2] + normal_bias[2] }); + } + temp_its.indices.push_back({ 0, 1, 2 }); m_one_tri_model.init_from(temp_its); } } @@ -250,7 +250,7 @@ void GLGizmoFlatten::on_render() void GLGizmoFlatten::on_render_for_picking() { - const auto& p_flat_shader = wxGetApp().get_shader("flat"); + const auto& p_flat_shader = wxGetApp().get_shader("flat"); if (!p_flat_shader) { return; } @@ -591,5 +591,10 @@ void GLGizmoFlatten::data_changed(bool is_serializing) } } +std::string GLGizmoFlatten::get_icon_filename(bool b_dark_mode) const +{ + return b_dark_mode ? "toolbar_flatten_dark.svg" : "toolbar_flatten.svg"; +} + } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp index cd21e8b..85c0f08 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoFlatten.hpp @@ -45,10 +45,11 @@ private: FlattenType m_faltten_type{FlattenType::Default}; public: - GLGizmoFlatten(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + GLGizmoFlatten(GLCanvas3D& parent, unsigned int sprite_id); Vec3d get_flattening_normal() const; void data_changed(bool is_serializing) override; + std::string get_icon_filename(bool b_dark_mode) const override; protected: virtual bool on_init() override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFuzzySkin.cpp b/src/slic3r/GUI/Gizmos/GLGizmoFuzzySkin.cpp new file mode 100644 index 0000000..794be57 --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoFuzzySkin.cpp @@ -0,0 +1,550 @@ +#include "GLGizmoFuzzySkin.hpp" + +#include "libslic3r/Model.hpp" +//QDS +#include "libslic3r/Layer.hpp" +#include "libslic3r/Thread.hpp" + +//#include "slic3r/GUI/3DScene.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/ImGuiWrapper.hpp" +#include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/GUI_ObjectList.hpp" +#include "slic3r/GUI/format.hpp" +#include "slic3r/GUI/GUI.hpp" +#include "slic3r/Utils/UndoRedo.hpp" + + +#include + +#include + +namespace Slic3r::GUI { + +GLGizmoFuzzySkin::GLGizmoFuzzySkin(GLCanvas3D& parent, unsigned int sprite_id) + : GLGizmoPainterBase(parent, sprite_id), m_current_tool(ImGui::CircleButtonIcon) +{ + m_tool_type = ToolType::BRUSH; + m_cursor_type = TriangleSelector::CursorType::CIRCLE; +} + +void GLGizmoFuzzySkin::data_changed(bool is_serializing) { + set_painter_gizmo_data(m_parent.get_selection()); +} + +void GLGizmoFuzzySkin::on_shutdown() +{ + //QDS + //wait the thread + if (m_thread.joinable()) { + Print *print = m_print_instance.print_object->print(); + if (print) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "cancel the print"; + print->cancel(); + } + //join the thread + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "try to join thread for 2000 ms"; + auto ret = m_thread.try_join_for(boost::chrono::milliseconds(2000)); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "join thread returns "<object_clipper()->render_cut(); + m_c->instances_hider()->render_cut(); + render_cursor(); + + glsafe(::glDisable(GL_BLEND)); +} + +// QDS +bool GLGizmoFuzzySkin::on_key_down_select_tool_type(int keyCode) { + switch (keyCode) + { + case 'F': + m_current_tool = ImGui::FillButtonIcon; + break; + case 'S': + m_current_tool = ImGui::SphereButtonIcon; + break; + case 'C': + m_current_tool = ImGui::CircleButtonIcon; + break; + case 'T': + m_current_tool = ImGui::TriangleButtonIcon; break; + default: + return false; + break; + } + return true; +} + +std::string GLGizmoFuzzySkin::get_icon_filename(bool is_dark_mode) const +{ + return is_dark_mode ? "toolbar_fuzzyskin_dark.svg" : "toolbar_fuzzyskin.svg"; +} + +// QDS +void GLGizmoFuzzySkin::render_triangles(const Selection& selection) const +{ + ClippingPlaneDataWrapper clp_data = this->get_clipping_plane_data(); + const auto & shader = wxGetApp().get_shader("mm_gouraud"); + if (!shader) return; + wxGetApp().bind_shader(shader); + shader->set_uniform("clipping_plane", clp_data.clp_dataf); + shader->set_uniform("z_range", clp_data.z_range); + shader->set_uniform("slope.actived", m_parent.is_using_slope()); + ScopeGuard guard([shader]() { + if (shader) wxGetApp().unbind_shader(); + }); + + // QDS: to improve the random white pixel issue + glsafe(::glDisable(GL_CULL_FACE)); + + const ModelObject *mo = m_c->selection_info()->model_object(); + int mesh_id = -1; + for (const ModelVolume *mv : mo->volumes) { + if (!mv->is_model_part()) continue; + + ++mesh_id; + + Transform3d trafo_matrix; + if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) { + trafo_matrix = mo->instances[selection.get_instance_idx()]->get_assemble_transformation().get_matrix() * mv->get_matrix(); + trafo_matrix.translate(mv->get_transformation().get_offset() * (GLVolume::explosion_ratio - 1.0) + + mo->instances[selection.get_instance_idx()]->get_offset_to_assembly() * (GLVolume::explosion_ratio - 1.0)); + } else { + trafo_matrix = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix(); + } + + bool is_left_handed = trafo_matrix.matrix().determinant() < 0.; + if (is_left_handed) glsafe(::glFrontFace(GL_CW)); + + const Camera & camera = wxGetApp().plater()->get_camera(); + const Transform3d matrix = camera.get_view_matrix() * trafo_matrix; + shader->set_uniform("view_model_matrix", matrix); + shader->set_uniform("projection_matrix", camera.get_projection_matrix()); + shader->set_uniform("normal_matrix", (Matrix3d) matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); + + shader->set_uniform("volume_world_matrix", trafo_matrix); + shader->set_uniform("volume_mirrored", is_left_handed); + m_triangle_selectors[mesh_id]->render(m_imgui, trafo_matrix); + + if (is_left_handed) glsafe(::glFrontFace(GL_CCW)); + } +} + +void GLGizmoFuzzySkin::on_set_state() +{ + GLGizmoPainterBase::on_set_state(); + + if (get_state() == On) { + + } + else if (get_state() == Off) { + ModelObject* mo = m_c->selection_info()->model_object(); + if (mo) Slic3r::save_object_mesh(*mo); + } +} + +void GLGizmoFuzzySkin::on_render_input_window(float x, float y, float bottom_limit) +{ + if (!m_c) { + return; + } + const auto& p_selection_info = m_c->selection_info(); + if (!p_selection_info) { + return; + } + const auto& p_model_object = p_selection_info->model_object(); + if (!p_model_object) { + return; + } + m_imgui_start_pos[0] = x; + m_imgui_start_pos[1] = y; + // QDS + wchar_t old_tool = m_current_tool; + + + const float approx_height = m_imgui->scaled(23.f); + y = std::min(y, bottom_limit - approx_height); + + GizmoImguiSetNextWIndowPos(x, y, ImGuiCond_Always, 0.0f, 0.0f); + + //QDS + ImGuiWrapper::push_toolbar_style(m_parent.get_scale()); + GizmoImguiBegin(get_name(), ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar); + + // First calculate width of all the texts that are could possibly be shown. We will decide set the dialog width based on that: + const float space_size = m_imgui->get_style_scaling() * 8; + const float clipping_slider_left = m_imgui->calc_text_size(m_desc.at("clipping_of_view")).x + m_imgui->scaled(1.5f); + const float cursor_slider_left = m_imgui->calc_text_size(m_desc.at("cursor_size")).x + m_imgui->scaled(1.5f); + const float reset_button_slider_left = m_imgui->calc_text_size(m_desc.at("reset_direction")).x + m_imgui->scaled(1.5f) + ImGui::GetStyle().FramePadding.x * 2; + const float remove_btn_width = m_imgui->calc_text_size(m_desc.at("remove_all")).x + m_imgui->scaled(1.5f); + const float smart_fill_angle_txt_width = m_imgui->calc_text_size(m_desc.at("smart_fill_angle")).x + m_imgui->scaled(1.5f); + const float buttons_width = remove_btn_width + m_imgui->scaled(1.5f); + const float empty_button_width = m_imgui->calc_button_size("").x; + + const float minimal_slider_width = m_imgui->scaled(4.f); + + float caption_max = 0.f; + float total_text_max = 0.f; + for (const auto &t : std::array{"add_fuzzyskin", "remove", "cursor_size", "clipping_of_view", "smart_fill_angle"}) { + caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc[t + "_caption"]).x); + total_text_max = std::max(total_text_max, m_imgui->calc_text_size(m_desc[t]).x); + } + total_text_max += caption_max + m_imgui->scaled(1.f); + caption_max += m_imgui->scaled(1.f); + + const float sliders_left_width = std::max(smart_fill_angle_txt_width, std::max(reset_button_slider_left, std::max(cursor_slider_left, clipping_slider_left))); + const float slider_icon_width = m_imgui->get_slider_icon_size().x; + const float max_tooltip_width = ImGui::GetFontSize() * 20.0f; + + const float sliders_width = m_imgui->scaled(7.0f); + const float drag_left_width = ImGui::GetStyle().WindowPadding.x + sliders_left_width + sliders_width - space_size; + + float drag_pos_times = 0.7; + + ImGui::AlignTextToFramePadding(); + m_imgui->text(m_desc.at("tool_type")); + std::array tool_ids = {ImGui::CircleButtonIcon, ImGui::SphereButtonIcon, ImGui::TriangleButtonIcon, ImGui::FillButtonIcon}; + std::array icons; + if (m_is_dark_mode) + icons = {ImGui::CircleButtonDarkIcon, ImGui::SphereButtonDarkIcon, ImGui::TriangleButtonDarkIcon, ImGui::FillButtonDarkIcon}; + else + icons = tool_ids; + + std::array tool_tips = {_L("Circle"), _L("Sphere"), _L("Triangle"), _L("Fill")}; + for (int i = 0; i < tool_ids.size(); i++) { + std::string str_label = std::string("##"); + std::wstring btn_name = icons[i] + boost::nowide::widen(str_label); + + if (i != 0) ImGui::SameLine((empty_button_width + m_imgui->scaled(1.75f)) * i + m_imgui->scaled(1.3f)); + + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0); + if (m_current_tool == tool_ids[i]) { + ImGui::PushStyleColor(ImGuiCol_Button, m_is_dark_mode ? ImVec4(43 / 255.0f, 64 / 255.0f, 54 / 255.0f, 1.00f) : ImVec4(0.86f, 0.99f, 0.91f, 1.00f)); // r, g, b, a + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, m_is_dark_mode ? ImVec4(43 / 255.0f, 64 / 255.0f, 54 / 255.0f, 1.00f) : ImVec4(0.86f, 0.99f, 0.91f, 1.00f)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, m_is_dark_mode ? ImVec4(43 / 255.0f, 64 / 255.0f, 54 / 255.0f, 1.00f) : ImVec4(0.86f, 0.99f, 0.91f, 1.00f)); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.00f, 0.68f, 0.26f, 1.00f)); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0); + ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 1.0); + } + bool btn_clicked = ImGui::Button(into_u8(btn_name).c_str()); + if (m_current_tool == tool_ids[i]) + { + ImGui::PopStyleColor(4); + ImGui::PopStyleVar(2); + } + ImGui::PopStyleVar(1); + + if (btn_clicked && m_current_tool != tool_ids[i]) { + m_current_tool = tool_ids[i]; + for (auto& triangle_selector : m_triangle_selectors) { + triangle_selector->seed_fill_unselect_all_triangles(); + triangle_selector->request_update_render_data(); + } + } + + if (ImGui::IsItemHovered()) { + m_imgui->tooltip(tool_tips[i], max_tooltip_width); + } + } + + ImGui::Separator(); + + if (m_current_tool != old_tool) + this->tool_changed(old_tool, m_current_tool); + + ImGui::Dummy(ImVec2(0.0f, ImGui::GetFontSize() * 0.1)); + + if (m_current_tool == ImGui::CircleButtonIcon) { + m_cursor_type = TriangleSelector::CursorType::CIRCLE; + m_tool_type = ToolType::BRUSH; + + ImGui::AlignTextToFramePadding(); + m_imgui->text(m_desc.at("cursor_size")); + ImGui::SameLine(sliders_left_width); + ImGui::PushItemWidth(sliders_width); + m_imgui->qdt_slider_float_style("##cursor_radius", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f", 1.0f, true); + ImGui::SameLine(drag_left_width); + ImGui::PushItemWidth(1.5 * slider_icon_width); + ImGui::QDTDragFloat("##cursor_radius_input", &m_cursor_radius, 0.05f, 0.0f, 0.0f, "%.2f"); + } else if (m_current_tool == ImGui::SphereButtonIcon) { + m_cursor_type = TriangleSelector::CursorType::SPHERE; + m_tool_type = ToolType::BRUSH; + + ImGui::AlignTextToFramePadding(); + m_imgui->text(m_desc.at("cursor_size")); + ImGui::SameLine(sliders_left_width); + ImGui::PushItemWidth(sliders_width); + m_imgui->qdt_slider_float_style("##cursor_radius", &m_cursor_radius, CursorRadiusMin, CursorRadiusMax, "%.2f", 1.0f, true); + ImGui::SameLine(drag_left_width); + ImGui::PushItemWidth(1.5 * slider_icon_width); + ImGui::QDTDragFloat("##cursor_radius_input", &m_cursor_radius, 0.05f, 0.0f, 0.0f, "%.2f"); + } else if (m_current_tool == ImGui::FillButtonIcon) { + m_cursor_type = TriangleSelector::CursorType::POINTER; + m_tool_type = ToolType::SMART_FILL; + + ImGui::AlignTextToFramePadding(); + m_imgui->text(m_desc.at("smart_fill_angle")); + std::string format_str = std::string("%.f") + I18N::translate_utf8("", "Face angle threshold, placed after the number with no whitespace in between."); + ImGui::SameLine(sliders_left_width); + ImGui::PushItemWidth(sliders_width); + if (m_imgui->qdt_slider_float_style("##smart_fill_angle", &m_smart_fill_angle, SmartFillAngleMin, SmartFillAngleMax, format_str.data(), 1.0f, true)) + for (auto& triangle_selector : m_triangle_selectors) { + triangle_selector->seed_fill_unselect_all_triangles(); + triangle_selector->request_update_render_data(); + } + ImGui::SameLine(drag_left_width); + ImGui::PushItemWidth(1.5 * slider_icon_width); + ImGui::QDTDragFloat("##smart_fill_angle_input", &m_smart_fill_angle, 0.05f, 0.0f, 0.0f, "%.2f"); + } else if (m_current_tool == ImGui::TriangleButtonIcon) { + m_cursor_type = TriangleSelector::CursorType::POINTER; + m_tool_type = ToolType::BRUSH; + } + + //ImGui::AlignTextToFramePadding(); + + if (m_current_tool != ImGui::GapFillIcon) { + ImGui::Separator(); + if (m_c->object_clipper()->get_position() == 0.f) { + ImGui::AlignTextToFramePadding(); + m_imgui->text(m_desc.at("clipping_of_view")); + } + else { + if (m_imgui->button(m_desc.at("reset_direction"))) { + wxGetApp().CallAfter([this]() { + m_c->object_clipper()->set_position(-1., false); + }); + } + } + + auto clp_dist = float(m_c->object_clipper()->get_position()); + ImGui::SameLine(sliders_left_width); + ImGui::PushItemWidth(sliders_width); + bool b_qdt_slider_float = m_imgui->qdt_slider_float_style("##clp_dist", &clp_dist, 0.f, 1.f, "%.2f", 1.0f, true); + + ImGui::SameLine(drag_left_width); + ImGui::PushItemWidth(1.5 * slider_icon_width); + bool b_drag_input = ImGui::QDTDragFloat("##clp_dist_input", &clp_dist, 0.05f, 0.0f, 0.0f, "%.2f"); + + if (b_qdt_slider_float || b_drag_input) m_c->object_clipper()->set_position(clp_dist, true); + } + + ImGui::Separator(); + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6.0f, 10.0f)); + float get_cur_y = ImGui::GetContentRegionMax().y + ImGui::GetFrameHeight() + y; + show_tooltip_information(caption_max, x, get_cur_y); + + float f_scale = m_parent.get_main_toolbar_scale(); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale)); + + ImGui::SameLine(); + + if (m_imgui->button(m_desc.at("remove_all"))) { + Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Reset selection", UndoRedo::SnapshotType::GizmoAction); + ModelObject * mo = m_c->selection_info()->model_object(); + int idx = -1; + for (ModelVolume *mv : mo->volumes) + if (mv->is_model_part()) { + ++idx; + m_triangle_selectors[idx]->reset(); + m_triangle_selectors[idx]->request_update_render_data(true); + } + + update_model_object(); + m_parent.set_as_dirty(); + } + ImGui::PopStyleVar(2); + m_imgui_end_pos[0] = m_imgui_start_pos[0] + ImGui::GetWindowWidth(); + m_imgui_end_pos[1] = m_imgui_start_pos[1] + ImGui::GetWindowHeight(); + GizmoImguiEnd(); + + // QDS + ImGuiWrapper::pop_toolbar_style(); +} + +void GLGizmoFuzzySkin::tool_changed(wchar_t old_tool, wchar_t new_tool) +{ + if ((old_tool == ImGui::GapFillIcon && new_tool == ImGui::GapFillIcon) || + (old_tool != ImGui::GapFillIcon && new_tool != ImGui::GapFillIcon)) + return; + + for (auto& selector_ptr : m_triangle_selectors) { + TriangleSelectorPatch* tsp = dynamic_cast(selector_ptr.get()); + tsp->set_filter_state(new_tool == ImGui::GapFillIcon); + } +} + +void GLGizmoFuzzySkin::show_tooltip_information(float caption_max, float x, float y) +{ + ImTextureID normal_id = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP); + ImTextureID hover_id = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TOOLBAR_TOOLTIP_HOVER); + + caption_max += m_imgui->calc_text_size(": ").x + 15.f; + + float font_size = ImGui::GetFontSize(); + ImVec2 button_size = ImVec2(font_size * 1.8, font_size * 1.3); + ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, { 0, ImGui::GetStyle().FramePadding.y }); + ImGui::ImageButton3(normal_id, hover_id, button_size); + + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip2(ImVec2(x, y)); + auto draw_text_with_caption = [this, &caption_max](const wxString &caption, const wxString &text) { + // QDS + m_imgui->text_colored(ImGuiWrapper::COL_ACTIVE, caption); + ImGui::SameLine(caption_max); + m_imgui->text_colored(ImGuiWrapper::COL_WINDOW_BG, text); + }; + + std::vector tip_items; + switch (m_tool_type) { + case ToolType::BRUSH: + tip_items = {"add_fuzzyskin", "remove", "cursor_size", "clipping_of_view"}; + break; + case ToolType::BUCKET_FILL: + break; + case ToolType::SMART_FILL: + tip_items = {"add_fuzzyskin", "remove", "smart_fill_angle", "clipping_of_view"}; + break; + default: + break; + } + for (const auto &t : tip_items) + draw_text_with_caption(m_desc.at(t + "_caption") + ": ", m_desc.at(t)); + + ImGui::EndTooltip(); + } + ImGui::PopStyleVar(2); +} + +//QDS: remove const +void GLGizmoFuzzySkin::update_model_object() +{ + bool updated = false; + ModelObject* mo = m_c->selection_info()->model_object(); + int idx = -1; + for (ModelVolume* mv : mo->volumes) { + if (! mv->is_model_part()) + continue; + ++idx; + updated |= mv->fuzzy_skin_facets.set(*m_triangle_selectors[idx].get()); + } + + if (updated) { + const ModelObjectPtrs& mos = wxGetApp().model().objects; + wxGetApp().obj_list()->update_info_items(std::find(mos.begin(), mos.end(), mo) - mos.begin()); + + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS)); + } +} + +//QDS +void GLGizmoFuzzySkin::update_from_model_object(bool first_update) +{ + wxBusyCursor wait; + + const ModelObject* mo = m_c->selection_info()->model_object(); + m_triangle_selectors.clear(); + //QDS: add timestamp logic + m_volume_timestamps.clear(); + + int volume_id = -1; + std::vector> ebt_colors; + ebt_colors.push_back(GLVolume::NEUTRAL_COLOR); + ebt_colors.push_back(TriangleSelectorGUI::enforcers_color); + ebt_colors.push_back(TriangleSelectorGUI::blockers_color); + for (const ModelVolume* mv : mo->volumes) { + if (! mv->is_model_part()) + continue; + + ++volume_id; + + // This mesh does not account for the possible Z up SLA offset. + const TriangleMesh* mesh = &mv->mesh(); + m_triangle_selectors.emplace_back(std::make_unique(*mesh, ebt_colors)); + // Reset of TriangleSelector is done inside TriangleSelectorGUI's constructor, so we don't need it to perform it again in deserialize(). + m_triangle_selectors.back()->deserialize(mv->fuzzy_skin_facets.get_data(), false); + m_triangle_selectors.back()->request_update_render_data(); + m_triangle_selectors.back()->set_wireframe_needed(true); + //QDS: add timestamp logic + m_volume_timestamps.emplace_back(mv->fuzzy_skin_facets.timestamp()); + } +} + +PainterGizmoType GLGizmoFuzzySkin::get_painter_type() const +{ + return PainterGizmoType::FUZZY_SKIN; +} + +wxString GLGizmoFuzzySkin::handle_snapshot_action_name(bool shift_down, GLGizmoPainterBase::Button button_down) const +{ + // QDS remove _L() + wxString action_name; + if (shift_down) + action_name = ("Unselect all"); + else { + if (button_down == Button::Left) + action_name = ("Add fuzzy skin"); + else + action_name = ("Remove fuzzy skin"); + } + return action_name; +} + +} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmoFuzzySkin.hpp b/src/slic3r/GUI/Gizmos/GLGizmoFuzzySkin.hpp new file mode 100644 index 0000000..d4580c5 --- /dev/null +++ b/src/slic3r/GUI/Gizmos/GLGizmoFuzzySkin.hpp @@ -0,0 +1,82 @@ +#ifndef slic3r_GLGizmoFuzzySkin_hpp_ +#define slic3r_GLGizmoFuzzySkin_hpp_ + +#include "GLGizmoPainterBase.hpp" +//QDS +#include "libslic3r/Print.hpp" +#include "libslic3r/ObjectID.hpp" + +#include + +namespace Slic3r::GUI { + +class GLGizmoFuzzySkin : public GLGizmoPainterBase +{ +public: + GLGizmoFuzzySkin(GLCanvas3D& parent, unsigned int sprite_id); + void data_changed(bool is_serializing) override; + void render_painter_gizmo() const override; + + //QDS: add edit state + enum EditState { + state_idle = 0, + state_generating = 1, + state_ready + }; + + //QDS + bool on_key_down_select_tool_type(int keyCode); + + std::string get_icon_filename(bool is_dark_mode) const override; + +protected: + void on_render_input_window(float x, float y, float bottom_limit) override; + std::string on_get_name() const override; + std::string on_get_name_str() override { return "FuzzySkin Painting"; } + + // QDS + void render_triangles(const Selection& selection) const override; + void on_set_state() override; + void show_tooltip_information(float caption_max, float x, float y); + wxString handle_snapshot_action_name(bool shift_down, Button button_down) const override; + + std::string get_gizmo_entering_text() const override { return "Entering Paint-on supports"; } + std::string get_gizmo_leaving_text() const override { return "Leaving Paint-on supports"; } + std::string get_action_snapshot_name() override { return "Paint-on supports editing"; } + EnforcerBlockerType get_left_button_state_type() const override { return EnforcerBlockerType::FUZZY_SKIN; } + EnforcerBlockerType get_right_button_state_type() const override; + // QDS + wchar_t m_current_tool = 0; + +private: + bool on_init() override; + + //QDS: remove const + void update_model_object() override; + //QDS: add logic to distinguish the first_time_update and later_update + void update_from_model_object(bool first_update) override; + void tool_changed(wchar_t old_tool, wchar_t new_tool); + void on_opening() override {} + void on_shutdown() override; + PainterGizmoType get_painter_type() const override; + + bool m_volume_valid = false; + + bool m_is_tree_support = false; + bool m_cancel = false; + size_t m_object_id; + std::vector m_volume_timestamps; + PrintInstance m_print_instance; + mutable EditState m_edit_state; + //thread + boost::thread m_thread; + // Mutex and condition variable to synchronize m_thread with the UI thread. + std::mutex m_mutex; + int m_generate_count; + + // This map holds all translated description texts, so they can be easily referenced during layout calculations + // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. + std::map m_desc; +}; +} // namespace Slic3r::GUI +#endif // slic3r_GLGizmoFuzzySkin_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp index aa41848..de51d19 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp @@ -17,13 +17,18 @@ namespace Slic3r { namespace GUI { -GLGizmoHollow::GLGizmoHollow(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) - : GLGizmoBase(parent, icon_filename, sprite_id) +GLGizmoHollow::GLGizmoHollow(GLCanvas3D& parent, unsigned int sprite_id) + : GLGizmoBase(parent, sprite_id) { m_vbo_cylinder.init_from(its_make_cylinder(1., 1.)); } +std::string GLGizmoHollow::get_icon_filename(bool is_dark_mode) const +{ + return "hollow.svg"; +} + bool GLGizmoHollow::on_init() { m_shortcut_key = WXK_CONTROL_H; @@ -456,7 +461,16 @@ GLGizmoHollow::get_config_options(const std::vector& keys) const void GLGizmoHollow::on_render_input_window(float x, float y, float bottom_limit) { - ModelObject* mo = m_c->selection_info()->model_object(); + if (!m_c) { + return; + } + + const auto& p_selection_info = m_c->selection_info(); + if (!p_selection_info) { + return; + } + + ModelObject* mo = p_selection_info->model_object(); if (! mo) return; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp index cc9c144..ea41816 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoHollow.hpp @@ -27,15 +27,17 @@ private: public: - GLGizmoHollow(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + GLGizmoHollow(GLCanvas3D& parent, unsigned int sprite_id); virtual ~GLGizmoHollow() = default; void set_sla_support_data(ModelObject* model_object, const Selection& selection); - bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); - void delete_selected_points(); + bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) override; + void delete_selected_points(); bool is_selection_rectangle_dragging() const { return m_selection_rectangle.is_dragging(); } + std::string get_icon_filename(bool is_dark_mode) const override; + private: bool on_init() override; void on_update(const UpdateData& data) override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp index 02bc52a..337602b 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp @@ -176,8 +176,8 @@ void TransformHelper::update(const std::array &viewport) TransformHelper::Cache TransformHelper::s_cache = { { 0, 0, 0, 0 }, Matrix4d::Identity(), Transform3d::Identity() }; -GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) -: GLGizmoBase(parent, icon_filename, sprite_id) +GLGizmoMeasure::GLGizmoMeasure(GLCanvas3D& parent, unsigned int sprite_id) +: GLGizmoBase(parent, sprite_id) { auto sphere_geometry = smooth_sphere(16, 7.5f); m_sphere = std::make_shared(); @@ -442,6 +442,11 @@ bool GLGizmoMeasure::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_po return true; } +std::string GLGizmoMeasure::get_icon_filename(bool is_dark_mode) const +{ + return is_dark_mode ? "toolbar_measure_dark.svg" : "toolbar_measure.svg"; +} + bool GLGizmoMeasure::on_init() { m_shortcut_key = WXK_CONTROL_U; @@ -495,7 +500,6 @@ std::string GLGizmoMeasure::on_get_name() const bool GLGizmoMeasure::on_is_activable() const { const Selection& selection = m_parent.get_selection(); - //1.9.5 if (selection.is_wipe_tower()) { return false; } @@ -680,8 +684,10 @@ void GLGizmoMeasure::on_render() } if (m_measure_mode == EMeasureMode::ONLY_ASSEMBLY) { if (m_assembly_mode == AssemblyMode::FACE_FACE) { - if (curr_feature->get_type() != Measure::SurfaceFeatureType::Plane) { - curr_feature.reset(); + if (curr_feature.has_value()) { + if (curr_feature->get_type() != Measure::SurfaceFeatureType::Plane) { + curr_feature.reset(); + } } } else if (m_assembly_mode == AssemblyMode::POINT_POINT) { if (!(curr_feature->get_type() == Measure::SurfaceFeatureType::Point || @@ -815,7 +821,7 @@ void GLGizmoMeasure::on_render() glsafe(::glEnable(GL_DEPTH_TEST)); glDisable(GL_BLEND); auto render_feature = - [this, view_matrix, projection_matrix](const Measure::SurfaceFeature &feature, const std::vector &colors, + [this, view_matrix, projection_matrix](const Measure::SurfaceFeature &feature, const std::vector &colors, float inv_zoom, bool hover, bool update_raycasters_transform,int featura_index = -1) { switch (feature.get_type()) { @@ -1896,16 +1902,16 @@ void GLGizmoMeasure::show_distance_xyz_ui() if (m_measure_mode == EMeasureMode::ONLY_MEASURE) { m_imgui->text(_u8L("Measure")); } - auto add_measure_row_to_table = [this](const std::string &col_1, const ImVec4 &col_1_color, const std::string &col_2, const ImVec4 &col_2_color) { + auto add_measure_row_to_table = [this](const std::string &col_1, const ImVec4 &col_1_color, const std::string &col_2, const std::string &unit, const ImVec4 &col_2_color) { ImGui::TableNextRow(); ImGui::TableSetColumnIndex(0); m_imgui->text_colored(col_1_color, col_1); ImGui::TableSetColumnIndex(1); - m_imgui->text_colored(col_2_color, col_2); + m_imgui->text_colored(col_2_color, col_2 + unit); ImGui::TableSetColumnIndex(2); if (m_imgui->image_button(m_is_dark_mode ? ImGui::ClipboardBtnDarkIcon : ImGui::ClipboardBtnIcon, _L("Copy to clipboard"))) { wxTheClipboard->Open(); - wxTheClipboard->SetData(new wxTextDataObject(wxString((col_1 + ": " + col_2).c_str(), wxConvUTF8))); + wxTheClipboard->SetData(new wxTextDataObject(wxString(col_2.c_str(), wxConvUTF8))); wxTheClipboard->Close(); } }; @@ -1962,7 +1968,7 @@ void GLGizmoMeasure::show_distance_xyz_ui() if (measure.angle.has_value() && m_measure_mode == EMeasureMode::ONLY_MEASURE) { ImGui::PushID("ClipboardAngle"); - add_measure_row_to_table(_u8L("Angle"), ImGuiWrapper::COL_QIDI, format_double(Geometry::rad2deg(measure.angle->angle)) + "°", + add_measure_row_to_table(_u8L("Angle"), ImGuiWrapper::COL_QIDI, format_double(Geometry::rad2deg(measure.angle->angle)),"°", ImGui::GetStyleColorVec4(ImGuiCol_Text)); ++measure_row_count; ImGui::PopID(); @@ -1975,7 +1981,7 @@ void GLGizmoMeasure::show_distance_xyz_ui() double distance = measure.distance_infinite->dist; if (m_use_inches) distance = GizmoObjectManipulation::mm_to_in * distance; ImGui::PushID("ClipboardDistanceInfinite"); - add_measure_row_to_table(show_strict ? _u8L("Perpendicular distance") : _u8L("Distance"), ImGuiWrapper::COL_QIDI, format_double(distance) + m_units, + add_measure_row_to_table(show_strict ? _u8L("Perpendicular distance") : _u8L("Distance"), ImGuiWrapper::COL_QIDI, format_double(distance), m_units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); ++measure_row_count; ImGui::PopID(); @@ -1988,7 +1994,7 @@ void GLGizmoMeasure::show_distance_xyz_ui() if (m_use_inches) distance = GizmoObjectManipulation::mm_to_in * distance; ImGui::PushID("ClipboardDistanceStrict"); - add_measure_row_to_table(_u8L("Direct distance"), ImGuiWrapper::COL_QIDI, format_double(distance) + m_units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_measure_row_to_table(_u8L("Direct distance"), ImGuiWrapper::COL_QIDI, format_double(distance), m_units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); ++measure_row_count; ImGui::PopID(); } @@ -1997,7 +2003,7 @@ void GLGizmoMeasure::show_distance_xyz_ui() if (m_use_inches) distance = GizmoObjectManipulation::mm_to_in * distance; if (measure.distance_xyz->norm() > EPSILON) { ImGui::PushID("ClipboardDistanceXYZ"); - add_measure_row_to_table(_u8L("Distance XYZ"), ImGuiWrapper::COL_QIDI, format_vec3(distance), ImGui::GetStyleColorVec4(ImGuiCol_Text)); + add_measure_row_to_table(_u8L("Distance XYZ"), ImGuiWrapper::COL_QIDI, format_vec3(distance), m_units, ImGui::GetStyleColorVec4(ImGuiCol_Text)); ++measure_row_count; ImGui::PopID(); } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp index b8140cb..9b9c745 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp @@ -217,7 +217,7 @@ protected: #endif // ENABLE_MEASURE_GIZMO_DEBUG public: - GLGizmoMeasure(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + GLGizmoMeasure(GLCanvas3D& parent, unsigned int sprite_id); /// /// Apply rotation on select plane /// @@ -227,16 +227,19 @@ public: void data_changed(bool is_serializing) override; - virtual bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); + bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) override; bool wants_enter_leave_snapshots() const override { return true; } std::string get_gizmo_entering_text() const override { return _u8L("Entering Measure gizmo"); } std::string get_gizmo_leaving_text() const override { return _u8L("Leaving Measure gizmo"); } //std::string get_action_snapshot_name() const override { return _u8L("Measure gizmo editing"); } + std::string get_icon_filename(bool is_dark_mode) const override; + protected: bool on_init() override; std::string on_get_name() const override; + virtual std::string on_get_name_str() override { return "Measure"; } bool on_is_activable() const override; void on_render() override; void on_set_state() override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.cpp index c2bbd96..b6a0d54 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.cpp @@ -21,8 +21,8 @@ namespace GUI { static const std::string warning_text_common = _u8L("Unable to perform boolean operation on selected parts"); static const std::string warning_text_intersection = _u8L("Performed boolean intersection fails because the selected parts have no intersection"); -GLGizmoMeshBoolean::GLGizmoMeshBoolean(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) - : GLGizmoBase(parent, icon_filename, sprite_id) +GLGizmoMeshBoolean::GLGizmoMeshBoolean(GLCanvas3D& parent, unsigned int sprite_id) + : GLGizmoBase(parent, sprite_id) { } @@ -113,6 +113,11 @@ bool GLGizmoMeshBoolean::gizmo_event(SLAGizmoEventType action, const Vec2d& mous return true; } +std::string GLGizmoMeshBoolean::get_icon_filename(bool b_dark_mode) const +{ + return b_dark_mode ? "toolbar_meshboolean_dark.svg" : "toolbar_meshboolean.svg"; +} + bool GLGizmoMeshBoolean::on_init() { m_shortcut_key = WXK_CONTROL_B; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.hpp index 0dcf2e7..2b113ac 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMeshBoolean.hpp @@ -38,7 +38,7 @@ struct VolumeInfo { class GLGizmoMeshBoolean : public GLGizmoBase { public: - GLGizmoMeshBoolean(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + GLGizmoMeshBoolean(GLCanvas3D& parent, unsigned int sprite_id); ~GLGizmoMeshBoolean(); void set_enable(bool enable) { m_enable = enable; } @@ -47,7 +47,9 @@ public: void set_src_volume(ModelVolume* mv); void set_tool_volume(ModelVolume* mv); - bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); + bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) override; + + std::string get_icon_filename(bool b_dark_mode) const override; protected: virtual bool on_init() override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index 928ca5c..333f5d5 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -62,6 +62,18 @@ bool GLGizmoMmuSegmentation::on_is_activable() const return !selection.is_empty() && (selection.is_single_full_instance() || selection.is_any_volume()); } +void GLGizmoMmuSegmentation::on_load(cereal::BinaryInputArchive &ar) +{ + GLGizmoPainterBase::on_load(ar); + ar(m_selected_extruder_idx); +} + +void GLGizmoMmuSegmentation::on_save(cereal::BinaryOutputArchive &ar) const +{ + GLGizmoPainterBase::on_save(ar); + ar(m_selected_extruder_idx); +} + static std::vector get_extruder_id_for_volumes(const ModelObject &model_object) { std::vector extruders_idx; @@ -104,6 +116,7 @@ bool GLGizmoMmuSegmentation::on_init() m_desc["erase"] = _L("Erase"); m_desc["shortcut_key_caption"] = _L("Key 1~9"); m_desc["shortcut_key"] = _L("Choose filament"); + m_desc["same_color_connection"] = _L("Connected same color"); m_desc["edge_detection"] = _L("Edge detection"); m_desc["gap_area_caption"] = ctrl + _L("Mouse wheel"); m_desc["gap_area"] = _L("Gap area"); @@ -137,9 +150,10 @@ bool GLGizmoMmuSegmentation::on_init() return true; } -GLGizmoMmuSegmentation::GLGizmoMmuSegmentation(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) - : GLGizmoPainterBase(parent, icon_filename, sprite_id), m_current_tool(ImGui::CircleButtonIcon) +GLGizmoMmuSegmentation::GLGizmoMmuSegmentation(GLCanvas3D& parent, unsigned int sprite_id) + : GLGizmoPainterBase(parent, sprite_id) { + m_current_tool =ImGui::CircleButtonIcon; } void GLGizmoMmuSegmentation::data_changed(bool is_serializing) { @@ -278,11 +292,67 @@ void GLGizmoMmuSegmentation::render_triangles(const Selection &selection) const shader->set_uniform("volume_world_matrix", trafo_matrix); shader->set_uniform("volume_mirrored", is_left_handed); - m_triangle_selectors[mesh_id]->render(m_imgui, trafo_matrix); + m_triangle_selectors[mesh_id]->render(m_imgui, trafo_matrix, m_tool_type != ToolType::BUCKET_FILL); + //add selected glvolume for BucketFillType::SameColor ,because no paint_contour + if (m_tool_type == ToolType::BUCKET_FILL) { + auto temp_patch = dynamic_cast(m_triangle_selectors[mesh_id].get()); + int state = -1; + auto its = m_triangle_selectors[mesh_id]->get_seed_fill_mesh(state); + TriangleMesh mesh(its); + if (m_rr.mesh_id == mesh_id && !(state == m_last_hit_state && m_last_hit_state_faces == mesh.facets_count() && (m_last_hit_its_center - mesh.bounding_box().center()).norm() > 0.01)) { + m_last_hit_state = state; + m_last_hit_state_faces = mesh.facets_count(); + m_last_hit_its_center =mesh.bounding_box().center(); + if (!m_parent.get_paint_outline_volumes().empty()) { m_parent.get_paint_outline_volumes().clear(); } + if (temp_patch && !m_triangle_selectors[mesh_id]->get_paint_contour_has_data()) { + auto colors =temp_patch->get_ebt_colors(); + auto triangles = temp_patch->get_triangles(); + if (triangles.size() > 0 && state >= 0) { + if (state < colors.size()) { + auto color = colors[state]; + m_parent.get_paint_outline_volumes().volumes.emplace_back(new GLVolume(color)); + auto& v = m_parent.get_paint_outline_volumes().volumes.back(); + + init_selected_glvolume(*v, mesh, Geometry::Transformation(trafo_matrix)); + } + } + } + } + } + if (m_rr.mesh_id < 0 || m_tool_type != ToolType::BUCKET_FILL) { + clear_parent_paint_outline_volumes(); + } if (is_left_handed) glsafe(::glFrontFace(GL_CCW)); } + if (m_tool_type == ToolType::BUCKET_FILL) { + mesh_id = -1; + for (const ModelVolume *mv : mo->volumes) { + if (!mv->is_model_part()) continue; + + ++mesh_id; + if (mesh_id != m_rr.mesh_id) { continue; } + Transform3d trafo_matrix; + if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) { + trafo_matrix = mo->instances[selection.get_instance_idx()]->get_assemble_transformation().get_matrix() * mv->get_matrix(); + trafo_matrix.translate(mv->get_transformation().get_offset() * (GLVolume::explosion_ratio - 1.0) + + mo->instances[selection.get_instance_idx()]->get_offset_to_assembly() * (GLVolume::explosion_ratio - 1.0)); + } else { + trafo_matrix = mo->instances[selection.get_instance_idx()]->get_transformation().get_matrix() * mv->get_matrix(); + } + + bool is_left_handed = trafo_matrix.matrix().determinant() < 0.; + if (is_left_handed) glsafe(::glFrontFace(GL_CW)); + + const Camera & camera = wxGetApp().plater()->get_camera(); + const Transform3d matrix = camera.get_view_matrix() * trafo_matrix; + + m_triangle_selectors[mesh_id]->render_paint_contour(trafo_matrix,true); + + if (is_left_handed) glsafe(::glFrontFace(GL_CCW)); + } + } } // QDS @@ -323,6 +393,11 @@ bool GLGizmoMmuSegmentation::on_key_down_select_tool_type(int keyCode) { return true; } +std::string GLGizmoMmuSegmentation::get_icon_filename(bool is_dark_mode) const +{ + return is_dark_mode ? "mmu_segmentation_dark.svg" : "mmu_segmentation.svg"; +} + static void render_extruders_combo(const std::string &label, const std::vector &extruders, const std::vector> &extruders_colors, @@ -425,7 +500,17 @@ void GLGizmoMmuSegmentation::show_tooltip_information(float caption_max, float x void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bottom_limit) { - if (!m_c->selection_info()->model_object()) return; + if (!m_c) { + return; + } + const auto& p_selection_info = m_c->selection_info(); + if (!p_selection_info) { + return; + } + const auto& p_model_object = p_selection_info->model_object(); + if (!p_model_object) { + return; + } const float approx_height = m_imgui->scaled(22.0f); y = std::min(y, bottom_limit - approx_height); @@ -480,7 +565,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott window_width = std::max(window_width, total_text_max); window_width = std::max(window_width, buttons_width); window_width = std::max(window_width, max_filament_items_per_line * filament_item_width + m_imgui->scaled(0.5f)); - + window_width = std::max(window_width, m_imgui->calc_button_size(m_desc["same_color_connection"]).x + m_imgui->calc_button_size("edge_detection").x + m_imgui->scaled(2.5f)); const float sliders_width = m_imgui->scaled(7.0f); const float drag_left_width = ImGui::GetStyle().WindowPadding.x + sliders_width - space_size; @@ -492,7 +577,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott float color_button = ImGui::GetCursorPos().y; - float textbox_width = 1.5 * slider_icon_width; + float textbox_width = 1.5 * slider_icon_width; SliderInputLayout slider_input_layout = {clipping_slider_left, sliders_width, drag_left_width + circle_max_width, textbox_width}; if (wxGetApp().plater()->is_show_non_manifold_edges()) { m_imgui->text(_L("hit face") + ":" + std::to_string(m_rr.facet)); @@ -703,10 +788,25 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott } else if (m_current_tool == ImGui::FillButtonIcon) { m_cursor_type = TriangleSelector::CursorType::POINTER; - m_imgui->qdt_checkbox(m_desc["edge_detection"], m_detect_geometry_edge); + bool is_same_color = m_bucket_fill_mode == BucketFillType::SameColor; + ImGuiWrapper::push_radio_style(); + if (ImGui::RadioButton(m_desc["same_color_connection"].ToUTF8().data(), is_same_color)) { + m_bucket_fill_mode = BucketFillType::SameColor; + m_smart_fill_angle = -1;// set to negative value to disable edge detection + } + ImGui::SameLine(); + bool is_detect_geometry_edge = m_bucket_fill_mode == BucketFillType::EdgeDetect; + if (ImGui::RadioButton(m_desc["edge_detection"].ToUTF8().data(), is_detect_geometry_edge)) { + m_bucket_fill_mode = BucketFillType::EdgeDetect; + m_smart_fill_angle = m_last_edge_detection_smart_fill_angle; + } + ImGuiWrapper::pop_radio_style(); m_tool_type = ToolType::BUCKET_FILL; - if (m_detect_geometry_edge) { + if (is_detect_geometry_edge) { + if (m_last_edge_detection_smart_fill_angle != m_smart_fill_angle) { + m_last_edge_detection_smart_fill_angle = m_smart_fill_angle; + } ImGui::AlignTextToFramePadding(); m_imgui->text(m_desc["smart_fill_angle"]); std::string format_str = std::string("%.f") + I18N::translate_utf8("°", "Face angle threshold," @@ -721,9 +821,6 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott ImGui::SameLine(drag_left_width + sliders_left_width); ImGui::PushItemWidth(1.5 * slider_icon_width); ImGui::QDTDragFloat("##smart_fill_angle_input", &m_smart_fill_angle, 0.05f, 0.0f, 0.0f, "%.2f"); - } else { - // set to negative value to disable edge detection - m_smart_fill_angle = -1.f; } ImGui::Separator(); if (m_c->object_clipper()->get_position() == 0.f) { @@ -810,8 +907,6 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott m_vertical_only = vertical_only; if (m_vertical_only) { m_horizontal_only = false; - m_is_front_view = true; - change_camera_view_angle(m_front_view_radian); } } @@ -851,7 +946,7 @@ void GLGizmoMmuSegmentation::on_render_input_window(float x, float y, float bott float get_cur_y = ImGui::GetContentRegionMax().y + ImGui::GetFrameHeight() + y; show_tooltip_information(caption_max, x, get_cur_y); - float f_scale =m_parent.get_gizmos_manager().get_layout_scale(); + float f_scale = m_parent.get_main_toolbar_scale(); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale)); ImGui::SameLine(); @@ -1021,8 +1116,12 @@ void GLGizmoMmuSegmentation::on_set_state() m_selected_extruder_idx = 1; } m_non_manifold_edges_model.reset(); + m_bucket_fill_mode = BucketFillType::SameColor; + m_smart_fill_angle = -1; } else if (get_state() == Off) { + clear_parent_paint_outline_volumes(); + ModelObject* mo = m_c->selection_info()->model_object(); if (mo) Slic3r::save_object_mesh(*mo); m_parent.post_event(SimpleEvent(EVT_GLCANVAS_FORCE_UPDATE)); @@ -1043,6 +1142,16 @@ wxString GLGizmoMmuSegmentation::handle_snapshot_action_name(bool shift_down, GL return action_name; } +void GLGizmoMmuSegmentation::clear_parent_paint_outline_volumes() const +{ + m_last_hit_state = -1; + m_last_hit_state_faces = -1; + m_last_hit_its_center = Vec3d::Zero(); + if (!m_parent.get_paint_outline_volumes().empty()) { + m_parent.get_paint_outline_volumes().clear(); + } +} + void GLMmSegmentationGizmo3DScene::release_geometry() { if (this->vertices_VBO_id) { glsafe(::glDeleteBuffers(1, &this->vertices_VBO_id)); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp index e2c8d6f..8423a76 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp @@ -64,12 +64,11 @@ public: class GLGizmoMmuSegmentation : public GLGizmoPainterBase { public: - GLGizmoMmuSegmentation(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + GLGizmoMmuSegmentation(GLCanvas3D& parent, unsigned int sprite_id); ~GLGizmoMmuSegmentation() override = default; void data_changed(bool is_serializing) override; void render_painter_gizmo() const override; void render_non_manifold_edges() const; - void render_triangles(const Selection& selection) const override; // TriangleSelector::serialization/deserialization has a limit to store 19 different states. @@ -84,6 +83,8 @@ public: bool on_number_key_down(int number); bool on_key_down_select_tool_type(int keyCode); + std::string get_icon_filename(bool is_dark_mode) const override; + protected: // QDS void set_painter_gizmo_data(const Selection &selection) override; @@ -99,9 +100,11 @@ protected: void show_tooltip_information(float caption_max, float x, float y); bool on_is_selectable() const override; bool on_is_activable() const override; - + void on_load(cereal::BinaryInputArchive &ar) override; + void on_save(cereal::BinaryOutputArchive &ar) const override; wxString handle_snapshot_action_name(bool shift_down, Button button_down) const override; + void clear_parent_paint_outline_volumes() const; std::string get_gizmo_entering_text() const override { return "Entering color painting"; } std::string get_gizmo_leaving_text() const override { return "Leaving color painting"; } std::string get_action_snapshot_name() override { return "Color painting editing"; } @@ -112,9 +115,6 @@ protected: std::vector m_volumes_extruder_idxs; // QDS - wchar_t m_current_tool = 0; - bool m_detect_geometry_edge = true; - static const constexpr float CursorRadiusMin = 0.1f; // cannot be zero private: diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp index 688e8e3..79863d9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.cpp @@ -22,8 +22,8 @@ const double GLGizmoMove3D::Offset = 10.0; #endif //QDS: GUI refactor: add obj manipulation -GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id, GizmoObjectManipulation* obj_manipulation) - : GLGizmoBase(parent, icon_filename, sprite_id) +GLGizmoMove3D::GLGizmoMove3D(GLCanvas3D& parent, unsigned int sprite_id, GizmoObjectManipulation* obj_manipulation) + : GLGizmoBase(parent, sprite_id) , m_displacement(Vec3d::Zero()) , m_snap_step(1.0) , m_starting_drag_position(Vec3d::Zero()) @@ -115,6 +115,11 @@ BoundingBoxf3 GLGizmoMove3D::get_bounding_box() const return t_aabb; } +std::string GLGizmoMove3D::get_icon_filename(bool b_dark_mode) const +{ + return b_dark_mode ? "toolbar_move_dark.svg" : "toolbar_move.svg"; +} + bool GLGizmoMove3D::on_init() { for (int i = 0; i < 3; ++i) { @@ -336,22 +341,21 @@ double GLGizmoMove3D::calc_projection(const UpdateData& data) const { double projection = 0.0; - Vec3d starting_vec = m_starting_drag_position - m_starting_box_center; - double len_starting_vec = starting_vec.norm(); - if (len_starting_vec != 0.0) { - Vec3d mouse_dir = data.mouse_ray.unit_vector(); - // finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position - // use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form - // in our case plane normal and ray direction are the same (orthogonal view) - // when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal - Vec3d inters = data.mouse_ray.a + (m_starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir; - // vector from the starting position to the found intersection - Vec3d inters_vec = inters - m_starting_drag_position; + Vec3d mouse_dir = data.mouse_ray.unit_vector(); + // finds the intersection of the mouse ray with the plane parallel to the camera viewport and passing throught the starting position + // use ray-plane intersection see i.e. https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection algebric form + // in our case plane normal and ray direction are the same (orthogonal view) + // when moving to perspective camera the negative z unit axis of the camera needs to be transformed in world space and used as plane normal + Vec3d inters = data.mouse_ray.a + (m_starting_drag_position - data.mouse_ray.a).dot(mouse_dir) / mouse_dir.squaredNorm() * mouse_dir; - projection = inters_vec.norm(); - const double sign = inters_vec.dot(starting_vec) > 1e-6f ? 1.0f : -1.0f; - - projection = projection * sign; + if (m_hover_id == 0) { + projection = inters.x() - m_starting_drag_position.x(); + } + else if (m_hover_id == 1) { + projection = inters.y() - m_starting_drag_position.y(); + } + else if (m_hover_id == 2) { + projection = inters.z() - m_starting_drag_position.z(); } if (wxGetKeyState(WXK_SHIFT)) @@ -363,7 +367,6 @@ double GLGizmoMove3D::calc_projection(const UpdateData& data) const void GLGizmoMove3D::render_grabber_extension(Axis axis, const BoundingBoxf3& box, bool picking) const { double size = get_grabber_size() * 0.75;//0.75 for arrow show - std::array color = m_grabbers[axis].color; if (!picking && m_hover_id != -1) { if (m_hover_id == axis) { @@ -376,7 +379,6 @@ void GLGizmoMove3D::render_grabber_extension(Axis axis, const BoundingBoxf3& box return; wxGetApp().bind_shader(shader); - const_cast(&m_vbo_cone)->set_color(-1, color); const Camera& camera = picking ? wxGetApp().plater()->get_picking_camera() : wxGetApp().plater()->get_camera(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp index baee695..8a39f44 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMove.hpp @@ -41,7 +41,7 @@ class GLGizmoMove3D : public GLGizmoBase public: //QDS: add obj manipulation logic //GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); - GLGizmoMove3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id, GizmoObjectManipulation* obj_manipulation); + GLGizmoMove3D(GLCanvas3D& parent, unsigned int sprite_id, GizmoObjectManipulation* obj_manipulation); virtual ~GLGizmoMove3D() = default; double get_snap_step(double step) const { return m_snap_step; } @@ -53,6 +53,8 @@ public: void data_changed(bool is_serializing) override; BoundingBoxf3 get_bounding_box() const override; + std::string get_icon_filename(bool b_dark_mode) const override; + protected: virtual bool on_init() override; virtual std::string on_get_name() const override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index f0a172b..e14efa6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -23,8 +23,8 @@ namespace Slic3r::GUI { -GLGizmoPainterBase::GLGizmoPainterBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) - : GLGizmoBase(parent, icon_filename, sprite_id) +GLGizmoPainterBase::GLGizmoPainterBase(GLCanvas3D& parent, unsigned int sprite_id) + : GLGizmoBase(parent, sprite_id) { // Make sphere and save it into a vertex buffer. m_vbo_sphere.load_its_flat_shading(its_make_sphere(1., (2*M_PI)/24.)); @@ -127,6 +127,20 @@ void GLGizmoPainterBase::render_triangles(const Selection& selection) const } } +void GLGizmoPainterBase::init_selected_glvolume(GLVolume &v,const TriangleMesh &mesh, const Geometry::Transformation &world_transformation) const +{ + v.force_native_color = true; + v.selected = true; + v.set_render_color(); +#if ENABLE_SMOOTH_NORMALS + v.indexed_vertex_array->load_mesh(mesh, true); +#else + v.indexed_vertex_array->load_mesh(mesh); +#endif // ENABLE_SMOOTH_NORMALS + v.indexed_vertex_array->finalize_geometry(true); + v.set_instance_transformation(world_transformation); + v.set_convex_hull(mesh.convex_hull_3d()); +} void GLGizmoPainterBase::render_cursor() const { @@ -1224,16 +1238,24 @@ void GLGizmoPainterBase::on_set_state() -void GLGizmoPainterBase::on_load(cereal::BinaryInputArchive&) +void GLGizmoPainterBase::on_load(cereal::BinaryInputArchive &ar) { // We should update the gizmo from current ModelObject, but it is not // possible at this point. That would require having updated selection and // common gizmos data, which is not done at this point. Instead, save // a flag to do the update in set_painter_gizmo_data, which will be called // soon after. + ar(m_tool_type, m_current_tool, m_cursor_type, m_cursor_radius, m_cursor_height, m_lock_x_for_height_bottom, m_smart_fill_angle, m_bucket_fill_mode, + TriangleSelectorPatch::gap_area); m_schedule_update = true; } +void GLGizmoPainterBase::on_save(cereal::BinaryOutputArchive &ar) const +{ + ar(m_tool_type, m_current_tool,m_cursor_type, m_cursor_radius, m_cursor_height, m_lock_x_for_height_bottom, + m_smart_fill_angle, m_bucket_fill_mode, TriangleSelectorPatch::gap_area); +} + TriangleSelector::ClippingPlane GLGizmoPainterBase::get_clipping_plane_in_volume_coordinates(const Transform3d &trafo) const { const ::Slic3r::GUI::ClippingPlane *const clipping_plane = m_c->object_clipper()->get_clipping_plane(); if (clipping_plane == nullptr || !clipping_plane->is_active()) @@ -1271,7 +1293,7 @@ std::array TriangleSelectorGUI::get_seed_fill_color(const std::arrayget_camera(); contour_shader->set_uniform("view_model_matrix", camera.get_view_matrix() * matrix); contour_shader->set_uniform("projection_matrix", camera.get_projection_matrix()); - + if (clear_depth){ + glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); + } glsafe(::glDepthFunc(GL_LEQUAL)); m_paint_contour.render_geometry(); glsafe(::glDepthFunc(GL_LESS)); @@ -1423,7 +1449,12 @@ bool TrianglePatch::is_fragment() const float TriangleSelectorPatch::gap_area = TriangleSelectorPatch::GapAreaMin; bool TriangleSelectorPatch::exist_gap_area = false; -void TriangleSelectorPatch::render(ImGuiWrapper* imgui, const Transform3d& matrix) +TriangleSelectorPatch::TriangleSelectorPatch(const TriangleMesh &mesh, const std::vector> ebt_colors, float edge_limit): TriangleSelectorGUI(mesh, edge_limit), m_ebt_colors(ebt_colors) +{ +} + + +void TriangleSelectorPatch::render(ImGuiWrapper *imgui, const Transform3d &matrix, bool render_paint_contour_at_same_time) { if (m_update_render_data) update_render_data(); @@ -1471,7 +1502,9 @@ void TriangleSelectorPatch::render(ImGuiWrapper* imgui, const Transform3d& matri ScopeGuard guard_mm_gouraud([shader]() { wxGetApp().bind_shader(shader); }); wxGetApp().unbind_shader(); - render_paint_contour(matrix); + if (render_paint_contour_at_same_time) { + render_paint_contour(matrix); + } m_update_render_data = false; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp index 6fc1a09..fc8186f 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp @@ -19,10 +19,10 @@ namespace Slic3r::GUI { enum class SLAGizmoEventType : unsigned char; class ClippingPlane; struct Camera; -class GLGizmoMmuSegmentation; enum class PainterGizmoType { FDM_SUPPORTS, + FUZZY_SKIN, SEAM, MMU_SEGMENTATION }; @@ -71,10 +71,11 @@ public: // Render current selection. Transformation matrices are supposed // to be already set. - virtual void render(ImGuiWrapper *imgui, const Transform3d& matrix); + virtual void render(ImGuiWrapper *imgui, const Transform3d &matrix, bool render_paint_contour_at_same_time = true); + void render_paint_contour(const Transform3d &matrix, bool clear_depth = false); void set_wireframe_needed(bool need_wireframe) { m_need_wireframe = need_wireframe; } bool get_wireframe_needed() { return m_need_wireframe; } - + bool get_paint_contour_has_data() { return m_paint_contour.is_initialized(); } // QDS void request_update_render_data(bool paint_changed = false) { @@ -94,7 +95,6 @@ public: protected: void update_paint_contour(); - void render_paint_contour(const Transform3d& matrix); protected: bool m_update_render_data = false; @@ -131,13 +131,12 @@ struct TrianglePatch { class TriangleSelectorPatch : public TriangleSelectorGUI { public: - explicit TriangleSelectorPatch(const TriangleMesh& mesh, const std::vector> ebt_colors, float edge_limit = 0.6f) - : TriangleSelectorGUI(mesh, edge_limit), m_ebt_colors(ebt_colors) {} + explicit TriangleSelectorPatch(const TriangleMesh &mesh, const std::vector> ebt_colors, float edge_limit = 0.6f); virtual ~TriangleSelectorPatch() = default; // Render current selection. Transformation matrices are supposed // to be already set. - void render(ImGuiWrapper* imgui, const Transform3d& matrix) override; + void render(ImGuiWrapper *imgui, const Transform3d &matrix, bool render_paint_contour_at_same_time) override; // TriangleSelector.m_triangles => m_gizmo_scene.triangle_patches void update_triangles_per_type(); // m_gizmo_scene.triangle_patches => TriangleSelector.m_triangles @@ -145,6 +144,7 @@ public: void update_triangles_per_patch(); void set_ebt_colors(const std::vector> ebt_colors) { m_ebt_colors = ebt_colors; } + const std::vector> &get_ebt_colors() const { return m_ebt_colors; } void set_filter_state(bool is_filter_state); constexpr static float GapAreaMin = 0.f; @@ -220,9 +220,9 @@ private: void on_render() override {} void on_render_for_picking() override {} public: - GLGizmoPainterBase(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + GLGizmoPainterBase(GLCanvas3D& parent, unsigned int sprite_id); ~GLGizmoPainterBase() override = default; - virtual bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); + bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) override; // Following function renders the triangles and cursor. Having this separated // from usual on_render method allows to render them before transparent @@ -243,6 +243,7 @@ public: protected: virtual void set_painter_gizmo_data(const Selection &selection); virtual void render_triangles(const Selection& selection) const; + void init_selected_glvolume(GLVolume &v, const TriangleMesh &mesh, const Geometry::Transformation &world_transformation) const; void render_cursor() const; void render_cursor_circle() const; void render_cursor_sphere(const Transform3d& trafo) const; @@ -301,8 +302,15 @@ protected: bool m_triangle_splitting_enabled = true; ToolType m_tool_type = ToolType::BRUSH; - float m_smart_fill_angle = 30.f; - + wchar_t m_current_tool = 0; + mutable int m_last_hit_state = -1; + mutable int m_last_hit_state_faces = -1; + mutable Vec3d m_last_hit_its_center = Vec3d::Zero(); + const float DEFAULT_FILL_ANGLE = 30.f; + float m_smart_fill_angle = DEFAULT_FILL_ANGLE; + float m_last_edge_detection_smart_fill_angle = DEFAULT_FILL_ANGLE; + enum class BucketFillType { SameColor, EdgeDetect }; + BucketFillType m_bucket_fill_mode = BucketFillType::SameColor; bool m_paint_on_overhangs_only = false; float m_highlight_by_angle_threshold_deg = 0.f; @@ -336,6 +344,18 @@ protected: TriangleSelector::ClippingPlane get_clipping_plane_in_volume_coordinates(const Transform3d &trafo) const; void change_camera_view_angle(float front_view_radian); + // Following cache holds result of a raycast query. The queries are asked + // during rendering the sphere cursor and painting, this saves repeated + // raycasts when the mouse position is the same as before. + struct RaycastResult + { + Vec2d mouse_position; + int mesh_id; + Vec3f hit; + size_t facet; + }; + mutable GLGizmoPainterBase::RaycastResult m_rr; + mutable bool m_lock_x_for_height_bottom{false}; private: std::vector> get_projected_mouse_positions(const Vec2d &mouse_position, double resolution, const std::vector &trafo_matrices) const; @@ -355,16 +375,7 @@ protected: Button m_button_down = Button::None; EState m_old_state = Off; // to be able to see that the gizmo has just been closed (see on_set_state) - // Following cache holds result of a raycast query. The queries are asked - // during rendering the sphere cursor and painting, this saves repeated - // raycasts when the mouse position is the same as before. - struct RaycastResult { - Vec2d mouse_position; - int mesh_id; - Vec3f hit; - size_t facet; - }; - mutable RaycastResult m_rr; + // QDS struct CutContours @@ -385,7 +396,6 @@ protected: mutable bool m_is_set_height_start_z_by_imgui{false}; mutable Vec2i m_height_start_pos{0, 0}; mutable float m_x_for_height_input{-1}; - mutable bool m_lock_x_for_height_bottom{false}; mutable Vec2f m_height_range_input_all_size; mutable bool m_is_cursor_in_imgui{false}; BoundingBoxf3 bounding_box() const; @@ -403,13 +413,12 @@ protected: bool on_is_activable() const override; bool on_is_selectable() const override; void on_load(cereal::BinaryInputArchive& ar) override; - void on_save(cereal::BinaryOutputArchive& ar) const override {} + void on_save(cereal::BinaryOutputArchive &ar) const override; CommonGizmosDataID on_get_requirements() const override; bool wants_enter_leave_snapshots() const override { return true; } virtual wxString handle_snapshot_action_name(bool shift_down, Button button_down) const = 0; bool is_mouse_hit_in_imgui()const; - friend class ::Slic3r::GUI::GLGizmoMmuSegmentation; mutable Vec2i m_imgui_start_pos{0, 0}; mutable Vec2i m_imgui_end_pos{0, 0}; }; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp index cc94229..92668af 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp @@ -29,7 +29,7 @@ const float GLGizmoRotate::GrabberOffset = 0.15f; // in percent of radius GLGizmoRotate::GLGizmoRotate(GLCanvas3D& parent, GLGizmoRotate::Axis axis) - : GLGizmoBase(parent, "", -1) + : GLGizmoBase(parent, -1) , m_axis(axis) , m_angle(0.0) , m_center(0.0, 0.0, 0.0) @@ -42,7 +42,7 @@ GLGizmoRotate::GLGizmoRotate(GLCanvas3D& parent, GLGizmoRotate::Axis axis) } GLGizmoRotate::GLGizmoRotate(const GLGizmoRotate& other) - : GLGizmoBase(other.m_parent, other.m_icon_filename, other.m_sprite_id) + : GLGizmoBase(other.m_parent, other.m_sprite_id) , m_axis(other.m_axis) , m_angle(other.m_angle) , m_center(other.m_center) @@ -157,12 +157,12 @@ void GLGizmoRotate::on_render() const auto& shader = wxGetApp().get_shader("flat"); if (shader) { wxGetApp().bind_shader(shader); + const Camera& camera = wxGetApp().plater()->get_camera(); Transform3d redius_scale_matrix; Geometry::scale_transform(redius_scale_matrix, { radius, radius, radius }); Transform3d view_model_matrix = camera.get_view_matrix() * m_base_model_matrix * redius_scale_matrix; - shader->set_uniform("view_model_matrix", view_model_matrix); shader->set_uniform("projection_matrix", camera.get_projection_matrix()); @@ -448,7 +448,6 @@ void GLGizmoRotate::render_grabber_extension(const BoundingBoxf3& box, bool pick Geometry::assemble_transform(center, Vec3d(-0.5 * PI, 0.0, m_angle)) * Geometry::assemble_transform(1.5 * size * Vec3d::UnitZ(), Vec3d::Zero(), Vec3d(0.75 * size, 0.75 * size, 3.0 * size)); - shader->set_uniform("view_model_matrix", view_model_matrix); shader->set_uniform("normal_matrix", (Matrix3d)view_model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose()); m_cone.render_geometry(); @@ -493,7 +492,6 @@ Transform3d GLGizmoRotate::transform_to_local(const Selection &selection) const return m_orient_matrix * ret; - } Vec3d GLGizmoRotate::mouse_position_in_local_plane(const Linef3& mouse_ray, const Selection& selection) const @@ -580,9 +578,14 @@ BoundingBoxf3 GLGizmoRotate::get_bounding_box() const return t_aabb; } +std::string GLGizmoRotate::get_icon_filename(bool b_dark_mode) const +{ + return ""; +} + //QDS: GUI refactor: add obj manipulation -GLGizmoRotate3D::GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id, GizmoObjectManipulation* obj_manipulation) - : GLGizmoBase(parent, icon_filename, sprite_id) +GLGizmoRotate3D::GLGizmoRotate3D(GLCanvas3D& parent, unsigned int sprite_id, GizmoObjectManipulation* obj_manipulation) + : GLGizmoBase(parent, sprite_id) //QDS: GUI refactor: add obj manipulation , m_object_manipulation(obj_manipulation) { @@ -622,6 +625,11 @@ BoundingBoxf3 GLGizmoRotate3D::get_bounding_box() const return t_aabb; } +std::string GLGizmoRotate3D::get_icon_filename(bool b_dark_mode) const +{ + return b_dark_mode ? "toolbar_rotate_dark.svg" : "toolbar_rotate.svg"; +} + bool GLGizmoRotate3D::on_init() { for (GLGizmoRotate& g : m_gizmos) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp index 8ff5161..85a83ca 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoRotate.hpp @@ -77,6 +77,8 @@ public: void set_custom_tran(const Transform3d &tran); BoundingBoxf3 get_bounding_box() const override; + std::string get_icon_filename(bool b_dark_mode) const override; + protected: bool on_init() override; std::string on_get_name() const override { return ""; } @@ -115,7 +117,7 @@ protected: public: //QDS: add obj manipulation logic //GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); - GLGizmoRotate3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id, GizmoObjectManipulation* obj_manipulation); + GLGizmoRotate3D(GLCanvas3D& parent, unsigned int sprite_id, GizmoObjectManipulation* obj_manipulation); Vec3d get_rotation() const { return Vec3d(m_gizmos[X].get_angle(), m_gizmos[Y].get_angle(), m_gizmos[Z].get_angle()); } void set_rotation(const Vec3d& rotation) { m_gizmos[X].set_angle(rotation(0)); m_gizmos[Y].set_angle(rotation(1)); m_gizmos[Z].set_angle(rotation(2)); } @@ -139,6 +141,8 @@ public: BoundingBoxf3 get_bounding_box() const override; + std::string get_icon_filename(bool b_dark_mode) const override; + protected: bool on_init() override; std::string on_get_name() const override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp index e322db8..733c992 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp @@ -210,7 +210,7 @@ std::string volume_name(const EmbossShape &shape) // QDS: GUI refactor: add obj manipulation GLGizmoSVG::GLGizmoSVG(GLCanvas3D& parent, unsigned int sprite_id) - : GLGizmoBase(parent, "tool_bar_svg.svg", sprite_id) //"toolbar_cut.svg" no use + : GLGizmoBase(parent, sprite_id) , m_gui_cfg(nullptr) , m_rotate_gizmo(parent, GLGizmoRotate::Axis::Z) // grab id = 2 (Z axis) { @@ -476,6 +476,11 @@ bool GLGizmoSVG::is_svg(const ModelVolume &volume) { return volume.emboss_shape.has_value() && volume.emboss_shape->svg_file.has_value(); } +bool GLGizmoSVG::on_is_selectable() const +{ + return true; +} + bool GLGizmoSVG::on_init() { m_rotate_gizmo.init(); @@ -493,11 +498,18 @@ std::string GLGizmoSVG::on_get_name() const bool GLGizmoSVG::on_is_activable() const { - const Selection &selection = m_parent.get_selection(); - if (selection.is_empty()) { - return true; + const Selection &t_selection = m_parent.get_selection(); + if (t_selection.is_empty()) { + return false; } - return !selection.is_any_connector();//maybe is negitive volume or modifier volume + if (t_selection.is_any_connector()) { + return false;//maybe is negitive volume or modifier volume + } + + if (!t_selection.has_emboss_shape()) { + return false; + } + return true; } void GLGizmoSVG::on_set_state() { @@ -1337,6 +1349,7 @@ void GLGizmoSVG::draw_window() draw_mirroring(); //draw_face_the_camera(); + if (!m_volume->is_the_only_one_part()) { ImGui::Separator(); draw_model_type(); @@ -1361,12 +1374,19 @@ void GLGizmoSVG::draw_preview() ImTextureID id = (void *) static_cast(m_texture.id); unsigned window_width = static_cast(ImGui::GetWindowSize().x - 2 * ImGui::GetStyle().WindowPadding.x); - if (m_texture.width > window_width && window_width > 0) { - m_texture.width = window_width; - } - if (m_texture.height > m_texture.width) { - m_texture.height = m_texture.width; + if (window_width > 0) { + if (m_texture.width > window_width) { + float scale = window_width / (float) m_texture.width; + m_texture.height = scale * m_texture.height; + m_texture.width = window_width; + } + if (m_texture.height > window_width) { + float scale = window_width / (float) m_texture.height; + m_texture.width = scale * m_texture.width; + m_texture.height = window_width; + } } + ImVec2 s(m_texture.width, m_texture.height); //std::optional spacing; @@ -1400,7 +1420,7 @@ void GLGizmoSVG::draw_preview() /* if (spacing.has_value()) ImGui::SetCursorPosY(ImGui::GetCursorPosY() + *spacing);*/ - } + } } void GLGizmoSVG::draw_filename() @@ -1409,7 +1429,7 @@ void GLGizmoSVG::draw_filename() const EmbossShape::SvgFile &svg = *es.svg_file; if (m_filename_preview.empty()) { // create filename preview - if (!svg.path.empty()) { + if (!svg.path.empty() && boost::filesystem::exists(svg.path)) { m_filename_preview = get_file_name(svg.path); } else if (!svg.path_in_3mf.empty()) { m_filename_preview = get_file_name(svg.path_in_3mf); @@ -1447,7 +1467,7 @@ void GLGizmoSVG::draw_filename() is_hovered |= ImGui::IsItemHovered(); if (is_hovered) { - wxString tooltip = GUI::format_wxstr(_L("SVG file path is \"%1%\""), svg.path); + wxString tooltip = GUI::format_wxstr(_L("SVG file path is \"%1%\""), boost::filesystem::exists(svg.path) ? svg.path : svg.path_in_3mf); m_imgui->tooltip(tooltip, m_gui_cfg->max_tooltip_width); } @@ -1940,7 +1960,8 @@ void GLGizmoSVG::draw_mirroring() m_parent.do_mirror(L("Set Mirror")); // Mirror is ignoring keep up !! - if (m_keep_up) m_angle = calc_angle(selection); + if (m_keep_up) + m_angle = calc_angle(selection); volume_transformation_changed(); @@ -2214,6 +2235,11 @@ BoundingBoxf3 GLGizmoSVG::get_bounding_box() const return t_aabb; } +std::string GLGizmoSVG::get_icon_filename(bool is_dark_mode) const +{ + return "tool_bar_svg.svg"; +} + void GLGizmoSVG::update_single_mesh_pick(GLVolume *v) { if (m_mesh_raycaster_map.find(v) != m_mesh_raycaster_map.end()) { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSVG.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSVG.hpp index 1fc1068..c089df4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSVG.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSVG.hpp @@ -35,7 +35,7 @@ public: void on_set_hover_id() override { m_rotate_gizmo.set_hover_id(m_hover_id); } void on_enable_grabber(unsigned int id) override; void on_disable_grabber(unsigned int id) override; - + bool wants_enter_leave_snapshots() const override { return true; } /// /// Create new text without given position /// @@ -63,11 +63,13 @@ public: void register_single_mesh_pick(); void update_single_mesh_pick(GLVolume *v); - bool gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_position, bool shift_down, bool alt_down, bool control_down); + bool gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_position, bool shift_down, bool alt_down, bool control_down) override; BoundingBoxf3 get_bounding_box() const override; + std::string get_icon_filename(bool is_dark_mode) const override; + protected: - bool on_is_selectable() const override { return false; } + bool on_is_selectable() const override; virtual bool on_init() override; virtual std::string on_get_name() const override; std::string on_get_name_str() override { return "Move"; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp index 37ede46..e0c30ef 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.cpp @@ -23,8 +23,8 @@ Vec3d GetIntersectionOfRayAndPlane(Vec3d ray_position, Vec3d ray_dir, Vec3d plan } //QDS: GUI refactor: add obj manipulation -GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id, GizmoObjectManipulation* obj_manipulation) - : GLGizmoBase(parent, icon_filename, sprite_id) +GLGizmoScale3D::GLGizmoScale3D(GLCanvas3D& parent, unsigned int sprite_id, GizmoObjectManipulation* obj_manipulation) + : GLGizmoBase(parent, sprite_id) , m_scale(Vec3d::Ones()) , m_offset(Vec3d::Zero()) , m_snap_step(0.05) @@ -141,6 +141,11 @@ bool GLGizmoScale3D::on_key(const wxKeyEvent& key_event) return b_processed; } +std::string GLGizmoScale3D::get_icon_filename(bool b_dark_mode) const +{ + return b_dark_mode ? "toolbar_scale_dark.svg" : "toolbar_scale.svg"; +} + bool GLGizmoScale3D::on_init() { for (int i = 0; i < 10; ++i) @@ -244,6 +249,7 @@ void GLGizmoScale3D::update_grabbers_data() const Vec3d box_half_size = 0.5 * m_bounding_box.size(); bool b_asymmetric_scalling = is_asymmetric_scalling_enabled(); + bool single_instance = selection.is_single_full_instance(); bool single_volume = selection.is_single_modifier() || selection.is_single_volume(); @@ -252,20 +258,17 @@ void GLGizmoScale3D::update_grabbers_data() m_grabbers[0].color = (b_asymmetric_scalling && m_hover_id == 1) ? CONSTRAINED_COLOR : AXES_COLOR[0]; m_grabbers[1].center = Vec3d(box_half_size.x(), 0.0, -box_half_size.z()); m_grabbers[1].color = (b_asymmetric_scalling && m_hover_id == 0) ? CONSTRAINED_COLOR : AXES_COLOR[0]; - // y axis m_grabbers[2].center = Vec3d(0.0, -(box_half_size.y()), -box_half_size.z()); m_grabbers[2].color = (b_asymmetric_scalling && m_hover_id == 3) ? CONSTRAINED_COLOR : AXES_COLOR[1]; m_grabbers[3].center = Vec3d(0.0, box_half_size.y(), -box_half_size.z()); m_grabbers[3].color = (b_asymmetric_scalling && m_hover_id == 2) ? CONSTRAINED_COLOR : AXES_COLOR[1]; - // z axis do not show 4 m_grabbers[4].center = Vec3d(0.0, 0.0, -(box_half_size.z())); m_grabbers[4].enabled = false; m_grabbers[5].center = Vec3d(0.0, 0.0, box_half_size.z()); m_grabbers[5].color = (b_asymmetric_scalling && m_hover_id == 4) ? CONSTRAINED_COLOR : AXES_COLOR[2]; - // uniform m_grabbers[6].center = Vec3d(-box_half_size.x(), -box_half_size.y(), -box_half_size.z()); m_grabbers[6].color = (b_asymmetric_scalling && m_hover_id == 8) ? CONSTRAINED_COLOR : GRABBER_UNIFORM_COL; @@ -290,6 +293,7 @@ void GLGizmoScale3D::update_grabbers_data() } } + void GLGizmoScale3D::change_cs_by_selection() { int obejct_idx, volume_idx; ModelVolume *model_volume = m_parent.get_selection().get_selected_single_volume(obejct_idx, volume_idx); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp index cd7e3d7..103ab55 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoScale.hpp @@ -55,7 +55,7 @@ class GLGizmoScale3D : public GLGizmoBase public: //QDS: add obj manipulation logic //GLGizmoScale3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); - GLGizmoScale3D(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id, GizmoObjectManipulation* obj_manipulation); + GLGizmoScale3D(GLCanvas3D& parent, unsigned int sprite_id, GizmoObjectManipulation* obj_manipulation); double get_snap_step(double step) const { return m_snap_step; } void set_snap_step(double step) { m_snap_step = step; } @@ -71,6 +71,8 @@ public: BoundingBoxf3 get_bounding_box() const override; bool on_key(const wxKeyEvent& key_event) override; + + std::string get_icon_filename(bool b_dark_mode) const override; protected: virtual bool on_init() override; virtual std::string on_get_name() const override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp index afcc7c1..37524f4 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp @@ -49,8 +49,8 @@ bool GLGizmoSeam::on_init() return true; } -GLGizmoSeam::GLGizmoSeam(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) - : GLGizmoPainterBase(parent, icon_filename, sprite_id), m_current_tool(ImGui::CircleButtonIcon) +GLGizmoSeam::GLGizmoSeam(GLCanvas3D& parent, unsigned int sprite_id) + : GLGizmoPainterBase(parent, sprite_id), m_current_tool(ImGui::CircleButtonIcon) { } @@ -104,6 +104,11 @@ bool GLGizmoSeam::on_key_down_select_tool_type(int keyCode) { return true; } +std::string GLGizmoSeam::get_icon_filename(bool b_dark_mode) const +{ + return b_dark_mode ? "toolbar_seam_dark.svg" : "toolbar_seam.svg"; +} + void GLGizmoSeam::render_triangles(const Selection& selection) const { ClippingPlaneDataWrapper clp_data = this->get_clipping_plane_data(); @@ -191,7 +196,16 @@ void GLGizmoSeam::tool_changed(wchar_t old_tool, wchar_t new_tool) void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit) { - if (! m_c->selection_info()->model_object()) + if (!m_c) { + return; + } + const auto& p_selection_info = m_c->selection_info(); + if (!p_selection_info) { + return; + } + const auto& p_model_object = p_selection_info->model_object(); + + if (!p_model_object) return; m_imgui_start_pos[0] = x; m_imgui_start_pos[1] = y; @@ -362,7 +376,7 @@ void GLGizmoSeam::on_render_input_window(float x, float y, float bottom_limit) float get_cur_y = ImGui::GetContentRegionMax().y + ImGui::GetFrameHeight() + y; show_tooltip_information(caption_max, x, get_cur_y); - float f_scale =m_parent.get_gizmos_manager().get_layout_scale(); + float f_scale = m_parent.get_main_toolbar_scale(); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale)); ImGui::SameLine(); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp index de232ff..39779db 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSeam.hpp @@ -8,13 +8,15 @@ namespace Slic3r::GUI { class GLGizmoSeam : public GLGizmoPainterBase { public: - GLGizmoSeam(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + GLGizmoSeam(GLCanvas3D& parent, unsigned int sprite_id); void data_changed(bool is_serializing) override; void render_painter_gizmo() const override; //QDS bool on_key_down_select_tool_type(int keyCode); + std::string get_icon_filename(bool b_dark_mode) const override; + protected: // QDS void on_set_state() override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp index 43be0f1..b8eb97c 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp @@ -56,10 +56,8 @@ static ModelVolume* get_model_volume(const Selection& selection, Model& model) return obj->volumes[cid.volume_id]; } -GLGizmoSimplify::GLGizmoSimplify(GLCanvas3D & parent, - const std::string &icon_filename, - unsigned int sprite_id) - : GLGizmoBase(parent, icon_filename, -1) +GLGizmoSimplify::GLGizmoSimplify(GLCanvas3D & parent, unsigned int sprite_id) + : GLGizmoBase(parent, sprite_id) , m_volume(nullptr) , m_show_wireframe(false) , m_move_to_center(false) @@ -137,6 +135,11 @@ void GLGizmoSimplify::add_simplify_suggestion_notification( } } +std::string GLGizmoSimplify::get_icon_filename(bool is_dark_mode) const +{ + return "reduce_triangles.svg"; +} + std::string GLGizmoSimplify::on_get_name() const { if (!on_is_activable()) { @@ -630,8 +633,15 @@ void GLGizmoSimplify::init_model(const indexed_triangle_set& its) m_glmodel.reset(); m_glmodel.init_from(its); m_parent.toggle_model_objects_visibility(true); // selected volume may have changed - m_parent.toggle_model_objects_visibility(false, m_c->selection_info()->model_object(), - m_c->selection_info()->get_active_instance(), m_volume); + if (m_c) { + const auto& p_selection_info = m_c->selection_info(); + if (p_selection_info) { + const auto& p_model_object = p_selection_info->model_object(); + const auto& active_instance = p_selection_info->get_active_instance(); + m_parent.toggle_model_objects_visibility(false, p_model_object, + active_instance, m_volume); + } + } if (const Selection&sel = m_parent.get_selection(); sel.get_volume_idxs().size() == 1) m_glmodel.set_color(-1, sel.get_volume(*sel.get_volume_idxs().begin())->color); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp index be667e4..c9979d8 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSimplify.hpp @@ -17,9 +17,9 @@ namespace GUI { class NotificationManager; // for simplify suggestion class GLGizmoSimplify: public GLGizmoBase -{ +{ public: - GLGizmoSimplify(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + GLGizmoSimplify(GLCanvas3D& parent, unsigned int sprite_id); virtual ~GLGizmoSimplify(); bool on_esc_key_down(); static void add_simplify_suggestion_notification( @@ -27,6 +27,8 @@ public: const std::vector & objects, NotificationManager & manager); + std::string get_icon_filename(bool is_dark_mode) const override; + protected: virtual std::string on_get_name() const override; virtual std::string on_get_name_str() override { return "Simplify"; } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp index 1f7f0f9..55c5fdf 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp @@ -26,12 +26,17 @@ namespace Slic3r { namespace GUI { -GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) - : GLGizmoBase(parent, icon_filename, sprite_id) +GLGizmoSlaSupports::GLGizmoSlaSupports(GLCanvas3D& parent, unsigned int sprite_id) + : GLGizmoBase(parent, sprite_id) { } +std::string GLGizmoSlaSupports::get_icon_filename(bool is_dark_mode) const +{ + return "sla_supports.svg"; +} + bool GLGizmoSlaSupports::on_init() { m_shortcut_key = WXK_CONTROL_L; @@ -178,7 +183,6 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) // Inverse matrix of the instance scaling is applied so that the mark does not scale with the object. const Transform3d support_matrix = Geometry::assemble_transform(support_point.pos.cast()) * instance_scaling_matrix_inverse; - if (vol->is_left_handed()) glFrontFace(GL_CW); @@ -191,6 +195,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) Eigen::Quaterniond q; q.setFromTwoVectors(Vec3d{0., 0., 1.}, instance_scaling_matrix_inverse * m_editing_cache[i].normal.cast()); + Eigen::AngleAxisd aa(q); const double cone_radius = 0.25; // mm const double cone_height = 0.75; @@ -251,7 +256,6 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking) glFrontFace(GL_CCW); } } - } bool GLGizmoSlaSupports::is_mesh_point_clipped(const Vec3d& point) const diff --git a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp index 3c9871f..35964ba 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.hpp @@ -55,10 +55,10 @@ private: }; public: - GLGizmoSlaSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + GLGizmoSlaSupports(GLCanvas3D& parent, unsigned int sprite_id); virtual ~GLGizmoSlaSupports() = default; void set_sla_support_data(ModelObject* model_object, const Selection& selection); - bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down); + bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position, bool shift_down, bool alt_down, bool control_down) override; void delete_selected_points(bool force = false); //ClippingPlane get_sla_clipping_plane() const; @@ -71,6 +71,8 @@ public: std::string get_gizmo_entering_text() const override { return "Entering SLA support points"; } std::string get_gizmo_leaving_text() const override { return "Leaving SLA support points"; } + std::string get_icon_filename(bool is_dark_mode) const override; + private: bool on_init() override; void on_update(const UpdateData& data) override; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoText.cpp b/src/slic3r/GUI/Gizmos/GLGizmoText.cpp index 8fc87b5..694f47e 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoText.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoText.cpp @@ -7,11 +7,25 @@ #include "slic3r/GUI/ImGuiWrapper.hpp" #include "slic3r/GUI/GUI_ObjectList.hpp" #include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/MsgDialog.hpp" +#include "slic3r/GUI/format.hpp" #include "libslic3r/Geometry/ConvexHull.hpp" #include "libslic3r/Model.hpp" - +#include "libslic3r/Timer.hpp" #include "libslic3r/Shape/TextShape.hpp" +#include "slic3r/Utils/WxFontUtils.hpp" +#include "slic3r/GUI/Jobs/CreateFontNameImageJob.hpp" +#include "slic3r/GUI/Jobs/NotificationProgressIndicator.hpp" +#include "slic3r/Utils/UndoRedo.hpp" + +#include +#include +#include +#include +#include // detection of change DPI +#include +#include #include #include @@ -19,182 +33,398 @@ #include +#include "imgui/imgui_stdlib.h" // using std::string for inputs #ifndef IMGUI_DEFINE_MATH_OPERATORS #define IMGUI_DEFINE_MATH_OPERATORS #endif #include #include "libslic3r/SVG.hpp" #include -#include "wx/fontenum.h" -#include "FontUtils.hpp" +#include "../ParamsPanel.hpp" +using namespace Slic3r; +using namespace Slic3r::GUI; +using namespace Slic3r::GUI::Emboss; +using namespace Slic3r::Emboss; +static std::size_t hash_value(wxString const &s) +{ + boost::hash hasher; + return hasher(s.ToStdString()); +} + +// increase number when change struct FacenamesSerializer +constexpr std::uint32_t FACENAMES_VERSION = 1; +struct FacenamesSerializer +{ + // hash number for unsorted vector of installed font into system + size_t hash = 0; + // assumption that is loadable + std::vector good; + // Can't load for some reason + std::vector bad; +}; +template void save(Archive &archive, wxString const &d) +{ + auto data = into_u8(d); + archive(data); +} +template void load(Archive &archive, wxString &d) +{ + std::string s; + archive(s); + d = from_u8(s); +} +template void serialize(Archive &ar, FacenamesSerializer &t, const std::uint32_t version) +{ + // When performing a load, the version associated with the class + // is whatever it was when that data was originally serialized + // When we save, we'll use the version that is defined in the macro + if (version != FACENAMES_VERSION) { + throw Slic3r::IOError("Version of hints.cereal is higher than current version."); + return; + } + ar(t.hash, t.good, t.bad); +} +CEREAL_CLASS_VERSION(FacenamesSerializer, FACENAMES_VERSION); // register class version namespace Slic3r { namespace GUI { +namespace Text { +template struct Limit +{ + // Limitation for view slider range in GUI + MinMax gui; + // Real limits for setting exacts values + MinMax values; +}; +static const struct Limits +{ + MinMax emboss{0.01, 1e4}; // in mm + MinMax size_in_mm{1.0f, 1000.f}; // in mm + Limit boldness{{-.1f, .1f}, {-5e5f, 5e5f}}; // in font points + Limit skew{{-1.f, 1.f}, {-100.f, 100.f}}; // ration without unit + MinMax char_gap{-20000, 20000}; // in font points + MinMax line_gap{-20000, 20000}; // in font points + // distance text object from surface + MinMax angle{-180.f, 180.f}; // in degrees +} limits; +enum class IconType : unsigned { + rename = 0, + warning, + undo, + save, + add, + erase, + /* + italic, + unitalic, + bold, + unbold, + system_selector, + open_file, + lock, + lock_bold, + unlock, + unlock_bold, + align_horizontal_left, + align_horizontal_center, + align_horizontal_right, + align_vertical_top, + align_vertical_center, + align_vertical_bottom,*/ + // automatic calc of icon's count + _count +}; +// Define rendered version of icon +enum class IconState : unsigned { activable = 0, hovered /*1*/, disabled /*2*/ }; +// selector for icon by enum +const IconManager::Icon &get_icon(const IconManager::VIcons &icons, IconType type, IconState state); -static const double PI = 3.141592653589793238; -static const wxColour FONT_TEXTURE_BG = wxColour(0, 0, 0, 0); +struct CurGuiCfg +{ + // Detect invalid config values when change monitor DPI + double screen_scale; + bool dark_mode = false; + + // Zero means it is calculated in init function + float height_of_volume_type_selector = 0.f; + float input_width = 0.f; + float delete_pos_x = 0.f; + float max_style_name_width = 0.f; + unsigned int icon_width = 0; + float max_tooltip_width = 0.f; + + // maximal width and height of style image + Vec2i32 max_style_image_size = Vec2i32(0, 0); + + float indent = 0.f; + float input_offset = 0.f; + float advanced_input_offset = 0.f; + float lock_offset = 0.f; + + ImVec2 text_size; + + // maximal size of face name image + Vec2i32 face_name_size = Vec2i32(0, 0); + float face_name_texture_offset_x = 0.f; + + // maximal texture generate jobs running at once + unsigned int max_count_opened_font_files = 10; + + // Only translations needed for calc GUI size + struct Translations + { + std::string font; + std::string height; + std::string depth; + + // advanced + std::string use_surface; + std::string per_glyph; + std::string alignment; + std::string char_gap; + std::string line_gap; + std::string boldness; + std::string skew_ration; + std::string from_surface; + std::string rotation; + }; + Translations translations; +}; +CurGuiCfg create_gui_configuration(); +} +using namespace Text; +IconManager::VIcons init_text_icons(IconManager &mng, const CurGuiCfg &cfg)//init_icons +{ + mng.release(); + + ImVec2 size(cfg.icon_width, cfg.icon_width); + // icon order has to match the enum IconType + std::vector filenames{ + "edit_button.svg", + "obj_warning.svg", // exclamation // ORCA: use obj_warning instead exclamation. exclamation is not compatible with low res + "text_undo.svg", // reset_value + "text_save.svg", // save + "add_copies.svg", + "delete2.svg", + //"text_refresh.svg", // refresh + //"text_open.svg", // changhe_file + //"text_bake.svg", // bake + + //"text_obj_warning.svg", // exclamation // ORCA: use obj_warning instead exclamation. exclamation is not compatible with low res + //"text_lock_closed.svg", // lock + //"text_lock_open.svg", // unlock + //"text_reflection_x.svg", // reflection_x + //"text_reflection_y.svg", // reflection_y + }; + + assert(filenames.size() == static_cast(IconType::_count)); + std::string path = resources_dir() + "/images/"; + for (std::string &filename : filenames) + filename = path + filename; + + auto type = IconManager::RasterType::color_wite_gray; + return mng.init(filenames, size, type); +} +bool is_text_empty(std::string_view text) { return text.empty() || text.find_first_not_of(" \n\t\r") == std::string::npos; } +struct GLGizmoText::GuiCfg : public Text::CurGuiCfg +{}; + +static const wxColour FONT_TEXTURE_BG = wxColour(0, 0, 0, 0); static const wxColour FONT_TEXTURE_FG = *wxWHITE; static const int FONT_SIZE = 12; static const float SELECTABLE_INNER_OFFSET = 8.0f; -static const wxFontEncoding font_encoding = wxFontEncoding::wxFONTENCODING_SYSTEM; const std::array TEXT_GRABBER_COLOR = {1.0, 1.0, 0.0, 1.0}; const std::array TEXT_GRABBER_HOVER_COLOR = {0.7, 0.7, 0.0, 1.0}; -#ifdef DEBUG_TEXT std::string formatFloat(float val) { std::stringstream ss; ss << std::fixed << std::setprecision(2) << val; return ss.str(); } -#endif -std::vector init_face_names() + +bool draw_button(const IconManager::VIcons &icons, IconType type, bool disable = false); +struct FaceName { - std::vector valid_font_names; - wxArrayString facenames = wxFontEnumerator::GetFacenames(font_encoding); - std::vector bad_fonts; + wxString wx_name; + std::string name_truncated = ""; + size_t texture_index = 0; + // State for generation of texture + // when start generate create share pointers + std::shared_ptr> cancel = nullptr; + // R/W only on main thread - finalize of job + std::shared_ptr is_created = nullptr; +}; +// Implementation of forwarded struct +// Keep sorted list of loadable face names +struct CurFacenames +{ + // flag to keep need of enumeration fonts from OS + // false .. wants new enumeration check by Hash + // true .. already enumerated(During opened combo box) + bool is_init = false; - BOOST_LOG_TRIVIAL(info) << "init_fonts_names start"; + bool has_truncated_names = false; - // validation lambda - auto is_valid_font = [coding = font_encoding, bad = bad_fonts](const wxString &name) { - if (name.empty()) - return false; + // data of can_load() faces + std::vector faces = {}; + std::vector faces_names = {}; + // Sorter set of Non valid face names in OS + std::vector bad = {}; - // vertical font start with @, we will filter it out - // Not sure if it is only in Windows so filtering is on all platforms - if (name[0] == '@') - return false; + // Configuration of font encoding + static const wxFontEncoding encoding = wxFontEncoding::wxFONTENCODING_SYSTEM; - // previously detected bad font - auto it = std::lower_bound(bad.begin(), bad.end(), name); - if (it != bad.end() && *it == name) - return false; + // Identify if preview texture exists + GLuint texture_id = 0; - wxFont wx_font(wxFontInfo().FaceName(name).Encoding(coding)); - // Faster chech if wx_font is loadable but not 100% - // names could contain not loadable font - if (!wx_font.IsOk()) - return false; + // protection for open too much font files together + // Gtk:ERROR:../../../../gtk/gtkiconhelper.c:494:ensure_surface_for_gicon: assertion failed (error == NULL): Failed to load + // /usr/share/icons/Yaru/48x48/status/image-missing.png: Error opening file /usr/share/icons/Yaru/48x48/status/image-missing.png: Too + // many open files (g-io-error-quark, 31) This variable must exist until no CreateFontImageJob is running + unsigned int count_opened_font_files = 0; - if (!can_load(wx_font)) - return false; + // Configuration for texture height + const int count_cached_textures = 32; + // index for new generated texture index(must be lower than count_cached_textures) + size_t texture_index = 0; + + // hash created from enumerated font from OS + // check when new font was installed + size_t hash = 0; + + // filtration pattern + // std::string search = ""; + // std::vector hide; // result of filtration +}; +struct GLGizmoText::Facenames : public CurFacenames +{}; + +bool store(const CurFacenames &facenames); +bool load(CurFacenames &facenames,const std::vector& delete_bad_font_list); +void init_face_names(CurFacenames &face_names); +void init_truncated_names(CurFacenames &face_names, float max_width); +std::optional get_installed_face_name(const std::optional &face_name_opt, CurFacenames &face_names); +void draw_font_preview(FaceName &face, const std::string &text, CurFacenames &faces, const CurGuiCfg &cfg, bool is_visible); +void init_text_lines(TextLinesModel &text_lines, const Selection &selection, /* const*/ StyleManager &style_manager, unsigned count_lines = 0); +class TextDataBase : public DataBase +{ +public: + TextDataBase(DataBase &&parent, const FontFileWithCache &font_file, TextConfiguration &&text_configuration, const EmbossProjection &projection) + : DataBase(std::move(parent)), m_font_file(font_file) /* copy */, m_text_configuration(std::move(text_configuration)) + { + assert(m_font_file.has_value()); + shape.projection = projection; // copy + + const FontProp &fp = m_text_configuration.style.prop; + const FontFile &ff = *m_font_file.font_file; + shape.scale = get_text_shape_scale(fp, ff); + } + // Create shape from text + font configuration + EmbossShape &create_shape() override; + void write(ModelVolume &volume) const override; + TextConfiguration get_text_configuration() override { + return m_text_configuration; + } + /// + /// Used only with text for embossing per glyph. + /// Create text lines only for new added volume to object + /// otherwise textline is already setted before + /// + /// Embossed volume final transformation in object + /// Volumes to be sliced to text lines + /// True on succes otherwise False(Per glyph shoud be disabled) + //bool create_text_lines(const Transform3d &tr, const ModelVolumePtrs &vols) override; + +private: + // Keep pointer on Data of font (glyph shapes) + FontFileWithCache m_font_file; + // font item is not used for create object + TextConfiguration m_text_configuration; +}; + +void TextDataBase::write(ModelVolume &volume) const +{ + //DataBase::write(volume); + volume.set_text_configuration(m_text_configuration);// volume.text_configuration = m_text_configuration; // copy + // Fix for object: stored attribute that volume is embossed per glyph when it is object + if (m_text_configuration.style.prop.per_glyph && volume.is_the_only_one_part()) { + volume.get_text_configuration().style.prop.per_glyph = false; + } +} +void GLGizmoText::calculate_scale() +{ + Transform3d to_world = m_parent.get_selection().get_first_volume()->world_matrix(); + auto to_world_linear = to_world.linear(); + auto calc = [&to_world_linear](const Vec3d &axe, std::optional &scale) -> bool { + Vec3d axe_world = to_world_linear * axe; + double norm_sq = axe_world.squaredNorm(); + if (is_approx(norm_sq, 1.)) { + if (scale.has_value()) + scale.reset(); + else + return false; + } else { + scale = sqrt(norm_sq); + } return true; }; - std::sort(facenames.begin(), facenames.end()); - for (const wxString &name : facenames) { - if (is_valid_font(name)) { - valid_font_names.push_back(name.ToStdString()); - } - else { - bad_fonts.emplace_back(name); - } - } - assert(std::is_sorted(bad_fonts.begin(), bad_fonts.end())); + bool exist_change = calc(Vec3d::UnitY(), m_scale_height); + exist_change |= calc(Vec3d::UnitZ(), m_scale_depth); - BOOST_LOG_TRIVIAL(info) << "init_fonts_names end"; - - return valid_font_names; + // Change of scale has to change font imgui font size + if (exist_change) + m_style_manager.clear_imgui_font(); } - -class Line_3D -{ -public: - Line_3D(Vec3d i_a, Vec3d i_b) : a(i_a), b(i_b) {} - - double length() { return (b - a).cast().norm(); } - - Vec3d vector() - { - Vec3d new_vec = b - a; - new_vec.normalize(); - return new_vec; - } - - void reverse() { std::swap(this->a, this->b); } - - Vec3d a; - Vec3d b; -}; - -class Polygon_3D -{ -public: - Polygon_3D(const std::vector &points) : m_points(points) {} - - std::vector get_lines() - { - std::vector lines; - lines.reserve(m_points.size()); - if (m_points.size() > 2) { - for (int i = 0; i < m_points.size() - 1; ++i) { lines.push_back(Line_3D(m_points[i], m_points[i + 1])); } - lines.push_back(Line_3D(m_points.back(), m_points.front())); - } - return lines; - } - std::vector m_points; -}; - -// for debug -void export_regions_to_svg(const Point &point, const Polygons &polylines) -{ - std::string path = "D:/svg_profiles/text_poly.svg"; - //BoundingBox bbox = get_extents(polylines); - SVG svg(path.c_str()); - svg.draw(polylines, "green"); - svg.draw(point, "red", 5e6); -} - -int preNUm(unsigned char byte) -{ - unsigned char mask = 0x80; - int num = 0; - for (int i = 0; i < 8; i++) { - if ((byte & mask) == mask) { - mask = mask >> 1; - num++; - } else { - break; - } - } - return num; -} - -// https://www.jianshu.com/p/a83d398e3606 -bool get_utf8_sub_strings(char *data, int len, std::vector &out_strs) -{ - out_strs.clear(); - std::string str = std::string(data); - - int num = 0; - int i = 0; - while (i < len) { - if ((data[i] & 0x80) == 0x00) { - out_strs.emplace_back(str.substr(i, 1)); - i++; - continue; - } else if ((num = preNUm(data[i])) > 2) { - int start = i; - i++; - for (int j = 0; j < num - 1; j++) { - if ((data[i] & 0xc0) != 0x80) { return false; } - i++; - } - out_strs.emplace_back(str.substr(start, i - start)); - } else { - return false; - } - } - return true; -} - /////////////////////// -/// GLGizmoText start -GLGizmoText::GLGizmoText(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) - : GLGizmoBase(parent, icon_filename, sprite_id) +class StyleNameEditDialog : public DPIDialog { +public: + StyleNameEditDialog(wxWindow * parent, + Emboss::StyleManager &style_manager, + wxWindowID id = wxID_ANY, + const wxString &title = wxEmptyString, + const wxPoint & pos = wxDefaultPosition, + const wxSize & size = wxDefaultSize, + long style = wxCLOSE_BOX | wxCAPTION); + + ~StyleNameEditDialog(); + void on_dpi_changed(const wxRect &suggested_rect) override; + + wxString get_name() const; + void set_name(const wxString &name); + void on_edit_text(wxCommandEvent &event); + void add_tip_label(); + +private: + bool check_empty_or_iillegal_character(const std::string &name); + +private: + Button * m_button_ok{nullptr}; + Button * m_button_cancel{nullptr}; + TextInput *m_name{nullptr}; + Label * m_tip{nullptr}; + bool m_add_tip{false}; + wxPanel * m_row_panel{nullptr}; + Emboss::StyleManager &m_style_manager; + wxFlexGridSizer * m_top_sizer{nullptr}; +}; +/// GLGizmoText start + +GLGizmoText::GLGizmoText(GLCanvas3D& parent, unsigned int sprite_id) + : GLGizmoBase(parent, sprite_id), + m_face_names(std::make_unique()), + m_style_manager(m_imgui->get_glyph_ranges(), create_default_styles), + m_gui_cfg(nullptr), m_rotate_gizmo(parent, GLGizmoRotate::Axis::Z) // grab id = 2 (Z axis) +{ + m_rotate_gizmo.set_group_id(0); + m_rotate_gizmo.set_force_local_coordinate(true); + + if (GUI::wxGetApp().app_config->get_bool("support_backup_fonts")) { + Slic3r::GUI::BackupFonts::generate_backup_fonts(); + } } GLGizmoText::~GLGizmoText() @@ -210,11 +440,19 @@ GLGizmoText::~GLGizmoText() bool GLGizmoText::on_init() { + m_rotate_gizmo.init(); + ColorRGBA gray_color(.6f, .6f, .6f, .3f); + m_rotate_gizmo.set_highlight_color(gray_color.get_data()); + // Set rotation gizmo upwardrotate + m_rotate_gizmo.set_angle(PI / 2); + m_init_texture = false; - m_avail_font_names = init_face_names(); - - m_thread = std::thread(&GLGizmoText::update_font_status, this); + m_style_manager.init(wxGetApp().app_config); + Emboss::StyleManager::Style &style = m_style_manager.get_style(); + std::optional installed_name = get_installed_face_name(style.prop.face_name, *m_face_names); + //m_avail_font_names = init_face_names();//todo + //m_thread = std::thread(&GLGizmoText::update_font_status, this); //m_avail_font_names = init_occt_fonts(); //update_font_texture(); m_scale = m_imgui->get_font_size(); @@ -222,9 +460,6 @@ bool GLGizmoText::on_init() reset_text_info(); - m_desc["rotate_text_caption"] = _L("Shift + Mouse move up or down"); - m_desc["rotate_text"] = _L("Rotate text"); - return true; } @@ -282,17 +517,113 @@ BoundingBoxf3 GLGizmoText::bounding_box() const const Selection::IndicesList &idxs = selection.get_volume_idxs(); for (unsigned int i : idxs) { const GLVolume *volume = selection.get_volume(i); - if (!volume->is_modifier) ret.merge(volume->transformed_convex_hull_bounding_box()); + if (!volume->is_modifier) + ret.merge(volume->transformed_convex_hull_bounding_box()); } return ret; } +#define SYSTEM_STYLE_MAX 6 +EmbossStyles GLGizmoText::create_default_styles() +{ + wxFontEnumerator::InvalidateCache(); + wxArrayString facenames = wxFontEnumerator::GetFacenames(CurFacenames::encoding); + + wxFont wx_font_normal = *wxNORMAL_FONT; +#ifdef __APPLE__ + // Set normal font to helvetica when possible + for (const wxString &facename : facenames) { + if (facename.IsSameAs("Helvetica")) { + wx_font_normal = wxFont(wxFontInfo().FaceName(facename).Encoding(Facenames::encoding)); + break; + } + } +#endif // __APPLE__ + + // https://docs.wxwidgets.org/3.0/classwx_font.html + // Predefined objects/pointers: wxNullFont, wxNORMAL_FONT, wxSMALL_FONT, wxITALIC_FONT, wxSWISS_FONT + EmbossStyles styles = { +#ifdef __APPLE__ + WxFontUtils::create_emboss_style(wx_font_normal, _u8L("Recommend")), // v2.0 version + WxFontUtils::create_emboss_style(wx_font_normal, _u8L("Old version")), // for 1.10 and 1.9 and old version +#else + WxFontUtils::create_emboss_style(wxFont(10, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD), _u8L("Recommend")), //v2.0 version + WxFontUtils::create_emboss_style(wxFont(10, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_BOLD), _u8L("Old version")), // for 1.10 and 1.9 and old version +#endif + }; + + // Not all predefined font for wx must be valid TTF, but at least one style must be loadable + styles.erase(std::remove_if(styles.begin(), styles.end(), + [](const EmbossStyle &style) { + wxFont wx_font = WxFontUtils::create_wxFont(style); + + // check that face name is setabled + if (style.prop.face_name.has_value()) { + wxString face_name = wxString::FromUTF8(style.prop.face_name->c_str()); + wxFont wx_font_temp; + if (!wx_font_temp.SetFaceName(face_name)) + return true; + } + + // Check that exsit valid TrueType Font for wx font + return WxFontUtils::create_font_file(wx_font) == nullptr; + }), + styles.end()); + + // exist some valid style? + if (!styles.empty()) + return styles; + + // No valid style in defult list + // at least one style must contain loadable font + wxFont wx_font; + for (const wxString &face : facenames) { + wx_font = wxFont(face); + if (WxFontUtils::create_font_file(wx_font) != nullptr) + break; + wx_font = wxFont(); // NotOk + } + + if (wx_font.IsOk()) { + // use first alphabetic sorted installed font + styles.push_back(WxFontUtils::create_emboss_style(wx_font, _u8L("First font"))); + } else { + // On current OS is not installed any correct TTF font + // use font packed with Slic3r + std::string font_path = Slic3r::resources_dir() + "/fonts/NotoSans-Regular.ttf"; + styles.push_back(EmbossStyle{_u8L("Default font"), font_path, EmbossStyle::Type::file_path}); + } + return styles; +} + +bool GLGizmoText::select_facename(const wxString &facename, bool update_text) +{ + if (!wxFontEnumerator::IsValidFacename(facename)) + return false; + // Select font + wxFont wx_font(wxFontInfo().FaceName(facename).Encoding(CurFacenames::encoding)); + if (!wx_font.IsOk()) + return false; +#ifdef USE_PIXEL_SIZE_IN_WX_FONT + // wx font could change source file by size of font + int point_size = static_cast(m_style_manager.get_font_prop().size_in_mm); + wx_font.SetPointSize(point_size); +#endif // USE_PIXEL_SIZE_IN_WX_FONT + if (!m_style_manager.set_wx_font(wx_font)) + return false; + if (update_text) { + process(); + } + return true; +} bool GLGizmoText::gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_position, bool shift_down, bool alt_down, bool control_down) { std::string text = std::string(m_text); if (text.empty()) return true; - + if (m_object_idx < 0) { + return true; + } const Selection &selection = m_parent.get_selection(); auto mo = selection.get_model()->objects[m_object_idx]; if (mo == nullptr) @@ -302,110 +633,38 @@ bool GLGizmoText::gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_posit const Camera & camera = wxGetApp().plater()->get_camera(); if (action == SLAGizmoEventType::Moving) { - if (shift_down && !alt_down && !control_down) { - float angle = m_rotate_angle + 0.5 * (m_mouse_position - mouse_position).y(); - if (angle == 0) - return true; - - while (angle < 0) - angle += 360; - - while (angle >= 360) - angle -= 360; - - m_rotate_angle = angle; - m_shift_down = true; - m_need_update_text = true; - } else { - m_shift_down = false; - m_origin_mouse_position = mouse_position; - } m_mouse_position = mouse_position; } else if (action == SLAGizmoEventType::LeftDown) { + if (is_only_text_case()) { + return false; + } if (!selection.is_empty() && get_hover_id() != -1) { start_dragging(); return true; } - if (m_is_modify) - return true; - - Plater *plater = wxGetApp().plater(); - if (!plater || m_thickness <= 0) - return true; - - ModelObject *model_object = selection.get_model()->objects[m_object_idx]; - if (m_preview_text_volume_id > 0) { - model_object->delete_volume(m_preview_text_volume_id); - plater->update(); - m_preview_text_volume_id = -1; - } - - // Precalculate transformations of individual meshes. - std::vector trafo_matrices; - for (const ModelVolume *mv : mo->volumes) { - if (mv->is_model_part()) { trafo_matrices.emplace_back(mi->get_transformation().get_matrix() * mv->get_matrix()); } - } - - Vec3f normal = Vec3f::Zero(); - Vec3f hit = Vec3f::Zero(); - size_t facet = 0; - Vec3f closest_hit = Vec3f::Zero(); - Vec3f closest_normal = Vec3f::Zero(); - double closest_hit_squared_distance = std::numeric_limits::max(); - int closest_hit_mesh_id = -1; - - // Cast a ray on all meshes, pick the closest hit and save it for the respective mesh - for (int mesh_id = 0; mesh_id < int(trafo_matrices.size()); ++mesh_id) { - MeshRaycaster mesh_raycaster = MeshRaycaster(mo->volumes[mesh_id]->mesh()); - - if (mesh_raycaster.unproject_on_mesh(mouse_position, trafo_matrices[mesh_id], camera, hit, normal, - m_c->object_clipper()->get_clipping_plane(), &facet)) { - // In case this hit is clipped, skip it. - if (is_mesh_point_clipped(hit.cast(), trafo_matrices[mesh_id])) - continue; - - // Is this hit the closest to the camera so far? - double hit_squared_distance = (camera.get_position() - trafo_matrices[mesh_id] * hit.cast()).squaredNorm(); - if (hit_squared_distance < closest_hit_squared_distance) { - closest_hit_squared_distance = hit_squared_distance; - closest_hit_mesh_id = mesh_id; - closest_hit = hit; - closest_normal = normal; - } - } - } - - if (closest_hit == Vec3f::Zero() && closest_normal == Vec3f::Zero()) - return true; - - m_rr = {mouse_position, closest_hit_mesh_id, closest_hit, closest_normal};//left down - - generate_text_volume(false); - m_is_modify = true; - plater->update(); } - return true; } void GLGizmoText::on_set_state() { + m_rotate_gizmo.set_state(GLGizmoBase::m_state); if (m_state == EState::On) { + m_text_tran_in_object.reset(); + m_style_manager.get_style().angle = 0; + m_last_text_mv = nullptr; - m_need_fix = false; m_show_text_normal_reset_tip = false; - load_init_text(); + load_init_text(true); if (m_last_text_mv) { m_reedit_text = true; - m_need_fix = true; m_load_text_tran_in_object = m_text_tran_in_object; if (m_really_use_surface_calc) { m_show_warning_regenerated = true; - m_need_fix = false; use_fix_normal_position(); - } else if (m_fix_old_tran_flag && m_font_version == "") { + } else if (m_fix_old_tran_flag && (m_font_version == "" || m_font_version == "1.0")) { m_show_warning_old_tran = false; auto offset = m_text_tran_in_object.get_offset(); auto rotation = m_text_tran_in_object.get_rotation(); @@ -421,24 +680,37 @@ void GLGizmoText::on_set_state() count += has_mirror ? 1 : 0; Geometry::Transformation expert_text_tran_in_world; - generate_text_tran_in_world(m_fix_text_normal_in_world.cast(), m_text_position_in_world, m_rotate_angle, expert_text_tran_in_world); - auto temp_expert_text_tran_in_object = m_object_cs_to_world_tran.inverse() * expert_text_tran_in_world.get_matrix(); + generate_text_tran_in_world(m_fix_text_normal_in_world.cast(), m_fix_text_position_in_world, m_rotate_angle, expert_text_tran_in_world); + auto temp_expert_text_tran_in_object = m_model_object_in_world_tran.get_matrix().inverse() * expert_text_tran_in_world.get_matrix(); Geometry::Transformation expert_text_tran_in_object(temp_expert_text_tran_in_object); - expert_text_tran_in_object.set_offset(m_text_tran_in_object.get_offset()); if (count >= 2) { m_show_warning_old_tran = true; } if (m_is_version1_10_xoy) { auto rotate_tran = Geometry::assemble_transform(Vec3d::Zero(), {0.5 * M_PI, 0.0, 0.0}); - m_load_text_tran_in_object.set_from_transform(m_load_text_tran_in_object.get_matrix() * rotate_tran); - m_load_text_tran_in_object.set_offset(m_load_text_tran_in_object.get_offset() + Vec3d(0, 1.65, 0)); // for size 16 + m_text_tran_in_object.set_from_transform(m_load_text_tran_in_object.get_matrix() * rotate_tran); + m_text_tran_in_object.set_offset(m_load_text_tran_in_object.get_offset() + Vec3d(0, 1.65, 0)); // for size 16 + + m_text_normal_in_world = m_fix_text_normal_in_world; + update_cut_plane_dir(); + return; + } else if (m_is_version1_8_yoz) {//Box+172x125x30_All_Bases + const Selection &selection = m_parent.get_selection(); + m_style_manager.get_style().angle = calc_angle(selection); + + auto rotate_tran = Geometry::assemble_transform(Vec3d::Zero(), {0.0, 0.0, 0.5 * M_PI}); + m_text_tran_in_object.set_from_transform(m_load_text_tran_in_object.get_matrix() * rotate_tran); + update_cut_plane_dir(); + m_text_tran_in_object.set_offset(expert_text_tran_in_object.get_offset()); return; } //go on if (has_rotation && m_show_warning_old_tran == false) { m_show_warning_lost_rotate = true; - m_need_fix = false; + if (m_is_version1_9_xoz) { + expert_text_tran_in_object.set_rotation(rotation); + } use_fix_normal_position(); } //not need set set_rotation//has_rotation @@ -448,50 +720,695 @@ void GLGizmoText::on_set_state() if (has_mirror) { expert_text_tran_in_object.set_mirror(mirror); } - m_load_text_tran_in_object.set_from_transform(expert_text_tran_in_object.get_matrix()); - if (m_is_version1_9_xoz) { - m_load_text_tran_in_object.set_offset(m_load_text_tran_in_object.get_offset()); - } + m_text_tran_in_object.set_from_transform(expert_text_tran_in_object.get_matrix()); + update_cut_plane_dir(); } } } else if (m_state == EState::Off) { ImGui::FocusWindow(nullptr);//exit cursor + m_trafo_matrices.clear(); m_reedit_text = false; m_fix_old_tran_flag = false; + m_warning_font = false; close_warning_flag_after_close_or_drag(); reset_text_info(); - delete_temp_preview_text_volume(); + m_parent.use_slope(false); m_parent.toggle_model_objects_visibility(true); + + m_style_manager.store_styles_to_app_config(false); } } -void GLGizmoText::check_text_type(bool is_surface_text, bool is_keep_horizontal) { - if (is_surface_text && is_keep_horizontal) { - m_text_type = TextType::SURFACE_HORIZONAL; - } else if (is_surface_text) { - m_text_type = TextType::SURFACE; - } else if (is_keep_horizontal) { - m_text_type = TextType::HORIZONAL; - } else { - m_text_type = TextType::HORIZONAL; +void GLGizmoText::load_old_font() { + const int old_font_index = 1; + const StyleManager::Style &style = m_style_manager.get_styles()[old_font_index]; + // create copy to be able do fix transformation only when successfully load style + if (m_style_manager.load_style(old_font_index)) { + if (m_italic && m_thickness) { + m_style_manager.get_font_prop().size_in_mm = m_font_size * 0.92; + } + else if (m_italic){ + m_style_manager.get_font_prop().size_in_mm = m_font_size * 0.98; + } + else if (m_thickness) { + m_style_manager.get_font_prop().size_in_mm = m_font_size * 0.93; + } else { + m_style_manager.get_font_prop().size_in_mm = m_font_size * 1.0; + } + wxString font_name(m_font_name); + bool update_text = !m_is_serializing ? true : false; + if (!select_facename(font_name, update_text)) { + wxString font_name("Arial"); + select_facename(font_name, update_text); + } } } +wxString FindLastName(const wxString &input) +{ + int lastSemicolonPos = input.Find(';', true); + if (lastSemicolonPos == wxNOT_FOUND) { return input; } + return input.Mid(lastSemicolonPos + 1); +} + +void GLGizmoText::draw_style_list(float caption_size) +{ + if (!m_style_manager.is_active_font()) + return; + + const StyleManager::Style *stored_style = nullptr; + bool is_stored = m_style_manager.exist_stored_style(); + if (is_stored) + stored_style = m_style_manager.get_stored_style(); + const StyleManager::Style ¤t_style = m_style_manager.get_style(); + + bool is_changed = true; + if (stored_style) { + wxString path0((*stored_style).path.c_str(), wxConvUTF8); + wxString path1(current_style.path.c_str(), wxConvUTF8); + auto stored_font_name = FindLastName(path0); + auto current_font_name = FindLastName(path1); + is_changed = !(*stored_style == current_style && stored_font_name == current_font_name); + } + bool is_modified = is_stored && is_changed; + const float &max_style_name_width = m_gui_cfg->max_style_name_width; + std::string &trunc_name = m_style_manager.get_truncated_name(); + m_style_name = m_style_manager.get_truncated_name(); + if (trunc_name.empty()) { + // generate trunc name + std::string current_name = current_style.name; + ImGuiWrapper::escape_double_hash(current_name); + trunc_name = ImGuiWrapper::trunc(current_name, max_style_name_width); + } + ImGui::AlignTextToFramePadding(); + std::string title = _u8L("Style"); + if (m_style_manager.exist_stored_style()) + ImGui::Text("%s", title.c_str()); + else + ImGui::TextColored(ImGuiWrapper::COL_QIDI, "%s", title.c_str()); + if (ImGui::IsItemHovered()) { + m_imgui->tooltip(_u8L("Save the parameters of the current text tool as a style for easy subsequent use."), m_gui_cfg->max_tooltip_width); + } + ImGui::SameLine(caption_size); + ImGui::PushItemWidth(m_gui_cfg->input_width); + auto add_text_modify = [&is_modified](const std::string &name) { + if (!is_modified) + return name; + return name + Preset::suffix_modified(); + }; + std::optional selected_style_index; + std::string tooltip = ""; + ImGuiWrapper::push_combo_style(m_parent.get_scale()); + if (ImGui::QDTBeginCombo("##style_selector", add_text_modify(trunc_name).c_str())) { + m_style_manager.init_style_images(m_gui_cfg->max_style_image_size, m_text); + m_style_manager.init_trunc_names(max_style_name_width); + std::optional> swap_indexes; + const StyleManager::Styles & styles = m_style_manager.get_styles(); + for (const StyleManager::Style &style : styles) { + size_t index = &style - &styles.front(); + const std::string &actual_style_name = style.name; + ImGui::PushID(actual_style_name.c_str()); + bool is_selected = (index == m_style_manager.get_style_index()); + + float select_height = static_cast(m_gui_cfg->max_style_image_size.y()); + ImVec2 select_size(0.f, select_height); // 0,0 --> calculate in draw + const std::optional &img = style.image; + // allow click delete button + ImGuiSelectableFlags_ flags = ImGuiSelectableFlags_AllowItemOverlap; + if (ImGui::QDTSelectable(style.truncated_name.c_str(), is_selected, flags, select_size)) { + selected_style_index = index; + }/* else if (ImGui::IsItemHovered()) + tooltip = actual_style_name;*/ + + // reorder items + if (ImGui::IsItemActive() && !ImGui::IsItemHovered()) { + if (ImGui::GetMouseDragDelta(0).y < 0.f) { + if (index > 0) swap_indexes = {index, index - 1}; + } else if ((index + 1) < styles.size()) + swap_indexes = {index, index + 1}; + if (swap_indexes.has_value()) ImGui::ResetMouseDragDelta(); + } + + // draw style name + if (img.has_value()) { + ImGui::SameLine(max_style_name_width); + ImVec4 tint_color = ImGui::GetStyleColorVec4(ImGuiCol_Text); + ImGui::Image(img->texture_id, img->tex_size, img->uv0, img->uv1, tint_color); + } + + ImGui::PopID(); + } + if (swap_indexes.has_value()) + m_style_manager.swap(swap_indexes->first, swap_indexes->second); + ImGui::EndCombo(); + } else { + // do not keep in memory style images when no combo box open + m_style_manager.free_style_images(); + if (ImGui::IsItemHovered()) { + std::string style_name = add_text_modify(current_style.name); + tooltip = is_modified ? GUI::format(_L("Modified style \"%1%\""), current_style.name) : GUI::format(_L("Current style is \"%1%\""), current_style.name); + } + } + ImGuiWrapper::pop_combo_style(); + if (!tooltip.empty()) + m_imgui->tooltip(tooltip, m_gui_cfg->max_tooltip_width); + + // Check whether user wants lose actual style modification + if (selected_style_index.has_value() && is_modified) { + const std::string &style_name = m_style_manager.get_styles()[*selected_style_index].name; + wxString message = GUI::format_wxstr(_L("Changing style to \"%1%\" will discard current style modification.\n\nWould you like to continue anyway?"), style_name); + MessageDialog not_loaded_style_message(nullptr, message, _L("Warning"), wxICON_WARNING | wxYES | wxNO); + if (not_loaded_style_message.ShowModal() != wxID_YES) + selected_style_index.reset(); + } + + // selected style from combo box + if (selected_style_index.has_value()) { + const StyleManager::Style &style = m_style_manager.get_styles()[*selected_style_index]; + // create copy to be able do fix transformation only when successfully load style + StyleManager::Style cur_s = current_style; // copy + StyleManager::Style new_s = style; // copy + if (m_style_manager.load_style(*selected_style_index)) { + m_bold = m_style_manager.get_font_prop().boldness > 0;//for update_italic + m_italic = m_style_manager.get_font_prop().skew > 0;//for update_boldness + process(true);//, fix_transformation(cur_s, new_s, m_parent)//todo + } else { + wxString title = _L("Not valid style."); + wxString message = GUI::format_wxstr(_L("Style \"%1%\" can't be used and will be removed from a list."), style.name); + MessageDialog not_loaded_style_message(nullptr, message, title, wxOK); + not_loaded_style_message.ShowModal(); + m_style_manager.erase(*selected_style_index); + } + } + + /*ImGui::SameLine(); + draw_style_rename_button();*/ + + ImGui::SameLine(); + draw_style_save_button(is_modified); + + ImGui::SameLine(); + draw_style_add_button(is_modified); + + // delete button + ImGui::SameLine(); + draw_delete_style_button(); +} + +void GLGizmoText::draw_style_save_button(bool is_modified) +{ + if (draw_button(m_icons, IconType::save, !is_modified)) { + // save styles to app config + m_style_manager.store_styles_to_app_config(); + } else if (ImGui::IsItemHovered()) { + std::string tooltip; + if (!m_style_manager.exist_stored_style()) { + tooltip = _u8L("First Add style to list."); + } else if (is_modified) { + tooltip = GUI::format(_L("Save %1% style"), m_style_manager.get_style().name); + } + if (!tooltip.empty()) { + m_imgui->tooltip(tooltip, m_gui_cfg->max_tooltip_width); + } + } +} + +bool draw_text_clickable(const IconManager::VIcons &icons, IconType type) +{ + return clickable(get_icon(icons, type, IconState::activable), get_icon(icons, type, IconState::hovered)); +} + +bool reset_text_button(const IconManager::VIcons &icons,int pos =-1) +{ + ImGui::SameLine(pos == -1 ? ImGui::GetStyle().WindowPadding.x : pos); + // from GLGizmoCut + // std::string label_id = "neco"; + // std::string btn_label; + // btn_label += ImGui::RevertButton; + // return ImGui::Button((btn_label + "##" + label_id).c_str()); + return draw_text_clickable(icons, IconType::undo); +} + +void GLGizmoText::draw_style_save_as_popup() +{ + ImGuiWrapper::text_colored(ImGuiWrapper::COL_WINDOW_BG_DARK, _u8L("New name of style") + ": "); + //use name inside of volume configuration as temporary new name + + std::string& new_name = m_style_new_name; // text_volume->get_text_configuration()->style.name;//QDS modify + bool is_unique = m_style_manager.is_unique_style_name(new_name); + bool allow_change = false; + if (new_name.empty()) { + ImGuiWrapper::text_colored(ImGuiWrapper::COL_WINDOW_BG_DARK, _u8L("Name can't be empty.")); + } else if (!is_unique) { + ImGuiWrapper::text_colored(ImGuiWrapper::COL_WINDOW_BG_DARK, _u8L("Name has to be unique.")); + } else { + allow_change = true; + } + + bool save_style = false; + ImGuiInputTextFlags flags = ImGuiInputTextFlags_EnterReturnsTrue; + if (ImGui::InputText("##save as style", &new_name, flags)) + save_style = true; + if (m_imgui->button(_L("OK"), ImVec2(0.f, 0.f), allow_change)) + save_style = true; + + ImGui::SameLine(); + if (ImGui::Button(_u8L("Cancel").c_str())) { + // write original name to volume TextConfiguration + //new_name = m_style_manager.get_style().name; + ImGui::CloseCurrentPopup(); + } + + if (save_style && allow_change) { + m_style_manager.add_style(new_name); + m_style_manager.store_styles_to_app_config(); + ImGui::CloseCurrentPopup(); + } +} + +void GLGizmoText::draw_style_add_button(bool is_modified) +{ + bool only_add_style = !m_style_manager.exist_stored_style(); + bool can_add = true; + //auto text_volume = m_last_text_mv; // m_volume + if (only_add_style)//&& text_volume->get_text_configuration().has_value() && text_volume->get_text_configuration()->style.type != WxFontUtils::get_current_type() + can_add = false; + + std::string title = _u8L("Save as new style"); + const char *popup_id = title.c_str(); + // save as new style + ImGui::SameLine(); + if (draw_button(m_icons, IconType::add, !can_add)) { + if (!m_style_manager.exist_stored_style()) { + m_style_manager.store_styles_to_app_config(wxGetApp().app_config); + } else { + if (is_modified) { + MessageDialog msg(wxGetApp().plater(), _L("The current style has been modified but not saved. Do you want to save it?"), _L("Save current style"), + wxICON_WARNING | wxOK | wxCANCEL); + auto result = msg.ShowModal(); + if (result == wxID_OK) { + m_style_manager.store_styles_to_app_config(); + } + } + StyleNameEditDialog dlg(wxGetApp().plater(), m_style_manager, wxID_ANY, _L("Add new style")); + //m_is_adding_new_style = 5; // about 100 ms,for delay to avoid deselect_all(); + auto result = dlg.ShowModal(); + if (result == wxID_YES) { + wxString style_name = dlg.get_name(); + m_style_manager.add_style(style_name.utf8_string()); + m_style_manager.store_styles_to_app_config(); + } + } + } else if (ImGui::IsItemHovered()) { + if (!can_add) { + m_imgui->tooltip(_u8L("Only valid font can be added to style"), m_gui_cfg->max_tooltip_width); + } else if (only_add_style) { + m_imgui->tooltip(_u8L("Add style to my list"), m_gui_cfg->max_tooltip_width); + } else { + m_imgui->tooltip(_u8L("Add new style"), m_gui_cfg->max_tooltip_width); + } + } +} + +void GLGizmoText::draw_delete_style_button() +{ + bool is_stored = m_style_manager.exist_stored_style(); + bool is_last = m_style_manager.get_styles().size() == 1; + bool can_delete = is_stored && !is_last; + if (draw_button(m_icons, IconType::erase, !can_delete)) { + std::string style_name = m_style_manager.get_style().name; // copy + wxString dialog_title = _L("Remove style"); + size_t next_style_index = std::numeric_limits::max(); + Plater * plater = wxGetApp().plater(); + bool exist_change = false; + while (true) { + // NOTE: can't use previous loaded activ index -> erase could change index + size_t active_index = m_style_manager.get_style_index(); + next_style_index = (active_index > 0) ? active_index - 1 : active_index + 1; + + if (next_style_index >= m_style_manager.get_styles().size()) { + MessageDialog msg(plater, _L("Can't remove the last existing style."), dialog_title, wxICON_ERROR | wxOK); + msg.ShowModal(); + break; + } + + // IMPROVE: add function can_load? + // clean unactivable styles + if (!m_style_manager.load_style(next_style_index)) { + m_style_manager.erase(next_style_index); + exist_change = true; + continue; + } + + wxString message = GUI::format_wxstr(_L("Are you sure you want to permanently remove the \"%1%\" style?"), style_name); + MessageDialog msg(plater, message, dialog_title, wxICON_WARNING | wxYES | wxNO); + if (msg.ShowModal() == wxID_YES) { + // delete style + m_style_manager.erase(active_index); + exist_change = true; + process(); + } else { + // load back style + m_style_manager.load_style(active_index); + } + break; + } + if (exist_change) m_style_manager.store_styles_to_app_config(wxGetApp().app_config); + } + + if (ImGui::IsItemHovered()) { + const std::string &style_name = m_style_manager.get_style().name; + std::string tooltip; + if (can_delete) + tooltip = GUI::format(_L("Delete \"%1%\" style."), style_name); + else if (is_last) + tooltip = GUI::format(_L("Can't delete \"%1%\". It is last style."), style_name); + else /*if(!is_stored)*/ + tooltip = GUI::format(_L("Can't delete temporary style \"%1%\"."), style_name); + m_imgui->tooltip(tooltip, m_gui_cfg->max_tooltip_width); + } +} + +void GLGizmoText::update_boldness() +{ + std::optional &boldness = m_style_manager.get_font_prop().boldness; + if (m_bold) { + set_default_boldness(boldness); + } else { + boldness = 0.0f; + } +} + +void GLGizmoText::update_italic() +{ + std::optional &skew = m_style_manager.get_font_prop().skew; + if (m_italic) { + skew = 0.2f; + } else { + skew = 0.0f; + } +} + +void GLGizmoText::set_default_boldness(std::optional &boldness) +{ + const FontFile & ff = *m_style_manager.get_font_file_with_cache().font_file; + const FontProp & fp = m_style_manager.get_font_prop(); + const FontFile::Info &font_info = get_font_info(ff, fp); + boldness = font_info.ascent / 4.f; +} + +void GLGizmoText::draw_model_type(int caption_width) +{ + ImGui::AlignTextToFramePadding(); + auto text_volume = m_last_text_mv; // m_volume + bool is_last_solid_part = text_volume->is_the_only_one_part(); + std::string title = _u8L("Operation"); + if (is_last_solid_part) { + ImVec4 color{.5f, .5f, .5f, 1.f}; + m_imgui->text_colored(color, title.c_str()); + } else { + ImGui::Text("%s", title.c_str()); + } + + std::optional new_type; + ModelVolumeType modifier = ModelVolumeType::PARAMETER_MODIFIER; + ModelVolumeType negative = ModelVolumeType::NEGATIVE_VOLUME; + ModelVolumeType part = ModelVolumeType::MODEL_PART; + ModelVolumeType type = text_volume->type(); + ImGui::SameLine(caption_width); + // TRN EmbossOperation + ImGuiWrapper::push_radio_style(); + if (ImGui::RadioButton(_u8L("Part").c_str(), type == part)) + new_type = part; + else if (ImGui::IsItemHovered()) + m_imgui->tooltip(_L("Click to change text into object part."), m_gui_cfg->max_tooltip_width); + ImGui::SameLine(); + + auto last_solid_part_hint = _L("You can't change a type of the last solid part of the object."); + if (ImGui::RadioButton(_CTX_utf8(L_CONTEXT("Cut", "EmbossOperation"), "EmbossOperation").c_str(), type == negative)) + new_type = negative; + else if (ImGui::IsItemHovered()) { + if (is_last_solid_part) + m_imgui->tooltip(last_solid_part_hint, m_gui_cfg->max_tooltip_width); + else if (type != negative) + m_imgui->tooltip(_L("Click to change part type into negative volume."), m_gui_cfg->max_tooltip_width); + } + + // In simple mode are not modifiers + if (wxGetApp().plater()->printer_technology() != ptSLA) { + ImGui::SameLine(); + if (ImGui::RadioButton(_u8L("Modifier").c_str(), type == modifier)) + new_type = modifier; + else if (ImGui::IsItemHovered()) { + if (is_last_solid_part) + m_imgui->tooltip(last_solid_part_hint, m_gui_cfg->max_tooltip_width); + else if (type != modifier) + m_imgui->tooltip(_L("Click to change part type into modifier."), m_gui_cfg->max_tooltip_width); + } + } + ImGuiWrapper::pop_radio_style(); + + if (text_volume != nullptr && new_type.has_value() && !is_last_solid_part) { + GUI_App & app = wxGetApp(); + Plater * plater = app.plater(); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Change Text Type", UndoRedo::SnapshotType::GizmoAction); + + text_volume->set_type(*new_type); + + bool is_volume_move_inside = (type == part); + bool is_volume_move_outside = (*new_type == part); + // Update volume position when switch (from part) or (into part) + if ((is_volume_move_inside || is_volume_move_outside)) process(); + + // inspiration in ObjectList::change_part_type() + // how to view correct side panel with objects + ObjectList * obj_list = app.obj_list(); + wxDataViewItemArray sel = obj_list->reorder_volumes_and_get_selection(obj_list->get_selected_obj_idx(), + [volume = text_volume](const ModelVolume *vol) { return vol == volume; }); + if (!sel.IsEmpty()) obj_list->select_item(sel.front()); + + // NOTE: on linux, function reorder_volumes_and_get_selection call GLCanvas3D::reload_scene(refresh_immediately = false) + // which discard m_volume pointer and set it to nullptr also selection is cleared so gizmo is automaticaly closed + auto &mng = m_parent.get_gizmos_manager(); + if (mng.get_current_type() != GLGizmosManager::Text) { + mng.open_gizmo(GLGizmosManager::Text);//Operation like NEGATIVE_VOLUME + } + } +} + +void GLGizmoText::draw_surround_type(int caption_width) +{ + if ((!m_last_text_mv) ||m_last_text_mv->is_the_only_one_part()) { + return; + } + auto label_width = caption_width; + float cur_cap = m_imgui->calc_text_size(_L("Surround projection by char")).x; + auto item_width = cur_cap * 1.2 + ImGui::GetStyle().FramePadding.x * 18.0f; + ImGui::AlignTextToFramePadding(); + size_t selection_idx = int(m_surface_type); + std::vector modes = {_u8L("Not surround") , _u8L("Surround surface"), _u8L("Surround") + "+" + _u8L("Horizonal")}; + if (m_rr.mesh_id < 0) { + modes.erase(modes.begin() + 2); + modes.erase(modes.begin() + 1); + m_surface_type = TextInfo::TextType ::HORIZONAL; + selection_idx = int(m_surface_type); + } + else { + modes.push_back(_u8L("Surround projection by char")); + } + bool is_changed = false; + + ImGuiWrapper::push_combo_style(m_parent.get_scale()); + if (render_combo(_u8L("Mode"), modes, selection_idx, label_width, item_width)) { + switch_text_type((TextInfo::TextType) selection_idx); + } + ImGuiWrapper::pop_combo_style(); +} + +void GLGizmoText::update_style_angle(ModelVolume *text_volume, float init_angle_degree, float roate_angle) +{ + double angle_rad = Geometry::deg2rad(roate_angle); + Geometry::to_range_pi_pi(angle_rad); + + if (text_volume) { + double diff_angle = angle_rad - Geometry::deg2rad(init_angle_degree); + + do_local_z_rotate(m_parent.get_selection(), diff_angle); + + const Selection &selection = m_parent.get_selection(); + const GLVolume * gl_volume = get_selected_gl_volume(selection); + // m_text_tran_in_object.set_from_transform(gl_volume->get_volume_transformation().get_matrix()); + // m_need_update_text = true; + // calc angle after rotation + m_style_manager.get_style().angle = calc_angle(m_parent.get_selection()); + } else { + m_style_manager.get_style().angle = angle_rad; + } +} + +void GLGizmoText::draw_rotation(int caption_size, int slider_width, int drag_left_width, int slider_icon_width) +{ + auto text_volume = m_last_text_mv; + if (!text_volume) { + return; + } + ImGui::AlignTextToFramePadding(); + m_imgui->text(_L("Angle")); + // Reset button + bool is_reseted = false; + if (abs(m_rotate_angle) > 0.01) { + if (reset_text_button(m_icons, m_gui_cfg->input_offset - m_gui_cfg->icon_width) && text_volume) { + do_local_z_rotate(m_parent.get_selection(), Geometry::deg2rad(-m_rotate_angle)); + m_rotate_angle = 0; + // recalculate for surface cut + /*if (m_volume->emboss_shape->projection.use_surface) + process_job();*/ + is_reseted = true; + } else if (ImGui::IsItemHovered()) { + m_imgui->tooltip(_L("Reset rotation"), m_gui_cfg->max_tooltip_width); + } + } + ImGui::SameLine(caption_size); + ImGui::PushItemWidth(slider_width); + + float angle_degree = get_angle_from_current_style(); + m_rotate_angle = angle_degree; + if (m_imgui->qdt_slider_float_style("##angle", &m_rotate_angle, -180.f, 180.f, "%.2f", 1.0f, true ,_L("Rotate the text counterclockwise."))) { + update_style_angle(text_volume, angle_degree, m_rotate_angle); + } + ImGui::SameLine(drag_left_width); + ImGui::PushItemWidth(1.5 * slider_icon_width); + bool set_rotate_angle_flag = false; + if (ImGui::QDTDragFloat("##angle_input", &m_rotate_angle, 0.05f, -180.f, 180.f, "%.2f")) { + set_rotate_angle_flag = true; + update_style_angle(text_volume, angle_degree, m_rotate_angle); + } + + bool is_stop_sliding = m_imgui->get_last_slider_status().deactivated_after_edit; + if (text_volume) { // Apply rotation on model (backend) + if (is_stop_sliding || is_reseted || set_rotate_angle_flag) { + m_need_update_tran = true; + m_parent.do_rotate(""); + const Selection &selection = m_parent.get_selection(); + const GLVolume * gl_volume = get_selected_gl_volume(selection); + m_text_tran_in_object.set_from_transform(gl_volume->get_volume_transformation().get_matrix()); // on_stop_dragging//rotate//set m_text_tran_in_object + volume_transformation_changed(); + } + } + + // Keep up - lock button icon + /*if (!text_volume->is_the_only_one_part()) { + ImGui::SameLine(m_gui_cfg->lock_offset); + const IconManager::Icon &icon = get_icon(m_icons, m_keep_up ? IconType::lock : IconType::unlock, IconState::activable); + const IconManager::Icon &icon_hover = get_icon(m_icons, m_keep_up ? IconType::lock : IconType::unlock, IconState::hovered); + if (button(icon, icon_hover, icon)) m_keep_up = !m_keep_up; + if (ImGui::IsItemHovered()) { + m_imgui->tooltip(_L("Lock/unlock rotation angle when dragging above the surface."), m_gui_cfg->max_tooltip_width); + } + }*/ +} + +std::unique_ptr GLGizmoText::create_emboss_data_base( + const std::string &text, Emboss::StyleManager &style_manager, const Selection &selection, ModelVolumeType type, std::shared_ptr> &cancel) +{ + // create volume_name + std::string volume_name = text; // copy + // contain_enter? + if (volume_name.find('\n') != std::string::npos) + // change enters to space + std::replace(volume_name.begin(), volume_name.end(), '\n', ' '); + + if (!style_manager.is_active_font()) { + style_manager.load_valid_style(); + assert(style_manager.is_active_font()); + if (!style_manager.is_active_font()) return {}; // no active font in style, should never happend !!! + } + + StyleManager::Style &style = style_manager.get_style(); // copy + // actualize font path - during changes in gui it could be corrupted + // volume must store valid path + assert(style_manager.get_wx_font().IsOk()); + assert(style.path.compare(WxFontUtils::store_wxFont(style_manager.get_wx_font())) == 0); + + bool is_outside = (type == ModelVolumeType::MODEL_PART); + + // Cancel previous Job, when it is in process + // worker.cancel(); --> Use less in this case I want cancel only previous EmbossJob no other jobs + // Cancel only EmbossUpdateJob no others + if (cancel != nullptr) + cancel->store(true); + // create new shared ptr to cancel new job + cancel = std::make_shared>(false); + + DataBase base(volume_name, cancel); + style.projection.depth = m_thickness; // QDS add + style.projection.embeded_depth = m_embeded_depth; // QDS add + base.is_outside = is_outside; + // base.text_lines = text_lines.get_lines(); + base.from_surface = style.distance; + + FontFileWithCache &font = style_manager.get_font_file_with_cache(); + TextConfiguration tc{static_cast(style), text}; + auto td_ptr = std::make_unique(std::move(base), font, std::move(tc), style.projection); + return td_ptr; +} + +bool GLGizmoText::process(bool make_snapshot, std::optional volume_transformation, bool update_text) +{ + auto text_volume = m_last_text_mv;//m_volume + //if (text_volume == nullptr) // m_volume + // return false; + // without text there is nothing to emboss + if (is_text_empty(m_text)) + return false; + // exist loaded font file? + if (!m_style_manager.is_active_font()) + return false; + update_boldness(); + update_italic(); + if (update_text) { + m_need_update_text = true; + } + //if (!start_update_volume(std::move(data), *m_volume, selection, m_raycast_manager)) + // return false; + // notification is removed befor object is changed by job + //remove_notification_not_valid_font(); + return true; +} + +ModelVolume *GLGizmoText::get_text_is_dragging() +{ + auto mv = get_selected_model_volume(m_parent); + if (mv && mv->is_text() && get_is_dragging()) { + return mv; + } + return nullptr; +} + +bool GLGizmoText::get_is_dragging() { + return m_dragging; +} + +bool GLGizmoText::get_selection_is_text() +{ + auto mv = get_selected_model_volume(m_parent); + if (mv && mv->is_text()) { + return true; + } + return false; +} + void GLGizmoText::generate_text_tran_in_world(const Vec3d &text_normal_in_world, const Vec3d &text_position_in_world,float rotate_degree, Geometry::Transformation &cur_tran) { - Vec3d temp_normal = text_normal_in_world; - Vec3d cut_plane_in_world = Vec3d::UnitY(); - double epson = 1e-6; - if (!(abs(temp_normal.x()) <= epson && abs(temp_normal.y()) <= epson && abs(temp_normal.z()) > epson)) { // temp_normal != Vec3d::UnitZ() - Vec3d v_plane = temp_normal.cross(Vec3d::UnitZ()); - cut_plane_in_world = v_plane.cross(temp_normal); - } + Vec3d temp_normal = text_normal_in_world.normalized(); + Vec3d cut_plane_in_world = Slic3r::Emboss::suggest_up(text_normal_in_world, UP_LIMIT); + Vec3d z_dir = text_normal_in_world; Vec3d y_dir = cut_plane_in_world; Vec3d x_dir_world = y_dir.cross(z_dir); - if (m_text_type == TextType::SURFACE_HORIZONAL && text_normal_in_world != Vec3d::UnitZ()) { + if (m_surface_type == TextInfo::TextType::SURFACE_HORIZONAL && text_normal_in_world != Vec3d::UnitZ()) { y_dir = Vec3d::UnitZ(); x_dir_world = y_dir.cross(z_dir); z_dir = x_dir_world.cross(y_dir); @@ -502,96 +1419,389 @@ void GLGizmoText::generate_text_tran_in_world(const Vec3d &text_normal_in_world, cur_tran.set_matrix(temp_tran.get_matrix() * rotate_trans.get_matrix()); } +bool GLGizmoText::on_shortcut_key() { + reset_text_info(); + Selection &selection = m_parent.get_selection(); + m_text = "Text"; + m_is_direct_create_text = selection.is_empty(); + auto mv = get_selected_model_volume(m_parent); + if (mv && mv->is_text()) {//not need to generate text,to edit text + Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Edit existed text"); + return false; + } + if (process(false, std::nullopt, false)) { + CreateTextInput temp_input_info; + DataBasePtr base = create_emboss_data_base(m_text, m_style_manager, selection, ModelVolumeType::MODEL_PART, m_job_cancel); + temp_input_info.base = std::move(base); + temp_input_info.text_info = get_text_info(); + if (m_is_direct_create_text) { + auto job = std::make_unique(std::move(temp_input_info)); + auto &worker = wxGetApp().plater()->get_ui_job_worker(); + queue_job(worker, std::move(job)); + } else { + int object_idx = selection.get_object_idx(); + Size s = m_parent.get_canvas_size(); + Vec2d screen_center(s.get_width() / 2., s.get_height() / 2.); + const ModelObjectPtrs & objects = selection.get_model()->objects; + m_object_idx = object_idx; + Vec2d coor; + const Camera &camera = wxGetApp().plater()->get_camera(); + auto finde_gl_volume = find_glvoloume_render_screen_cs(selection, screen_center, camera, objects, &coor); + if (finde_gl_volume != nullptr && object_idx >= 0) { + int temp_object_idx; + auto mo = selection.get_selected_single_object(temp_object_idx); + update_trafo_matrices(); + m_c->update(get_requirements()); + if (m_trafo_matrices.size() > 0 && update_raycast_cache(coor, camera, m_trafo_matrices,false) && m_rr.mesh_id >= 0) { + auto hit_pos = m_trafo_matrices[m_rr.mesh_id] * m_rr.hit.cast(); + Geometry::Transformation tran(m_trafo_matrices[m_rr.mesh_id]); + auto hit_normal = (tran.get_matrix_no_offset() * m_rr.normal.cast()).normalized(); + Transform3d surface_trmat = create_transformation_onto_surface(hit_pos, hit_normal, UP_LIMIT); + mv = mo->volumes[m_rr.mesh_id]; + if (mv) { + auto instance = mo->instances[m_parent.get_selection().get_instance_idx()]; + Transform3d transform = instance->get_matrix().inverse() * surface_trmat; + + m_text_tran_in_object.set_from_transform(transform); + m_text_position_in_world = hit_pos; + m_text_normal_in_world = hit_normal.cast(); + m_need_update_text = true; + m_volume_idx = -1; + if (generate_text_volume()) { // first on surface + return true; + } + } + } else { + ModelObject * mo = objects[object_idx]; + BoundingBoxf3 instance_bb; + size_t instance_index = selection.get_instance_idx(); + instance_bb = mo->instance_bounding_box(instance_index); + // Vec3d volume_size = volume->mesh().bounding_box().size(); + // Translate the new modifier to be pickable: move to the left front corner of the instance's bounding box, lift to print bed. + Vec3d offset_tr(0, -instance_bb.size().y() / 2.f - 2.f, -instance_bb.size().z() / 2.f); // lay on bed + auto mo_tran = mo->instances[instance_index]->get_transformation(); + + m_text_tran_in_object.reset(); + m_text_tran_in_object.set_offset(offset_tr); + m_text_position_in_world = mo_tran.get_matrix() * offset_tr; + m_text_normal_in_world = Vec3f::UnitZ(); + m_need_update_text = true; + m_volume_idx = -1; + m_surface_type = TextInfo::TextType ::HORIZONAL; + if (generate_text_volume()) { // first on surface + return true; + } + } + } + m_is_direct_create_text = true; + auto job = std::make_unique(std::move(temp_input_info)); + auto &worker = wxGetApp().plater()->get_ui_job_worker(); + queue_job(worker, std::move(job)); + } + } else { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "check error"; + } + return true; +} + +bool GLGizmoText::is_only_text_case() const { + return m_last_text_mv && m_last_text_mv->is_the_only_one_part(); +} + +void GLGizmoText::close() +{ + auto &mng = m_parent.get_gizmos_manager(); + if (mng.get_current_type() == GLGizmosManager::Text) + mng.open_gizmo(GLGizmosManager::Text); +} + +std::string GLGizmoText::get_icon_filename(bool b_dark_mode) const +{ + return b_dark_mode ? "toolbar_text_dark.svg" : "toolbar_text.svg"; +} + void GLGizmoText::use_fix_normal_position() { m_text_normal_in_world = m_fix_text_normal_in_world; m_text_position_in_world = m_fix_text_position_in_world; } -void GLGizmoText::load_init_text() +void GLGizmoText::load_init_text(bool first_open_text) { Plater *plater = wxGetApp().plater(); - if (m_parent.get_selection().is_single_volume() || m_parent.get_selection().is_single_modifier()) { - ModelVolume *model_volume = m_parent.get_selection().get_selected_single_volume(m_object_idx, m_volume_idx); + Selection &selection = m_parent.get_selection(); + + if (selection.is_single_full_instance() || selection.is_single_full_object()) { + const GLVolume *gl_volume = selection.get_volume(*selection.get_volume_idxs().begin()); + int object_idx = gl_volume->object_idx(); + if (get_selection_is_text()) { + m_object_idx = object_idx; + m_volume_idx = gl_volume->volume_idx(); + } else { + if (object_idx != m_object_idx || (object_idx == m_object_idx && m_volume_idx != -1)) { + m_object_idx = object_idx; + m_volume_idx = -1; + reset_text_info(); + } + } + } else if (selection.is_single_volume_or_modifier()) { + int object_idx, volume_idx; + ModelVolume *model_volume = m_parent.get_selection().get_selected_single_volume(object_idx, volume_idx); + if ((object_idx != m_object_idx || (object_idx == m_object_idx && volume_idx != m_volume_idx)) && model_volume) { + m_volume_idx = volume_idx; + m_object_idx = object_idx; + } + } + + if (selection.is_single_volume_or_modifier() || selection.is_single_full_object()) { + auto model_volume = get_selected_model_volume(m_parent); if (model_volume) { TextInfo text_info = model_volume->get_text_info(); - if (!text_info.m_text.empty()) { - if (m_last_text_mv == model_volume) { - m_last_text_mv = model_volume; + if (model_volume->is_text()) { + if (m_last_text_mv == model_volume && !m_is_serializing) { return; } - if (plater) { + m_last_text_mv = model_volume; + m_is_direct_create_text = is_only_text_case(); + if (!m_is_serializing && plater && first_open_text && !is_old_text_info(model_volume->get_text_info())) { plater->take_snapshot("enter Text"); } auto box = model_volume->get_mesh_shared_ptr()->bounding_box(); + auto box_size = box.size(); auto valid_z = text_info.m_embeded_depth + text_info.m_thickness; - auto text_height = get_text_height(text_info.m_text); - m_really_use_surface_calc = true; - int index = -1; - for (size_t i = 0; i < 3; i++) { - if (abs(box.size()[i] - valid_z) < 0.1) { - m_really_use_surface_calc = false; - index = i; - break; - } - } - if (abs(box.size()[1] - text_height) > 0.1) { - m_fix_old_tran_flag = true; - } - m_last_text_mv = model_volume; load_from_text_info(text_info); - m_text_volume_tran = model_volume->get_matrix(); - m_text_tran_in_object.set_matrix(m_text_volume_tran); + + auto text_volume_tran = model_volume->get_matrix(); + m_text_tran_in_object.set_matrix(text_volume_tran); // load text int temp_object_idx; - auto mo = m_parent.get_selection().get_selected_single_object(temp_object_idx); - const ModelInstance *mi = mo->instances[m_parent.get_selection().get_instance_idx()]; - m_object_cs_to_world_tran = mi->get_transformation().get_matrix(); - auto world_tran = m_object_cs_to_world_tran * m_text_volume_tran; + auto mo = m_parent.get_selection().get_selected_single_object(temp_object_idx); + const ModelInstance *mi = mo->instances[m_parent.get_selection().get_instance_idx()]; + m_model_object_in_world_tran = mi->get_transformation(); + auto world_tran = m_model_object_in_world_tran.get_matrix() * text_volume_tran; m_text_tran_in_world.set_matrix(world_tran); m_text_position_in_world = m_text_tran_in_world.get_offset(); m_text_normal_in_world = m_text_tran_in_world.get_matrix().linear().col(2).cast(); - { - TriangleMesh text_attach_mesh(mo->volumes[m_rr.mesh_id]->mesh()); - text_attach_mesh.transform(mo->volumes[m_rr.mesh_id]->get_matrix()); - MeshRaycaster temp_ray_caster(text_attach_mesh); - Vec3f local_center = m_text_tran_in_object.get_offset().cast();//(m_text_tran_in_object.get_matrix() * box.center()).cast(); // - Vec3f temp_normal; - Vec3f closest_pt = temp_ray_caster.get_closest_point(local_center, &temp_normal); - m_fix_text_position_in_world = m_object_cs_to_world_tran * closest_pt.cast(); - m_fix_text_normal_in_world = (mi->get_transformation().get_matrix_no_offset().cast() * temp_normal).normalized(); - int face_id; - Vec3f direction = m_text_tran_in_world.get_matrix().linear().col(2).cast(); - if (index == 2 && abs(box.size()[1] - text_height) < 0.1) { - m_is_version1_9_xoz = true; - m_fix_old_tran_flag = true; + m_cut_plane_dir_in_world = m_text_tran_in_world.get_matrix().linear().col(1); // for horizonal text + {//recovery style + auto temp_angle = calc_angle(selection); + calculate_scale(); + + auto & tc = text_info.text_configuration; + const EmbossStyle & style = tc.style; + std::optional installed_name = get_installed_face_name(style.prop.face_name, *m_face_names); + + wxFont wx_font; + // load wxFont from same OS when font name is installed + if (style.type == WxFontUtils::get_current_type() && installed_name.has_value()) { wx_font = WxFontUtils::load_wxFont(style.path); } + // Flag that is selected same font + bool is_exact_font = true; + // Different OS or try found on same OS + if (!wx_font.IsOk()) { + is_exact_font = false; + // Try create similar wx font by FontFamily + wx_font = WxFontUtils::create_wxFont(style); + if (installed_name.has_value() && !installed_name->empty()) is_exact_font = wx_font.SetFaceName(*installed_name); + // Have to use some wxFont + if (!wx_font.IsOk()) wx_font = wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT); } - if (index == 1 && abs(box.size()[2] - text_height) < 0.1) {//for 1.10 version, xoy plane cut,just fix - m_is_version1_10_xoy = true; - direction = m_text_tran_in_world.get_matrix().linear().col(1).cast(); - if (!temp_ray_caster.get_closest_point_and_normal(local_center, direction, &closest_pt, &temp_normal, &face_id)) { - m_show_warning_error_mesh = true; + + const auto & styles = m_style_manager.get_styles(); + auto has_same_name = [&name = style.name](const StyleManager::Style &style_item) { return style_item.name == name; }; + StyleManager::Style style_{style}; // copy + style_.projection.depth = m_thickness; + style_.projection.embeded_depth = m_embeded_depth; + style_.prop.char_gap = m_text_gap; + style_.prop.size_in_mm = m_font_size; + if (temp_angle.has_value()) { style_.angle = temp_angle; } + if (auto it = std::find_if(styles.begin(), styles.end(), has_same_name); it == styles.end()) { + // style was not found + m_style_manager.load_style(style_, wx_font); + if (m_style_manager.get_styles().size() >= 2) { + auto default_style_index = 1; // + m_style_manager.load_style(default_style_index); } - } - else if (!temp_ray_caster.get_closest_point_and_normal(local_center, -direction, &closest_pt, &temp_normal, &face_id)) { - if (!temp_ray_caster.get_closest_point_and_normal(local_center, direction, &closest_pt, &temp_normal, &face_id)) { - m_show_warning_error_mesh = true; + } else { + // style name is in styles list + size_t style_index = it - styles.begin(); + if (!m_style_manager.load_style(style_index)) { + // can`t load stored style + m_style_manager.erase(style_index); + m_style_manager.load_style(style_, wx_font); + } else { + // stored style is loaded, now set modification of style + m_style_manager.get_style() = style_; + m_style_manager.set_wx_font(wx_font); } } } - // m_rr.mesh_id - m_need_update_text = false; - m_is_modify = true; + if (m_is_serializing) { // undo redo + m_style_manager.get_style().angle = calc_angle(selection); + m_rotate_angle = get_angle_from_current_style(); + m_rr.normal =Vec3f::Zero(); + update_text_tran_in_model_object(true); + return; + } + float old_text_height =0; + int old_index = -1; + if (is_old_text_info(text_info)) { + try { + old_text_height = get_text_height(text_info.m_text); // old text + } catch (const std::exception &) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " error:get_text_height"; + } + if (is_only_text_case()) { + m_really_use_surface_calc = false; + } else { + m_really_use_surface_calc = true; + } + for (size_t i = 0; i < 3; i++) { + if (abs(box_size[i] - valid_z) < 0.1) { + m_really_use_surface_calc = false; + old_index = i; + break; + } + } + if (abs(box.size()[1] - old_text_height) > 0.1) { + m_fix_old_tran_flag = true; + } + } + if (first_open_text && !is_only_text_case()) { + auto valid_mesh_id = 0; + Vec3f closest_normal; + Vec3f closest_pt; + float min_dist = 1e6; + Vec3f local_center = m_text_tran_in_object.get_offset().cast(); //(m_text_tran_in_object.get_matrix() * box.center()).cast(); + for (int i = 0; i < mo->volumes.size(); i++) { + auto mv = mo->volumes[i]; + if (mv == model_volume || !filter_model_volume(mv)) { + continue; + } + TriangleMesh text_attach_mesh(mv->mesh()); + text_attach_mesh.transform(mv->get_matrix()); + MeshRaycaster temp_ray_caster(text_attach_mesh); + Vec3f temp_normal; + Vec3f temp_closest_pt = temp_ray_caster.get_closest_point(local_center, &temp_normal); + auto dist = (temp_closest_pt - local_center).norm(); + if (min_dist > dist) { + min_dist = dist; + closest_pt = temp_closest_pt; + valid_mesh_id = i; + closest_normal = temp_normal; + } + } + if (min_dist < 1.0f) { + if (m_rr.mesh_id != valid_mesh_id) { + m_rr.mesh_id = valid_mesh_id; + } + if (m_trafo_matrices.empty()) { + update_trafo_matrices(); + } + auto mv = mo->volumes[m_rr.mesh_id]; + TriangleMesh text_attach_mesh(mv->mesh()); + text_attach_mesh.transform(mv->get_matrix()); + MeshRaycaster temp_ray_caster(text_attach_mesh); + + m_fix_text_position_in_world = m_model_object_in_world_tran.get_matrix() * closest_pt.cast(); + m_fix_text_normal_in_world = (mi->get_transformation().get_matrix_no_offset().cast() * closest_normal).normalized(); + if (is_old_text_info(text_info)) { + int face_id; + Vec3f direction = m_text_tran_in_world.get_matrix().linear().col(2).cast(); + if (old_index == 2 && abs(box_size[1] - old_text_height) < 0.1) { + m_is_version1_9_xoz = true; + m_fix_old_tran_flag = true; + } else if (old_index == 2 && abs(box_size[0] - old_text_height) < 0.1) { + m_is_version1_8_yoz = true; + m_fix_old_tran_flag = true; + } else if (old_index == 1 && abs(box_size[2] - old_text_height) < 0.1) { // for 1.10 version, xoy plane cut,just fix + m_is_version1_10_xoy = true; + m_fix_old_tran_flag = true; + direction = m_text_tran_in_world.get_matrix().linear().col(1).cast(); + if (!temp_ray_caster.get_closest_point_and_normal(local_center, direction, &closest_pt, &closest_normal, &face_id)) { + m_show_warning_error_mesh = true; + } + } else if (!temp_ray_caster.get_closest_point_and_normal(local_center, -direction, &closest_pt, &closest_normal, &face_id)) { + if (!temp_ray_caster.get_closest_point_and_normal(local_center, direction, &closest_pt, &closest_normal, &face_id)) { + m_show_warning_error_mesh = true; + } + } + } + } else { + m_surface_type = TextInfo::TextType::HORIZONAL; + } + } + if (!m_font_name.empty()) {//font version 1.10 and before + m_need_update_text = false; + } + return; } } } + if (!m_is_serializing && m_last_text_mv == nullptr) { + close(); + } } void GLGizmoText::data_changed(bool is_serializing) { - load_init_text(); + m_is_serializing = is_serializing; + load_init_text(false); + m_is_serializing = false; + if (is_only_text_case()) { + m_parent.get_gizmos_manager().set_object_located_outside_plate(true); + } else { + m_parent.get_gizmos_manager().check_object_located_outside_plate(); + } + + if (wxGetApp().plater()->is_show_text_cs()) { + m_lines_mark.reset(); + } +} + +void GLGizmoText::on_set_hover_id() +{ + m_rotate_gizmo.set_hover_id(m_hover_id); +} + +void GLGizmoText::on_enable_grabber(unsigned int id) { + m_rotate_gizmo.enable_grabber(0); +} + +void GLGizmoText::on_disable_grabber(unsigned int id) { + m_rotate_gizmo.disable_grabber(0); +} + +bool GLGizmoText::on_mouse(const wxMouseEvent &mouse_event) +{ + if (m_last_text_mv == nullptr) + return false; + if (m_hover_id != m_move_cube_id) { + if (on_mouse_for_rotation(mouse_event)) + return true; + } + return false; +} + +bool GLGizmoText::on_mouse_for_rotation(const wxMouseEvent &mouse_event) +{ + if (mouse_event.Moving()) + return false; + + bool used = use_grabbers(mouse_event); + if (!m_dragging) + return used; + + if (mouse_event.Dragging()){//m_angle means current angle + std::optional &angle_opt = m_style_manager.get_style().angle; + dragging_rotate_gizmo(m_rotate_gizmo.get_angle(), angle_opt, m_rotate_start_angle, m_parent.get_selection()); + } + + return used; } CommonGizmosDataID GLGizmoText::on_get_requirements() const { + if (m_is_direct_create_text) { + return CommonGizmosDataID(0); + } return CommonGizmosDataID( int(CommonGizmosDataID::SelectionInfo) | int(CommonGizmosDataID::InstancesHider) @@ -610,102 +1820,63 @@ std::string GLGizmoText::on_get_name() const bool GLGizmoText::on_is_activable() const { - // This is assumed in GLCanvas3D::do_rotate, do not change this - // without updating that function too. - if (m_parent.get_selection().is_single_full_instance()) - return true; - - int obejct_idx, volume_idx; - ModelVolume *model_volume = m_parent.get_selection().get_selected_single_volume(obejct_idx, volume_idx); - if (model_volume) - return !model_volume->get_text_info().m_text.empty(); - - return false; + const Selection &selection = m_parent.get_selection(); + if (selection.is_wipe_tower() || selection.is_any_connector()) + return false; + return true; } void GLGizmoText::on_render() { - glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); - glsafe(::glEnable(GL_DEPTH_TEST)); - - std::string text = std::string(m_text); - if (text.empty()) { - delete_temp_preview_text_volume(); - return; - } - + if (m_text.empty()) { return; } + if (m_object_idx < 0) { return; } + Plater *plater = wxGetApp().plater(); + if (!plater) return; ModelObject *mo = nullptr; const Selection &selection = m_parent.get_selection(); - mo = selection.get_model()->objects[m_object_idx]; + const auto p_model = selection.get_model(); + if (p_model) { + if (m_object_idx < p_model->objects.size()) { + mo = p_model->objects[m_object_idx]; + } + } if (mo == nullptr) { BOOST_LOG_TRIVIAL(info) << boost::format("Text: selected object is null"); return; } - - const Camera& camera = wxGetApp().plater()->get_camera(); + const Camera &camera = plater->get_camera(); const auto& projection_matrix = camera.get_projection_matrix(); const auto& view_matrix = camera.get_view_matrix(); // First check that the mouse pointer is on an object. const ModelInstance *mi = mo->instances[selection.get_instance_idx()]; - Plater *plater = wxGetApp().plater(); - if (!plater) - return; -#ifdef DEBUG_TEXT - if (m_text_normal_in_world.norm() > 0.1) { // debug - Geometry::Transformation tran(m_text_volume_tran); - if (tran.get_offset().norm() > 1) { - auto text_volume_tran_world = mi->get_transformation().get_matrix() * m_text_volume_tran; - render_cross_mark(text_volume_tran_world, Vec3f::Zero()); + glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); + glsafe(::glEnable(GL_DEPTH_TEST)); + if (plater->is_show_text_cs()) { + if (m_text_normal_in_world.norm() > 0.1) { // debug + Geometry::Transformation tran(m_text_tran_in_object.get_matrix()); + if (tran.get_offset().norm() > 1) { + auto text_volume_tran_world = mi->get_transformation().get_matrix() * tran.get_matrix(); + render_cross_mark(text_volume_tran_world, Vec3f::Zero(),true); + } + render_lines(GenerateTextJob::debug_cut_points_in_world); } - - render_cross_mark(m_text_tran_in_world, Vec3f::Zero()); - - glsafe(::glLineWidth(2.0f)); - ::glBegin(GL_LINES); - glsafe(::glColor3f(1.0f, 0.0f, 0.0f)); - - for (size_t i = 1; i < m_cut_points_in_world.size(); i++) {//draw points - auto target0 = m_cut_points_in_world[i - 1].cast(); - auto target1 = m_cut_points_in_world[i].cast(); - glsafe(::glVertex3f(target0(0), target0(1), target0(2))); - glsafe(::glVertex3f(target1(0), target1(1), target1(2))); - } - glsafe(::glEnd()); } -#endif - if (!m_is_modify || m_shift_down) {//for temp text - // Precalculate transformations of individual meshes. - std::vector trafo_matrices; - for (const ModelVolume *mv : mo->volumes) { - if (mv->is_model_part()) - trafo_matrices.emplace_back(mi->get_transformation().get_matrix() * mv->get_matrix()); + if (m_last_text_mv) { + if (is_only_text_case()) {//drag in parent + if ((m_text_position_in_world - mi->get_transformation().get_offset()).norm() > 0.01) { + m_text_position_in_world = mi->get_transformation().get_offset(); + m_need_update_tran = true; + update_text_tran_in_model_object(false); + } } - // Raycast and return if there's no hit. - Vec2d mouse_pos; - if (m_shift_down) { - if (m_is_modify) - mouse_pos = m_rr.mouse_position; - else - mouse_pos = m_origin_mouse_position; + if (m_draging_cube) { + } else { + m_rotate_gizmo.set_custom_tran(m_text_tran_in_world.get_matrix()); + m_rotate_gizmo.render(); } - else { - mouse_pos = m_parent.get_local_mouse_position(); - } - - bool position_changed = update_raycast_cache(mouse_pos, camera, trafo_matrices); - - if (m_rr.mesh_id == -1) { - delete_temp_preview_text_volume(); - return; - } - - if (!position_changed && !m_need_update_text && !m_shift_down) - return; - update_text_pos_normal(); } - - if (m_is_modify) { + if (!is_only_text_case()) { update_text_pos_normal(); Geometry::Transformation tran;//= m_text_tran_in_world; { @@ -730,61 +1901,90 @@ void GLGizmoText::on_render() render_glmodel(m_move_grabber.get_cube(), render_color, view_matrix * cube_mat, projection_matrix); } - delete_temp_preview_text_volume(); - - if (m_is_modify && !m_need_update_text) - return; - - generate_text_volume(); - plater->update(); + if (m_need_update_text) { + if (generate_text_volume()) { + } + } } void GLGizmoText::on_render_for_picking() { glsafe(::glDisable(GL_DEPTH_TEST)); - const auto& shader = wxGetApp().get_shader("flat"); - if (shader == nullptr) - return; - wxGetApp().bind_shader(shader); - int obejct_idx, volume_idx; - ModelVolume *model_volume = m_parent.get_selection().get_selected_single_volume(obejct_idx, volume_idx); - if (model_volume && !model_volume->get_text_info().m_text.empty()) { - const Selection &selection = m_parent.get_selection(); - auto mo = selection.get_model()->objects[m_object_idx]; - if (mo == nullptr) - return; - auto color = picking_color_component(m_move_cube_id); - m_move_grabber.color[0] = color[0]; - m_move_grabber.color[1] = color[1]; - m_move_grabber.color[2] = color[2]; - m_move_grabber.color[3] = color[3]; - m_move_grabber.render_for_picking(); + if (!m_draging_cube) { + m_rotate_gizmo.render_for_picking(); + } + if (!is_only_text_case()) { + const auto &shader = wxGetApp().get_shader("flat"); + if (shader == nullptr) return; + wxGetApp().bind_shader(shader); + int obejct_idx, volume_idx; + ModelVolume *model_volume = m_parent.get_selection().get_selected_single_volume(obejct_idx, volume_idx); + if (model_volume && !model_volume->get_text_info().m_text.empty()) { + const Selection &selection = m_parent.get_selection(); + auto mo = selection.get_model()->objects[m_object_idx]; + if (mo == nullptr) return; + auto color = picking_color_component(m_move_cube_id); + m_move_grabber.color[0] = color[0]; + m_move_grabber.color[1] = color[1]; + m_move_grabber.color[2] = color[2]; + m_move_grabber.color[3] = color[3]; + m_move_grabber.render_for_picking(); + } + wxGetApp().unbind_shader(); } - wxGetApp().unbind_shader(); } void GLGizmoText::on_start_dragging() { + if (m_hover_id < 0) { return; } + update_trafo_matrices(); + + if (m_hover_id == m_move_cube_id) { + m_draging_cube = true; + } else { + m_rotate_gizmo.start_dragging(); + } } void GLGizmoText::on_stop_dragging() { + m_draging_cube = false; + m_need_update_tran = true;//dragging + if (m_hover_id == m_move_cube_id) { + m_parent.do_move("");//replace by wxGetApp() .plater()->take_snapshot("Modify Text"); in EmbossJob.cpp + m_need_update_text = true; + } else { + m_rotate_gizmo.stop_dragging(); + // TODO: when start second rotatiton previous rotation rotate draggers + // This is fast fix for second try to rotate + // When fixing, move grabber above text (not on side) + m_rotate_gizmo.set_angle(PI / 2); + + // apply rotation + // TRN This is an item label in the undo-redo stack. + m_parent.do_rotate(""); + + const Selection &selection = m_parent.get_selection(); + const GLVolume * gl_volume = get_selected_gl_volume(selection); + m_text_tran_in_object.set_from_transform(gl_volume->get_volume_transformation().get_matrix());//on_stop_dragging//rotate//set m_text_tran_in_object + m_rotate_start_angle.reset(); + volume_transformation_changed(); + } } void GLGizmoText::on_update(const UpdateData &data) { - Vec2d mouse_pos = Vec2d(data.mouse_pos.x(), data.mouse_pos.y()); - const Selection &selection = m_parent.get_selection(); - auto mo = selection.get_model()->objects[m_object_idx]; - if (mo == nullptr) + if (m_hover_id == 0) { + m_rotate_gizmo.update(data); return; - const ModelInstance *mi = mo->instances[selection.get_instance_idx()]; - const Camera & camera = wxGetApp().plater()->get_camera(); - - std::vector trafo_matrices; - for (const ModelVolume *mv : mo->volumes) { - if (mv->is_model_part()) { trafo_matrices.emplace_back(mi->get_transformation().get_matrix() * mv->get_matrix()); } } + if (!m_c) { return; } + if (!m_c->raycaster()) { + m_c->update(get_requirements()); + } + if (m_object_idx < 0) { return; } + Vec2d mouse_pos = Vec2d(data.mouse_pos.x(), data.mouse_pos.y()); + const Camera & camera = wxGetApp().plater()->get_camera(); Vec3f normal = Vec3f::Zero(); Vec3f hit = Vec3f::Zero(); @@ -793,19 +1993,16 @@ void GLGizmoText::on_update(const UpdateData &data) Vec3f closest_normal = Vec3f::Zero(); double closest_hit_squared_distance = std::numeric_limits::max(); int closest_hit_mesh_id = -1; - + auto & trafo_matrices = m_trafo_matrices; // Cast a ray on all meshes, pick the closest hit and save it for the respective mesh + const Selection &selection = m_parent.get_selection(); + auto mo = selection.get_model()->objects[m_object_idx]; for (int mesh_id = 0; mesh_id < int(trafo_matrices.size()); ++mesh_id) { - if (mesh_id == m_volume_idx) + if (!filter_model_volume(mo->volumes[mesh_id])) { continue; - - MeshRaycaster mesh_raycaster = MeshRaycaster(mo->volumes[mesh_id]->mesh()); - - if (mesh_raycaster.unproject_on_mesh(mouse_pos, trafo_matrices[mesh_id], camera, hit, normal, m_c->object_clipper()->get_clipping_plane(), - &facet)) { - // In case this hit is clipped, skip it. - if (is_mesh_point_clipped(hit.cast(), trafo_matrices[mesh_id])) continue; - + } + if (mesh_id < m_c->raycaster()->raycasters().size() && m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh(mouse_pos, trafo_matrices[mesh_id], camera, hit, normal, + m_c->object_clipper()->get_clipping_plane(),&facet)) { // Is this hit the closest to the camera so far? double hit_squared_distance = (camera.get_position() - trafo_matrices[mesh_id] * hit.cast()).squaredNorm(); if (hit_squared_distance < closest_hit_squared_distance) { @@ -817,23 +2014,40 @@ void GLGizmoText::on_update(const UpdateData &data) } } - if (closest_hit == Vec3f::Zero() && closest_normal == Vec3f::Zero()) return; + if (closest_hit == Vec3f::Zero() && closest_normal == Vec3f::Zero()) { + m_parent.set_as_dirty(); + return; + } if (closest_hit_mesh_id != -1) { + bool is_last_attach = m_rr.mesh_id >= 0; m_rr = {mouse_pos, closest_hit_mesh_id, closest_hit, closest_normal};//on drag - m_need_fix = false; close_warning_flag_after_close_or_drag(); - m_need_update_text = true; + if (is_last_attach) { // surface drag + std::optional fix; + auto cur_world = m_model_object_in_world_tran.get_matrix() * m_text_tran_in_object.get_matrix(); + if (has_reflection(cur_world)) { + update_text_tran_in_model_object(true); + cur_world = m_model_object_in_world_tran.get_matrix() * m_text_tran_in_object.get_matrix(); + } + auto result = get_drag_volume_transformation(cur_world, m_text_normal_in_world.cast(), m_text_position_in_world, fix, + m_model_object_in_world_tran.get_matrix().inverse(), Geometry::deg2rad(m_rotate_angle)); + m_text_tran_in_object.set_from_transform(result); + auto gl_v = get_selected_gl_volume(m_parent); + gl_v->set_volume_transformation(m_text_tran_in_object.get_matrix()); + } else { + update_text_pos_normal(); + } } } -//B + void GLGizmoText::push_button_style(bool pressed) { if (m_is_dark_mode) { if (pressed) { ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(43 / 255.f, 64 / 255.f, 54 / 255.f, 1.f)); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(43 / 255.f, 64 / 255.f, 54 / 255.f, 1.f)); ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(43 / 255.f, 64 / 255.f, 54 / 255.f, 1.f)); - ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(68.f/ 255.f, 121 / 255.f, 251 / 255.f, 1.f)); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.f, 174 / 255.f, 66 / 255.f, 1.f)); } else { ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(45.f / 255.f, 45.f / 255.f, 49.f / 255.f, 1.f)); @@ -847,7 +2061,7 @@ void GLGizmoText::push_button_style(bool pressed) { ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(219 / 255.f, 253 / 255.f, 231 / 255.f, 1.f)); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(219 / 255.f, 253 / 255.f, 231 / 255.f, 1.f)); ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(219 / 255.f, 253 / 255.f, 231 / 255.f, 1.f)); - ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(68.f /255.f, 121 / 255.f, 251 / 255.f, 1.f)); + ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.f, 174 / 255.f, 66 / 255.f, 1.f)); } else { ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.f, 1.f, 1.f, 1.f)); @@ -868,11 +2082,10 @@ void GLGizmoText::push_combo_style(const float scale) { ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 1.0f * scale); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f * scale); ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BG_DARK); - // y96 - ImGui::PushStyleColor(ImGuiCol_BorderActive, ImVec4(0.27f, 0.47f, 0.98f, 1.00f)); - ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.27f, 0.47f, 0.98f, 0.0f)); - ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.27f, 0.47f, 0.98f, 1.0f)); - ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.27f, 0.47f, 0.98f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_BorderActive, ImVec4(0.00f, 0.68f, 0.26f, 1.00f)); + ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.00f, 0.68f, 0.26f, 0.0f)); + ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.00f, 0.68f, 0.26f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.00f, 0.68f, 0.26f, 1.0f)); ImGui::PushStyleColor(ImGuiCol_ScrollbarBg, ImGuiWrapper::COL_WINDOW_BG_DARK); ImGui::PushStyleColor(ImGuiCol_Button, { 1.00f, 1.00f, 1.00f, 0.0f }); } @@ -880,11 +2093,10 @@ void GLGizmoText::push_combo_style(const float scale) { ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 1.0f * scale); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f * scale); ImGui::PushStyleColor(ImGuiCol_PopupBg, ImGuiWrapper::COL_WINDOW_BG); - // y96 - ImGui::PushStyleColor(ImGuiCol_BorderActive, ImVec4(0.27f, 0.47f, 0.98f, 1.00f)); // y96 change color - ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.27f, 0.47f, 0.98f, 0.0f)); - ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.27f, 0.47f, 0.98f, 1.0f)); - ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.27f, 0.47f, 0.98f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_BorderActive, ImVec4(0.00f, 0.68f, 0.26f, 1.00f)); + ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.00f, 0.68f, 0.26f, 0.0f)); + ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.00f, 0.68f, 0.26f, 1.0f)); + ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.00f, 0.68f, 0.26f, 1.0f)); ImGui::PushStyleColor(ImGuiCol_ScrollbarBg, ImGuiWrapper::COL_WINDOW_BG); ImGui::PushStyleColor(ImGuiCol_Button, { 1.00f, 1.00f, 1.00f, 0.0f }); } @@ -899,45 +2111,8 @@ void GLGizmoText::pop_combo_style() // QDS void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit) { - if (!m_init_texture) { - update_font_texture(); - m_init_texture = true; - } - - if (m_imgui->get_font_size() != m_scale) { - m_scale = m_imgui->get_font_size(); - update_font_texture(); - } - if (m_textures.size() == 0) { - BOOST_LOG_TRIVIAL(info) << "GLGizmoText has no texture"; - return; - } - - const Selection &selection = m_parent.get_selection(); - if (selection.is_single_full_instance() || selection.is_single_full_object()) { - const GLVolume * gl_volume = selection.get_volume(*selection.get_volume_idxs().begin()); - int object_idx = gl_volume->object_idx(); - if (object_idx != m_object_idx || (object_idx == m_object_idx && m_volume_idx != -1)) { - m_object_idx = object_idx; - m_volume_idx = -1; - reset_text_info(); - } - } else if (selection.is_single_volume() || selection.is_single_modifier()) { - int object_idx, volume_idx; - ModelVolume *model_volume = m_parent.get_selection().get_selected_single_volume(object_idx, volume_idx); - if ((object_idx != m_object_idx || (object_idx == m_object_idx && volume_idx != m_volume_idx)) - && model_volume) { - m_last_text_mv = model_volume; - TextInfo text_info = model_volume->get_text_info(); - load_from_text_info(text_info);//mouse click down - m_is_modify = true; - m_volume_idx = volume_idx; - m_object_idx = object_idx; - } - } - const float win_h = ImGui::GetWindowHeight(); - y = std::min(y, bottom_limit - win_h); + y = std::min(y, bottom_limit - win_h); GizmoImguiSetNextWIndowPos(x, y, ImGuiCond_Always, 0.0f, 0.0f); static float last_y = 0.0f; @@ -945,44 +2120,84 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit) const float currt_scale = m_parent.get_scale(); ImGuiWrapper::push_toolbar_style(currt_scale); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4.0,5.0) * currt_scale); + double screen_scale = wxDisplay(wxGetApp().plater()).GetScaleFactor(); + // Configuration creation + if (m_gui_cfg == nullptr || // Exist configuration - first run + m_gui_cfg->screen_scale != screen_scale || // change of DPI + m_gui_cfg->dark_mode != m_is_dark_mode // change of dark mode + ) { + // Create cache for gui offsets + auto cfg = Text::create_gui_configuration(); + cfg.screen_scale = screen_scale; + cfg.dark_mode = m_is_dark_mode; + + GuiCfg gui_cfg{std::move(cfg)}; + m_gui_cfg = std::make_unique(std::move(gui_cfg)); + + // change resolution regenerate icons + m_icons = init_text_icons(m_icon_manager, *m_gui_cfg); // init_icons();//todo + m_style_manager.clear_imgui_font(); + } + + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(4.0, 5.0) * currt_scale); ImGui::PushStyleVar(ImGuiStyleVar_ScrollbarSize, 4.0f * currt_scale); GizmoImguiBegin("Text", ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar); -#ifdef DEBUG_TEXT - std::string world_hit = "world hit x:" + formatFloat(m_text_position_in_world[0]) + " y:" + formatFloat(m_text_position_in_world[1]) + - " z:" + formatFloat(m_text_position_in_world[2]); - std::string hit = "local hit x:" + formatFloat(m_rr.hit[0]) + " y:" + formatFloat(m_rr.hit[1]) + " z:" + formatFloat(m_rr.hit[2]); - std::string normal = "normal x:" + formatFloat(m_rr.normal[0]) + " y:" + formatFloat(m_rr.normal[1]) + " z:" + formatFloat(m_rr.normal[2]); - auto cut_dir = "cut_dir x:" + formatFloat(m_cut_plane_dir_in_world[0]) + " y:" + formatFloat(m_cut_plane_dir_in_world[1]) + " z:" + formatFloat(m_cut_plane_dir_in_world[2]); - auto fix_position_str = "fix position:" + formatFloat(m_fix_text_position_in_world[0]) + " y:" + formatFloat(m_fix_text_position_in_world[1]) + - " z:" + formatFloat(m_fix_text_position_in_world[2]); - auto fix_normal_str = "fix normal:" + formatFloat(m_fix_text_normal_in_world[0]) + " y:" + formatFloat(m_fix_text_normal_in_world[1]) + - " z:" + formatFloat(m_fix_text_normal_in_world[2]); - m_imgui->text(world_hit); - m_imgui->text(hit); - m_imgui->text(normal); - m_imgui->text(cut_dir); - m_imgui->text(fix_position_str); - m_imgui->text(fix_normal_str); +#if QDT_RELEASE_TO_PUBLIC == 0 + if (wxGetApp().plater()->is_show_text_cs()) { + std::string world_hit = "world hit x:" + formatFloat(m_text_position_in_world[0]) + " y:" + formatFloat(m_text_position_in_world[1]) +" z:" + formatFloat(m_text_position_in_world[2]); + std::string hit = "local hit x:" + formatFloat(m_rr.hit[0]) + " y:" + formatFloat(m_rr.hit[1]) + " z:" + formatFloat(m_rr.hit[2]); + std::string normal = "normal x:" + formatFloat(m_rr.normal[0]) + " y:" + formatFloat(m_rr.normal[1]) + " z:" + formatFloat(m_rr.normal[2]); + auto cut_dir = "cut_dir x:" + formatFloat(m_cut_plane_dir_in_world[0]) + " y:" + formatFloat(m_cut_plane_dir_in_world[1]) + + " z:" + formatFloat(m_cut_plane_dir_in_world[2]); + auto fix_position_str = "fix position:" + formatFloat(m_fix_text_position_in_world[0]) + " y:" + formatFloat(m_fix_text_position_in_world[1]) + + " z:" + formatFloat(m_fix_text_position_in_world[2]); + auto fix_normal_str = "fix normal:" + formatFloat(m_fix_text_normal_in_world[0]) + " y:" + formatFloat(m_fix_text_normal_in_world[1]) + + " z:" + formatFloat(m_fix_text_normal_in_world[2]); + m_imgui->text(world_hit); + if (!is_only_text_case()) { + m_imgui->text(hit); + m_imgui->text(normal); + } + m_imgui->text(cut_dir); + m_imgui->text(fix_position_str); + m_imgui->text(fix_normal_str); + m_imgui->text("calc tpye:" + std::to_string(m_show_calc_meshtod)); + if (m_normal_points.size() > 0) { + auto normal = m_normal_points[0]; + auto normal_str = "text normal:" + formatFloat(normal[0]) + " y:" + formatFloat(normal[1]) + " z:" + formatFloat(normal[2]); + m_imgui->text(normal_str); + } + auto tran_x_dir = m_text_tran_in_object.get_matrix().linear().col(0); + auto tran_y_dir = m_text_tran_in_object.get_matrix().linear().col(1); + auto tran_z_dir = m_text_tran_in_object.get_matrix().linear().col(2); + auto tran_pos = m_text_tran_in_object.get_matrix().linear().col(3); + auto tran_x_dir_str = "text tran_x_dir:" + formatFloat(tran_x_dir[0]) + " y:" + formatFloat(tran_x_dir[1]) + " z:" + formatFloat(tran_x_dir[2]); + m_imgui->text(tran_x_dir_str); + auto tran_y_dir_str = "text tran_y_dir:" + formatFloat(tran_y_dir[0]) + " y:" + formatFloat(tran_y_dir[1]) + " z:" + formatFloat(tran_y_dir[2]); + m_imgui->text(tran_y_dir_str); + auto tran_z_dir_str = "text tran_z_dir:" + formatFloat(tran_z_dir[0]) + " y:" + formatFloat(tran_z_dir[1]) + " z:" + formatFloat(tran_z_dir[2]); + m_imgui->text(tran_z_dir_str); + auto tran_pos_str = "text pos in_object:" + formatFloat(tran_pos[0]) + " y:" + formatFloat(tran_pos[1]) + " z:" + formatFloat(tran_pos[2]); + m_imgui->text(tran_pos_str); + } #endif - float space_size = m_imgui->get_style_scaling() * 8; - float font_cap = m_imgui->calc_text_size(_L("Font")).x; - float size_cap = m_imgui->calc_text_size(_L("Size")).x; + float space_size = m_imgui->get_style_scaling() * 8; + float font_cap = m_imgui->calc_text_size(_L("Font")).x; + float size_cap = m_imgui->calc_text_size(_L("Size")).x; float thickness_cap = m_imgui->calc_text_size(_L("Thickness")).x; - float input_cap = m_imgui->calc_text_size(_L("Input text")).x; - float depth_cap = m_imgui->calc_text_size(_L("Embeded")).x; - float caption_size = std::max(std::max(font_cap, size_cap), std::max(depth_cap, input_cap)) + space_size + ImGui::GetStyle().WindowPadding.x; + float input_cap = m_imgui->calc_text_size(_L("Input text")).x; + float caption_size = std::max(std::max(font_cap, size_cap), input_cap) + space_size + ImGui::GetStyle().WindowPadding.x; float input_text_size = m_imgui->scaled(10.0f); - float button_size = ImGui::GetFrameHeight(); + float button_size = ImGui::GetFrameHeight(); ImVec2 selectable_size(std::max((input_text_size + ImGui::GetFrameHeight() * 2), m_combo_width + SELECTABLE_INNER_OFFSET * currt_scale), m_combo_height); - float list_width = selectable_size.x + ImGui::GetStyle().ScrollbarSize + 2 * currt_scale; + float list_width = selectable_size.x + ImGui::GetStyle().ScrollbarSize + 2 * currt_scale; float input_size = list_width - button_size * 2 - ImGui::GetStyle().ItemSpacing.x * 4; - ImTextureID normal_B = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TEXT_B); - ImTextureID normal_T = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TEXT_T); + ImTextureID normal_B = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TEXT_B); + ImTextureID normal_T = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TEXT_T); ImTextureID normal_B_dark = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TEXT_B_DARK); ImTextureID normal_T_dark = m_parent.get_gizmos_manager().get_icon_texture_id(GLGizmosManager::MENU_ICON_NAME::IC_TEXT_T_DARK); @@ -990,77 +2205,49 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit) if (last_h != win_h || last_y != y) { // ask canvas for another frame to render the window in the correct position m_imgui->set_requires_extra_frame(); - if (last_h != win_h) - last_h = win_h; - if (last_y != y) - last_y = y; + if (last_h != win_h) last_h = win_h; + if (last_y != y) last_y = y; } + if (m_gui_cfg && (int) m_gui_cfg->input_offset != (int) caption_size) { m_gui_cfg->input_offset = caption_size; } + ImGui::AlignTextToFramePadding(); + m_imgui->text(_L("Input text")); + ImGui::SameLine(caption_size); + ImGui::PushItemWidth(2 * m_gui_cfg->input_width); + draw_text_input(caption_size); ImGui::AlignTextToFramePadding(); - m_imgui->text(_L("Font")); ImGui::SameLine(caption_size); - ImGui::PushItemWidth(list_width); - push_combo_style(currt_scale); - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); - ImGui::PushStyleVar(ImGuiStyleVar_PopupRounding, 4.0f * currt_scale); + ImGui::PushItemWidth(2 * m_gui_cfg->input_width); + draw_font_list(); - std::vector filtered_items_idx; - bool is_filtered = false; - if (m_imgui->qdt_combo_with_filter("##Combo_Font", m_font_names[m_curr_font_idx], m_font_names, &filtered_items_idx, &is_filtered, selectable_size.y)) { - int show_items_count = is_filtered ? filtered_items_idx.size() : m_textures.size(); - - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); - ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing, ImVec2(SELECTABLE_INNER_OFFSET, 0)* currt_scale); - ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 0); - for (int i = 0; i < show_items_count; i++) - { - int idx = is_filtered ? filtered_items_idx[i] : i; - const bool is_selected = (idx == m_curr_font_idx); - ImTextureID icon_id = (ImTextureID)(intptr_t)(m_textures[idx].texture->get_id()); - ImVec4 tint_color = ImGui::GetStyleColorVec4(ImGuiCol_Text); - if (ImGui::QDTImageSelectable(icon_id, selectable_size, { (float)m_textures[idx].w, (float)m_textures[idx].h }, m_textures[idx].hl, tint_color, { 0, 0 }, { 1, 1 }, is_selected)) - { - m_curr_font_idx = idx; - m_font_name = m_textures[m_curr_font_idx].font_name; - ImGui::CloseCurrentPopup(); - m_need_update_text = true; - } - if (is_selected) { - ImGui::SetItemDefaultFocus(); - } - } - ImGui::PopStyleVar(3); - ImGui::EndListBox(); - ImGui::EndPopup(); - } - - ImGui::PopStyleVar(2); - pop_combo_style(); - ImGui::AlignTextToFramePadding(); - m_imgui->text(_L("Size")); - ImGui::SameLine(caption_size); - ImGui::PushItemWidth(input_size); - if (ImGui::InputFloat("###font_size", &m_font_size, 0.0f, 0.0f, "%.2f")) { - limit_value(m_font_size, m_font_size_min, m_font_size_max); - m_need_update_text = true; - } + draw_height(); ImGui::SameLine(); ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f * currt_scale); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, {1.0f * currt_scale, 1.0f * currt_scale }); + ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, {1.0f * currt_scale, 1.0f * currt_scale}); ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 2.0f * currt_scale); push_button_style(m_bold); + bool exist_change = false; if (ImGui::ImageButton(m_is_dark_mode ? normal_B_dark : normal_B, {button_size - 2 * ImGui::GetStyle().FramePadding.x, button_size - 2 * ImGui::GetStyle().FramePadding.y})) { m_bold = !m_bold; - m_need_update_text = true; + // update_boldness(); in process(); + exist_change = true; } pop_button_style(); - ImGui::SameLine(); + auto temp_input_width = m_gui_cfg->input_width - ImGui::GetFrameHeight() * 1.6 * 2; // - space_size + ImGuiWindow *imgui_window = ImGui::GetCurrentWindow(); + auto cur_temp_x = imgui_window->DC.CursorPosPrevLine.x - imgui_window->Pos.x; + ImGui::SameLine(cur_temp_x); + ImGui::PushItemWidth(button_size); push_button_style(m_italic); if (ImGui::ImageButton(m_is_dark_mode ? normal_T_dark : normal_T, {button_size - 2 * ImGui::GetStyle().FramePadding.x, button_size - 2 * ImGui::GetStyle().FramePadding.y})) { m_italic = !m_italic; - m_need_update_text = true; + exist_change = true; + } + if (exist_change) { + m_style_manager.clear_glyphs_cache(); + process(); } pop_button_style(); ImGui::PopStyleVar(3); @@ -1068,12 +2255,34 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit) ImGui::AlignTextToFramePadding(); m_imgui->text(_L("Thickness")); ImGui::SameLine(caption_size); - ImGui::PushItemWidth(list_width); + ImGui::PushItemWidth(temp_input_width); // 2 * m_gui_cfg->input_width float old_value = m_thickness; ImGui::InputFloat("###text_thickness", &m_thickness, 0.0f, 0.0f, "%.2f"); m_thickness = ImClamp(m_thickness, m_thickness_min, m_thickness_max); - if (old_value != m_thickness) - m_need_update_text = true; + if (old_value != m_thickness) { + process(); + } + auto full_width = caption_size + 2 * m_gui_cfg->input_width; + if (!is_only_text_case()) { + auto depth_x = caption_size + space_size * 2 + temp_input_width; + ImGui::SameLine(depth_x); + float depth_cap = m_imgui->calc_text_size(_L("Embeded depth")).x; + ImGui::PushItemWidth(depth_cap); + m_imgui->text(_L("Embeded depth")); + + auto depth_input_x = depth_x + depth_cap + space_size * 2; + ImGui::SameLine(depth_input_x); + + auto valid_width = full_width - depth_input_x; + ImGui::PushItemWidth(valid_width); + old_value = m_embeded_depth; + if (ImGui::InputFloat("###text_embeded_depth", &m_embeded_depth, 0.0f, 0.0f, "%.2f")) { + limit_value(m_embeded_depth, 0.0f, m_embeded_depth_max); + } + if (old_value != m_embeded_depth) { + process(); + } + } const float slider_icon_width = m_imgui->get_slider_icon_size().x; const float slider_width = list_width - 1.5 * slider_icon_width - space_size; @@ -1083,45 +2292,38 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit) m_imgui->text(_L("Text Gap")); ImGui::SameLine(caption_size); ImGui::PushItemWidth(slider_width); - if (m_imgui->qdt_slider_float_style("##text_gap", &m_text_gap, -100, 100, "%.2f", 1.0f, true)) - m_need_update_text = true; - ImGui::SameLine(drag_left_width); - ImGui::PushItemWidth(1.5 * slider_icon_width); - if (ImGui::QDTDragFloat("##text_gap_input", &m_text_gap, 0.05f, 0.0f, 0.0f, "%.2f")) + if (m_imgui->qdt_slider_float_style("##text_gap", &m_text_gap, -10.f, 100.f, "%.2f", 1.0f, true)) m_need_update_text = true; - ImGui::AlignTextToFramePadding(); - m_imgui->text(_L("Angle")); - ImGui::SameLine(caption_size); - ImGui::PushItemWidth(slider_width); - if (m_imgui->qdt_slider_float_style("##angle", &m_rotate_angle, 0, 360, "%.2f", 1.0f, true)) - m_need_update_text = true; ImGui::SameLine(drag_left_width); ImGui::PushItemWidth(1.5 * slider_icon_width); - if (ImGui::QDTDragFloat("##angle_input", &m_rotate_angle, 0.05f, 0.0f, 0.0f, "%.2f")) + if (ImGui::QDTDragFloat("##text_gap_input", &m_text_gap, 0.05f, -10.f, 100.f, "%.2f")) m_need_update_text = true; - ImGui::AlignTextToFramePadding(); - m_imgui->text(_L("Embeded\r\ndepth")); - ImGui::SameLine(caption_size); - ImGui::PushItemWidth(list_width); - old_value = m_embeded_depth; - if (ImGui::InputFloat("###text_embeded_depth", &m_embeded_depth, 0.0f, 0.0f, "%.2f")) { - limit_value(m_embeded_depth, 0.0f, m_embeded_depth_max); + draw_rotation(caption_size, slider_width, drag_left_width, slider_icon_width); +#if QDT_RELEASE_TO_PUBLIC + if (GUI::wxGetApp().app_config->get("enable_text_styles") == "true") { + draw_style_list(caption_size); } - if (old_value != m_embeded_depth) - m_need_update_text = true; - - ImGui::AlignTextToFramePadding(); - m_imgui->text(_L("Input text")); - ImGui::SameLine(caption_size); - ImGui::PushItemWidth(list_width); - - if(ImGui::InputText("", m_text, sizeof(m_text))) - m_need_update_text = true; - std::string text = std::string(m_text); - if (text.empty() && m_is_modify) { - m_imgui->warning_text(_L("Warning:Input cannot be empty!")); +#else + draw_style_list(caption_size); +#endif + draw_surround_type(caption_size); + auto text_volume = m_last_text_mv; + if (text_volume && !text_volume->is_the_only_one_part()) { + ImGui::Separator(); + draw_model_type(caption_size); + } + //warnning + std::string text = m_text; + auto cur_world = m_model_object_in_world_tran.get_matrix() * m_text_tran_in_object.get_matrix(); + if (!is_only_text_case() && has_reflection(cur_world)) { + m_imgui->warning_text_wrapped(_L("Warning:There is a mirror in the text matrix, and dragging it will completely regenerate it."), full_width); + m_parent.request_extra_frame(); + } + if (m_warning_font) { + m_imgui->warning_text_wrapped(_L("Warning:Due to font upgrades,previous font may not necessarily be replaced successfully, and recommend you to modify the font."), full_width); + m_parent.request_extra_frame(); } if (m_show_warning_text_create_fail) { m_imgui->warning_text(_L("Warning:create text fail.")); @@ -1132,60 +2334,40 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit) if (m_show_text_normal_reset_tip) { m_imgui->warning_text(_L("Warning:text normal has been reset.")); } - if (m_show_warning_regenerated) { - m_imgui->warning_text(_L("Warning:Because current text does indeed use surround algorithm,\nif continue to edit, text has to regenerated according to new location.")); - } + /* if (m_show_warning_regenerated && m_font_version != CUR_FONT_VERSION) { + m_imgui->warning_text_wrapped(_L("Warning:Because current text does indeed use surround algorithm,if continue to edit, text has to regenerated according to new location."), + full_width); + m_parent.request_extra_frame(); + }*/ if (m_show_warning_old_tran) { - m_imgui->warning_text(_L("Warning:old matrix has at least two parameters: mirroring, scaling, and rotation. \nIf you continue editing, it may not be correct. \nPlease dragging text or cancel using current pose, \nsave and reedit again.")); + m_imgui->warning_text_wrapped(_L("Warning:old matrix has at least two parameters: mirroring, scaling, and rotation. If you continue editing, it may not be correct. Please " + "dragging text or cancel using current pose, save and reedit again."), + full_width); + m_parent.request_extra_frame(); } if (m_show_warning_error_mesh) { - m_imgui->warning_text(_L("Error:Detecting an incorrect mesh id or an unknown error, \nregenerating text may result in incorrect outcomes.\nPlease drag text,save it then reedit it again.")); + m_imgui->warning_text_wrapped( + _L("Error:Detecting an incorrect mesh id or an unknown error, regenerating text may result in incorrect outcomes.Please drag text,save it then reedit it again."), + full_width); + m_parent.request_extra_frame(); } if (m_show_warning_lost_rotate) { - m_imgui->warning_text(_L("Warning:Due to functional upgrade, rotation information \ncannot be restored. Please drag or modify text,\n save it and reedit it will ok.")); + m_imgui->warning_text_wrapped( + _L("Warning:Due to functional upgrade, rotation information cannot be restored. Please drag or modify text,save it and reedit it will ok."), + full_width); + m_parent.request_extra_frame(); } - ImGui::Separator(); - if (m_need_fix && m_text_type > TextType::HORIZONAL) { - ImGui::AlignTextToFramePadding(); - ImGui::SameLine(caption_size); - ImGui::PushItemWidth(list_width); - ImGui::AlignTextToFramePadding(); - if (m_imgui->qdt_checkbox(_L("Use opened text pose"), m_use_current_pose)) { - m_need_update_text = true; - } + if (m_last_text_mv && m_rr.mesh_id < 0 && !is_only_text_case()) { + m_imgui->warning_text_wrapped(_L("Warning") + ":"+ _L("Detected that text did not adhere to mesh surface. Please manually drag yellow square to mesh surface that needs to be adhered."), + full_width); + m_parent.request_extra_frame(); } + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(6.0f, 10.0f)); float get_cur_y = ImGui::GetContentRegionMax().y + ImGui::GetFrameHeight() + y; show_tooltip_information(x, get_cur_y); - float f_scale = m_parent.get_gizmos_manager().get_layout_scale(); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0f, 4.0f * f_scale)); - - ImGui::SameLine(caption_size); - ImGui::AlignTextToFramePadding(); - auto is_surface_text = m_text_type == TextType::SURFACE || m_text_type == TextType::SURFACE_HORIZONAL; - if (m_imgui->qdt_checkbox(_L("Surface"), is_surface_text)) { - m_need_update_text = true; - } - ImGui::SameLine(); - ImGui::AlignTextToFramePadding(); - auto is_keep_horizontal = m_text_type == TextType::HORIZONAL || m_text_type == TextType::SURFACE_HORIZONAL; - if (m_imgui->qdt_checkbox(_L("Horizontal text"), is_keep_horizontal)) { - m_need_update_text = true; - if (is_surface_text && is_keep_horizontal == false) { - update_text_normal_in_world(); - } - } - check_text_type(is_surface_text, is_keep_horizontal); - - //ImGui::SameLine(); - //ImGui::AlignTextToFramePadding(); - //m_imgui->text(_L("Status:")); - //float status_cap = m_imgui->calc_text_size(_L("Status:")).x + space_size + ImGui::GetStyle().WindowPadding.x; - //ImGui::SameLine(); - //m_imgui->text(m_is_modify ? _L("Modify") : _L("Add")); - - ImGui::PopStyleVar(2); + ImGui::PopStyleVar(1); #if 0 ImGuiIO& io = ImGui::GetIO(); @@ -1203,6 +2385,7 @@ void GLGizmoText::on_render_input_window(float x, float y, float bottom_limit) void GLGizmoText::show_tooltip_information(float x, float y) { + if (m_desc.empty()) { return; } std::array info_array = std::array{"rotate_text"}; float caption_max = 0.f; for (const auto &t : info_array) { caption_max = std::max(caption_max, m_imgui->calc_text_size(m_desc[t + "_caption"]).x); } @@ -1226,28 +2409,480 @@ void GLGizmoText::show_tooltip_information(float x, float y) m_imgui->text_colored(ImGuiWrapper::COL_WINDOW_BG, text); }; - for (const auto &t : info_array) draw_text_with_caption(m_desc.at(t + "_caption") + ": ", m_desc.at(t)); + for (const auto &t : info_array) + draw_text_with_caption(m_desc.at(t + "_caption") + ": ", m_desc.at(t)); ImGui::EndTooltip(); } ImGui::PopStyleVar(2); } +bool GLGizmoText::set_height() +{ + float &value = m_style_manager.get_font_prop().size_in_mm; + // size can't be zero or negative + apply(value, Text::limits.size_in_mm); + auto text_volume = m_last_text_mv; + if (text_volume == nullptr) { + return false; + } + // only different value need process + //if (is_approx(value, text_volume->get_text_info().m_font_size_in_mm)) // m_volume->text_configuration->style.prop.size_in_mm + // return false; + /* if (m_style_manager.get_font_prop().per_glyph) + reinit_text_lines(m_text_lines.get_lines().size());*///todo + + return true; +} + +void GLGizmoText::draw_text_input(int caption_width) +{ + bool allow_multi_line = false; + bool support_backup_fonts = GUI::wxGetApp().app_config->get_bool("support_backup_fonts"); + auto create_range_text_prep = [&mng = m_style_manager, &text = m_text, &exist_unknown = m_text_contain_unknown_glyph, support_backup_fonts]() { + if (text.empty()) { return std::string(); } + auto &ff = mng.get_font_file_with_cache(); + assert(ff.has_value()); + const auto & cn = mng.get_font_prop().collection_number; + unsigned int font_index = (cn.has_value()) ? *cn : 0; + if (support_backup_fonts) { + std::vector> fonts; + fonts.emplace_back(ff.font_file); + for (int i = 0; i < Slic3r::GUI::BackupFonts::backup_fonts.size(); i++) { + if (Slic3r::GUI::BackupFonts::backup_fonts[i].has_value()) { + fonts.emplace_back(Slic3r::GUI::BackupFonts::backup_fonts[i].font_file); + } + } + return create_range_text(text, fonts, font_index, &exist_unknown); + } else { + return create_range_text(text, *ff.font_file, font_index, &exist_unknown); + } + }; + + double scale = 1.;//m_scale_height.has_value() ? *m_scale_height : + ImFont *imgui_font = m_style_manager.get_imgui_font(); + if (imgui_font == nullptr) { + // try create new imgui font + double screen_scale = wxDisplay(wxGetApp().plater()).GetScaleFactor(); + double imgui_scale = scale * screen_scale; + + m_style_manager.create_imgui_font(create_range_text_prep(), imgui_scale, support_backup_fonts); + imgui_font = m_style_manager.get_imgui_font(); + } + bool exist_font = imgui_font != nullptr && imgui_font->IsLoaded() && imgui_font->Scale > 0.f && imgui_font->ContainerAtlas != nullptr; + // NOTE: Symbol fonts doesn't have atlas + // when their glyph range is out of language character range + if (exist_font){ + ImGui::PushFont(imgui_font); + } + + // show warning about incorrectness view of font + std::string warning_tool_tip; + if (!exist_font) { + warning_tool_tip = _u8L("The text cannot be written using the selected font. Please try choosing a different font."); + } else { + auto append_warning = [&warning_tool_tip](std::string t) { + if (!warning_tool_tip.empty()) + warning_tool_tip += "\n"; + warning_tool_tip += t; + }; + if (is_text_empty(m_text)) {//QDS modify + append_warning(_u8L("Embossed text cannot contain only white spaces.")); + } + if (m_text_contain_unknown_glyph) { + append_warning(_u8L("Unsupported characters automatically switched to fallback font.")); + } + const FontProp &prop = m_style_manager.get_font_prop(); + /* if (prop.skew.has_value())//QDS modify + append_warning(_u8L("Text input doesn't show font skew.")); + if (prop.boldness.has_value()) + append_warning(_u8L("Text input doesn't show font boldness.")); + if (prop.line_gap.has_value()) + append_warning(_u8L("Text input doesn't show gap between lines."));*/ + auto &ff = m_style_manager.get_font_file_with_cache(); + /*float imgui_size = StyleManager::get_imgui_font_size(prop, *ff.font_file, scale); + if (imgui_size > StyleManager::max_imgui_font_size) + append_warning(_u8L("Too tall, diminished font height inside text input.")); + if (imgui_size < StyleManager::min_imgui_font_size) + append_warning(_u8L("Too small, enlarged font height inside text input."));*/ + bool is_multiline = m_text_lines.get_lines().size() > 1; + if (is_multiline && (prop.align.first == FontProp::HorizontalAlign::center || prop.align.first == FontProp::HorizontalAlign::right)) + append_warning(_u8L("Text doesn't show current horizontal alignment.")); + } + + // flag for extend font ranges if neccessary + // ranges can't be extend during font is activ(pushed) + std::string range_text; + ImVec2 input_size(2 * m_gui_cfg->input_width, m_gui_cfg->text_size.y); // 2 * m_gui_cfg->input_width - caption_width + const ImGuiInputTextFlags flags = ImGuiInputTextFlags_AllowTabInput;// | ImGuiInputTextFlags_AutoSelectAll + if (ImGui::InputTextMultiline("##Text", &m_text, input_size, flags)) { + if (m_style_manager.get_font_prop().per_glyph) { + unsigned count_lines = get_count_lines(m_text); + //if (count_lines != m_text_lines.get_lines().size()) + // // Necesarry to initialize count by given number (differ from stored in volume at the moment) + // reinit_text_lines(count_lines); + } + if (m_last_text_mv ==nullptr) { + m_need_update_tran = false; + } + process(); + range_text = create_range_text_prep(); + } + + if (exist_font) + ImGui::PopFont(); + + // warning tooltip has to be with default font + if (!warning_tool_tip.empty() && ( !allow_multi_line ||(allow_multi_line && ImGui::GetCurrentWindow()->DC.ChildWindows.Data))) { + // Multiline input has hidden window for scrolling + float scrollbar_width, scrollbar_height; + const ImGuiStyle &style = ImGui::GetStyle(); + if (allow_multi_line) { + const ImGuiWindow *input = ImGui::GetCurrentWindow()->DC.ChildWindows.front(); + scrollbar_width = (input->ScrollbarY) ? style.ScrollbarSize : 0.f; + scrollbar_height = (input->ScrollbarX) ? style.ScrollbarSize : 0.f; + } else { + scrollbar_width = 4; + scrollbar_height = 2; + } + if (ImGui::IsItemHovered()) + m_imgui->tooltip(warning_tool_tip, m_gui_cfg->max_tooltip_width); + } + + // NOTE: must be after ImGui::font_pop() + // -> imgui_font has to be unused + // IMPROVE: only extend not clear + // Extend font ranges + if (!range_text.empty() && !ImGuiWrapper::contain_all_glyphs(imgui_font, range_text)) + m_style_manager.clear_imgui_font(); +} + +void GLGizmoText::draw_font_list() +{ + ImGuiWrapper::push_combo_style(m_gui_cfg->screen_scale); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); + ImGui::PushStyleVar(ImGuiStyleVar_PopupRounding, 4.0f * m_gui_cfg->screen_scale); + + wxString tooltip_name = ""; + + // Set partial + wxString actual_face_name; + if (m_style_manager.is_active_font()) { + const wxFont &wx_font = m_style_manager.get_wx_font(); + if (wx_font.IsOk()){ + actual_face_name = wx_font.GetFaceName(); + m_cur_font_name = actual_face_name; + m_font_name = actual_face_name.utf8_string(); + } + } + // name of actual selected font + const char *selected = (!actual_face_name.empty()) ? (const char *) actual_face_name.c_str() : " --- "; + + // Do not remove font face during enumeration + // When deletation of font appear this variable is set + std::optional del_index; + + ImGui::SetNextItemWidth(2 * m_gui_cfg->input_width); + std::vector filtered_items_idx; + bool is_filtered = false; + ImGuiStyle & style = ImGui::GetStyle(); + float old_scrollbar_size = style.ScrollbarSize; + style.ScrollbarSize = 16.f; + if (m_imgui->qdt_combo_with_filter("##Combo_Font", selected, m_face_names->faces_names, &filtered_items_idx, &is_filtered, m_imgui->scaled(32.f / 15.f))) { + bool set_selection_focus = false; + if (!m_face_names->is_init) { + init_face_names(*m_face_names); + set_selection_focus = true; + } + + if (!m_face_names->has_truncated_names) + init_truncated_names(*m_face_names, m_gui_cfg->input_width); + + if (m_face_names->texture_id == 0) + init_font_name_texture(); + + int show_items_count = is_filtered ? filtered_items_idx.size() : m_face_names->faces.size(); + + ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); + ImGui::PushStyleVar(ImGuiStyleVar_ItemInnerSpacing, ImVec2(0, 0)); + ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 0); + + for (int i = 0; i < show_items_count; i++) { + int idx = is_filtered ? filtered_items_idx[i] : i; + FaceName & face = m_face_names->faces[idx]; + const wxString &wx_face_name = face.wx_name; + + ImGui::PushID(idx); + ScopeGuard sg([]() { ImGui::PopID(); }); + bool is_selected = (actual_face_name == wx_face_name); + ImVec2 selectable_size(0, m_imgui->scaled(32.f / 15.f)); + ImGuiSelectableFlags flags = 0; + if (ImGui::QDTSelectable(face.name_truncated.c_str(), is_selected, flags, selectable_size)) { + if (!select_facename(wx_face_name,true)) { + del_index = idx; + MessageDialog(wxGetApp().plater(), GUI::format_wxstr(_L("Font \"%1%\" can't be selected."), wx_face_name)); + } else { + ImGui::CloseCurrentPopup(); + } + } + // tooltip as full name of font face + if (ImGui::IsItemHovered()) + tooltip_name = wx_face_name; + + if (is_selected) { + ImGui::SetItemDefaultFocus(); + } + + // on first draw set focus on selected font + if (set_selection_focus && is_selected) + ImGui::SetScrollHereY(); + draw_font_preview(face, m_text, *m_face_names, *m_gui_cfg, ImGui::IsItemVisible()); + } + ImGui::PopStyleVar(3); + ImGui::EndListBox(); + ImGui::EndPopup(); + } else if (m_face_names->is_init) { + // Just one after close combo box + // free texture and set id to zero + m_face_names->is_init = false; + // cancel all process for generation of texture + for (FaceName &face : m_face_names->faces) + if (face.cancel != nullptr) + face.cancel->store(true); + glsafe(::glDeleteTextures(1, &m_face_names->texture_id)); + m_face_names->texture_id = 0; + } + style.ScrollbarSize = old_scrollbar_size; + // delete unloadable face name when try to use + if (del_index.has_value()) { + auto face = m_face_names->faces.begin() + (*del_index); + std::vector &bad = m_face_names->bad; + // sorted insert into bad fonts + auto it = std::upper_bound(bad.begin(), bad.end(), face->wx_name); + bad.insert(it, face->wx_name); + m_face_names->faces.erase(face); + m_face_names->faces_names.erase(m_face_names->faces_names.begin() + (*del_index)); + // update cached file + store(*m_face_names); + } + +#ifdef ALLOW_ADD_FONT_BY_FILE + ImGui::SameLine(); + // select font file by file browser + if (draw_button(IconType::open_file)) { + if (choose_true_type_file()) { process(); } + } else if (ImGui::IsItemHovered()) + ImGui::SetTooltip("Add file with font(.ttf, .ttc)"); +#endif // ALLOW_ADD_FONT_BY_FILE + +#ifdef ALLOW_ADD_FONT_BY_OS_SELECTOR + ImGui::SameLine(); + if (draw_button(IconType::system_selector)) { + if (choose_font_by_wxdialog()) { process(); } + } else if (ImGui::IsItemHovered()) + ImGui::SetTooltip("Open dialog for choose from fonts."); +#endif // ALLOW_ADD_FONT_BY_OS_SELECTOR + + ImGui::PopStyleVar(2); + ImGuiWrapper::pop_combo_style(); + + if (!tooltip_name.IsEmpty()) + m_imgui->tooltip(tooltip_name, m_gui_cfg->max_tooltip_width); +} + +void GLGizmoText::draw_height(bool use_inch) +{ + float & value = m_style_manager.get_font_prop().size_in_mm; + const EmbossStyle *stored_style = m_style_manager.get_stored_style(); + const float * stored = (stored_style != nullptr) ? &stored_style->prop.size_in_mm : nullptr; + const char * size_format = use_inch ? "%.2f in" : "%.1f mm"; + const std::string revert_text_size = _u8L("Revert text size."); + const std::string &name = m_gui_cfg->translations.height; + if (rev_input_mm(name, value, stored, revert_text_size, 0.1f, 1.f, size_format, use_inch, m_scale_height)) { + if (set_height()) { + process(); + } + } +} + +//template<> +bool imgui_input(const char *label, float *v, float step, float step_fast, const char *format, ImGuiInputTextFlags flags) +{ + return ImGui::InputFloat(label, v, step, step_fast, format, flags); +} +//template<> +bool imgui_input(const char *label, double *v, double step, double step_fast, const char *format, ImGuiInputTextFlags flags) +{ + return ImGui::InputDouble(label, v, step, step_fast, format, flags); +} + +bool exist_change(const std::optional &value, const std::optional *default_value) +{ + if (default_value == nullptr) return false; + return !is_approx(value, *default_value); +} + +bool exist_change(const float &value, const float *default_value) +{ + if (default_value == nullptr) return false; + return !is_approx(value, *default_value); +} + +template +bool GLGizmoText::revertible(const std::string &name, T &value, const T *default_value, const std::string &undo_tooltip, float undo_offset, Draw draw) const +{ + ImGui::AlignTextToFramePadding(); + bool changed = exist_change(value, default_value); + if (changed || default_value == nullptr) + ImGuiWrapper::text_colored(ImGuiWrapper::COL_QIDI_CHANGE, name); + else + ImGuiWrapper::text(name); + + // render revert changes button + if (changed) { + ImGuiWindow *window = ImGui::GetCurrentWindow(); + float prev_x = window->DC.CursorPosPrevLine.x; + ImGui::SameLine(undo_offset); // change cursor postion + if (draw_button(m_icons, IconType::undo)) { + value = *default_value; + + // !! Fix to detect change of value after revert of float-slider + m_imgui->get_last_slider_status().deactivated_after_edit = true; + + return true; + } else if (ImGui::IsItemHovered()) + m_imgui->tooltip(undo_tooltip, m_gui_cfg->max_tooltip_width); + window->DC.CursorPosPrevLine.x = prev_x; // set back previous position + } + return draw(); +} + +template +bool GLGizmoText::rev_input( + const std::string &name, T &value, const T *default_value, const std::string &undo_tooltip, T step, T step_fast, const char *format, ImGuiInputTextFlags flags) const +{ + // draw offseted input + auto draw_offseted_input = [&offset = m_gui_cfg->input_offset, &width = m_gui_cfg->input_width, &name, &value, &step, &step_fast, format, flags]() { + ImGui::SameLine(offset); + ImGui::SetNextItemWidth(width); + return imgui_input(("##" + name).c_str(), &value, step, step_fast, format, flags); + }; + float undo_offset = m_gui_cfg->input_offset - m_gui_cfg->icon_width; // ImGui::GetStyle().WindowPadding.x;//todo + return revertible(name, value, default_value, undo_tooltip, undo_offset, draw_offseted_input); +} + +template +bool GLGizmoText::rev_input_mm(const std::string & name, + T & value, + const T * default_value_ptr, + const std::string & undo_tooltip, + T step, + T step_fast, + const char * format, + bool use_inch, + const std::optional &scale) const +{ + // _variable which temporary keep value + T value_ = value; + T default_value_; + if (use_inch) { + // calc value in inch + value_ *= GizmoObjectManipulation::mm_to_in; + if (default_value_ptr) { + default_value_ = GizmoObjectManipulation::mm_to_in * (*default_value_ptr); + default_value_ptr = &default_value_; + } + } + if (scale.has_value()) value_ *= *scale; + bool use_correction = use_inch || scale.has_value(); + if (rev_input(name, use_correction ? value_ : value, default_value_ptr, undo_tooltip, step, step_fast, format)) { + if (use_correction) { + value = value_; + if (use_inch) value *= GizmoObjectManipulation::in_to_mm; + if (scale.has_value()) value /= *scale; + } + return true; + } + return false; +} + +void GLGizmoText::draw_depth(bool use_inch) {} + +void GLGizmoText::init_font_name_texture() +{ + Timer t("init_font_name_texture"); + // check if already exists + GLuint &id = m_face_names->texture_id; + if (id != 0) return; + // create texture for font + GLenum target = GL_TEXTURE_2D; + glsafe(::glGenTextures(1, &id)); + glsafe(::glBindTexture(target, id)); + glsafe(::glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); + glsafe(::glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); + const Vec2i32 & size = m_gui_cfg->face_name_size; + GLint w = size.x(), h = m_face_names->count_cached_textures * size.y(); + std::vector data(4 * w * h, {0}); + const GLenum format = GL_RGBA, type = GL_UNSIGNED_BYTE; + const GLint level = 0, internal_format = GL_RGBA, border = 0; + glsafe(::glTexImage2D(target, level, internal_format, w, h, border, format, type, data.data())); + + // bind default texture + GLuint no_texture_id = 0; + glsafe(::glBindTexture(target, no_texture_id)); + + // clear info about creation of texture - no one is initialized yet + for (FaceName &face : m_face_names->faces) { + face.cancel = nullptr; + face.is_created = nullptr; + } +} + +void GLGizmoText::reinit_text_lines(unsigned count_lines) { + init_text_lines(m_text_lines, m_parent.get_selection(), m_style_manager, count_lines); +} + +bool GLGizmoText::check(ModelVolumeType volume_type) +{ + return volume_type == ModelVolumeType::MODEL_PART || volume_type == ModelVolumeType::NEGATIVE_VOLUME || volume_type == ModelVolumeType::PARAMETER_MODIFIER; +} + +bool GLGizmoText::init_create(ModelVolumeType volume_type) { // check valid volume type + if (!check(volume_type)) { + BOOST_LOG_TRIVIAL(error) << "Can't create embossed volume with this type: " << (int) volume_type; + return false; + } + if (!is_activable()) { + BOOST_LOG_TRIVIAL(error) << "Can't create text. Gizmo is not activabled."; + return false; + } + // Check can't be inside is_activable() cause crash + // steps to reproduce: start App -> key 't' -> key 'delete' + if (wxGetApp().obj_list()->has_selected_cut_object()) { + BOOST_LOG_TRIVIAL(error) << "Can't create text on cut object"; + return false; + } + m_style_manager.discard_style_changes(); + assert(!m_text_lines.is_init()); + m_text_lines.reset(); // remove not current text lines + // set default text + return true; +} + void GLGizmoText::reset_text_info() { - m_font_name = ""; - m_font_size = 16.f; - m_curr_font_idx = 0; - m_bold = true; - m_italic = false; - m_thickness = 2.f; - strcpy(m_text, m_font_name.c_str()); - m_embeded_depth = 0.f; - m_rotate_angle = 0; - m_text_gap = 0.f; - m_text_type = TextType::SURFACE; + m_object_idx = -1; + m_volume_idx = -1; + m_font_size = m_style_manager.get_font_prop().size_in_mm; + m_bold = m_style_manager.get_font_prop().boldness > 0; + m_italic = m_style_manager.get_font_prop().skew > 0; + m_thickness = m_style_manager.get_style().projection.depth; + m_text = ""; + m_embeded_depth = m_style_manager.get_style().projection.embeded_depth; + m_rotate_angle = get_angle_from_current_style(); + m_text_gap = m_style_manager.get_style().prop.char_gap.value_or(0); + m_surface_type = TextInfo::TextType::SURFACE; m_rr = RaycastResult(); - - m_is_modify = false; + m_last_text_mv = nullptr; } void GLGizmoText::update_text_pos_normal() { @@ -1263,11 +2898,9 @@ void GLGizmoText::update_text_pos_normal() { std::vector w_matrices; std::vector mv_trans; - for (const ModelVolume *mv : mo->volumes) { - if (mv->is_model_part()) { - w_matrices.emplace_back(Geometry::Transformation(mi->get_transformation().get_matrix() * mv->get_matrix())); - mv_trans.emplace_back(Geometry::Transformation(mv->get_matrix())); - } + for (ModelVolume *mv : mo->volumes) { + w_matrices.emplace_back(Geometry::Transformation(mi->get_transformation().get_matrix() * mv->get_matrix())); + mv_trans.emplace_back(Geometry::Transformation(mv->get_matrix())); } #ifdef DEBUG_TEXT_VALUE m_rr.hit = Vec3f(-0.58, -1.70, -12.8); @@ -1277,21 +2910,14 @@ void GLGizmoText::update_text_pos_normal() { m_text_normal_in_world = (w_matrices[m_rr.mesh_id].get_matrix_no_offset().cast() * m_rr.normal).normalized(); } -void GLGizmoText::update_font_status() -{ - std::unique_lock lock(m_mutex); - m_font_status.reserve(m_avail_font_names.size()); - for (std::string font_name : m_avail_font_names) { - if (!can_generate_text_shape(font_name)) { - m_font_status.emplace_back(false); - } - else { - m_font_status.emplace_back(true); - } +bool GLGizmoText::filter_model_volume(ModelVolume *mv) { + if (mv && mv->is_model_part() && !mv->is_text()) { + return true; } + return false; } -float GLGizmoText::get_text_height(const std::string &text) +float GLGizmoText::get_text_height(const std::string &text)//todo { std::wstring_convert> str_cnv; std::wstring ws = boost::nowide::widen(text); @@ -1330,6 +2956,7 @@ void GLGizmoText::close_warning_flag_after_close_or_drag() m_show_warning_lost_rotate = false; m_is_version1_10_xoy = false; m_is_version1_9_xoz = false; + m_is_version1_8_yoz = false; } void GLGizmoText::update_text_normal_in_world() @@ -1338,7 +2965,7 @@ void GLGizmoText::update_text_normal_in_world() auto mo = m_parent.get_selection().get_selected_single_object(temp_object_idx); if (mo && m_rr.mesh_id >= 0) { const ModelInstance *mi = mo->instances[m_parent.get_selection().get_instance_idx()]; - TriangleMesh text_attach_mesh(mo->volumes[m_rr.mesh_id]->mesh()); + TriangleMesh text_attach_mesh(mo->volumes[m_rr.mesh_id]->mesh()); text_attach_mesh.transform(mo->volumes[m_rr.mesh_id]->get_matrix()); MeshRaycaster temp_ray_caster(text_attach_mesh); Vec3f local_center = m_text_tran_in_object.get_offset().cast(); //(m_text_tran_in_object.get_matrix() * box.center()).cast(); // @@ -1351,53 +2978,14 @@ void GLGizmoText::update_text_normal_in_world() } } -bool GLGizmoText::update_text_positions(const std::vector& texts) +bool GLGizmoText::update_text_tran_in_model_object(bool rewrite_text_tran) { - std::vector text_lengths; - for (int i = 0; i < texts.size(); ++i) { - std::string alpha; - if (texts[i] == " ") { - alpha = "i"; - } else { - alpha = texts[i]; - } - TextResult text_result; - load_text_shape(alpha.c_str(), m_font_name.c_str(), m_font_size, m_thickness + m_embeded_depth, m_bold, m_italic, text_result); - double half_x_length = text_result.text_width / 2; - text_lengths.emplace_back(half_x_length); - } - - int text_num = texts.size(); - m_position_points.clear(); - m_normal_points.clear(); - - const Selection &selection = m_parent.get_selection(); - auto mo = selection.get_model()->objects[m_object_idx]; - if (mo == nullptr) - return false; - - const ModelInstance *mi = mo->instances[selection.get_instance_idx()]; - m_model_object_in_world_tran = mi->get_transformation(); - // Precalculate transformations of individual meshes. - std::vector trafo_matrices; - std::vector rotate_trafo_matrices; - for (const ModelVolume *mv : mo->volumes) { - if (mv->is_model_part()) { - trafo_matrices.emplace_back(mi->get_transformation().get_matrix() * mv->get_matrix()); - rotate_trafo_matrices.emplace_back(mi->get_transformation().get_matrix(true, false, true, true) * mv->get_matrix(true, false, true, true)); - } - } - - if (m_rr.mesh_id == -1) { + if (m_object_idx < 0 && !is_only_text_case()) { BOOST_LOG_TRIVIAL(info) << boost::format("Text: mrr_mesh_id is -1"); return false; } - - // mouse_position_world may is error after user modified - if (m_need_fix) { - if (m_text_normal_in_world.norm() < 0.1) { - m_show_text_normal_reset_tip = true; - } + if (m_text_normal_in_world.norm() < 0.1) { + m_show_text_normal_reset_tip = true; use_fix_normal_position(); } if (m_text_normal_in_world.norm() < 0.1) { @@ -1405,405 +2993,117 @@ bool GLGizmoText::update_text_positions(const std::vector& texts) BOOST_LOG_TRIVIAL(info) << "m_text_normal_in_object is error"; return false; } - m_show_text_normal_error = false; - auto mouse_position_world = m_text_position_in_world.cast(); - auto mouse_normal_world = m_text_normal_in_world.cast(); - - TriangleMesh slice_meshs; - int mesh_index = 0; - int volume_index = 0; - for (int i = 0; i < mo->volumes.size(); ++i) { - // skip the editing text volume - if (m_is_modify && m_volume_idx == i) - continue; - - ModelVolume *mv = mo->volumes[i]; - if (mv->is_model_part()) { - if (mesh_index == m_rr.mesh_id) { - volume_index = i; - } - TriangleMesh vol_mesh(mv->mesh()); - vol_mesh.transform(mv->get_matrix()); - slice_meshs.merge(vol_mesh); - mesh_index++; + m_show_text_normal_error = false; + if (!rewrite_text_tran) { + if (!m_need_update_tran) { + return true; } + m_need_update_tran = false; } - - ModelVolume* volume = mo->volumes[volume_index]; + const Selection &selection = m_parent.get_selection(); + auto mo = selection.get_model()->objects[m_object_idx]; + if (mo == nullptr) + return false; + const ModelInstance *mi = mo->instances[selection.get_instance_idx()]; + m_model_object_in_world_tran = mi->get_transformation(); generate_text_tran_in_world(m_text_normal_in_world.cast(), m_text_position_in_world, m_rotate_angle, m_text_tran_in_world); - + if (m_fix_text_tran.has_value()) { + m_text_tran_in_world = m_text_tran_in_world * (m_fix_text_tran.value()); + } m_cut_plane_dir_in_world = m_text_tran_in_world.get_matrix().linear().col(1); m_text_normal_in_world = m_text_tran_in_world.get_matrix().linear().col(2).cast(); - // generate clip cs at click pos - auto text_position_in_object = mi->get_transformation().get_matrix().inverse() * m_text_position_in_world.cast(); - auto rotate_mat_inv = mi->get_transformation().get_matrix_no_offset().inverse(); - auto text_tran_in_object = mi->get_transformation().get_matrix().inverse() * m_text_tran_in_world.get_matrix(); // Geometry::generate_transform(cs_x_dir, cs_y_dir, cs_z_dir, text_position_in_object); // todo modify by m_text_tran_in_world - m_text_tran_in_object.set_matrix(text_tran_in_object); - m_text_cs_to_world_tran = mi->get_transformation().get_matrix() * m_text_tran_in_object.get_matrix(); - auto rotate_tran = Geometry::assemble_transform(Vec3d::Zero(), {-0.5 * M_PI, 0.0, 0.0}); - if (m_text_type == TextType::HORIZONAL || (m_need_fix && m_use_current_pose)) { - m_position_points.resize(text_num); - m_normal_points.resize(text_num); - - Vec3d pos_dir = m_cut_plane_dir_in_world.cross(mouse_normal_world); - pos_dir.normalize(); - if (text_num % 2 == 1) { - m_position_points[text_num / 2] = mouse_position_world; - for (int i = 0; i < text_num / 2; ++i) { - double left_gap = text_lengths[text_num / 2 - i - 1] + m_text_gap + text_lengths[text_num / 2 - i]; - if (left_gap < 0) - left_gap = 0; - - double right_gap = text_lengths[text_num / 2 + i + 1] + m_text_gap + text_lengths[text_num / 2 + i]; - if (right_gap < 0) - right_gap = 0; - - m_position_points[text_num / 2 - 1 - i] = m_position_points[text_num / 2 - i] - left_gap * pos_dir; - m_position_points[text_num / 2 + 1 + i] = m_position_points[text_num / 2 + i] + right_gap * pos_dir; - } - } else { - for (int i = 0; i < text_num / 2; ++i) { - double left_gap = i == 0 ? (text_lengths[text_num / 2 - i - 1] + m_text_gap / 2) : - (text_lengths[text_num / 2 - i - 1] + m_text_gap + text_lengths[text_num / 2 - i]); - if (left_gap < 0) - left_gap = 0; - - double right_gap = i == 0 ? (text_lengths[text_num / 2 + i] + m_text_gap / 2) : - (text_lengths[text_num / 2 + i] + m_text_gap + text_lengths[text_num / 2 + i - 1]); - if (right_gap < 0) - right_gap = 0; - - if (i == 0) { - m_position_points[text_num / 2 - 1 - i] = mouse_position_world - left_gap * pos_dir; - m_position_points[text_num / 2 + i] = mouse_position_world + right_gap * pos_dir; - continue; - } - - m_position_points[text_num / 2 - 1 - i] = m_position_points[text_num / 2 - i] - left_gap * pos_dir; - m_position_points[text_num / 2 + i] = m_position_points[text_num / 2 + i - 1] + right_gap * pos_dir; - } - } - - for (int i = 0; i < text_num; ++i) { - m_normal_points[i] = mouse_normal_world; - } - - return true; - } - - MeshSlicingParams slicing_params; - auto cut_tran = (m_text_tran_in_object.get_matrix() * rotate_tran); - slicing_params.trafo = cut_tran.inverse(); - // for debug - // its_write_obj(slice_meshs.its, "D:/debug_files/mesh.obj"); - // generate polygons - const Polygons temp_polys = slice_mesh(slice_meshs.its, 0, slicing_params); - Vec3d scale_click_pt(scale_(0), scale_(0), 0); - // for debug - // export_regions_to_svg(Point(scale_pt.x(), scale_pt.y()), temp_polys); - - Polygons polys = union_(temp_polys); - - auto point_in_line_rectange = [](const Line &line, const Point &point, double& distance) { - distance = line.distance_to(point); - return distance < line.length() / 2; - }; - - int index = 0; - double min_distance = 1e12; - Polygon hit_ploy; - for (const Polygon poly : polys) { - if (poly.points.size() == 0) - continue; - - Lines lines = poly.lines(); - for (int i = 0; i < lines.size(); ++i) { - Line line = lines[i]; - double distance = min_distance; - if (point_in_line_rectange(line, Point(scale_click_pt.x(), scale_click_pt.y()), distance)) { - if (distance < min_distance) { - min_distance = distance; - index = i; - hit_ploy = poly; - } - } - } - } - - if (hit_ploy.points.size() == 0) { - BOOST_LOG_TRIVIAL(info) << boost::format("Text: the hit polygon is null"); - return false; - } - - m_cut_points_in_world.clear(); - m_cut_points_in_world.reserve(hit_ploy.points.size()); - for (int i = 0; i < hit_ploy.points.size(); ++i) { - m_cut_points_in_world.emplace_back(m_text_cs_to_world_tran * rotate_tran * Vec3d(unscale_(hit_ploy.points[i].x()), unscale_(hit_ploy.points[i].y()), 0)); - } - - Polygon_3D new_polygon(m_cut_points_in_world); - m_position_points.resize(text_num); - if (text_num % 2 == 1) { - m_position_points[text_num / 2] = Vec3d(mouse_position_world.x(), mouse_position_world.y(), mouse_position_world.z()); - - std::vector lines = new_polygon.get_lines(); - Line_3D line = lines[index]; - { - int index1 = index; - double left_length = (mouse_position_world - line.a).cast().norm(); - int left_num = text_num / 2; - while (left_num > 0) { - double gap_length = (text_lengths[left_num] + m_text_gap + text_lengths[left_num - 1]); - if (gap_length < 0) - gap_length = 0; - - while (gap_length > left_length) { - gap_length -= left_length; - if (index1 == 0) - index1 = lines.size() - 1; - else - --index1; - left_length = lines[index1].length(); - } - - Vec3d direction = lines[index1].vector(); - direction.normalize(); - double distance_to_a = (left_length - gap_length); - Line_3D new_line = lines[index1]; - - double norm_value = direction.cast().norm(); - double deta_x = distance_to_a * direction.x() / norm_value; - double deta_y = distance_to_a * direction.y() / norm_value; - double deta_z = distance_to_a * direction.z() / norm_value; - Vec3d new_pos = new_line.a + Vec3d(deta_x, deta_y, deta_z); - left_num--; - m_position_points[left_num] = new_pos; - left_length = distance_to_a; - } - } - - { - int index2 = index; - double right_length = (line.b - mouse_position_world).cast().norm(); - int right_num = text_num / 2; - while (right_num > 0) { - double gap_length = (text_lengths[text_num - right_num] + m_text_gap + text_lengths[text_num - right_num - 1]); - if (gap_length < 0) - gap_length = 0; - - while (gap_length > right_length) { - gap_length -= right_length; - if (index2 == lines.size() - 1) - index2 = 0; - else - ++index2; - right_length = lines[index2].length(); - } - - Line_3D line2 = lines[index2]; - line2.reverse(); - Vec3d direction = line2.vector(); - direction.normalize(); - double distance_to_b = (right_length - gap_length); - Line_3D new_line = lines[index2]; - - double norm_value = direction.cast().norm(); - double deta_x = distance_to_b * direction.x() / norm_value; - double deta_y = distance_to_b * direction.y() / norm_value; - double deta_z = distance_to_b * direction.z() / norm_value; - Vec3d new_pos = new_line.b + Vec3d(deta_x, deta_y, deta_z); - m_position_points[text_num - right_num] = new_pos; - right_length = distance_to_b; - right_num--; - } - } - } - else { - for (int i = 0; i < text_num / 2; ++i) { - std::vector lines = new_polygon.get_lines(); - Line_3D line = lines[index]; - { - int index1 = index; - double left_length = (mouse_position_world - line.a).cast().norm(); - int left_num = text_num / 2; - for (int i = 0; i < text_num / 2; ++i) { - double gap_length = 0; - if (i == 0) { - gap_length = m_text_gap / 2 + text_lengths[text_num / 2 - 1 - i]; - } - else { - gap_length = text_lengths[text_num / 2 - i] + m_text_gap + text_lengths[text_num / 2 - 1 - i]; - } - if (gap_length < 0) - gap_length = 0; - - while (gap_length > left_length) { - gap_length -= left_length; - if (index1 == 0) - index1 = lines.size() - 1; - else - --index1; - left_length = lines[index1].length(); - } - - Vec3d direction = lines[index1].vector(); - direction.normalize(); - double distance_to_a = (left_length - gap_length); - Line_3D new_line = lines[index1]; - - double norm_value = direction.cast().norm(); - double deta_x = distance_to_a * direction.x() / norm_value; - double deta_y = distance_to_a * direction.y() / norm_value; - double deta_z = distance_to_a * direction.z() / norm_value; - Vec3d new_pos = new_line.a + Vec3d(deta_x, deta_y,deta_z); - - m_position_points[text_num / 2 - 1 - i] = new_pos; - left_length = distance_to_a; - } - } - - { - int index2 = index; - double right_length = (line.b - mouse_position_world).cast().norm(); - int right_num = text_num / 2; - double gap_length = 0; - for (int i = 0; i < text_num / 2; ++i) { - double gap_length = 0; - if (i == 0) { - gap_length = m_text_gap / 2 + text_lengths[text_num / 2 + i]; - } else { - gap_length = text_lengths[text_num / 2 + i] + m_text_gap + text_lengths[text_num / 2 + i - 1]; - } - if (gap_length < 0) - gap_length = 0; - - while (gap_length > right_length) { - gap_length -= right_length; - if (index2 == lines.size() - 1) - index2 = 0; - else - ++index2; - right_length = lines[index2].length(); - } - - Line_3D line2 = lines[index2]; - line2.reverse(); - Vec3d direction = line2.vector(); - direction.normalize(); - double distance_to_b = (right_length - gap_length); - Line_3D new_line = lines[index2]; - - double norm_value = direction.cast().norm(); - double deta_x = distance_to_b * direction.x() / norm_value; - double deta_y = distance_to_b * direction.y() / norm_value; - double deta_z = distance_to_b * direction.z() / norm_value; - Vec3d new_pos = new_line.b + Vec3d(deta_x, deta_y, deta_z); - m_position_points[text_num / 2 + i] = new_pos; - right_length = distance_to_b; - } - } - } - } - - TriangleMesh mesh = slice_meshs; - std::vector mesh_values(m_position_points.size(), 1e9); - m_normal_points.resize(m_position_points.size()); - auto point_in_triangle_delete_area = [](const Vec3d &point, const Vec3d &point0, const Vec3d &point1, const Vec3d &point2) { - Vec3d p0_p = point - point0; - Vec3d p0_p1 = point1 - point0; - Vec3d p0_p2 = point2 - point0; - Vec3d p_p0 = point0 - point; - Vec3d p_p1 = point1 - point; - Vec3d p_p2 = point2 - point; - - double s = p0_p1.cross(p0_p2).norm(); - double s0 = p_p0.cross(p_p1).norm(); - double s1 = p_p1.cross(p_p2).norm(); - double s2 = p_p2.cross(p_p0).norm(); - - return abs(s0 + s1 + s2 - s); - }; - bool is_mirrored =m_model_object_in_world_tran.is_left_handed(); - for (int i = 0; i < m_position_points.size(); ++i) { - for (auto indice : mesh.its.indices) { - stl_vertex stl_point0 = mesh.its.vertices[indice[0]]; - stl_vertex stl_point1 = mesh.its.vertices[indice[1]]; - stl_vertex stl_point2 = mesh.its.vertices[indice[2]]; - - Vec3d point0 = Vec3d(stl_point0[0], stl_point0[1], stl_point0[2]); - Vec3d point1 = Vec3d(stl_point1[0], stl_point1[1], stl_point1[2]); - Vec3d point2 = Vec3d(stl_point2[0], stl_point2[1], stl_point2[2]); - - point0 = mi->get_transformation().get_matrix() * point0; - point1 = mi->get_transformation().get_matrix() * point1; - point2 = mi->get_transformation().get_matrix() * point2; - - double abs_area = point_in_triangle_delete_area(m_position_points[i], point0, point1, point2); - if (mesh_values[i] > abs_area) { - mesh_values[i] = abs_area; - - Vec3d s1 = point1 - point0; - Vec3d s2 = point2 - point0; - m_normal_points[i] = s1.cross(s2); - m_normal_points[i].normalize(); - if(is_mirrored){ - m_normal_points[i] = -m_normal_points[i]; - } - } - } + auto text_tran_in_object = m_model_object_in_world_tran.get_matrix().inverse() * + m_text_tran_in_world.get_matrix(); // Geometry::generate_transform(cs_x_dir, cs_y_dir, cs_z_dir, text_position_in_object); // todo modify by m_text_tran_in_world + Geometry::Transformation text_tran_in_object_(text_tran_in_object); + if (rewrite_text_tran) { + m_text_tran_in_object.set_matrix(text_tran_in_object); // for preview } return true; } -TriangleMesh GLGizmoText::get_text_mesh(const char* text_str, const Vec3d &position, const Vec3d &normal, const Vec3d& text_up_dir) +void GLGizmoText::update_trafo_matrices() { - TextResult text_result; - load_text_shape(text_str, m_font_name.c_str(), m_font_size, m_thickness + m_embeded_depth, m_bold, m_italic, text_result); - TriangleMesh mesh = text_result.text_mesh; - - - mesh.translate(-text_result.text_width / 2, -m_font_size / 4, 0); - - double phi; - Vec3d rotation_axis; - Matrix3d rotation_matrix; - Geometry::rotation_from_two_vectors(Vec3d::UnitZ(), normal, rotation_axis, phi, &rotation_matrix); - mesh.rotate(phi, rotation_axis); - - auto project_on_plane = [](const Vec3d& dir, const Vec3d& plane_normal) -> Vec3d { - return dir - (plane_normal.dot(dir) * plane_normal.dot(plane_normal)) * plane_normal; - }; - - Vec3d old_text_dir = Vec3d::UnitY(); - old_text_dir = rotation_matrix * old_text_dir; - Vec3d new_text_dir = project_on_plane(text_up_dir, normal); - new_text_dir.normalize(); - Geometry::rotation_from_two_vectors(old_text_dir, new_text_dir, rotation_axis, phi, &rotation_matrix); - - if (abs(phi - PI) < EPSILON) - rotation_axis = normal; - - mesh.rotate(phi, rotation_axis); - - - Vec3d offset = position - m_embeded_depth * normal; - mesh.translate(offset.x(), offset.y(), offset.z()); - - return mesh;//mesh in object cs + m_trafo_matrices.clear(); + const Selection &selection = m_parent.get_selection(); + auto mo = selection.get_model()->objects[m_object_idx]; + if (mo == nullptr) + return; + const ModelInstance *mi = mo->instances[selection.get_instance_idx()]; + for (ModelVolume *mv : mo->volumes) { + m_trafo_matrices.emplace_back(mi->get_transformation().get_matrix() * mv->get_matrix()); + } } -bool GLGizmoText::update_raycast_cache(const Vec2d &mouse_position, const Camera &camera, const std::vector &trafo_matrices) +void GLGizmoText::update_cut_plane_dir() +{ + auto cur_world = m_model_object_in_world_tran.get_matrix() * m_text_tran_in_object.get_matrix(); + m_cut_plane_dir_in_world = cur_world.linear().col(1).normalized(); +} + +void GLGizmoText::switch_text_type(TextInfo::TextType type) +{ + m_need_update_tran = true; + m_need_update_text = true; + if (m_surface_type == TextInfo::TextType::SURFACE_HORIZONAL && type != TextInfo::TextType::SURFACE_HORIZONAL) { + update_text_normal_in_world(); + } + m_surface_type = type; + process(); +} + +float GLGizmoText::get_angle_from_current_style() +{ + StyleManager::Style ¤t_style = m_style_manager.get_style(); + float angle = current_style.angle.value_or(0.f); + float angle_deg = static_cast(angle * 180 / M_PI); + if (abs(angle_deg) < 0.0001) { + return 0.f; + } + return angle_deg; +} + +void GLGizmoText::volume_transformation_changed() +{ + auto text_volume = m_last_text_mv; + if (!text_volume) { + return; + } + m_need_update_text = true; + auto &tc = text_volume->get_text_info().text_configuration; + //const EmbossShape & es = *text_volume->emboss_shape; + + //bool per_glyph = tc.style.prop.per_glyph; + //if (per_glyph) init_text_lines(m_text_lines, m_parent.get_selection(), m_style_manager, m_text_lines.get_lines().size()); + + //bool use_surface = es.projection.use_surface; + + // Update surface by new position + //if (use_surface || per_glyph) + // process(); + //else { + // // inform slicing process that model changed + // // SLA supports, processing + // // ensure on bed + // wxGetApp().plater()->changed_object(*m_volume->get_object()); + //} + m_style_manager.get_style().angle = calc_angle(m_parent.get_selection()); + m_rotate_angle = get_angle_from_current_style(); + process(); + // Show correct value of height & depth inside of inputs + calculate_scale(); +} + + + +bool GLGizmoText::update_raycast_cache(const Vec2d &mouse_position, const Camera &camera, const std::vector &trafo_matrices, bool exclude_last) { if (m_rr.mouse_position == mouse_position) { return false; } - if (m_is_modify) - return false; - Vec3f normal = Vec3f::Zero(); Vec3f hit = Vec3f::Zero(); size_t facet = 0; @@ -1814,15 +3114,11 @@ bool GLGizmoText::update_raycast_cache(const Vec2d &mouse_position, const Camera // Cast a ray on all meshes, pick the closest hit and save it for the respective mesh for (int mesh_id = 0; mesh_id < int(trafo_matrices.size()); ++mesh_id) { - if (m_preview_text_volume_id != -1 && mesh_id == int(trafo_matrices.size()) - 1) + if (exclude_last && mesh_id == int(trafo_matrices.size()) - 1) continue; - - if (m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh(mouse_position, trafo_matrices[mesh_id], camera, hit, normal, m_c->object_clipper()->get_clipping_plane(), + if (mesh_id < m_c->raycaster()->raycasters().size()&& m_c->raycaster()->raycasters()[mesh_id]->unproject_on_mesh(mouse_position, trafo_matrices[mesh_id], camera, hit, normal, + m_c->object_clipper()->get_clipping_plane(), &facet)) { - // In case this hit is clipped, skip it. - if (is_mesh_point_clipped(hit.cast(), trafo_matrices[mesh_id])) - continue; - double hit_squared_distance = (camera.get_position() - trafo_matrices[mesh_id] * hit.cast()).squaredNorm(); if (hit_squared_distance < closest_hit_squared_distance) { closest_hit_squared_distance = hit_squared_distance; @@ -1837,133 +3133,110 @@ bool GLGizmoText::update_raycast_cache(const Vec2d &mouse_position, const Camera return true; } -void GLGizmoText::generate_text_volume(bool is_temp) +bool GLGizmoText::generate_text_volume() { - std::string text = std::string(m_text); - if (text.empty()) - return; - - std::wstring_convert> str_cnv; - std::wstring ws = boost::nowide::widen(m_text); - std::vector alphas; - for (auto w : ws) { - alphas.push_back(str_cnv.to_bytes(w)); - } - - update_text_positions(alphas);//update m_model_object_in_world_tran - - if (m_position_points.size() == 0) - return; - auto inv_text_cs_in_object = (m_model_object_in_world_tran.get_matrix() * m_text_tran_in_object.get_matrix()).inverse(); - auto inv_text_cs_in_object_no_offset = (m_model_object_in_world_tran.get_matrix_no_offset() * m_text_tran_in_object.get_matrix_no_offset()).inverse(); - TriangleMesh mesh; - for (int i = 0; i < alphas.size(); ++i) { - auto position = inv_text_cs_in_object * m_position_points[i]; - auto normal = inv_text_cs_in_object_no_offset * m_normal_points[i]; - auto cut_plane_dir = inv_text_cs_in_object_no_offset * m_cut_plane_dir_in_world; - TriangleMesh sub_mesh = get_text_mesh(alphas[i].c_str(), position, normal, cut_plane_dir); - mesh.merge(sub_mesh); - } - if (mesh.empty()) - return; - auto center = mesh.bounding_box().center(); - if (abs(mesh.bounding_box().size()[2] - (m_embeded_depth + m_thickness)) < 0.01) { - mesh.translate(Vec3f(-center.x(), -center.y(), 0)); // align horizontal and vertical center - } - else { - mesh.translate(Vec3f(0, -center.y(), 0)); // align vertical center - } - + if (m_text.empty()) + return false; Plater *plater = wxGetApp().plater(); if (!plater) - return; + return false; + m_show_calc_meshtod = 0; + if (m_object_idx < 0) {//m_object_idx < 0 && !is_only_text_case() + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "error:m_object_idx < 0"; + return false; + } + if (auto text_mv = get_text_is_dragging()) { // moving or dragging; + m_show_calc_meshtod = 2; + update_text_tran_in_model_object(); + text_mv->set_transformation(m_text_tran_in_object.get_matrix()); + return true; + /* auto gl_v = get_selected_gl_volume(m_parent); + gl_v->set_volume_transformation(m_text_tran_in_object.get_matrix()); + return false;*/ + } + bool rewrite_text_tran = m_need_update_tran ? true : m_last_text_mv == nullptr; + if (!update_text_tran_in_model_object(rewrite_text_tran)) { + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " fail:update_text_tran_in_model_object"; + return false; + } + + const Selection & selection = m_parent.get_selection(); + Emboss::GenerateTextJob::InputInfo input_info; + auto text_volume = m_last_text_mv; + DataBasePtr base = create_emboss_data_base(m_text, m_style_manager, selection, text_volume == nullptr ? ModelVolumeType::MODEL_PART : text_volume->type(), + m_job_cancel); // m_text_lines + Emboss::DataUpdate data_update = {std::move(base), text_volume == nullptr ? -1 : text_volume->id(), false}; + m_ui_text_configuration = data_update.base->get_text_configuration(); + input_info.text_surface_type = m_surface_type; + input_info.is_outside = data_update.base->is_outside; + input_info.shape_scale = data_update.base->shape.scale; + input_info.m_data_update = std::move(data_update); + + input_info.m_text_tran_in_object = m_text_tran_in_object; + + input_info.m_volume_idx = m_volume_idx ; + input_info.first_generate = m_volume_idx < 0 ? true : false; + input_info.m_cut_points_in_world = m_cut_points_in_world; + input_info.m_text_tran_in_world = m_text_tran_in_world; + + input_info.m_text_position_in_world = m_text_position_in_world; + input_info.m_text_normal_in_world = m_text_normal_in_world; + input_info.m_text_gap = m_text_gap; + input_info.m_model_object_in_world_tran = m_model_object_in_world_tran; + input_info.m_position_points = m_position_points; + input_info.m_normal_points = m_normal_points; + input_info.m_embeded_depth = m_embeded_depth; + input_info.m_thickness = m_thickness; + input_info.m_cut_plane_dir_in_world = m_cut_plane_dir_in_world; + input_info.use_surface = m_surface_type == TextInfo::TextType::SURFACE_CHAR ? true : false; TextInfo text_info = get_text_info(); - const Selection &selection = m_parent.get_selection(); - ModelObject * model_object = selection.get_model()->objects[m_object_idx]; - int cur_volume_id; - if (m_is_modify && m_need_update_text) { - if (m_object_idx == -1 || m_volume_idx == -1) { - BOOST_LOG_TRIVIAL(error) << boost::format("Text: selected object_idx = %1%, volume_idx = %2%") % m_object_idx % m_volume_idx; - return; - } - if (!is_temp) { - plater->take_snapshot("Modify Text"); - } - text_info.m_font_version = CUR_FONT_VERSION; - ModelVolume * model_volume = model_object->volumes[m_volume_idx]; - ModelVolume * new_model_volume = model_object->add_volume(std::move(mesh),false); - if (m_need_fix && // m_reedit_text//m_need_fix - (m_text_type == TextType::HORIZONAL || (m_text_type > TextType::HORIZONAL && m_use_current_pose))) { - new_model_volume->set_transformation(m_load_text_tran_in_object.get_matrix()); - } - else { - new_model_volume->set_transformation(m_text_tran_in_object.get_matrix()); - } - new_model_volume->set_text_info(text_info); - new_model_volume->name = model_volume->name; - new_model_volume->set_type(model_volume->type()); - new_model_volume->config.apply(model_volume->config); - std::swap(model_object->volumes[m_volume_idx], model_object->volumes.back()); - model_object->delete_volume(model_object->volumes.size() - 1); - model_object->invalidate_bounding_box(); - plater->update(); - m_text_volume_tran = new_model_volume->get_matrix(); - } else { - if (!is_temp && m_need_update_text) - plater->take_snapshot("Add Text"); - ObjectList *obj_list = wxGetApp().obj_list(); - cur_volume_id = obj_list->add_text_part(mesh, "text_shape", text_info, m_text_tran_in_object.get_matrix(), is_temp); - m_preview_text_volume_id = is_temp ? cur_volume_id : -1; - if (cur_volume_id >= 0) { - m_show_warning_text_create_fail = false; - ModelVolume *text_model_volume = model_object->volumes[cur_volume_id]; - m_text_volume_tran = text_model_volume->get_matrix(); - } - else { - m_show_warning_text_create_fail = true; - } - } - m_need_update_text = false; + ModelObject *model_object = selection.get_model()->objects[m_object_idx]; + input_info.mo = model_object; + m_show_warning_lost_rotate = false; -} - -void GLGizmoText::delete_temp_preview_text_volume() -{ - const Selection &selection = m_parent.get_selection(); - if (m_preview_text_volume_id > 0) { - ModelObject *model_object = selection.get_model()->objects[m_object_idx]; - if (m_preview_text_volume_id < model_object->volumes.size()) { - Plater *plater = wxGetApp().plater(); - if (!plater) - return; - - model_object->delete_volume(m_preview_text_volume_id); - - plater->update(); - } - m_preview_text_volume_id = -1; + if (m_need_update_text) { + m_need_update_text = false; + input_info.text_info = text_info; + input_info.m_final_text_tran_in_object = m_text_tran_in_object; + auto job = std::make_unique(std::move(input_info)); + auto &worker = wxGetApp().plater()->get_ui_job_worker(); + queue_job(worker, std::move(job)); } + return true; } TextInfo GLGizmoText::get_text_info() { TextInfo text_info; + const wxFont &wx_font = m_style_manager.get_wx_font(); + if (wx_font.IsOk()) { + auto actual_face_name = wx_font.GetFaceName(); + m_cur_font_name = actual_face_name; + m_font_name = actual_face_name.utf8_string(); + } text_info.m_font_name = m_font_name; text_info.m_font_version = m_font_version; + text_info.text_configuration.style.name = m_style_name; + m_font_size = m_style_manager.get_font_prop().size_in_mm; text_info.m_font_size = m_font_size; - text_info.m_curr_font_idx = m_curr_font_idx; + text_info.m_curr_font_idx = -1; + m_bold = m_style_manager.get_font_prop().boldness > 0; + m_italic = m_style_manager.get_font_prop().skew > 0; text_info.m_bold = m_bold; text_info.m_italic = m_italic; + text_info.m_thickness = m_thickness; + text_info.m_embeded_depth = m_embeded_depth; + text_info.m_text = m_text; text_info.m_rr.mesh_id = m_rr.mesh_id; - text_info.m_embeded_depth = m_embeded_depth; text_info.m_rotate_angle = m_rotate_angle; text_info.m_text_gap = m_text_gap; - text_info.m_is_surface_text = m_text_type == TextType::SURFACE || m_text_type == TextType::SURFACE_HORIZONAL; - text_info.m_keep_horizontal = m_text_type == TextType::HORIZONAL || m_text_type == TextType::SURFACE_HORIZONAL; + text_info.m_surface_type = m_surface_type; + text_info.text_configuration = m_ui_text_configuration; + text_info.m_font_version = CUR_FONT_VERSION; return text_info; } @@ -1971,24 +3244,688 @@ void GLGizmoText::load_from_text_info(const TextInfo &text_info) { m_font_name = text_info.m_font_name; m_font_version = text_info.m_font_version; + m_style_name = text_info.text_configuration.style.name; m_font_size = text_info.m_font_size; - // from other user's computer may exist case:font library size is different - if (text_info.m_curr_font_idx < m_font_names.size()) { - m_curr_font_idx = text_info.m_curr_font_idx; - } - else { - m_curr_font_idx = 0; - } + m_bold = text_info.m_bold; m_italic = text_info.m_italic; m_thickness = text_info.m_thickness; - strcpy(m_text, text_info.m_text.c_str()); - m_rr.mesh_id = text_info.m_rr.mesh_id; + bool is_text_changed = m_text != text_info.m_text; + m_text = text_info.m_text; + m_rr.mesh_id = text_info.m_rr.mesh_id; m_embeded_depth = text_info.m_embeded_depth; - m_rotate_angle = text_info.m_rotate_angle; + double limit_angle = Geometry::deg2rad((double) text_info.m_rotate_angle); + Geometry::to_range_pi_pi(limit_angle); + m_rotate_angle = (float) Geometry::rad2deg(limit_angle); + m_text_gap = text_info.m_text_gap; - check_text_type(text_info.m_is_surface_text, text_info.m_keep_horizontal); + m_surface_type = (TextInfo::TextType) text_info.m_surface_type; + + if (is_old_text_info(text_info)) { // compatible with older versions + load_old_font(); + auto cur_text_info = const_cast(&text_info); + cur_text_info->text_configuration.style = m_style_manager.get_style(); + } + else { + m_style_manager.get_font_prop().size_in_mm = m_font_size;//m_font_size + wxString font_name = wxString::FromUTF8(m_font_name.c_str());//wxString(m_font_name.c_str(), wxConvUTF8); + select_facename(font_name,false); + } + update_boldness(); + update_italic(); + if (is_text_changed) { + process(true,std::nullopt,false); + } } +bool GLGizmoText::is_old_text_info(const TextInfo &text_info) +{ + auto cur_font_version = text_info.m_font_version; + bool is_old = cur_font_version.empty(); + if (!cur_font_version.empty()) { + float version = std::atof(cur_font_version.c_str()); + if (version < 2.0) { + is_old = true; + } + } + return is_old; +} + +std::string concat(std::vector data) +{ + std::stringstream ss; + for (const auto &d : data) ss << d.c_str() << ", "; + return ss.str(); +} +boost::filesystem::path get_fontlist_cache_path() { + return boost::filesystem::path(data_dir()) / "cache" / "fonts.cereal"; +} +bool store(const CurFacenames &facenames) +{ + std::string cache_path = get_fontlist_cache_path().string(); + boost::filesystem::path path(cache_path); + if (!boost::filesystem::exists(path.parent_path())) { boost::filesystem::create_directory(path.parent_path()); } + boost::nowide::ofstream file(cache_path, std::ios::binary); + ::cereal::BinaryOutputArchive archive(file); + std::vector good; + good.reserve(facenames.faces.size()); + for (const FaceName &face : facenames.faces) good.push_back(face.wx_name); + FacenamesSerializer data = {facenames.hash, good, facenames.bad}; + + assert(std::is_sorted(data.bad.begin(), data.bad.end())); + assert(std::is_sorted(data.good.begin(), data.good.end())); + try { + archive(data); + } catch (const std::exception &ex) { + BOOST_LOG_TRIVIAL(error) << "Failed to write fontlist cache - " << cache_path << ex.what(); + return false; + } + return true; +} +bool load(CurFacenames &facenames, const std::vector &delete_bad_font_list) +{ + boost::filesystem::path path = get_fontlist_cache_path(); + std::string path_str = path.string(); + if (!boost::filesystem::exists(path)) { + BOOST_LOG_TRIVIAL(warning) << "Fontlist cache - '" << PathSanitizer::sanitize(path_str) << "' does not exists."; + return false; + } + boost::nowide::ifstream file(path_str, std::ios::binary); + ::cereal::BinaryInputArchive archive(file); + + FacenamesSerializer data; + try { + archive(data); + } catch (const std::exception &ex) { + BOOST_LOG_TRIVIAL(error) << "Failed to load fontlist cache - '" << PathSanitizer::sanitize(path_str) << "'. Exception: " << ex.what(); + return false; + } + + assert(std::is_sorted(data.bad.begin(), data.bad.end())); + assert(std::is_sorted(data.good.begin(), data.good.end())); + + facenames.hash = data.hash; + facenames.faces.reserve(data.good.size()); + for (const wxString &face : data.good) { + bool is_find = std::find(delete_bad_font_list.begin(), delete_bad_font_list.end(), face) != delete_bad_font_list.end(); + if (is_find) { + continue; + } + facenames.faces.push_back({face}); + } + facenames.bad = data.bad; + + return true; +} + +// validation lambda +bool is_valid_font(const wxString &name, wxFontEncoding encoding, std::vector bad ) // face_names.encoding , face_names.bad +{ + if (name.empty()) + return false; + + // vertical font start with @, we will filter it out + // Not sure if it is only in Windows so filtering is on all platforms + if (name[0] == '@') return false; + + // previously detected bad font + auto it = std::lower_bound(bad.begin(), bad.end(), name); + if (it != bad.end() && *it == name) return false; + + wxFont wx_font(wxFontInfo().FaceName(name).Encoding(encoding)); + //* + // Faster chech if wx_font is loadable but not 100% + // names could contain not loadable font + if (!WxFontUtils::can_load(wx_font)) return false; + + /*/ + // Slow copy of font files to try load font + // After this all files are loadable + auto font_file = WxFontUtils::create_font_file(wx_font); + if (font_file == nullptr) + return false; // can't create font file + // */ + return true; +} + +bool draw_button(const IconManager::VIcons &icons, IconType type, bool disable) +{ + return Slic3r::GUI::button(get_icon(icons, type, IconState::activable), get_icon(icons, type, IconState::hovered), get_icon(icons, type, IconState::disabled), disable); +} + +void init_face_names(CurFacenames &face_names) +{ + Timer t("enumerate_fonts"); + if (face_names.is_init) return; + face_names.is_init = true; + + // to reload fonts from system, when install new one + wxFontEnumerator::InvalidateCache(); + std::vector delete_bad_font_list;//QDS +#ifdef _WIN32 + delete_bad_font_list = {"Symbol","Wingdings","Wingdings 2","Wingdings 3", "Webdings"}; +#endif + // try load cache + // Only not OS enumerated face has hash value 0 + if (face_names.hash == 0) { + load(face_names, delete_bad_font_list); + face_names.has_truncated_names = false; + } + + using namespace std::chrono; + steady_clock::time_point enumerate_start = steady_clock::now(); + ScopeGuard sg([&enumerate_start, &face_names = face_names]() { + steady_clock::time_point enumerate_end = steady_clock::now(); + long long enumerate_duration = duration_cast(enumerate_end - enumerate_start).count(); + BOOST_LOG_TRIVIAL(info) << "OS enumerate " << face_names.faces.size() << " fonts " + << "(+ " << face_names.bad.size() << " can't load " + << "= " << face_names.faces.size() + face_names.bad.size() << " fonts) " + << "in " << enumerate_duration << " ms\n" + << concat(face_names.bad); + }); + wxArrayString facenames = wxFontEnumerator::GetFacenames(face_names.encoding); + size_t hash = boost::hash_range(facenames.begin(), facenames.end()); + // Zero value is used as uninitialized hash + if (hash == 0) hash = 1; + // check if it is same as last time + if (face_names.hash == hash) { + if (face_names.faces_names.size() == 0) {//FIX by bbs + face_names.faces_names.clear(); + face_names.faces_names.reserve(face_names.faces.size()); + for (size_t i = 0; i < face_names.faces.size(); i++) { + auto cur = face_names.faces[i].wx_name.ToUTF8().data(); + face_names.faces_names.push_back(cur); + } + } + // no new installed font + BOOST_LOG_TRIVIAL(info) << "Same FontNames hash, cache is used. " + << "For clear cache delete file: " << PathSanitizer::sanitize(get_fontlist_cache_path()); + return; + } + + BOOST_LOG_TRIVIAL(info) << ((face_names.hash == 0) ? "FontName list is generate from scratch." : "Hash are different. Only previous bad fonts are used and set again as bad"); + face_names.hash = hash; + + face_names.faces.clear(); + face_names.faces_names.clear(); + face_names.bad.clear(); + face_names.faces.reserve(facenames.size()); + face_names.faces_names.reserve(facenames.size()); + std::sort(facenames.begin(), facenames.end()); + + for (const wxString& name : facenames) { + bool is_find = std::find(delete_bad_font_list.begin(), delete_bad_font_list.end(), name) != delete_bad_font_list.end(); + if (is_find) { + face_names.bad.push_back(name); + continue; + } + if (is_valid_font(name, face_names.encoding, face_names.bad)) { + face_names.faces.push_back({name}); + face_names.faces_names.push_back(name.utf8_string()); + } else { + face_names.bad.push_back(name); + } + } + assert(std::is_sorted(face_names.bad.begin(), face_names.bad.end())); + face_names.has_truncated_names = false; + store(face_names); +} +void init_truncated_names(CurFacenames& face_names, float max_width) { + for (FaceName &face : face_names.faces) { + std::string name_str(face.wx_name.ToUTF8().data()); + face.name_truncated = ImGuiWrapper::trunc(name_str, max_width); + } + face_names.has_truncated_names = true; +} + +std::optional get_installed_face_name(const std::optional& face_name_opt, CurFacenames& face_names) { + // Could exist OS without getter on face_name, + // but it is able to restore font from descriptor + // Soo default value must be TRUE + if (!face_name_opt.has_value()) + return wxString(); + + wxString face_name = wxString::FromUTF8(face_name_opt->c_str()); + + // search in enumerated fonts + // refresh list of installed font in the OS. + init_face_names(face_names); + face_names.is_init = false; + + auto cmp = [](const FaceName &fn, const wxString &wx_name) { return fn.wx_name < wx_name; }; + const std::vector &faces = face_names.faces; + // is font installed? + if (auto it = std::lower_bound(faces.begin(), faces.end(), face_name, cmp); it != faces.end() && it->wx_name == face_name) return face_name; + + const std::vector &bad = face_names.bad; + auto it_bad = std::lower_bound(bad.begin(), bad.end(), face_name); + if (it_bad == bad.end() || *it_bad != face_name) { + // check if wx allowed to set it up - another encoding of name + wxFontEnumerator::InvalidateCache(); + wxFont wx_font_; // temporary structure + if (wx_font_.SetFaceName(face_name) && WxFontUtils::create_font_file(wx_font_) != nullptr // can load TTF file? + ) { + return wxString(); + // QUESTION: add this name to allowed faces? + // Could create twin of font face name + // When not add it will be hard to select it again when change font + } + } + return {}; // not installed +} + +void draw_font_preview(FaceName& face, const std::string& text, CurFacenames& faces, const CurGuiCfg& cfg, bool is_visible) +{ + // Limit for opened font files at one moment + unsigned int &count_opened_fonts = faces.count_opened_font_files; + // Size of texture + ImVec2 size(cfg.face_name_size.x(), cfg.face_name_size.y()); + float count_cached_textures_f = static_cast(faces.count_cached_textures); + std::string state_text; + // uv0 and uv1 set to pixel 0,0 in texture + ImVec2 uv0(0.f, 0.f), uv1(1.f / size.x, 1.f / size.y / count_cached_textures_f); + if (face.is_created != nullptr) { + // not created preview + if (*face.is_created) { + // Already created preview + size_t texture_index = face.texture_index; + uv0 = ImVec2(0.f, texture_index / count_cached_textures_f); + uv1 = ImVec2(1.f, (texture_index + 1) / count_cached_textures_f); + } else { + // Not finished preview + if (is_visible) { + // when not canceled still loading + state_text = (face.cancel->load()) ? " " + _u8L("No symbol") : " ... " + _u8L("Loading"); + } else { + // not finished and not visible cancel job + face.is_created = nullptr; + face.cancel->store(true); + } + } + } else if (is_visible && count_opened_fonts < cfg.max_count_opened_font_files) { + ++count_opened_fonts; + face.cancel = std::make_shared(false); + face.is_created = std::make_shared(false); + + const unsigned char gray_level = 5; + // format type and level must match to texture data + const GLenum format = GL_RGBA, type = GL_UNSIGNED_BYTE; + const GLint level = 0; + // select next texture index + size_t texture_index = (faces.texture_index + 1) % faces.count_cached_textures; + + // set previous cach as deleted + for (FaceName &f : faces.faces) + if (f.texture_index == texture_index) { + if (f.cancel != nullptr) f.cancel->store(true); + f.is_created = nullptr; + } + + faces.texture_index = texture_index; + face.texture_index = texture_index; + + // render text to texture + FontImageData data{ + text, face.wx_name, faces.encoding, faces.texture_id, faces.texture_index, cfg.face_name_size, gray_level, format, type, level, &count_opened_fonts, + face.cancel, // copy + face.is_created // copy + }; + auto job = std::make_unique(std::move(data)); + auto &worker = wxGetApp().plater()->get_ui_job_worker(); + queue_job(worker, std::move(job)); + } else { + // cant start new thread at this moment so wait in queue + state_text = " ... " + _u8L("In queue"); + } + + if (!state_text.empty()) { + ImGui::SameLine(cfg.face_name_texture_offset_x); + ImGui::Text("%s", state_text.c_str()); + } + + ImGui::SameLine(cfg.face_name_texture_offset_x); + ImTextureID tex_id = (void *) (intptr_t) faces.texture_id; + ImVec4 tint_color = ImGui::GetStyleColorVec4(ImGuiCol_Text); + ImGui::Image(tex_id, size, uv0, uv1, tint_color); +} + +void init_text_lines(TextLinesModel &text_lines, const Selection &selection, /* const*/ StyleManager &style_manager, unsigned count_lines) +{ + text_lines.reset(); + if (selection.is_empty()) + return; + const GLVolume *gl_volume_ptr = selection.get_first_volume(); + if (gl_volume_ptr == nullptr) return; + const GLVolume & gl_volume = *gl_volume_ptr; + const ModelObjectPtrs &objects = selection.get_model()->objects; + const ModelVolume * mv_ptr = get_model_volume(gl_volume, objects); + if (mv_ptr == nullptr) return; + const ModelVolume &mv = *mv_ptr; + if (mv.is_the_only_one_part()) return; + + const std::optional &es_opt = mv.emboss_shape; + if (!es_opt.has_value()) return; + const EmbossShape &es = *es_opt; + + //const std::optional &tc_opt = mv.text_configuration; + //if (!tc_opt.has_value()) return; + //const TextConfiguration &tc = *tc_opt; + + //// calculate count lines when not set + //if (count_lines == 0) { + // count_lines = get_count_lines(tc.text); + // if (count_lines == 0) return; + //} + + //// prepare volumes to slice + //ModelVolumePtrs volumes = prepare_volumes_to_slice(mv); + + //// For interactivity during drag over surface it must be from gl_volume not volume. + //Transform3d mv_trafo = gl_volume.get_volume_transformation().get_matrix(); + //if (es.fix_3mf_tr.has_value()) + // mv_trafo = mv_trafo * (es.fix_3mf_tr->inverse()); + //text_lines.init(mv_trafo, volumes, style_manager, count_lines); +} + +const IconManager::Icon &Text::get_icon(const IconManager::VIcons &icons, Text::IconType type, Text::IconState state) { + return *icons[(unsigned) type][(unsigned) state]; +} +CurGuiCfg Text::create_gui_configuration() +{ + CurGuiCfg cfg; // initialize by default values; + + float line_height = ImGui::GetTextLineHeight(); + float line_height_with_spacing = ImGui::GetTextLineHeightWithSpacing(); + float space = line_height_with_spacing - line_height; + const ImGuiStyle &style = ImGui::GetStyle(); + + cfg.max_style_name_width = ImGui::CalcTextSize("Maximal font name, extended").x; + + cfg.icon_width = static_cast(std::ceil(line_height)); + // make size pair number + if (cfg.icon_width % 2 != 0) ++cfg.icon_width; + + cfg.delete_pos_x = cfg.max_style_name_width + space; + const float count_line_of_text = 3.f; + cfg.text_size = ImVec2(-FLT_MIN, line_height_with_spacing * count_line_of_text); + ImVec2 letter_m_size = ImGui::CalcTextSize("M"); + const float count_letter_M_in_input = 12.f; + cfg.input_width = letter_m_size.x * count_letter_M_in_input; + CurGuiCfg::Translations &tr = cfg.translations; + + // TRN - Input label. Be short as possible + // Select look of letter shape + tr.font = _u8L("Font"); + // TRN - Input label. Be short as possible + // Height of one text line - Font Ascent + tr.height = _u8L("Size"); + // TRN - Input label. Be short as possible + // Size in emboss direction + tr.depth = _u8L("Depth"); + + float max_text_width = std::max({ImGui::CalcTextSize(tr.font.c_str()).x, ImGui::CalcTextSize(tr.height.c_str()).x, ImGui::CalcTextSize(tr.depth.c_str()).x}); + cfg.indent = static_cast(cfg.icon_width); + cfg.input_offset = style.WindowPadding.x + cfg.indent + max_text_width + space; + + // TRN - Input label. Be short as possible + // Copy surface of model on surface of the embossed text + tr.use_surface = _u8L("Use surface"); + // TRN - Input label. Be short as possible + // Option to change projection on curved surface + // for each character(glyph) in text separately + tr.per_glyph = _u8L("Per glyph"); + // TRN - Input label. Be short as possible + // Align Top|Middle|Bottom and Left|Center|Right + tr.alignment = _u8L("Alignment"); + // TRN - Input label. Be short as possible + tr.char_gap = _u8L("Text gap"); + // TRN - Input label. Be short as possible + tr.line_gap = _u8L("Line gap"); + // TRN - Input label. Be short as possible + tr.boldness = _u8L("Boldness"); + + // TRN - Input label. Be short as possible + // Like Font italic + tr.skew_ration = _u8L("Skew ratio"); + + // TRN - Input label. Be short as possible + // Distance from model surface to be able + // move text as part fully into not flat surface + // move text as modifier fully out of not flat surface + tr.from_surface = _u8L("From surface"); + + // TRN - Input label. Be short as possible + // Angle between Y axis and text line direction. + tr.rotation = _u8L("Rotation"); + + + float max_advanced_text_width = std::max({ImGui::CalcTextSize(tr.use_surface.c_str()).x, ImGui::CalcTextSize(tr.per_glyph.c_str()).x, + ImGui::CalcTextSize(tr.alignment.c_str()).x, ImGui::CalcTextSize(tr.char_gap.c_str()).x, ImGui::CalcTextSize(tr.line_gap.c_str()).x, + ImGui::CalcTextSize(tr.boldness.c_str()).x, ImGui::CalcTextSize(tr.skew_ration.c_str()).x, + ImGui::CalcTextSize(tr.from_surface.c_str()).x, ImGui::CalcTextSize(tr.rotation.c_str()).x + cfg.icon_width + 2 * space, + }); + cfg.advanced_input_offset = max_advanced_text_width + 3 * space + cfg.indent; + + cfg.lock_offset = cfg.advanced_input_offset - (cfg.icon_width + space); + // calculate window size + float input_height = line_height_with_spacing + 2 * style.FramePadding.y; + float separator_height = 2 + style.FramePadding.y; + + // "Text is to object" + radio buttons + cfg.height_of_volume_type_selector = separator_height + line_height_with_spacing + input_height; + + int max_style_image_width = static_cast(std::round(cfg.max_style_name_width / 2 - 2 * style.FramePadding.x)); + int max_style_image_height = static_cast(std::round(input_height)); + cfg.max_style_image_size = Vec2i32(max_style_image_width, line_height); + cfg.face_name_size = Vec2i32(cfg.input_width, line_height_with_spacing); + cfg.face_name_texture_offset_x = cfg.face_name_size.x() + style.WindowPadding.x + space; + + cfg.max_tooltip_width = ImGui::GetFontSize() * 20.0f; + + return cfg; +} + +EmbossShape &TextDataBase::create_shape() +{ + if (!shape.shapes_with_ids.empty()) + return shape; + // create shape by configuration + const char * text = m_text_configuration.text.c_str(); + std::wstring text_w = boost::nowide::widen(text); + const FontProp &fp = m_text_configuration.style.prop; + auto was_canceled = [&c = cancel]() { return c->load(); }; + auto ft_fn = [](){ + return Slic3r::GUI::BackupFonts::backup_fonts; + }; + bool support_backup_fonts = GUI::wxGetApp().app_config->get_bool("support_backup_fonts"); + if (support_backup_fonts) { + text2vshapes(shape, m_font_file, text_w, fp, was_canceled, ft_fn); + } else { + text2vshapes(shape, m_font_file, text_w, fp, was_canceled); + } + return shape; +} + +StateColor ok_btn_bg(std::pair(wxColour(27, 136, 68), StateColor::Pressed), + std::pair(wxColour(61, 203, 115), StateColor::Hovered), + std::pair(wxColour(0, 174, 66), StateColor::Normal)); +StateColor ok_btn_disable_bg(std::pair(wxColour(205, 201, 201), StateColor::Pressed), + std::pair(wxColour(205, 201, 201), StateColor::Hovered), + std::pair(wxColour(205, 201, 201), StateColor::Normal)); + +StyleNameEditDialog::StyleNameEditDialog( + wxWindow *parent, Emboss::StyleManager &style_manager, wxWindowID id, const wxString &title, const wxPoint &pos, const wxSize &size, long style) + : DPIDialog(parent, id, title, pos, size, style), m_style_manager(style_manager) +{ + std::string icon_path = (boost::format("%1%/images/QIDIStudioTitle.ico") % resources_dir()).str(); + SetIcon(wxIcon(encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO)); + + SetBackgroundColour(*wxWHITE); + wxBoxSizer *m_sizer_main = new wxBoxSizer(wxVERTICAL); + auto m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(400), -1)); + m_line_top->SetBackgroundColour(wxColour(166, 169, 170)); + m_sizer_main->Add(m_line_top, 0, wxEXPAND, 0); + m_sizer_main->Add(0, 0, 0, wxTOP, FromDIP(5)); + + m_top_sizer = new wxFlexGridSizer(1, 2, FromDIP(5), 0); + //m_top_sizer->AddGrowableCol(0, 1); + m_top_sizer->SetFlexibleDirection(wxBOTH); + m_top_sizer->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); + + //m_row_panel = new wxPanel(this); + auto empty_name_txt = new Label(this, ""); + + auto plate_name_txt = new Label(this, _L("Style name")); + plate_name_txt->SetFont(Label::Body_14); + int text_size = ImGuiWrapper::calc_text_size(_L("There is already a text style with the same name.")).x; + auto input_len = std::max(240, text_size - 30); + m_name = new TextInput(this, wxString::FromDouble(0.0), "", "", wxDefaultPosition, wxSize(FromDIP(input_len), -1), wxTE_PROCESS_ENTER); + m_name->GetTextCtrl()->SetValue(""); + m_name->GetTextCtrl()->Bind(wxEVT_TEXT, &StyleNameEditDialog::on_edit_text, this); + + m_top_sizer->Add(plate_name_txt, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT | wxALL, FromDIP(5)); + m_top_sizer->Add(m_name, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT | wxALL, FromDIP(5)); + m_top_sizer->Add(empty_name_txt, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT | wxALL, FromDIP(5)); + + m_name->GetTextCtrl()->SetMaxLength(20); + empty_name_txt->Hide(); + + m_sizer_main->Add(m_top_sizer, 0, wxEXPAND | wxALL, FromDIP(20)); + + auto sizer_button = new wxBoxSizer(wxHORIZONTAL); + StateColor btn_bg_white(std::pair(wxColour(206, 206, 206), StateColor::Pressed), std::pair(wxColour(238, 238, 238), StateColor::Hovered), + std::pair(*wxWHITE, StateColor::Normal)); + StateColor ok_btn_text(std::pair(wxColour(255, 255, 254), StateColor::Normal)); + StateColor ok_btn_bd(std::pair(wxColour(0, 174, 66), StateColor::Normal)); + m_button_ok = new Button(this, _L("OK")); + m_button_ok->Enable(true); + m_button_ok->SetBackgroundColor(ok_btn_bg); + m_button_ok->SetBorderColor(ok_btn_bd); + m_button_ok->SetTextColor(ok_btn_text); + m_button_ok->SetFont(Label::Body_12); + m_button_ok->SetSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_ok->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_ok->SetCornerRadius(FromDIP(12)); + m_button_ok->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this](wxCommandEvent &e) { + if (get_name().empty()) { + add_tip_label(); + if (m_tip) { + m_tip->SetLabel(_L("Name can't be empty.")); + } + m_button_ok->Enable(false); + m_button_ok->SetBackgroundColor(ok_btn_disable_bg); + return; + } + if (this->IsModal()) + EndModal(wxID_YES); + else + this->Close(); + e.StopPropagation(); + }); + m_button_cancel = new Button(this, _L("Cancel")); + m_button_cancel->SetBackgroundColor(btn_bg_white); + m_button_cancel->SetBorderColor(wxColour(38, 46, 48)); + m_button_cancel->SetFont(Label::Body_12); + m_button_cancel->SetSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_cancel->SetMinSize(wxSize(FromDIP(58), FromDIP(24))); + m_button_cancel->SetCornerRadius(FromDIP(12)); + m_button_cancel->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this](wxCommandEvent &e) { + if (this->IsModal()) + EndModal(wxID_CANCEL); + else + this->Close(); + e.StopPropagation(); + }); + sizer_button->AddStretchSpacer(); + sizer_button->Add(m_button_ok, 0, wxALL, FromDIP(5)); + sizer_button->Add(m_button_cancel, 0, wxALL, FromDIP(5)); + sizer_button->Add(FromDIP(30), 0, 0, 0); + + m_sizer_main->Add(sizer_button, 0, wxEXPAND | wxBOTTOM, FromDIP(20)); + + SetSizer(m_sizer_main); + Layout(); + m_sizer_main->Fit(this); + + CenterOnParent(); + + wxGetApp().UpdateDlgDarkUI(this); +} + +StyleNameEditDialog::~StyleNameEditDialog() {} + +void StyleNameEditDialog::on_dpi_changed(const wxRect &suggested_rect) +{ + m_button_ok->Rescale(); + m_button_cancel->Rescale(); +} + +wxString StyleNameEditDialog::get_name() const { return m_name->GetTextCtrl()->GetValue(); } + +void StyleNameEditDialog::set_name(const wxString &name) +{ + m_name->GetTextCtrl()->SetValue(name); + m_name->GetTextCtrl()->SetFocus(); + m_name->GetTextCtrl()->SetInsertionPointEnd(); +} +#define StyleNameEditDialogHeight FromDIP(170) +#define StyleNameEditDialogHeight_BIG FromDIP(200) +void Slic3r::GUI::StyleNameEditDialog::on_edit_text(wxCommandEvent &event) +{ + add_tip_label(); + std::string new_name = m_name->GetTextCtrl()->GetValue().utf8_string(); + bool is_unique = m_style_manager.is_unique_style_name(new_name); + if (new_name.empty()) { + m_button_ok->Enable(false); + if (m_tip) { + m_tip->Show(); + m_tip->SetLabel(_L("Name can't be empty.")); + } + SetMinSize(wxSize(-1, StyleNameEditDialogHeight_BIG)); + SetMaxSize(wxSize(-1, StyleNameEditDialogHeight_BIG)); + } else if (!is_unique) { + m_button_ok->Enable(false); + m_tip->Show(); + m_tip->SetLabel(_L("There is already a text style with the same name.")); + SetMinSize(wxSize(-1, StyleNameEditDialogHeight_BIG)); + SetMaxSize(wxSize(-1, StyleNameEditDialogHeight_BIG)); + } else if (check_empty_or_iillegal_character(new_name)) { + m_button_ok->Enable(false); + m_tip->Show(); + m_tip->SetLabel(_L("There are spaces or illegal characters present.")); + SetMinSize(wxSize(-1, StyleNameEditDialogHeight_BIG)); + SetMaxSize(wxSize(-1, StyleNameEditDialogHeight_BIG)); + } else { + m_tip->SetLabel(""); + m_tip->Show(false); + m_button_ok->Enable(true); + SetMinSize(wxSize(-1, StyleNameEditDialogHeight)); + SetMaxSize(wxSize(-1, StyleNameEditDialogHeight)); + } + m_button_ok->SetBackgroundColor(m_button_ok->IsEnabled() ? ok_btn_bg : ok_btn_disable_bg); + Layout(); + Fit(); +} + +void Slic3r::GUI::StyleNameEditDialog::add_tip_label() +{ + if (!m_add_tip) { + m_add_tip = true; + m_tip = new Label(this, _L("Name can't be empty.")); + m_tip->SetForegroundColour(wxColour(241, 117, 78, 255)); + m_top_sizer->Add(m_tip, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT | wxALL, FromDIP(5)); + SetMinSize(wxSize(-1, StyleNameEditDialogHeight_BIG)); + SetMaxSize(wxSize(-1, StyleNameEditDialogHeight_BIG)); + Layout(); + Fit(); + } +} + +bool Slic3r::GUI::StyleNameEditDialog::check_empty_or_iillegal_character(const std::string &name) +{ + if (Plater::has_illegal_filename_characters(name)) { return true; } + if (name.find_first_of(' ') != std::string::npos) { return true; } + return false; +} } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/Gizmos/GLGizmoText.hpp b/src/slic3r/GUI/Gizmos/GLGizmoText.hpp index f4d17b8..27c2c94 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoText.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoText.hpp @@ -1,51 +1,59 @@ #ifndef slic3r_GLGizmoText_hpp_ #define slic3r_GLGizmoText_hpp_ -#include "GLGizmoBase.hpp" +#include "GLGizmoRotate.hpp" #include "slic3r/GUI/3DScene.hpp" +#include "slic3r/GUI/SurfaceDrag.hpp" #include "../GLTexture.hpp" #include "../Camera.hpp" #include "libslic3r/Model.hpp" - +#include "slic3r/GUI/IconManager.hpp" +#include "slic3r/Utils/EmbossStyleManager.hpp" +#include "slic3r/GUI/Jobs/EmbossJob.hpp" +#include "libslic3r/TextConfiguration.hpp" +#include "slic3r/GUI/TextLines.hpp" namespace Slic3r { enum class ModelVolumeType : int; class ModelVolume; namespace GUI { -//#define DEBUG_TEXT //#define DEBUG_TEXT_VALUE - +//search TextToDo enum class SLAGizmoEventType : unsigned char; -const std::string CUR_FONT_VERSION = "1.0"; +//1.0 mean v1.10 qidi version +//1.1 mean v2.0 v2.1 qidi version(202505) +//2.0 mean v2.2 qidi version(202507) +const std::string CUR_FONT_VERSION = "2.0"; class GLGizmoText : public GLGizmoBase { private: + bool m_is_direct_create_text = false; + std::vector m_trafo_matrices;//Need to correspond m_c->raycaster()->raycasters + int m_show_calc_meshtod = 0;//1 preview //2 draging std::vector m_avail_font_names; - char m_text[1024] = { 0 }; - std::string m_font_name; + std::string m_text{""}; + std::string m_font_name; + wxString m_cur_font_name; std::string m_font_version = CUR_FONT_VERSION; - float m_font_size = 16.f; + std::string m_style_name; + float m_font_size = 10.f; const float m_font_size_min = 3.f; const float m_font_size_max = 1000.f; - int m_curr_font_idx = 0; + bool m_warning_font = false; bool m_bold = true; bool m_italic = false; float m_thickness = 2.f; - const float m_thickness_min = 0.01f; + const float m_thickness_min = 0.1f; const float m_thickness_max = 1000.f; float m_embeded_depth = 0.f; const float m_embeded_depth_max = 1000.f; float m_rotate_angle = 0; float m_text_gap = 0.f; - enum TextType { - HORIZONAL, - SURFACE, - SURFACE_HORIZONAL - }; - TextType m_text_type{TextType ::SURFACE}; + TextConfiguration m_ui_text_configuration; + TextInfo::TextType m_surface_type{TextInfo::TextType ::SURFACE}; bool m_really_use_surface_calc = false; - bool m_use_current_pose = true; + bool m_draging_cube = false; mutable RaycastResult m_rr; float m_combo_height = 0.0f; @@ -54,7 +62,6 @@ private: Vec2d m_mouse_position = Vec2d::Zero(); Vec2d m_origin_mouse_position = Vec2d::Zero(); - bool m_shift_down = false; class TextureInfo { public: @@ -75,8 +82,8 @@ private: std::mutex m_mutex; std::thread m_thread; - bool m_is_modify = false; bool m_need_update_text = false; + bool m_need_update_tran = false; bool m_reedit_text = false; bool m_show_warning_text_create_fail = false; bool m_show_text_normal_error = false; @@ -88,21 +95,42 @@ private: bool m_fix_old_tran_flag = false; bool m_is_version1_10_xoy = false; bool m_is_version1_9_xoz = false; + bool m_is_version1_8_yoz = false; int m_object_idx = -1; int m_volume_idx = -1; + //font deal + struct Facenames; // forward declaration + std::unique_ptr m_face_names; + // Keep information about stored styles and loaded actual style to compare with + Emboss::StyleManager m_style_manager; + std::shared_ptr> m_job_cancel = nullptr; + // When open text loaded from .3mf it could be written with unknown font + bool m_is_unknown_font = false; + // Is open tree with advanced options + bool m_is_advanced_edit_style = false; + // True when m_text contain character unknown by selected font + bool m_text_contain_unknown_glyph = false; + std::string m_style_new_name = ""; + // For text on scaled objects + std::optional m_scale_height; + std::optional m_scale_depth; + void calculate_scale(); + std::optional m_scale_width; + TextLinesModel m_text_lines; + // drawing icons + IconManager m_icon_manager; + IconManager::VIcons m_icons; - int m_preview_text_volume_id = -1; Vec3d m_fix_text_position_in_world = Vec3d::Zero(); Vec3f m_fix_text_normal_in_world = Vec3f::Zero(); - bool m_need_fix; Vec3d m_text_position_in_world = Vec3d::Zero(); Vec3f m_text_normal_in_world = Vec3f::Zero(); Geometry::Transformation m_text_tran_in_object; Geometry::Transformation m_text_tran_in_world; Geometry::Transformation m_load_text_tran_in_object; Geometry::Transformation m_model_object_in_world_tran; - Transform3d m_text_cs_to_world_tran; - Transform3d m_object_cs_to_world_tran; + std::optional m_fix_text_tran; + Vec3d m_cut_plane_dir_in_world = Vec3d::UnitZ(); std::vector m_position_points; @@ -111,25 +139,38 @@ private: // This map holds all translated description texts, so they can be easily referenced during layout calculations // etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect. std::map m_desc; - Transform3d m_text_volume_tran; - ModelVolume * m_last_text_mv; + ModelVolume * m_last_text_mv{nullptr}; // move gizmo Grabber m_move_grabber; const int m_move_cube_id = 1; - + // Rotation gizmo + GLGizmoRotate m_rotate_gizmo; + std::optional m_distance; + std::optional m_rotate_start_angle; // TRN - Title in Undo/Redo stack after move with SVG along emboss axe - From surface const std::string move_snapshot_name = "Text move"; + const std::string rotation_snapshot_name = "Text rotate"; public: - GLGizmoText(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); + GLGizmoText(GLCanvas3D& parent, unsigned int sprite_id); ~GLGizmoText(); void update_font_texture(); - bool gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_position, bool shift_down, bool alt_down, bool control_down); + bool gizmo_event(SLAGizmoEventType action, const Vec2d &mouse_position, bool shift_down, bool alt_down, bool control_down) override; bool is_mesh_point_clipped(const Vec3d &point, const Transform3d &trafo) const; BoundingBoxf3 bounding_box() const; + static EmbossStyles create_default_styles(); + bool select_facename(const wxString &facename,bool update_text ); + bool on_shortcut_key(); + bool is_only_text_case() const; + void close(); + + std::string get_icon_filename(bool b_dark_mode) const override; + virtual std::string get_gizmo_entering_text() const{return "Enter Text gizmo";} + virtual std::string get_gizmo_leaving_text() const{return "Leave Text gizmo";} + bool wants_enter_leave_snapshots() const override { return true; } protected: virtual bool on_init() override; @@ -147,34 +188,103 @@ protected: void pop_button_style(); virtual void on_set_state() override; virtual void data_changed(bool is_serializing) override; + void on_set_hover_id() override; + void on_enable_grabber(unsigned int id) override; + void on_disable_grabber(unsigned int id) override; + bool on_mouse(const wxMouseEvent &mouse_event) override; + bool on_mouse_for_rotation(const wxMouseEvent &mouse_event); virtual CommonGizmosDataID on_get_requirements() const override; virtual void on_render_input_window(float x, float y, float bottom_limit); void show_tooltip_information(float x, float y); private: - void check_text_type(bool is_surface_text,bool is_keep_horizontal); + bool set_height(); + //ui + void draw_text_input(int width); + void draw_font_list(); + void draw_height(bool use_inch = false); + void draw_depth(bool use_inch); + void init_font_name_texture(); + void reinit_text_lines(unsigned count_lines = 0); + bool check(ModelVolumeType volume_type); + bool init_create(ModelVolumeType volume_type); + + template + bool revertible(const std::string &name, T &value, const T *default_value, const std::string &undo_tooltip, float undo_offset, Draw draw) const; + template + bool rev_input( + const std::string &name, T &value, const T *default_value, const std::string &undo_tooltip, T step, T step_fast, const char *format, ImGuiInputTextFlags flags = 0) const; + template + bool rev_input_mm(const std::string & name, + T & value, + const T * default_value, + const std::string & undo_tooltip, + T step, + T step_fast, + const char * format, + bool use_inch, + const std::optional &scale) const; + void load_old_font(); + void draw_style_list(float caption_size); + void draw_style_save_button(bool is_modified); + void draw_style_save_as_popup(); + void draw_style_add_button(bool is_modified); + void draw_delete_style_button(); + void update_boldness(); + void update_italic(); + void set_default_boldness(std::optional &boldness); + void draw_model_type(int caption_width); + void draw_surround_type(int caption_width); + void draw_rotation(int caption_size, int slider_width, int drag_left_width, int slider_icon_width); + +private: // ui + struct GuiCfg; + std::unique_ptr m_gui_cfg; + +private: + std::unique_ptr create_emboss_data_base(const std::string & text, + Emboss::StyleManager & style_manager, + //TextLinesModel & text_lines,//todo + const Selection & selection, + ModelVolumeType type, + std::shared_ptr> &cancel); + bool process(bool make_snapshot = true, std::optional volume_transformation = std::nullopt, bool update_text =true); + + ModelVolume *get_text_is_dragging(); + bool get_is_dragging(); + bool get_selection_is_text(); + bool update_text_tran_in_model_object(bool rewrite_text_tran = false); + void update_trafo_matrices(); + void update_cut_plane_dir(); + void switch_text_type(TextInfo::TextType type); + float get_angle_from_current_style(); + void volume_transformation_changed(); + void update_style_angle(ModelVolume *text_volume, float init_angle_degree, float roate_angle); + +private: void generate_text_tran_in_world(const Vec3d &text_normal_in_world, const Vec3d &text_position_in_world, float rotate_degree, Geometry::Transformation &tran); void use_fix_normal_position(); - void load_init_text(); + void load_init_text(bool first_open_text = false); void update_text_pos_normal(); - void update_font_status(); + bool filter_model_volume(ModelVolume* mv); + //void update_font_status(); void reset_text_info(); + float get_text_height(const std::string &text); void close_warning_flag_after_close_or_drag(); void update_text_normal_in_world(); - bool update_text_positions(const std::vector& texts); - TriangleMesh get_text_mesh(const char* text_str, const Vec3d &position, const Vec3d &normal, const Vec3d &text_up_dir); + //bool update_text_positions(); - bool update_raycast_cache(const Vec2d &mouse_position, const Camera &camera, const std::vector &trafo_matrices); - void generate_text_volume(bool is_temp = true); - void delete_temp_preview_text_volume(); + bool update_raycast_cache(const Vec2d &mouse_position, const Camera &camera, const std::vector &trafo_matrices, bool exclude_last = true); + bool generate_text_volume(); TextInfo get_text_info(); void load_from_text_info(const TextInfo &text_info); + bool is_old_text_info(const TextInfo &text_info); }; } // namespace GUI } // namespace Slic3r -#endif // slic3r_GLGizmoText_hpp_ \ No newline at end of file +#endif // slic3r_GLGizmoText_hpp_ diff --git a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp index 6d4b3d2..4fef3e3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosCommon.cpp @@ -138,14 +138,20 @@ void SelectionInfo::on_release() int SelectionInfo::get_active_instance() const { - const Selection& selection = get_pool()->get_canvas()->get_selection(); + const auto& p_pool = get_pool(); + if (!p_pool) { + return -1; + } + + const auto& p_canvas = p_pool->get_canvas(); + if (!p_canvas) { + return -1; + } + + const Selection& selection = p_canvas->get_selection(); return selection.get_instance_idx(); } - - - - void InstancesHider::on_update() { const ModelObject* mo = get_pool()->selection_info()->model_object(); @@ -181,7 +187,7 @@ void InstancesHider::on_update() for (const TriangleMesh* mesh : meshes) { m_clippers.emplace_back(new MeshClipper); m_clippers.back()->set_plane(ClippingPlane(-Vec3d::UnitZ(), z_min)); - m_clippers.back()->set_mesh(*mesh); + m_clippers.back()->set_mesh(mesh->its); } m_old_meshes = meshes; } @@ -379,29 +385,24 @@ void ObjectClipper::on_update() // which mesh should be cut? std::vector meshes; std::vector trafos; - bool force_clipper_regeneration = false; - std::unique_ptr mc; Geometry::Transformation mc_tr; - if (!mc && meshes.empty()) { + if (meshes.empty()) { for (const ModelVolume *mv : mo->volumes) { meshes.emplace_back(&mv->mesh()); trafos.emplace_back(mv->get_transformation()); } } - if (mc || force_clipper_regeneration || meshes != m_old_meshes) { + if (meshes != m_old_meshes) { m_clippers.clear(); for (size_t i = 0; i < meshes.size(); ++i) { m_clippers.emplace_back(new MeshClipper, trafos[i]); - auto tri_mesh = new TriangleMesh(meshes[i]->its); - m_clippers.back().first->set_mesh(*tri_mesh); + m_clippers.back().first->set_mesh(meshes[i]->its); } m_old_meshes = std::move(meshes); - if (mc) { m_clippers.emplace_back(std::move(mc), mc_tr); } - m_active_inst_bb_radius = mo->instance_bounding_box(get_pool()->selection_info()->get_active_instance()).radius(); } } @@ -434,6 +435,7 @@ void CommonGizmosDataObjects::ObjectClipper::render_cut(const std::vectorget_sla_shift(); std::vector ignore_idxs_local = ignore_idxs ? *ignore_idxs : std::vector(); + double z_min; if (canvas->get_gizmos_manager().is_paint_gizmo()) { z_min = -FLT_MAX; @@ -481,9 +483,21 @@ void CommonGizmosDataObjects::ObjectClipper::set_behaviour(bool hide_clipped, bo void ObjectClipper::set_position(double pos, bool keep_normal, bool vertical_normal) { - const ModelObject *mo = get_pool()->selection_info()->model_object(); - int active_inst = get_pool()->selection_info()->get_active_instance(); - double z_shift = get_pool()->selection_info()->get_sla_shift(); + const auto p_pool = get_pool(); + if (!p_pool) { + return; + } + + const auto& p_selection_info = p_pool->selection_info(); + if (!p_selection_info) { + return; + } + const ModelObject* mo = p_selection_info->model_object(); + if (!mo) { + return; + } + int active_inst = p_selection_info->get_active_instance(); + double z_shift = p_selection_info->get_sla_shift(); if (active_inst < 0) { return; } @@ -494,8 +508,8 @@ void ObjectClipper::set_position(double pos, bool keep_normal, bool vertical_nor normal = (keep_normal && m_clp) ? m_clp->get_normal() : -wxGetApp().plater()->get_camera().get_dir_forward(); } Vec3d center; - if (get_pool()->get_canvas()->get_canvas_type() == GLCanvas3D::CanvasAssembleView) { - const SelectionInfo *sel_info = get_pool()->selection_info(); + if (p_pool->get_canvas()->get_canvas_type() == GLCanvas3D::CanvasAssembleView) { + const SelectionInfo *sel_info = p_selection_info; auto trafo = mo->instances[sel_info->get_active_instance()]->get_assemble_transformation(); auto offset_to_assembly = mo->instances[0]->get_offset_to_assembly(); center = trafo.get_offset() + offset_to_assembly * (GLVolume::explosion_ratio - 1.0); @@ -505,7 +519,7 @@ void ObjectClipper::set_position(double pos, bool keep_normal, bool vertical_nor float dist = normal.dot(center); if (pos < 0.) - pos = m_clp_ratio; + pos = 0.; m_clp_ratio = pos; m_clp.reset(new ClippingPlane(normal, (dist - (-m_active_inst_bb_radius) - m_clp_ratio * 2 * m_active_inst_bb_radius))); @@ -594,7 +608,7 @@ void SupportsClipper::on_update() // The timestamp has changed. m_clipper.reset(new MeshClipper); // The mesh should already have the shared vertices calculated. - m_clipper->set_mesh(print_object->support_mesh()); + m_clipper->set_mesh(print_object->support_mesh().its); m_old_timestamp = timestamp; } } @@ -750,7 +764,7 @@ void ModelObjectsClipper::on_update() m_clippers.clear(); for (const TriangleMesh* mesh : meshes) { m_clippers.emplace_back(new MeshClipper); - m_clippers.back()->set_mesh(*mesh); + m_clippers.back()->set_mesh(mesh->its); } m_old_meshes = meshes; diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp index 1bfa2e8..abcbeb9 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.cpp @@ -8,6 +8,7 @@ #include "slic3r/GUI/Plater.hpp" #include "slic3r/Utils/UndoRedo.hpp" #include "slic3r/GUI/NotificationManager.hpp" +#include "slic3r/GUI/GLToolbar.hpp" #include "slic3r/GUI/Gizmos/GLGizmoMove.hpp" #include "slic3r/GUI/Gizmos/GLGizmoScale.hpp" @@ -22,6 +23,7 @@ #include "slic3r/GUI/Gizmos/GLGizmoHollow.hpp" #include "slic3r/GUI/Gizmos/GLGizmoSeam.hpp" #include "slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp" +#include "slic3r/GUI/Gizmos/GLGizmoFuzzySkin.hpp" #include "slic3r/GUI/Gizmos/GLGizmoSimplify.hpp" #include "slic3r/GUI/Gizmos/GLGizmoText.hpp" #include "slic3r/GUI/Gizmos/GLGizmoSVG.hpp" @@ -46,9 +48,7 @@ const float GLGizmosManager::Default_Icons_Size = 64; GLGizmosManager::GLGizmosManager(GLCanvas3D& parent) : m_parent(parent) , m_enabled(false) - , m_icons_texture_dirty(true) , m_current(Undefined) - , m_tooltip("") , m_serializing(false) //QDS: GUI refactor: add object manipulation in gizmo , m_object_manipulation(parent) @@ -76,169 +76,36 @@ std::vector GLGizmosManager::get_selectable_idxs() const return out; } -//QDS: GUI refactor: GLToolbar&&Gizmo adjust -//when judge the mouse position, {0, 0} is at the left-up corner, and no need to multiply camera_zoom -size_t GLGizmosManager::get_gizmo_idx_from_mouse(const Vec2d& mouse_pos) const -{ - if (! m_enabled) - return Undefined; - - float cnv_h = (float)m_parent.get_canvas_size().get_height(); - float height = get_scaled_total_height(); - float icons_size = m_layout.scaled_icons_size(); - float border = m_layout.scaled_border(); - - //QDS: GUI refactor: GLToolbar&&Gizmo adjust - float cnv_w = (float)m_parent.get_canvas_size().get_width(); - float width = get_scaled_total_width(); -#if QDS_TOOLBAR_ON_TOP - //float space_width = GLGizmosManager::Default_Icons_Size * wxGetApp().toolbar_icon_scale();; - float top_x = 0; - if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) { - top_x = 0.5f * cnv_w + 0.5f * (m_parent.get_assembly_paint_toolbar_width()); - } else { - const float separator_width = m_parent.get_separator_toolbar_width(); - - top_x = m_parent.get_main_toolbar_offset(); - top_x += m_parent.get_main_toolbar_width() + separator_width / 2 + border; - } - float top_y = 0; - float stride_x = m_layout.scaled_stride_x(); - - // is mouse vertically in the area? - //if ((border <= (float)mouse_pos(0) && ((float)mouse_pos(0) <= border + icons_size))) { - if (((top_y + border) <= (float)mouse_pos(1)) && ((float)mouse_pos(1) <= (top_y + border + icons_size))) { - // which icon is it on? - int from_left = (float) mouse_pos(0) - top_x < 0 ? -1 : (int) ((float) mouse_pos(0) - top_x) / stride_x; - if (from_left < 0) - return Undefined; - // is it really on the icon or already past the border? - if ((float)mouse_pos(0) <= top_x + from_left * stride_x + icons_size) { - std::vector selectable = get_selectable_idxs(); - if (from_left < selectable.size()) - return selectable[from_left]; - } - } -#else - //float top_y = 0.5f * (cnv_h - height) + border; - float top_x = cnv_w - width; - float top_y = 0.5f * (cnv_h - height + m_parent.get_main_toolbar_height() - m_parent.get_assemble_view_toolbar_width()) + border; - float stride_y = m_layout.scaled_stride_y(); - - // is mouse horizontally in the area? - //if ((border <= (float)mouse_pos(0) && ((float)mouse_pos(0) <= border + icons_size))) { - if (((top_x + border) <= (float)mouse_pos(0)) && ((float)mouse_pos(0) <= (top_x + border + icons_size))) { - // which icon is it on? - size_t from_top = (size_t)((float)mouse_pos(1) - top_y) / stride_y; - if (from_top < 0) - return Undefined; - // is it really on the icon or already past the border? - if ((float)mouse_pos(1) <= top_y + from_top * stride_y + icons_size) { - std::vector selectable = get_selectable_idxs(); - if (from_top < selectable.size()) - return selectable[from_top]; - } - } -#endif - - return Undefined; -} - -void GLGizmosManager::switch_gizmos_icon_filename() -{ - m_background_texture.metadata.filename = m_is_dark ? "toolbar_background_dark.png" : "toolbar_background.png"; - m_background_texture.metadata.left = 16; - m_background_texture.metadata.top = 16; - m_background_texture.metadata.right = 16; - m_background_texture.metadata.bottom = 16; - if (!m_background_texture.metadata.filename.empty()) - m_background_texture.texture.load_from_file(resources_dir() + "/images/" + m_background_texture.metadata.filename, false, GLTexture::SingleThreaded, false); - - for (auto& gizmo : m_gizmos) { - gizmo->on_change_color_mode(m_is_dark); - switch (gizmo->get_sprite_id()) - { - case(EType::Move): - gizmo->set_icon_filename(m_is_dark ? "toolbar_move_dark.svg" : "toolbar_move.svg"); - break; - case(EType::Rotate): - gizmo->set_icon_filename(m_is_dark ? "toolbar_rotate_dark.svg" : "toolbar_rotate.svg"); - break; - case(EType::Scale): - gizmo->set_icon_filename(m_is_dark ? "toolbar_scale_dark.svg" : "toolbar_scale.svg"); - break; - case(EType::Flatten): - gizmo->set_icon_filename(m_is_dark ? "toolbar_flatten_dark.svg" : "toolbar_flatten.svg"); - break; - case(EType::Cut): - gizmo->set_icon_filename(m_is_dark ? "toolbar_cut_dark.svg" : "toolbar_cut.svg"); - break; - case(EType::FdmSupports): - gizmo->set_icon_filename(m_is_dark ? "toolbar_support_dark.svg" : "toolbar_support.svg"); - break; - case(EType::Seam): - gizmo->set_icon_filename(m_is_dark ? "toolbar_seam_dark.svg" : "toolbar_seam.svg"); - break; - case(EType::Text): - gizmo->set_icon_filename(m_is_dark ? "toolbar_text_dark.svg" : "toolbar_text.svg"); - break; - case(EType::MmuSegmentation): - gizmo->set_icon_filename(m_is_dark ? "mmu_segmentation_dark.svg" : "mmu_segmentation.svg"); - break; - case(EType::MeshBoolean): - gizmo->set_icon_filename(m_is_dark ? "toolbar_meshboolean_dark.svg" : "toolbar_meshboolean.svg"); - break; - case (EType::Measure): - gizmo->set_icon_filename(m_is_dark ? "toolbar_measure_dark.svg" : "toolbar_measure.svg"); - break; - case (EType::Assembly): - gizmo->set_icon_filename(m_is_dark ? "toolbar_assembly_dark.svg" : "toolbar_assembly.svg"); - break; - } - - } -} - bool GLGizmosManager::init() { if (!m_gizmos.empty()) return true; init_icon_textures(); - m_background_texture.metadata.filename = m_is_dark ? "toolbar_background_dark.png" : "toolbar_background.png"; - m_background_texture.metadata.left = 16; - m_background_texture.metadata.top = 16; - m_background_texture.metadata.right = 16; - m_background_texture.metadata.bottom = 16; - - if (!m_background_texture.metadata.filename.empty()) - { - if (!m_background_texture.texture.load_from_file(resources_dir() + "/images/" + m_background_texture.metadata.filename, false, GLTexture::SingleThreaded, false)) - return false; - } - // Order of gizmos in the vector must match order in EType! //QDS: GUI refactor: add obj manipulation m_gizmos.clear(); unsigned int sprite_id = 0; - m_gizmos.emplace_back(new GLGizmoMove3D(m_parent, m_is_dark ? "toolbar_move_dark.svg" : "toolbar_move.svg", EType::Move, &m_object_manipulation)); - m_gizmos.emplace_back(new GLGizmoRotate3D(m_parent, m_is_dark ? "toolbar_rotate_dark.svg" : "toolbar_rotate.svg", EType::Rotate, &m_object_manipulation)); - m_gizmos.emplace_back(new GLGizmoScale3D(m_parent, m_is_dark ? "toolbar_scale_dark.svg" : "toolbar_scale.svg", EType::Scale, &m_object_manipulation)); - m_gizmos.emplace_back(new GLGizmoFlatten(m_parent, m_is_dark ? "toolbar_flatten_dark.svg" : "toolbar_flatten.svg", EType::Flatten)); - m_gizmos.emplace_back(new GLGizmoAdvancedCut(m_parent, m_is_dark ? "toolbar_cut_dark.svg" : "toolbar_cut.svg", EType::Cut)); - m_gizmos.emplace_back(new GLGizmoMeshBoolean(m_parent, m_is_dark ? "toolbar_meshboolean_dark.svg" : "toolbar_meshboolean.svg", EType::MeshBoolean)); - m_gizmos.emplace_back(new GLGizmoFdmSupports(m_parent, m_is_dark ? "toolbar_support_dark.svg" : "toolbar_support.svg", EType::FdmSupports)); - m_gizmos.emplace_back(new GLGizmoSeam(m_parent, m_is_dark ? "toolbar_seam_dark.svg" : "toolbar_seam.svg", EType::Seam)); - m_gizmos.emplace_back(new GLGizmoText(m_parent, m_is_dark ? "toolbar_text_dark.svg" : "toolbar_text.svg", EType::Text)); + m_gizmos.emplace_back(new GLGizmoMove3D(m_parent, EType::Move, &m_object_manipulation)); + m_gizmos.emplace_back(new GLGizmoRotate3D(m_parent, EType::Rotate, &m_object_manipulation)); + m_gizmos.emplace_back(new GLGizmoScale3D(m_parent, EType::Scale, &m_object_manipulation)); + m_gizmos.emplace_back(new GLGizmoFlatten(m_parent, EType::Flatten)); + m_gizmos.emplace_back(new GLGizmoAdvancedCut(m_parent, EType::Cut)); + m_gizmos.emplace_back(new GLGizmoMeshBoolean(m_parent, EType::MeshBoolean)); + m_gizmos.emplace_back(new GLGizmoAssembly(m_parent, EType::Assembly)); + m_gizmos.emplace_back(new GLGizmoMmuSegmentation(m_parent, EType::MmuSegmentation)); + m_gizmos.emplace_back(new GLGizmoText(m_parent, EType::Text)); m_gizmos.emplace_back(new GLGizmoSVG(m_parent, EType::Svg)); - m_gizmos.emplace_back(new GLGizmoMmuSegmentation(m_parent, m_is_dark ? "mmu_segmentation_dark.svg" : "mmu_segmentation.svg", EType::MmuSegmentation)); - m_gizmos.emplace_back(new GLGizmoMeasure(m_parent, m_is_dark ? "toolbar_measure_dark.svg" : "toolbar_measure.svg", EType::Measure)); - m_gizmos.emplace_back(new GLGizmoAssembly(m_parent, m_is_dark ? "toolbar_assembly_dark.svg" : "toolbar_assembly.svg", EType::Assembly)); - m_gizmos.emplace_back(new GLGizmoSimplify(m_parent, "reduce_triangles.svg", EType::Simplify)); - m_gizmos.emplace_back(new GLGizmoBrimEars(m_parent, m_is_dark ? "toolbar_brimears_dark.svg" : "toolbar_brimears.svg", EType::BrimEars)); - //m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, "sla_supports.svg", sprite_id++)); - //m_gizmos.emplace_back(new GLGizmoFaceDetector(m_parent, "face recognition.svg", sprite_id++)); - //m_gizmos.emplace_back(new GLGizmoHollow(m_parent, "hollow.svg", sprite_id++)); + m_gizmos.emplace_back(new GLGizmoFdmSupports(m_parent, EType::FdmSupports)); + m_gizmos.emplace_back(new GLGizmoSeam(m_parent, EType::Seam)); + m_gizmos.emplace_back(new GLGizmoBrimEars(m_parent, EType::BrimEars)); + m_gizmos.emplace_back(new GLGizmoFuzzySkin(m_parent, EType::FuzzySkin)); + m_gizmos.emplace_back(new GLGizmoMeasure(m_parent, EType::Measure)); + m_gizmos.emplace_back(new GLGizmoSimplify(m_parent, EType::Simplify)); + + //m_gizmos.emplace_back(new GLGizmoSlaSupports(m_parent, sprite_id++)); + //m_gizmos.emplace_back(new GLGizmoFaceDetector(m_parent, sprite_id++)); + //m_gizmos.emplace_back(new GLGizmoHollow(m_parent, sprite_id++)); m_common_gizmos_data.reset(new CommonGizmosDataPool(&m_parent)); if(!m_assemble_view_data) @@ -319,7 +186,7 @@ bool GLGizmosManager::init_icon_textures() else return false; - if (IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/text_B.svg", 20, 20, texture_id)) + if (IMTexture::load_from_svg_file(Slic3r::resources_dir() + "/images/text_B.svg", 20, 20, texture_id)) icon_list.insert(std::make_pair((int)IC_TEXT_B, texture_id)); else return false; @@ -342,45 +209,6 @@ bool GLGizmosManager::init_icon_textures() return true; } -float GLGizmosManager::get_layout_scale() -{ - return m_layout.scale; -} - -bool GLGizmosManager::init_arrow(const BackgroundTexture::Metadata& arrow_texture) -{ - if (m_arrow_texture.texture.get_id() != 0) - return true; - - std::string path = resources_dir() + "/images/"; - bool res = false; - - if (!arrow_texture.filename.empty()) - res = m_arrow_texture.texture.load_from_svg_file(path + arrow_texture.filename, false, false, false, 1000); - if (res) - m_arrow_texture.metadata = arrow_texture; - - return res; -} - -void GLGizmosManager::set_overlay_icon_size(float size) -{ - if (m_layout.icons_size != size) - { - m_layout.icons_size = size; - m_icons_texture_dirty = true; - } -} - -void GLGizmosManager::set_overlay_scale(float scale) -{ - if (m_layout.scale != scale) - { - m_layout.scale = scale; - m_icons_texture_dirty = true; - } -} - void GLGizmosManager::refresh_on_off_state() { if (m_serializing || m_current == Undefined || m_gizmos.empty()) @@ -402,7 +230,8 @@ void GLGizmosManager::reset_all_states() open_gizmo(current); activate_gizmo(Undefined); - m_hover = Undefined; + //do not clear hover state, as Emboss gizmo can be used without selection + //m_hover = Undefined; } bool GLGizmosManager::open_gizmo(EType type) @@ -469,9 +298,8 @@ void GLGizmosManager::update_data() { if (!m_enabled) return; - + wxBusyCursor wait; const Selection& selection = m_parent.get_selection(); - if (m_common_gizmos_data) { m_common_gizmos_data->update(get_current() ? get_current()->get_requirements() @@ -501,7 +329,7 @@ bool GLGizmosManager::is_running() const bool GLGizmosManager::handle_shortcut(int key) { - if (!m_enabled || m_parent.get_selection().is_empty()) + if (!m_enabled) return false; auto it = std::find_if(m_gizmos.begin(), m_gizmos.end(), @@ -514,6 +342,13 @@ bool GLGizmosManager::handle_shortcut(int key) if (it == m_gizmos.end()) return false; + // allowe open shortcut even when selection is empty + if (Text == it - m_gizmos.begin()) { + if (dynamic_cast(m_gizmos[Text].get())->on_shortcut_key()) { + return true; + } + } + EType gizmo_type = EType(it - m_gizmos.begin()); return open_gizmo(gizmo_type); } @@ -604,14 +439,13 @@ Vec3d GLGizmosManager::get_flattening_normal() const return dynamic_cast(m_gizmos[Flatten].get())->get_flattening_normal(); } -bool GLGizmosManager::is_gizmo_activable_when_single_full_instance() { +bool GLGizmosManager::is_gizmo_activable_when_single_full_instance() +{ if (get_current_type() == GLGizmosManager::EType::Flatten || get_current_type() == GLGizmosManager::EType::Cut || get_current_type() == GLGizmosManager::EType::MeshBoolean || get_current_type() == GLGizmosManager::EType::Text || - get_current_type() == GLGizmosManager::EType::Seam || - get_current_type() == GLGizmosManager::EType::FdmSupports || - get_current_type() == GLGizmosManager::EType::MmuSegmentation || + is_paint_gizmo() || get_current_type() == GLGizmosManager::EType::Simplify ) { return true; @@ -623,9 +457,7 @@ bool GLGizmosManager::is_gizmo_click_empty_not_exit() { if (get_current_type() == GLGizmosManager::EType::Cut || get_current_type() == GLGizmosManager::EType::MeshBoolean || - get_current_type() == GLGizmosManager::EType::Seam || - get_current_type() == GLGizmosManager::EType::FdmSupports || - get_current_type() == GLGizmosManager::EType::MmuSegmentation || + is_paint_gizmo() || get_current_type() == GLGizmosManager::EType::Measure || get_current_type() == GLGizmosManager::EType::Assembly) { return true; @@ -633,16 +465,23 @@ bool GLGizmosManager::is_gizmo_click_empty_not_exit() return false; } -bool GLGizmosManager::is_show_only_active_plate() -{ - if (get_current_type() == GLGizmosManager::EType::Cut || - get_current_type() == GLGizmosManager::EType::Text) { +bool GLGizmosManager::is_only_text_volume() const { + auto gizmo_text = dynamic_cast(get_current()); + if (gizmo_text->is_only_text_case()) { return true; } return false; } -bool GLGizmosManager::is_ban_move_glvolume() +bool GLGizmosManager::is_show_only_active_plate() const +{ + if (get_current_type() == GLGizmosManager::EType::Cut) { + return true; + } + return false; +} + +bool GLGizmosManager::is_ban_move_glvolume() const { auto current_type = get_current_type(); if (current_type == GLGizmosManager::EType::Undefined || @@ -654,8 +493,6 @@ bool GLGizmosManager::is_ban_move_glvolume() return true; } - -//1.9.5 bool GLGizmosManager::get_gizmo_active_condition(GLGizmosManager::EType type) { if (auto cur_gizmo = get_gizmo(type)) { return cur_gizmo->is_activable(); @@ -663,6 +500,13 @@ bool GLGizmosManager::get_gizmo_active_condition(GLGizmosManager::EType type) { return false; } +void GLGizmosManager::update_show_only_active_plate() +{ + if (is_show_only_active_plate()) { + check_object_located_outside_plate(); + } +} + void GLGizmosManager::check_object_located_outside_plate(bool change_plate) { PartPlateList &plate_list = wxGetApp().plater()->get_partplate_list(); @@ -702,42 +546,24 @@ bool GLGizmosManager::gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_p if (!m_enabled || m_gizmos.empty()) return false; - if (m_current == SlaSupports) - return dynamic_cast(m_gizmos[SlaSupports].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); - else if (m_current == Hollow) - return dynamic_cast(m_gizmos[Hollow].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); - else if (m_current == FdmSupports) - return dynamic_cast(m_gizmos[FdmSupports].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); - else if (m_current == Seam) - return dynamic_cast(m_gizmos[Seam].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); - else if (m_current == MmuSegmentation) - return dynamic_cast(m_gizmos[MmuSegmentation].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); - else if (m_current == Text) - return dynamic_cast(m_gizmos[Text].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); - else if (m_current == Svg) - return dynamic_cast(m_gizmos[Svg].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); - else if (m_current == Measure) - return dynamic_cast(m_gizmos[Measure].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); - else if (m_current == Assembly) - return dynamic_cast(m_gizmos[Assembly].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); - else if (m_current == Cut) - return dynamic_cast(m_gizmos[Cut].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); - else if (m_current == MeshBoolean) - return dynamic_cast(m_gizmos[MeshBoolean].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); - else if (m_current == BrimEars) - return dynamic_cast(m_gizmos[BrimEars].get())->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); - else - return false; + for (size_t i = 0; i < m_gizmos.size(); ++i) { + if (m_gizmos[i]->get_sprite_id() == static_cast(m_current)) { + return m_gizmos[i]->gizmo_event(action, mouse_position, shift_down, alt_down, control_down); + } + } + + return false; } bool GLGizmosManager::is_paint_gizmo() const { return m_current == EType::FdmSupports || + m_current == EType::FuzzySkin || m_current == EType::MmuSegmentation || m_current == EType::Seam; } -bool GLGizmosManager::is_allow_select_all() { +bool GLGizmosManager::is_allow_select_all() const { if (m_current == Undefined || m_current == EType::Move|| m_current == EType::Rotate || m_current == EType::Scale) { @@ -746,6 +572,27 @@ bool GLGizmosManager::is_allow_select_all() { return false; } +bool GLGizmosManager::is_allow_show_volume_highlight_outline() const +{ + if (m_current == EType::Cut) { + return false; + } + return true; +} + +bool GLGizmosManager::is_allow_drag_volume() const +{ + if (m_current == EType::Cut) { return false; } + return true; +} + +bool GLGizmosManager::is_allow_mouse_drag_selected() const +{ + if (m_current == Measure || m_current == Assembly) + return false; + return true; +} + ClippingPlane GLGizmosManager::get_clipping_plane() const { if (! m_common_gizmos_data @@ -816,22 +663,8 @@ void GLGizmosManager::render_current_gizmo_for_picking_pass() const m_gizmos[m_current]->render_for_picking(); } -void GLGizmosManager::render_overlay() -{ - if (!m_enabled) - return; - - if (m_icons_texture_dirty) - generate_icons_texture(); - - do_render_overlay(); -} - std::string GLGizmosManager::get_tooltip() const { - if (!m_tooltip.empty()) - return m_tooltip; - const GLGizmoBase* curr = get_current(); return (curr != nullptr) ? curr->get_tooltip() : ""; } @@ -840,7 +673,7 @@ bool GLGizmosManager::on_mouse_wheel(wxMouseEvent& evt) { bool processed = false; - if (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation || m_current == BrimEars) { + if (m_current == SlaSupports || m_current == Hollow || is_paint_gizmo() || m_current == BrimEars) { float rot = (float)evt.GetWheelRotation() / (float)evt.GetWheelDelta(); if (gizmo_event((rot > 0.f ? SLAGizmoEventType::MouseWheelUp : SLAGizmoEventType::MouseWheelDown), Vec2d::Zero(), evt.ShiftDown(), evt.AltDown() // QDS @@ -882,16 +715,10 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) } } else if (evt.Moving()) { - m_tooltip = update_hover_state(mouse_pos); - if (m_current == MmuSegmentation || m_current == FdmSupports || m_current == Text || m_current == BrimEars || m_current == Svg) - // QDS + if (is_paint_gizmo() ||m_current == Text || m_current == BrimEars || m_current == Svg) gizmo_event(SLAGizmoEventType::Moving, mouse_pos, evt.ShiftDown(), evt.AltDown(), evt.ControlDown()); } else if (evt.LeftUp()) { - if (m_mouse_capture.left) { - processed = true; - m_mouse_capture.left = false; - } - else if (is_dragging()) { + if (is_dragging()) { switch (m_current) { case Move: { wxGetApp().plater()->take_snapshot(_u8L("Tool-Move"), UndoRedo::SnapshotType::GizmoAction); @@ -924,35 +751,12 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) processed = true; } -// else -// return false; } else if (evt.MiddleUp()) { - if (m_mouse_capture.middle) { - processed = true; - m_mouse_capture.middle = false; - } - else - return false; } else if (evt.RightUp()) { - if (pending_right_up) { - pending_right_up = false; - return true; - } - if (m_mouse_capture.right) { - processed = true; - m_mouse_capture.right = false; - } -// else -// return false; } else if (evt.Dragging() && !is_dragging()) { - if (m_mouse_capture.any()) - // if the button down was done on this toolbar, prevent from dragging into the scene - processed = true; -// else -// return false; } else if (evt.Dragging() && is_dragging()) { if (!m_parent.get_wxglcanvas()->HasCapture()) @@ -1041,130 +845,89 @@ bool GLGizmosManager::on_mouse(wxMouseEvent& evt) processed = true; } - if (get_gizmo_idx_from_mouse(mouse_pos) == Undefined) { - // mouse is outside the toolbar - m_tooltip.clear(); + if (evt.LeftDown() && (!control_down || grabber_contains_mouse())) { + if ((m_current == SlaSupports || m_current == Hollow || m_current == Svg || is_paint_gizmo() || m_current == Text || m_current == Cut || m_current == MeshBoolean || + m_current == BrimEars) + && gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, evt.ShiftDown(), evt.AltDown())) + // the gizmo got the event and took some action, there is no need to do anything more + processed = true; + else if (!selection.is_empty() && grabber_contains_mouse()) { + if (is_allow_mouse_drag_selected()) { - if (evt.LeftDown() && (!control_down || grabber_contains_mouse())) { - if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Svg || - m_current == Seam || m_current == MmuSegmentation || m_current == Text || m_current == Cut || m_current == MeshBoolean || m_current == BrimEars) - && gizmo_event(SLAGizmoEventType::LeftDown, mouse_pos, evt.ShiftDown(), evt.AltDown())) - // the gizmo got the event and took some action, there is no need to do anything more - processed = true; - else if (!selection.is_empty() && grabber_contains_mouse()) { - if (!(m_current == Measure || m_current == Assembly)) { + selection.start_dragging(); + start_dragging(); - selection.start_dragging(); - start_dragging(); + // Let the plater know that the dragging started + m_parent.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_STARTED)); - // Let the plater know that the dragging started - m_parent.post_event(SimpleEvent(EVT_GLCANVAS_MOUSE_DRAGGING_STARTED)); - - if (m_current == Flatten) { - // Rotate the object so the normal points downward: - m_parent.do_flatten(get_flattening_normal(), L("Tool-Lay on Face")); - // QDS - // wxGetApp().obj_manipul()->set_dirty(); - } - - m_parent.set_as_dirty(); + if (m_current == Flatten) { + // Rotate the object so the normal points downward: + m_parent.do_flatten(get_flattening_normal(), L("Tool-Lay on Face")); + // QDS + // wxGetApp().obj_manipul()->set_dirty(); } - processed = true; + + m_parent.set_as_dirty(); } - } - else if (evt.RightDown() && selected_object_idx != -1 && (m_current == SlaSupports || m_current == Hollow || m_current == BrimEars) - && gizmo_event(SLAGizmoEventType::RightDown, mouse_pos)) { - // we need to set the following right up as processed to avoid showing the context menu if the user release the mouse over the object - pending_right_up = true; - // event was taken care of by the SlaSupports gizmo processed = true; } - else if (evt.RightDown() && !control_down && selected_object_idx != -1 - && (m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation || m_current == Cut) - && gizmo_event(SLAGizmoEventType::RightDown, mouse_pos)) { - // event was taken care of by the FdmSupports / Seam / MMUPainting gizmo - processed = true; - } - else if (evt.Dragging() && m_parent.get_move_volume_id() != -1 - && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation || m_current == BrimEars)) - // don't allow dragging objects with the Sla gizmo on - processed = true; - //1.9.5 - else if (evt.Dragging() && !control_down - && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation || m_current == Cut || m_current == BrimEars) - && gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, evt.ShiftDown(), evt.AltDown())) { - // the gizmo got the event and took some action, no need to do anything more here - m_parent.set_as_dirty(); - processed = true; - } - else if (evt.Dragging() && control_down && (evt.LeftIsDown() || evt.RightIsDown())) { - // CTRL has been pressed while already dragging -> stop current action - if (evt.LeftIsDown()) - gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), true); - else if (evt.RightIsDown()) - gizmo_event(SLAGizmoEventType::RightUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), true); - } - else if (evt.LeftUp() - && (m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation || m_current == Cut || m_current == BrimEars) - && gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), control_down) - && !m_parent.is_mouse_dragging()) { - // in case SLA/FDM gizmo is selected, we just pass the LeftUp event and stop processing - neither - // object moving or selecting is suppressed in that case - processed = true; - } else if (evt.LeftUp() && m_current == Svg && m_gizmos[m_current]->get_hover_id() != -1) { - // QDS - // wxGetApp().obj_manipul()->set_dirty(); - processed = true; - } - else if (evt.LeftUp() && m_current == Flatten && m_gizmos[m_current]->get_hover_id() != -1) { - // to avoid to loose the selection when user clicks an the white faces of a different object while the Flatten gizmo is active - selection.stop_dragging(); - // QDS - //wxGetApp().obj_manipul()->set_dirty(); - processed = true; - } - else if (evt.RightUp() && m_current != EType::Undefined && !m_parent.is_mouse_dragging() ) { - gizmo_event(SLAGizmoEventType::RightUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), control_down); - processed = true; - } - else if (evt.LeftUp()) { - selection.stop_dragging(); - // QDS - //wxGetApp().obj_manipul()->set_dirty(); - } } - else { - // mouse inside toolbar - if (evt.LeftDown() || evt.LeftDClick()) { - m_mouse_capture.left = true; - m_mouse_capture.parent = &m_parent; - processed = true; - if (!selection.is_empty()) { - update_on_off_state(mouse_pos); - update_data(); - m_parent.set_as_dirty(); - try { - if ((int)m_hover >= 0 && (int)m_hover < m_gizmos.size()) { - std::string name = get_name_from_gizmo_etype(m_hover); - int count = m_gizmos[m_hover]->get_count(); - NetworkAgent* agent = GUI::wxGetApp().getAgent(); - if (agent) { - agent->track_update_property(name, std::to_string(count)); - } - } - } - catch (...) {} - } - } - else if (evt.MiddleDown()) { - m_mouse_capture.middle = true; - m_mouse_capture.parent = &m_parent; - } - else if (evt.RightDown()) { - m_mouse_capture.right = true; - m_mouse_capture.parent = &m_parent; - return true; - } + else if (evt.RightDown() && selected_object_idx != -1 && (m_current == SlaSupports || m_current == Hollow || m_current == BrimEars) + && gizmo_event(SLAGizmoEventType::RightDown, mouse_pos)) { + // we need to set the following right up as processed to avoid showing the context menu if the user release the mouse over the object + pending_right_up = true; + // event was taken care of by the SlaSupports gizmo + processed = true; + } + else if (evt.RightDown() && !control_down && selected_object_idx != -1 && (is_paint_gizmo() || m_current == Cut) + && gizmo_event(SLAGizmoEventType::RightDown, mouse_pos)) { + // event was taken care of by the paint_gizmo + processed = true; + } + else if (evt.Dragging() && m_parent.get_move_volume_id() != -1 && (m_current == SlaSupports || m_current == Hollow || is_paint_gizmo() || m_current == BrimEars)) + // don't allow dragging objects with the Sla gizmo on + processed = true; + else if (evt.Dragging() && !control_down && (m_current == SlaSupports || m_current == Hollow || is_paint_gizmo() || m_current == Cut || m_current == BrimEars) + && gizmo_event(SLAGizmoEventType::Dragging, mouse_pos, evt.ShiftDown(), evt.AltDown())) { + // the gizmo got the event and took some action, no need to do anything more here + m_parent.set_as_dirty(); + processed = true; + } + else if (evt.Dragging() && control_down && (evt.LeftIsDown() || evt.RightIsDown())) { + // CTRL has been pressed while already dragging -> stop current action + if (evt.LeftIsDown()) + gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), true); + else if (evt.RightIsDown()) + gizmo_event(SLAGizmoEventType::RightUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), true); + } + else if (evt.LeftUp() + && (m_current == SlaSupports || m_current == Hollow || is_paint_gizmo() || m_current == Cut || m_current == BrimEars) + && gizmo_event(SLAGizmoEventType::LeftUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), control_down) + && !m_parent.is_mouse_dragging()) { + // in case SLA/FDM gizmo is selected, we just pass the LeftUp event and stop processing - neither + // object moving or selecting is suppressed in that case + processed = true; + } + else if (evt.LeftUp() && m_current == Svg && m_gizmos[m_current]->get_hover_id() != -1) { + // QDS + // wxGetApp().obj_manipul()->set_dirty(); + processed = true; + } + else if (evt.LeftUp() && m_current == Flatten && m_gizmos[m_current]->get_hover_id() != -1) { + // to avoid to loose the selection when user clicks an the white faces of a different object while the Flatten gizmo is active + selection.stop_dragging(); + // QDS + //wxGetApp().obj_manipul()->set_dirty(); + processed = true; + } + else if (evt.RightUp() && m_current != EType::Undefined && !m_parent.is_mouse_dragging()) { + gizmo_event(SLAGizmoEventType::RightUp, mouse_pos, evt.ShiftDown(), evt.AltDown(), control_down); + processed = true; + } + else if (evt.LeftUp()) { + selection.stop_dragging(); + // QDS + //wxGetApp().obj_manipul()->set_dirty(); } return processed; @@ -1223,24 +986,6 @@ bool GLGizmosManager::on_char(wxKeyEvent& evt) } break; } - //case WXK_RETURN: - //{ - // if ((m_current == SlaSupports) && gizmo_event(SLAGizmoEventType::ApplyChanges)) - // processed = true; - - // break; - //} - - //case 'r' : - //case 'R' : - //{ - //if ((m_current == SlaSupports || m_current == Hollow || m_current == FdmSupports || m_current == Seam || m_current == MmuSegmentation) && gizmo_event(SLAGizmoEventType::ResetClippingPlane)) - // processed = true; - - //break; - //} - - //case WXK_BACK: case WXK_DELETE: { if ((m_current == Cut || m_current == Measure || m_current == Assembly || m_current == BrimEars) && gizmo_event(SLAGizmoEventType::Delete)) @@ -1426,10 +1171,19 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) } } } + } else if (m_current == FuzzySkin) { + GLGizmoFuzzySkin *fuzzy_skin = dynamic_cast(get_current()); + if (fuzzy_skin != nullptr && (keyCode == 'F' || keyCode == 'S' || keyCode == 'C' || keyCode == 'T')) { + processed = fuzzy_skin->on_key_down_select_tool_type(keyCode); + } + if (processed) { + // force extra frame to automatically update window size + wxGetApp().imgui()->set_requires_extra_frame(); + } } else if (m_current == FdmSupports) { GLGizmoFdmSupports* fdm_support = dynamic_cast(get_current()); - if (fdm_support != nullptr && keyCode == 'F' || keyCode == 'S' || keyCode == 'C' || keyCode == 'G') { + if (fdm_support != nullptr && (keyCode == 'F' || keyCode == 'S' || keyCode == 'C' || keyCode == 'G')) { processed = fdm_support->on_key_down_select_tool_type(keyCode); } if (processed) { @@ -1439,7 +1193,7 @@ bool GLGizmosManager::on_key(wxKeyEvent& evt) } else if (m_current == Seam) { GLGizmoSeam* seam = dynamic_cast(get_current()); - if (seam != nullptr && keyCode == 'S' || keyCode == 'C') { + if (seam != nullptr && (keyCode == 'S' || keyCode == 'C')) { processed = seam->on_key_down_select_tool_type(keyCode); } if (processed) { @@ -1490,258 +1244,121 @@ BoundingBoxf3 GLGizmosManager::get_bounding_box() const return t_aabb; } -void GLGizmosManager::render_background(float left, float top, float right, float bottom, float border) const +void GLGizmosManager::add_toolbar_items(const std::shared_ptr& p_toolbar, uint8_t& sprite_id, const std::function& p_callback) { - unsigned int tex_id = m_background_texture.texture.get_id(); - float tex_width = (float)m_background_texture.texture.get_width(); - float tex_height = (float)m_background_texture.texture.get_height(); - if ((tex_id != 0) && (tex_width > 0) && (tex_height > 0)) - { - //QDS: GUI refactor: remove the corners of gizmo - float inv_tex_width = (tex_width != 0.0f) ? 1.0f / tex_width : 0.0f; - float inv_tex_height = (tex_height != 0.0f) ? 1.0f / tex_height : 0.0f; - - float internal_left_uv = (float)m_background_texture.metadata.left * inv_tex_width; - float internal_right_uv = 1.0f - (float)m_background_texture.metadata.right * inv_tex_width; - float internal_top_uv = 1.0f - (float)m_background_texture.metadata.top * inv_tex_height; - float internal_bottom_uv = (float)m_background_texture.metadata.bottom * inv_tex_height; - - GLTexture::render_sub_texture(tex_id, left, right, bottom, top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); - - /* - float inv_tex_width = (tex_width != 0.0f) ? 1.0f / tex_width : 0.0f; - float inv_tex_height = (tex_height != 0.0f) ? 1.0f / tex_height : 0.0f; - - float internal_left = left + border; - float internal_right = right - border; - float internal_top = top - border; - float internal_bottom = bottom + border; - - // float left_uv = 0.0f; - float right_uv = 1.0f; - float top_uv = 1.0f; - float bottom_uv = 0.0f; - - float internal_left_uv = (float)m_background_texture.metadata.left * inv_tex_width; - float internal_right_uv = 1.0f - (float)m_background_texture.metadata.right * inv_tex_width; - float internal_top_uv = 1.0f - (float)m_background_texture.metadata.top * inv_tex_height; - float internal_bottom_uv = (float)m_background_texture.metadata.bottom * inv_tex_height; - - // top-left corner - GLTexture::render_sub_texture(tex_id, left, internal_left, internal_top, top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); - - // top edge - GLTexture::render_sub_texture(tex_id, internal_left, internal_right, internal_top, top, { { internal_left_uv, internal_top_uv }, { internal_right_uv, internal_top_uv }, { internal_right_uv, top_uv }, { internal_left_uv, top_uv } }); - - // top-right corner - GLTexture::render_sub_texture(tex_id, internal_right, right, internal_top, top, { { internal_right_uv, internal_top_uv }, { right_uv, internal_top_uv }, { right_uv, top_uv }, { internal_right_uv, top_uv } }); - - // center-left edge - GLTexture::render_sub_texture(tex_id, left, internal_left, internal_bottom, internal_top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); - - // center - GLTexture::render_sub_texture(tex_id, internal_left, internal_right, internal_bottom, internal_top, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); - - // center-right edge - GLTexture::render_sub_texture(tex_id, internal_right, right, internal_bottom, internal_top, { { internal_right_uv, internal_bottom_uv }, { right_uv, internal_bottom_uv }, { right_uv, internal_top_uv }, { internal_right_uv, internal_top_uv } }); - - // bottom-left corner - GLTexture::render_sub_texture(tex_id, left, internal_left, bottom, internal_bottom, { { internal_left_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_right_uv, internal_top_uv }, { internal_left_uv, internal_top_uv } }); - - // bottom edge - GLTexture::render_sub_texture(tex_id, internal_left, internal_right, bottom, internal_bottom, { { internal_left_uv, bottom_uv }, { internal_right_uv, bottom_uv }, { internal_right_uv, internal_bottom_uv }, { internal_left_uv, internal_bottom_uv } }); - - // bottom-right corner - GLTexture::render_sub_texture(tex_id, internal_right, right, bottom, internal_bottom, { { internal_right_uv, bottom_uv }, { right_uv, bottom_uv }, { right_uv, internal_bottom_uv }, { internal_right_uv, internal_bottom_uv } }); - */ + if (!p_toolbar) { + return; + } + + if (m_gizmos.empty()) { + return; } -} -void GLGizmosManager::render_arrow(const GLCanvas3D& parent, EType highlighted_type) const -{ std::vector selectable_idxs = get_selectable_idxs(); - if (selectable_idxs.empty()) - return; - float cnv_w = (float)m_parent.get_canvas_size().get_width(); - float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); - float height = get_scaled_total_height(); - float zoomed_border = m_layout.scaled_border() * inv_zoom; - float zoomed_top_x = (-0.5f * cnv_w) * inv_zoom; - float zoomed_top_y = (0.5f * height) * inv_zoom; - zoomed_top_x += zoomed_border; - zoomed_top_y -= zoomed_border; - float icons_size = m_layout.scaled_icons_size(); - float zoomed_icons_size = icons_size * inv_zoom; - float zoomed_stride_y = m_layout.scaled_stride_y() * inv_zoom; - for (size_t idx : selectable_idxs) + + auto p_gizmo_manager = this; + for (size_t i = 0; i < m_gizmos.size(); ++i) { - if (idx == highlighted_type) { - int tex_width = m_icons_texture.get_width(); - int tex_height = m_icons_texture.get_height(); - unsigned int tex_id = m_arrow_texture.texture.get_id(); - float inv_tex_width = (tex_width != 0.0f) ? 1.0f / tex_width : 0.0f; - float inv_tex_height = (tex_height != 0.0f) ? 1.0f / tex_height : 0.0f; - - float internal_left_uv = (float)m_arrow_texture.metadata.left * inv_tex_width; - float internal_right_uv = 1.0f - (float)m_arrow_texture.metadata.right * inv_tex_width; - float internal_top_uv = 1.0f - (float)m_arrow_texture.metadata.top * inv_tex_height; - float internal_bottom_uv = (float)m_arrow_texture.metadata.bottom * inv_tex_height; - - float arrow_sides_ratio = (float)m_arrow_texture.texture.get_height() / (float)m_arrow_texture.texture.get_width(); - - GLTexture::render_sub_texture(tex_id, zoomed_top_x + zoomed_icons_size * 1.2f, zoomed_top_x + zoomed_icons_size * 1.2f + zoomed_icons_size * 2.2f * arrow_sides_ratio, zoomed_top_y - zoomed_icons_size * 1.6f , zoomed_top_y + zoomed_icons_size * 0.6f, { { internal_left_uv, internal_bottom_uv }, { internal_left_uv, internal_top_uv }, { internal_right_uv, internal_top_uv }, { internal_right_uv, internal_bottom_uv } }); - break; + const auto idx = i; + if (!m_gizmos[idx]) { + continue; } - zoomed_top_y -= zoomed_stride_y; + + if (m_gizmos[idx]->get_sprite_id() == (unsigned int)EType::Measure) { + p_callback(sprite_id); + } + + GLToolbarItem::Data item; + + item.name = convert_gizmo_type_to_string(static_cast(m_gizmos[idx]->get_sprite_id())); + item.icon_filename_callback = [p_gizmo_manager, idx](bool is_dark_mode)->std::string { + return p_gizmo_manager->m_gizmos[idx]->get_icon_filename(is_dark_mode); + }; + item.tooltip = ""; + item.sprite_id = sprite_id++; + const auto t_type = m_gizmos[idx]->get_sprite_id(); + item.left.action_callback = [p_gizmo_manager, t_type]() { + p_gizmo_manager->on_click(t_type); + }; + item.enabling_callback = [p_gizmo_manager, idx]()->bool { + return p_gizmo_manager->m_gizmos[idx]->is_activable(); + }; + item.on_hover = [p_gizmo_manager, idx]()->std::string { + return p_gizmo_manager->on_hover(idx); + }; + item.left.toggable = true; + item.b_toggle_disable_others = false; + item.b_toggle_affectable = false; + item.left.render_callback = [p_gizmo_manager, idx](float left, float right, float bottom, float top, float toolbar_height) { + if (p_gizmo_manager->get_current_type() != idx) { + return; + } + float cnv_h = (float)p_gizmo_manager->m_parent.get_canvas_size().get_height(); + p_gizmo_manager->m_gizmos[idx]->render_input_window(left, toolbar_height, cnv_h); + }; + item.pressed_recheck_callback = [p_gizmo_manager, t_type]()->bool { + return p_gizmo_manager->m_current == t_type; + }; + const bool b_is_selectable = (std::find(selectable_idxs.begin(), selectable_idxs.end(), idx) != selectable_idxs.end()); + item.visibility_callback = [p_gizmo_manager, idx, b_is_selectable]()->bool { + bool rt = b_is_selectable; + if (idx == EType::Svg) { + rt = rt && (p_gizmo_manager->m_current == EType::Svg); + } + else if (idx == EType::Text) { + rt = rt && p_gizmo_manager->m_current != EType::Svg; + } + return rt; + }; + item.visible = b_is_selectable; + p_toolbar->add_item(item); } } -//QDS: GUI refactor: GLToolbar&&Gizmo adjust -//when rendering, {0, 0} is at the center, {-0.5, 0.5} at the left-top -void GLGizmosManager::do_render_overlay() const +std::string GLGizmosManager::convert_gizmo_type_to_string(Slic3r::GUI::GLGizmosManager::EType t_type) { - std::vector selectable_idxs = get_selectable_idxs(); - if (selectable_idxs.empty()) - return; - - float cnv_w = (float)m_parent.get_canvas_size().get_width(); - float cnv_h = (float)m_parent.get_canvas_size().get_height(); - float zoom = (float)wxGetApp().plater()->get_camera().get_zoom(); - float inv_zoom = (float)wxGetApp().plater()->get_camera().get_inv_zoom(); - - float height = get_scaled_total_height(); - float width = get_scaled_total_width(); - float zoomed_border = m_layout.scaled_border() * inv_zoom; - - float zoomed_top_x; - if (m_parent.get_canvas_type() == GLCanvas3D::CanvasAssembleView) { - zoomed_top_x = 0.5f * m_parent.get_assembly_paint_toolbar_width() * inv_zoom; + switch (t_type) { + case Slic3r::GUI::GLGizmosManager::EType::Move: + return "Move"; + case Slic3r::GUI::GLGizmosManager::EType::Rotate: + return "Rotate"; + case Slic3r::GUI::GLGizmosManager::EType::Scale: + return "Scale"; + case Slic3r::GUI::GLGizmosManager::EType::Flatten: + return "Flatten"; + case Slic3r::GUI::GLGizmosManager::EType::Cut: + return "Cut"; + case Slic3r::GUI::GLGizmosManager::EType::MeshBoolean: + return "MeshBoolean"; + case Slic3r::GUI::GLGizmosManager::EType::FdmSupports: + return "FdmSupports"; + case Slic3r::GUI::GLGizmosManager::EType::Seam: + return "Seam"; + case Slic3r::GUI::GLGizmosManager::EType::Text: + return "Text"; + case Slic3r::GUI::GLGizmosManager::EType::Svg: + return "Svg"; + case Slic3r::GUI::GLGizmosManager::EType::MmuSegmentation: + return "Color Painting"; + case Slic3r::GUI::GLGizmosManager::EType::FuzzySkin: + return "FuzzySkin"; + case Slic3r::GUI::GLGizmosManager::EType::Measure: + return "Mesause"; + case Slic3r::GUI::GLGizmosManager::EType::Assembly: + return "Assembly"; + case Slic3r::GUI::GLGizmosManager::EType::Simplify: + return "Simplify"; + case Slic3r::GUI::GLGizmosManager::EType::BrimEars: + return "BrimEars"; + case Slic3r::GUI::GLGizmosManager::EType::SlaSupports: + return "SlaSupports"; + case Slic3r::GUI::GLGizmosManager::EType::Hollow: + return "Hollow"; + case Slic3r::GUI::GLGizmosManager::EType::Undefined: + return "Undefined"; + default: + return "Unknow"; } - else { - //QDS: GUI refactor: GLToolbar&&Gizmo adjust -#if QDS_TOOLBAR_ON_TOP - float main_toolbar_width = (float)m_parent.get_main_toolbar_width(); - float assemble_view_width = (float)m_parent.get_assemble_view_toolbar_width(); - float collapse_width = (float)m_parent.get_collapse_toolbar_width(); - float separator_width = m_parent.get_separator_toolbar_width(); - //float space_width = GLGizmosManager::Default_Icons_Size * wxGetApp().toolbar_icon_scale(); - //float zoomed_top_x = 0.5f *(cnv_w + main_toolbar_width - 2 * space_width - width) * inv_zoom; - - float main_toolbar_left = m_parent.get_main_toolbar_left(cnv_w, inv_zoom); - //float zoomed_top_x = 0.5f *(main_toolbar_width + collapse_width - width - assemble_view_width) * inv_zoom; - zoomed_top_x = main_toolbar_left + (main_toolbar_width + separator_width /2.f) * inv_zoom; - } - float zoomed_top_y = 0.5f * cnv_h * inv_zoom; -#else - //float zoomed_top_x = (-0.5f * cnv_w) * inv_zoom; - //float zoomed_top_y = (0.5f * height) * inv_zoom; - float zoomed_top_x = (0.5f * cnv_w - width) * inv_zoom; - float main_toolbar_height = (float)m_parent.get_main_toolbar_height(); - float assemble_view_height = (float)m_parent.get_assemble_view_toolbar_height(); - //float space_height = GLGizmosManager::Default_Icons_Size * wxGetApp().toolbar_icon_scale(); - float zoomed_top_y = 0.5f * (height + assemble_view_height - main_toolbar_height) * inv_zoom; -#endif - //BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": zoomed_top_y %1%, space_height %2%, main_toolbar_height %3% zoomed_top_x %4%") % zoomed_top_y % space_height % main_toolbar_height % zoomed_top_x; - - float zoomed_left = zoomed_top_x; - float zoomed_top = zoomed_top_y; - float zoomed_right = zoomed_left + width * inv_zoom; - float zoomed_bottom = zoomed_top - height * inv_zoom; - - render_background(zoomed_left, zoomed_top, zoomed_right, zoomed_bottom, zoomed_border); - - zoomed_top_x += zoomed_border; - zoomed_top_y -= zoomed_border; - - float icons_size = m_layout.scaled_icons_size(); - float zoomed_icons_size = icons_size * inv_zoom; - float zoomed_stride_y = m_layout.scaled_stride_y() * inv_zoom; - //QDS: GUI refactor: GLToolbar&&Gizmo adjust - float zoomed_stride_x = m_layout.scaled_stride_x() * inv_zoom; - - unsigned int icons_texture_id = m_icons_texture.get_id(); - int tex_width = m_icons_texture.get_width(); - int tex_height = m_icons_texture.get_height(); - - if ((icons_texture_id == 0) || (tex_width <= 1) || (tex_height <= 1)) - return; - - float du = (float)(tex_width - 1) / (6.0f * (float)tex_width); // 6 is the number of possible states if the icons - float dv = (float)(tex_height - 1) / (float)(m_gizmos.size() * tex_height); - - // tiles in the texture are spaced by 1 pixel - float u_offset = 1.0f / (float)tex_width; - float v_offset = 1.0f / (float)tex_height; - - bool is_render_current = false; - - for (size_t idx : selectable_idxs) - { - GLGizmoBase* gizmo = m_gizmos[idx].get(); - bool selected_svg = is_svg_selected((int)idx); - if (selected_svg) { - gizmo = m_gizmos[m_current].get(); - } - unsigned int sprite_id = gizmo->get_sprite_id(); - // higlighted state needs to be decided first so its highlighting in every other state - int icon_idx = (m_highlight.first == idx ? (m_highlight.second ? 4 : 5) : - (m_current == idx || selected_svg) ? 2 : - ((m_hover == idx) ? 1 : (gizmo->is_activable() ? 0 : 3))); - - float v_top = v_offset + sprite_id * dv; - float u_left = u_offset + icon_idx * du; - float v_bottom = v_top + dv - v_offset; - float u_right = u_left + du - u_offset; - - GLTexture::render_sub_texture(icons_texture_id, zoomed_top_x, zoomed_top_x + zoomed_icons_size, zoomed_top_y - zoomed_icons_size, zoomed_top_y, { { u_left, v_bottom }, { u_right, v_bottom }, { u_right, v_top }, { u_left, v_top } }); - - if (idx == m_current// Orca: Show Svg dialog at the same place as emboss gizmo - || (selected_svg)) { - //QDS: GUI refactor: GLToolbar&&Gizmo adjust - //render_input_window uses a different coordination(imgui) - //1. no need to scale by camera zoom, set {0,0} at left-up corner for imgui -#if QDS_TOOLBAR_ON_TOP - //gizmo->render_input_window(width, 0.5f * cnv_h - zoomed_top_y * zoom, toolbar_top); - m_gizmos[m_current]->render_input_window(0.5 * cnv_w + zoomed_top_x * zoom, height, cnv_h); - - is_render_current = true; -#else - float toolbar_top = cnv_h - wxGetApp().plater()->get_view_toolbar().get_height(); - //gizmo->render_input_window(width, 0.5f * cnv_h - zoomed_top_y * zoom, toolbar_top); - gizmo->render_input_window(cnv_w - width, 0.5f * cnv_h - zoomed_top_y * zoom, toolbar_top); -#endif - } -#if QDS_TOOLBAR_ON_TOP - zoomed_top_x += zoomed_stride_x; -#else - zoomed_top_y -= zoomed_stride_y; -#endif - } - - // QDS simplify gizmo is not a selected gizmo and need to render input window - if (!is_render_current && m_current != Undefined) { - m_gizmos[m_current]->render_input_window(0.5 * cnv_w + zoomed_top_x * zoom, height, cnv_h); - } -} - -float GLGizmosManager::get_scaled_total_height() const -{ -//QDS: GUI refactor: to support top layout -#if QDS_TOOLBAR_ON_TOP - return 2.0f * m_layout.scaled_border() + m_layout.scaled_icons_size(); -#else - return m_layout.scale * (2.0f * m_layout.border + (float)get_selectable_idxs().size() * m_layout.stride_y() - m_layout.gap_y); -#endif -} - -float GLGizmosManager::get_scaled_total_width() const -{ -//QDS: GUI refactor: to support top layout -#if QDS_TOOLBAR_ON_TOP - return m_layout.scale * (2.0f * m_layout.border + (float)get_selectable_idxs().size() * m_layout.stride_x() - m_layout.gap_x); -#else - return 2.0f * m_layout.scaled_border() + m_layout.scaled_icons_size(); -#endif } GLGizmoBase* GLGizmosManager::get_current() const @@ -1756,10 +1373,11 @@ GLGizmoBase* GLGizmosManager::get_gizmo(GLGizmosManager::EType type) const GLGizmosManager::EType GLGizmosManager::get_gizmo_from_name(const std::string& gizmo_name) const { + const auto is_dark_mode = m_parent.get_dark_mode_status(); std::vector selectable_idxs = get_selectable_idxs(); for (size_t idx = 0; idx < selectable_idxs.size(); ++idx) { - std::string filename = m_gizmos[selectable_idxs[idx]]->get_icon_filename(); + std::string filename = m_gizmos[selectable_idxs[idx]]->get_icon_filename(is_dark_mode); filename = filename.substr(0, filename.find_first_of('.')); if (filename == gizmo_name) return (GLGizmosManager::EType)selectable_idxs[idx]; @@ -1767,82 +1385,27 @@ GLGizmosManager::EType GLGizmosManager::get_gizmo_from_name(const std::string& g return GLGizmosManager::EType::Undefined; } -bool GLGizmosManager::generate_icons_texture() const -{ - std::string path = resources_dir() + "/images/"; - std::vector filenames; - for (size_t idx=0; idxget_icon_filename(); - if (!icon_filename.empty()) - filenames.push_back(path + icon_filename); - } - } - - std::vector> states; - states.push_back(std::make_pair(1, false)); // Activable - states.push_back(std::make_pair(0, false)); // Hovered - states.push_back(std::make_pair(0, true)); // Selected - states.push_back(std::make_pair(2, false)); // Disabled - states.push_back(std::make_pair(0, false)); // HighlightedShown - states.push_back(std::make_pair(2, false)); // HighlightedHidden - - unsigned int sprite_size_px = (unsigned int)m_layout.scaled_icons_size(); -// // force even size -// if (sprite_size_px % 2 != 0) -// sprite_size_px += 1; - - bool res = m_icons_texture.load_from_svg_files_as_sprites_array(filenames, states, sprite_size_px, false); - if (res) - m_icons_texture_dirty = false; - - return res; -} - -void GLGizmosManager::update_on_off_state(const Vec2d& mouse_pos) +void GLGizmosManager::update_on_off_state(size_t idx) { if (!m_enabled) return; - - size_t idx = get_gizmo_idx_from_mouse(mouse_pos); + if (is_text_first_clicked(idx)) { // open text gizmo + GLGizmoBase *gizmo_text= m_gizmos[EType::Text].get(); + if (dynamic_cast(gizmo_text)->on_shortcut_key()) {//create text on mesh + return; + } + } if (is_svg_selected(idx)) {// close svg gizmo open_gizmo(EType::Svg); return; } - if (idx != Undefined && m_gizmos[idx]->is_activable() && m_hover == idx) { + if (idx != Undefined && m_gizmos[idx]->is_activable()) { activate_gizmo(m_current == idx ? Undefined : (EType)idx); // QDS wxGetApp().obj_list()->select_object_item((EType) idx <= Scale || (EType) idx == Text); } } -std::string GLGizmosManager::update_hover_state(const Vec2d& mouse_pos) -{ - std::string name = ""; - - if (!m_enabled) - return name; - - m_hover = Undefined; - - size_t idx = get_gizmo_idx_from_mouse(mouse_pos); - if (idx != Undefined) { - if (is_svg_selected(idx)) { - name = m_gizmos[m_current]->get_name(); - } - else { - name = m_gizmos[idx]->get_name(); - } - - if (m_gizmos[idx]->is_activable()) - m_hover = (EType)idx; - } - - return name; -} - bool GLGizmosManager::activate_gizmo(EType type) { if (m_gizmos.empty() || m_current == type) @@ -1878,10 +1441,20 @@ bool GLGizmosManager::activate_gizmo(EType type) //if (m_current == Text) { // wxGetApp().imgui()->load_fonts_texture(); //} + new_gizmo->set_serializing(m_serializing); new_gizmo->set_state(GLGizmoBase::On); - if (is_show_only_active_plate()) { - check_object_located_outside_plate(); + new_gizmo->set_serializing(false); + update_show_only_active_plate(); + + try { + if ((int)m_hover >= 0 && (int)m_hover < m_gizmos.size()) { + std::string name = convert_gizmo_type_to_string(m_hover); + int count = m_gizmos[m_hover]->get_count(); + NetworkAgent* agent = GUI::wxGetApp().getAgent(); + if (agent) { agent->track_update_property(name, std::to_string(count)); } + } } + catch (...) {} } return true; } @@ -1896,10 +1469,45 @@ bool GLGizmosManager::grabber_contains_mouse() const return (curr != nullptr) ? (curr->get_hover_id() != -1) : false; } +bool GLGizmosManager::is_text_first_clicked(int idx) const { + return m_current == Undefined && idx == Text; +} + bool GLGizmosManager::is_svg_selected(int idx) const { return m_current == Svg && idx == Text; } +std::string GLGizmosManager::on_hover(int idx) +{ + std::string t_name{}; + if (is_svg_selected(idx)) { + t_name = m_gizmos[m_current]->get_name(); + } + else { + t_name = m_gizmos[idx]->get_name(); + } + + if (m_gizmos[idx]->is_activable()) + m_hover = (EType)idx; + return t_name; +} + +void GLGizmosManager::on_click(int idx) +{ + Selection &selection = m_parent.get_selection(); + if (selection.is_empty()) { + if (is_text_first_clicked(idx)) { // open text gizmo + GLGizmoBase* gizmo_text = m_gizmos[EType::Text].get(); + dynamic_cast(gizmo_text)->on_shortcut_key();//direct create text on plate + return; + } + } + + update_on_off_state(idx); + update_data(); + m_parent.set_as_dirty(); +} + bool GLGizmosManager::is_in_editing_mode(bool error_notification) const { if (m_current == SlaSupports && dynamic_cast(get_current())->is_in_editing_mode()) { @@ -1910,6 +1518,7 @@ bool GLGizmosManager::is_in_editing_mode(bool error_notification) const } else { return false; } + } @@ -1929,33 +1538,9 @@ int GLGizmosManager::get_shortcut_key(GLGizmosManager::EType type) const return m_gizmos[type]->get_shortcut_key(); } -std::string get_name_from_gizmo_etype(GLGizmosManager::EType type) +void GLGizmosManager::set_highlight(EType gizmo, bool highlight_shown) { - switch (type) { - case GLGizmosManager::EType::Move: - return "Move"; - case GLGizmosManager::EType::Rotate: - return "Rotate"; - case GLGizmosManager::EType::Scale: - return "Scale"; - case GLGizmosManager::EType::Flatten: - return "Flatten"; - case GLGizmosManager::EType::Cut: - return "Cut"; - case GLGizmosManager::EType::MeshBoolean: - return "MeshBoolean"; - case GLGizmosManager::EType::FdmSupports: - return "FdmSupports"; - case GLGizmosManager::EType::Seam: - return "Seam"; - case GLGizmosManager::EType::Text: - return "Text"; - case GLGizmosManager::EType::MmuSegmentation: - return "Color Painting"; - default: - return ""; - } - return ""; + m_highlight = std::pair(gizmo, highlight_shown); } } // namespace GUI diff --git a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp index 95ab37d..b0ebe10 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmosManager.hpp @@ -2,7 +2,6 @@ #define slic3r_GUI_GLGizmosManager_hpp_ #include "slic3r/GUI/GLTexture.hpp" -#include "slic3r/GUI/GLToolbar.hpp" #include "slic3r/GUI/Gizmos/GLGizmoBase.hpp" #include "slic3r/GUI/Gizmos/GLGizmosCommon.hpp" //QDS: GUI refactor: add object manipulation @@ -13,6 +12,8 @@ #include "wx/timer.h" #include +#include +#include //QDS: GUI refactor: to support top layout #define QDS_TOOLBAR_ON_TOP 1 @@ -31,6 +32,7 @@ enum class SLAGizmoEventType : unsigned char; class CommonGizmosDataPool; //QDS: GUI refactor: add object manipulation class GizmoObjectManipulation; +class GLToolbar; class Rect { float m_left; @@ -73,16 +75,16 @@ public: Flatten, Cut, MeshBoolean, - FdmSupports, - Seam, - // QDS + Assembly, + MmuSegmentation, Text, Svg, - MmuSegmentation, - Measure, - Assembly, - Simplify, + FdmSupports, + Seam, BrimEars, + FuzzySkin, + Measure, + Simplify, SlaSupports, // QDS //FaceRecognition, @@ -91,34 +93,10 @@ public: }; private: - struct Layout - { - float scale{ 1.0f }; - float icons_size{ Default_Icons_Size }; - float border{ 5.0f }; - float gap_y{ 5.0f }; - //QDS: GUI refactor: to support top layout - float gap_x{ 5.0f }; - float stride_x() const { return icons_size + gap_x;} - float scaled_gap_x() const { return scale * gap_x; } - float scaled_stride_x() const { return scale * stride_x(); } - - float stride_y() const { return icons_size + gap_y;} - - float scaled_icons_size() const { return scale * icons_size; } - float scaled_border() const { return scale * border; } - float scaled_gap_y() const { return scale * gap_y; } - float scaled_stride_y() const { return scale * stride_y(); } - }; GLCanvas3D& m_parent; bool m_enabled; std::vector> m_gizmos; - mutable GLTexture m_icons_texture; - mutable bool m_icons_texture_dirty; - BackgroundTexture m_background_texture; - BackgroundTexture m_arrow_texture; - Layout m_layout; EType m_current; EType m_hover; std::pair m_highlight; // bool true = higlightedShown, false = highlightedHidden @@ -127,25 +105,9 @@ private: GizmoObjectManipulation m_object_manipulation; std::vector get_selectable_idxs() const; - size_t get_gizmo_idx_from_mouse(const Vec2d& mouse_pos) const; bool activate_gizmo(EType type); - struct MouseCapture - { - bool left; - bool middle; - bool right; - GLCanvas3D* parent; - - MouseCapture() { reset(); } - - bool any() const { return left || middle || right; } - void reset() { left = middle = right = false; parent = nullptr; } - }; - - MouseCapture m_mouse_capture; - std::string m_tooltip; bool m_serializing; std::unique_ptr m_common_gizmos_data; @@ -180,16 +142,10 @@ public: explicit GLGizmosManager(GLCanvas3D& parent); - void switch_gizmos_icon_filename(); - bool init(); bool init_icon_textures(); - float get_layout_scale(); - - bool init_arrow(const BackgroundTexture::Metadata& arrow_texture); - template void load(Archive& ar) { @@ -228,10 +184,6 @@ public: bool is_enabled() const { return m_enabled; } void set_enabled(bool enable) { m_enabled = enable; } - void set_icon_dirty() { m_icons_texture_dirty = true; } - void set_overlay_icon_size(float size); - void set_overlay_scale(float scale); - void refresh_on_off_state(); void reset_all_states(); bool is_serializing() const { return m_serializing; } @@ -285,14 +237,20 @@ public: bool is_gizmo_activable_when_single_full_instance(); bool is_gizmo_click_empty_not_exit(); - bool is_show_only_active_plate(); - bool is_ban_move_glvolume(); - bool get_gizmo_active_condition(GLGizmosManager::EType type); //1.9.5 + bool is_only_text_volume() const; + bool is_show_only_active_plate() const; + bool is_ban_move_glvolume() const; + bool get_gizmo_active_condition(GLGizmosManager::EType type); + void update_show_only_active_plate(); void check_object_located_outside_plate(bool change_plate =true); - bool get_object_located_outside_plate() { return m_object_located_outside_plate; } + void set_object_located_outside_plate(bool flag) { m_object_located_outside_plate = flag; } + bool get_object_located_outside_plate() const { return m_object_located_outside_plate; } bool gizmo_event(SLAGizmoEventType action, const Vec2d& mouse_position = Vec2d::Zero(), bool shift_down = false, bool alt_down = false, bool control_down = false); bool is_paint_gizmo()const; - bool is_allow_select_all(); + bool is_allow_select_all() const; + bool is_allow_show_volume_highlight_outline() const; + bool is_allow_drag_volume() const; + bool is_allow_mouse_drag_selected() const; ClippingPlane get_clipping_plane() const; ClippingPlane get_assemble_view_clipping_plane() const; bool wants_reslice_supports_on_undo() const; @@ -306,10 +264,6 @@ public: void render_painter_gizmo() const; void render_painter_assemble_view() const; - void render_overlay(); - - void render_arrow(const GLCanvas3D& parent, EType highlighted_type) const; - std::string get_tooltip() const; bool on_mouse(wxMouseEvent& evt); @@ -323,34 +277,29 @@ public: int get_shortcut_key(GLGizmosManager::EType) const; // To end highlight set gizmo = undefined - void set_highlight(EType gizmo, bool highlight_shown) { m_highlight = std::pair(gizmo, highlight_shown); } + void set_highlight(EType gizmo, bool highlight_shown); bool get_highlight_state() const { return m_highlight.second; } - //QDS: GUI refactor: GLToolbar adjust - float get_scaled_total_height() const; - float get_scaled_total_width() const; GizmoObjectManipulation& get_object_manipulation() { return m_object_manipulation; } bool get_uniform_scaling() const { return m_object_manipulation.get_uniform_scaling();} BoundingBoxf3 get_bounding_box() const; + void add_toolbar_items(const std::shared_ptr& p_toolbar, uint8_t& sprite_id, const std::function& p_callback); + + static std::string convert_gizmo_type_to_string(Slic3r::GUI::GLGizmosManager::EType t_type); private: - void render_background(float left, float top, float right, float bottom, float border) const; - void do_render_overlay() const; - - bool generate_icons_texture() const; - - void update_on_off_state(const Vec2d& mouse_pos); - std::string update_hover_state(const Vec2d& mouse_pos); + void update_on_off_state(size_t idx); bool grabber_contains_mouse() const; + bool is_text_first_clicked(int idx) const; bool is_svg_selected(int idx) const; + std::string on_hover(int idx); + void on_click(int idx); private: bool m_object_located_outside_plate{false}; }; -std::string get_name_from_gizmo_etype(GLGizmosManager::EType type); - } // namespace GUI } // namespace Slic3r diff --git a/src/slic3r/GUI/GuiTextShape.cpp b/src/slic3r/GUI/GuiTextShape.cpp new file mode 100644 index 0000000..10bf7ad --- /dev/null +++ b/src/slic3r/GUI/GuiTextShape.cpp @@ -0,0 +1,47 @@ +#include "GuiTextShape.hpp" +#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +#include "imgui/imstb_truetype.h" // stbtt_fontinfo +namespace Slic3r { + using namespace Emboss; + namespace GUI { + ExPolygons GuiTextShape::letter2shapes(wchar_t letter, Point &cursor, FontFileWithCache &font_with_cache, const FontProp &font_prop, fontinfo_opt &font_info_cache) + { + assert(font_with_cache.has_value()); + if (!font_with_cache.has_value()) return {}; + + Glyphs & cache = *font_with_cache.cache; + const FontFile &font = *font_with_cache.font_file; + + if (letter == '\n') { + cursor.x() = 0; + // 2d shape has opposit direction of y + cursor.y() -= get_line_height(font, font_prop); + return {}; + } + if (letter == '\t') { + // '\t' = 4*space => same as imgui + const int count_spaces = 4; + const Glyph *space = ::get_glyph(int(' '), font, font_prop, cache, font_info_cache); + if (space == nullptr) return {}; + cursor.x() += count_spaces * space->advance_width; + return {}; + } + if (letter == '\r') return {}; + + int unicode = static_cast(letter); + auto it = cache.find(unicode); + + // Create glyph from font file and cache it + const Glyph *glyph_ptr = (it != cache.end()) ? &it->second : get_glyph(unicode, font, font_prop, cache, font_info_cache); + if (glyph_ptr == nullptr) return {}; + + // move glyph to cursor position + ExPolygons expolygons = glyph_ptr->shape; // copy + for (ExPolygon &expolygon : expolygons) expolygon.translate(cursor); + + cursor.x() += glyph_ptr->advance_width; + return expolygons; +} +} // namespace GUI + +}; // namespace Slic3r diff --git a/src/slic3r/GUI/GuiTextShape.hpp b/src/slic3r/GUI/GuiTextShape.hpp new file mode 100644 index 0000000..4d989f0 --- /dev/null +++ b/src/slic3r/GUI/GuiTextShape.hpp @@ -0,0 +1,23 @@ +#ifndef slic3r_Text_Shape_hpp_ +#define slic3r_Text_Shape_hpp_ + +#include "libslic3r/TriangleMesh.hpp" +#include +#include +struct stbtt_fontinfo; +using fontinfo_opt = std::optional; +namespace Slic3r { + namespace GUI { + class GuiTextShape + { + public: + static ExPolygons letter2shapes(wchar_t letter, Point &cursor, FontFileWithCache &font_with_cache, const FontProp &font_prop, fontinfo_opt &font_info_cache); + + private: + }; + +} + +}; // namespace Slic3r + +#endif // slic3r_Text_Shape_hpp_ \ No newline at end of file diff --git a/src/slic3r/GUI/HMS.cpp b/src/slic3r/GUI/HMS.cpp index c12f0f8..14a9e5d 100644 --- a/src/slic3r/GUI/HMS.cpp +++ b/src/slic3r/GUI/HMS.cpp @@ -76,7 +76,6 @@ int HMSQuery::download_hms_related(const std::string& hms_type, const std::strin bool to_save_local = false; json j; - BOOST_LOG_TRIVIAL(info) << "hms: download url = " << url; Slic3r::Http http = Slic3r::Http::get(url); http.on_complete([this, receive_json, hms_type, &to_save_local, &j, & local_version](std::string body, unsigned status) { try { diff --git a/src/slic3r/GUI/HintNotification.cpp b/src/slic3r/GUI/HintNotification.cpp index e9026c4..7f39d7b 100644 --- a/src/slic3r/GUI/HintNotification.cpp +++ b/src/slic3r/GUI/HintNotification.cpp @@ -83,7 +83,7 @@ namespace { { boost::filesystem::path path(boost::filesystem::path(data_dir()) / "user" / "hints.cereal"); if (!boost::filesystem::exists(path)) { - BOOST_LOG_TRIVIAL(warning) << "Failed to load to hints.cereal. File does not exists. " << path.string(); + BOOST_LOG_TRIVIAL(warning) << "Failed to load to hints.cereal. File does not exists. " << PathSanitizer::sanitize(path); return; } boost::nowide::ifstream file(path.string()); diff --git a/src/slic3r/GUI/IMSlider.cpp b/src/slic3r/GUI/IMSlider.cpp index 1211994..3ac7149 100644 --- a/src/slic3r/GUI/IMSlider.cpp +++ b/src/slic3r/GUI/IMSlider.cpp @@ -1153,8 +1153,8 @@ void IMSlider::render_input_custom_gcode(std::string custom_gcode) strcpy(m_custom_gcode, custom_gcode.c_str()); } const int text_height = 6; - - ImGui::InputTextMultiline("##text", m_custom_gcode, sizeof(m_custom_gcode), ImVec2(-1, ImGui::GetTextLineHeight() * text_height)); + const ImGuiInputTextFlags flag = ImGuiInputTextFlags_Multiline; + ImGui::InputTextMultiline("##text", m_custom_gcode, sizeof(m_custom_gcode), ImVec2(-1, ImGui::GetTextLineHeight() * text_height), flag); ImGui::NewLine(); ImGui::SameLine(ImGui::GetStyle().WindowPadding.x * 14); diff --git a/src/slic3r/GUI/IMToolbar.cpp b/src/slic3r/GUI/IMToolbar.cpp index 1dbfc9e..ab04873 100644 --- a/src/slic3r/GUI/IMToolbar.cpp +++ b/src/slic3r/GUI/IMToolbar.cpp @@ -61,8 +61,6 @@ void IMToolbar::del_stats_item() void IMToolbar::set_enabled(bool enable) { m_enabled = enable; - if (!m_enabled) - is_render_finish = false; } bool IMReturnToolbar::init() diff --git a/src/slic3r/GUI/IMToolbar.hpp b/src/slic3r/GUI/IMToolbar.hpp index add067d..03e51a2 100644 --- a/src/slic3r/GUI/IMToolbar.hpp +++ b/src/slic3r/GUI/IMToolbar.hpp @@ -49,7 +49,6 @@ public: float icon_height; bool is_display_scrollbar; bool show_stats_item{ false }; - bool is_render_finish{false}; IMToolbar() { icon_width = DEFAULT_TOOLBAR_BUTTON_WIDTH; icon_height = DEFAULT_TOOLBAR_BUTTON_HEIGHT; diff --git a/src/slic3r/GUI/ImGuiWrapper.cpp b/src/slic3r/GUI/ImGuiWrapper.cpp index 920dc67..d986684 100644 --- a/src/slic3r/GUI/ImGuiWrapper.cpp +++ b/src/slic3r/GUI/ImGuiWrapper.cpp @@ -152,6 +152,7 @@ const ImVec4 ImGuiWrapper::COL_BUTTON_HOVERED = COL_ORANGE_LIGHT; const ImVec4 ImGuiWrapper::COL_BUTTON_ACTIVE = ImGuiWrapper::COL_BUTTON_HOVERED; //QDS //B +const ImVec4 ImGuiWrapper::COL_WHITE = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); const ImVec4 ImGuiWrapper::COL_RED = ImVec4(1.0f, 0.0f, 0.0f, 1.0f); const ImVec4 ImGuiWrapper::COL_GREEN = ImVec4(0.0f, 1.0f, 0.0f, 1.0f); const ImVec4 ImGuiWrapper::COL_BLUE = ImVec4(0.0f, 0.0f, 1.0f, 1.0f); @@ -165,6 +166,7 @@ const ImVec4 ImGuiWrapper::COL_TITLE_BG = { 0.745f, 0.745f, 0.745f, 1.0 const ImVec4 ImGuiWrapper::COL_WINDOW_BG = { 1.000f, 1.000f, 1.000f, 1.0f }; const ImVec4 ImGuiWrapper::COL_WINDOW_BG_DARK = { 45 / 255.f, 45 / 255.f, 49 / 255.f, 1.f }; const ImVec4 ImGuiWrapper::COL_QIDI = {68.0f/ 255.0f, 121.0f / 255.0f, 251.0f / 255, 1.0f}; +const ImVec4 ImGuiWrapper::COL_QIDI_CHANGE = {1.0f, 111.0 / 255.0f, 0.0f / 255, 1.0f}; int ImGuiWrapper::TOOLBAR_WINDOW_FLAGS = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove @@ -2947,6 +2949,10 @@ void ImGuiWrapper::render_draw_data(ImDrawData *draw_data) 0.0f, 0.0f, 0.0f, 1.0f; shader->set_uniform("Texture", 0); shader->set_uniform("ProjMtx", ortho_projection); + + const uint8_t stage = 0; + shader->set_uniform("s_texture", stage); + // Will project scissor/clipping rectangles into framebuffer space const ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports const ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2) @@ -2994,6 +3000,7 @@ void ImGuiWrapper::render_draw_data(ImDrawData *draw_data) // Apply scissor/clipping rectangle (Y is inverted in OpenGL) glsafe(::glScissor((int)clip_min.x, (int)(fb_height - clip_max.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y))); // Bind texture, Draw + glsafe(::glActiveTexture(GL_TEXTURE0 + stage)); glsafe(::glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID())); glsafe(::glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)))); } @@ -3013,6 +3020,7 @@ void ImGuiWrapper::render_draw_data(ImDrawData *draw_data) glsafe(::glDisable(GL_SCISSOR_TEST)); glsafe(::glEnable(GL_CULL_FACE)); glsafe(::glEnable(GL_DEPTH_TEST)); + glsafe(::glDisable(GL_BLEND)); wxGetApp().unbind_shader(); diff --git a/src/slic3r/GUI/ImGuiWrapper.hpp b/src/slic3r/GUI/ImGuiWrapper.hpp index 5298aa6..23bb73a 100644 --- a/src/slic3r/GUI/ImGuiWrapper.hpp +++ b/src/slic3r/GUI/ImGuiWrapper.hpp @@ -342,6 +342,7 @@ public: static const ImVec4 COL_BUTTON_ACTIVE; //QDS add more colors + static const ImVec4 COL_WHITE; static const ImVec4 COL_RED; static const ImVec4 COL_GREEN; static const ImVec4 COL_BLUE; @@ -355,6 +356,7 @@ public: static const ImVec4 COL_SEPARATOR; static const ImVec4 COL_SEPARATOR_DARK; static const ImVec4 COL_QIDI; + static const ImVec4 COL_QIDI_CHANGE; //QDS static void on_change_color_mode(bool is_dark); static void push_toolbar_style(const float scale); diff --git a/src/slic3r/GUI/ImageDPIFrame.cpp b/src/slic3r/GUI/ImageDPIFrame.cpp index 3c7f4a7..998ff44 100644 --- a/src/slic3r/GUI/ImageDPIFrame.cpp +++ b/src/slic3r/GUI/ImageDPIFrame.cpp @@ -81,6 +81,23 @@ void ImageDPIFrame::on_timer(wxTimerEvent &event) Raise(); } m_timer_count++; + }else{ + wxPoint mouse_pos = wxGetMousePosition(); + wxRect window_rect = GetScreenRect(); + + wxPoint center(window_rect.x + window_rect.width / 2, window_rect.y + window_rect.height / 2); + int half_width = window_rect.width / 2; + int half_height = window_rect.height / 2; + wxRect expanded_rect( + center.x - half_width * 2, + center.y - half_height * 2, + window_rect.width * 2, + window_rect.height * 2 + ); + + if (!expanded_rect.Contains(mouse_pos)) { + on_hide(); + } } } diff --git a/src/slic3r/GUI/Jobs/ArrangeJob.cpp b/src/slic3r/GUI/Jobs/ArrangeJob.cpp index 83f774d..e5f3f80 100644 --- a/src/slic3r/GUI/Jobs/ArrangeJob.cpp +++ b/src/slic3r/GUI/Jobs/ArrangeJob.cpp @@ -194,7 +194,7 @@ void ArrangeJob::prepare_selected() { void ArrangeJob::prepare_all() { clear_input(); - PartPlateList& plate_list = m_plater->get_partplate_list(); + PartPlateList& plate_list = m_plater->get_partplate_list(); for (size_t i = 0; i < plate_list.get_plate_count(); i++) { PartPlate* plate = plate_list.get_plate(i); bool same_as_global_print_seq = true; @@ -798,7 +798,7 @@ void ArrangeJob::finalize() // Move the unprintable items to the last virtual bed. // Note ap.apply() moves relatively according to bed_idx, so we need to subtract the orignal bed_idx for (ArrangePolygon& ap : m_unprintable) { - ap.bed_idx = beds + 1; + ap.bed_idx = -1; plate_list.postprocess_arrange_polygon(ap, true); ap.apply(); @@ -836,6 +836,7 @@ void ArrangeJob::finalize() NotificationManager::NotificationLevel::RegularNotificationLevel, _u8L("Arranging canceled.")); } Job::finalize(); + m_plater->m_arrange_running.store(false); } diff --git a/src/slic3r/GUI/Jobs/CreateFontNameImageJob.cpp b/src/slic3r/GUI/Jobs/CreateFontNameImageJob.cpp new file mode 100644 index 0000000..3e6d093 --- /dev/null +++ b/src/slic3r/GUI/Jobs/CreateFontNameImageJob.cpp @@ -0,0 +1,222 @@ +#include "CreateFontNameImageJob.hpp" + +// rasterization of ExPoly +#include "libslic3r/SLA/AGGRaster.hpp" + +#include "slic3r/Utils/WxFontUtils.hpp" +#include "slic3r/GUI/3DScene.hpp" // ::glsafe + +// ability to request new frame after finish rendering +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" + +#include "wx/fontenum.h" + +#include + +using namespace Slic3r; +using namespace Slic3r::GUI; + +const std::string CreateFontImageJob::default_text = "AaBbCc123"; + +CreateFontImageJob::CreateFontImageJob(FontImageData &&input) + : m_input(std::move(input)) +{ + assert(wxFontEnumerator::IsValidFacename(m_input.font_name)); + assert(m_input.gray_level > 0 && m_input.gray_level < 255); + assert(m_input.texture_id != 0); +} + +void CreateFontImageJob::process(Ctl &ctl) +{ + auto font_file_with_cache = Slic3r::GUI::BackupFonts::gener_font_with_cache(m_input.font_name, m_input.encoding); + if (!font_file_with_cache.has_value()) { + return; + } + // use only first line of text + std::string& text = m_input.text; + if (text.empty()) + text = default_text; // copy + + size_t enter_pos = text.find('\n'); + if (enter_pos < text.size()) { + // text start with enter + if (enter_pos == 0) + return; + // exist enter, soo delete all after enter + text = text.substr(0, enter_pos); + } + + std::function was_canceled = [&ctl, cancel = m_input.cancel]() -> bool { + if (ctl.was_canceled()) return true; + if (cancel->load()) return true; + return false; + }; + auto ft_fn = []() { + return Slic3r::GUI::BackupFonts::backup_fonts; + }; + FontProp fp; // create default font parameters + auto &ff = *font_file_with_cache.font_file; + double standard_scale = get_text_shape_scale(fp, ff); + bool support_backup_fonts = GUI::wxGetApp().app_config->get_bool("support_backup_fonts"); + EmbossShape emboss_shape; + ExPolygons shapes = support_backup_fonts ? Emboss::text2shapes(emboss_shape, font_file_with_cache, text.c_str(), fp, was_canceled, ft_fn, standard_scale): + Emboss::text2shapes(emboss_shape, font_file_with_cache, text.c_str(), fp, was_canceled); + m_input.generate_origin_text = true; + if (shapes.empty()) {// select some character from font e.g. default text + m_input.generate_origin_text = false; + shapes = Emboss::text2shapes(emboss_shape, font_file_with_cache, default_text.c_str(), fp, was_canceled, ft_fn, standard_scale); + } + + if (shapes.empty()) { + m_input.cancel->store(true); + return; + } + + // normalize height of font + BoundingBox bounding_box; + for (const ExPolygon &shape : shapes) + bounding_box.merge(BoundingBox(shape.contour.points)); + if (bounding_box.size().x() < 1 || bounding_box.size().y() < 1) { + m_input.cancel->store(true); + return; + } + double scale = m_input.size.y() / (double) bounding_box.size().y(); + BoundingBoxf bb2(bounding_box.min.cast(), + bounding_box.max.cast()); + bb2.scale(scale); + Vec2d size_f = bb2.size(); + m_tex_size = Point(std::ceil(size_f.x()), std::ceil(size_f.y())); + // crop image width + if (m_tex_size.x() > m_input.size.x()) + m_tex_size.x() = m_input.size.x(); + if (m_tex_size.y() > m_input.size.y()) + m_tex_size.y() = m_input.size.y(); + + // Set up result + unsigned bit_count = 4; // RGBA + m_result = std::vector(m_tex_size.x() * m_tex_size.y() * bit_count, {255}); + + sla::Resolution resolution(m_tex_size.x(), m_tex_size.y()); + double pixel_dim = SCALING_FACTOR / scale; + sla::PixelDim dim(pixel_dim, pixel_dim); + double gamma = 1.; + std::unique_ptr raster = sla::create_raster_grayscale_aa(resolution, dim, gamma); + for (ExPolygon &shape : shapes) + shape.translate(-bounding_box.min); + for (const ExPolygon &shape : shapes) + raster->draw(shape); + + // copy rastered data to pixels + sla::RasterEncoder encoder = + [&pix = m_result, w = m_tex_size.x(), h = m_tex_size.y(), + gray_level = m_input.gray_level] + (const void *ptr, size_t width, size_t height, size_t num_components) { + size_t size {static_cast(w*h)}; + const unsigned char *ptr2 = (const unsigned char *) ptr; + for (size_t x = 0; x < width; ++x) + for (size_t y = 0; y < height; ++y) { + size_t index = y*w + x; + assert(index < size); + if (index >= size) continue; + pix[3+4*index] = ptr2[y * width + x] / gray_level; + } + return sla::EncodedRaster(); + }; + raster->encode(encoder); +} + +void CreateFontImageJob::finalize(bool canceled, std::exception_ptr &) +{ + if (m_input.count_opened_font_files) + --(*m_input.count_opened_font_files); + if (canceled || m_input.cancel->load()) return; + + *m_input.is_created = true; + + // Exist result bitmap with preview? + // (not valid input. e.g. not loadable font) + if (m_result.empty()) { + // TODO: write text cannot load into texture + m_result = std::vector(m_tex_size.x() * m_tex_size.y() * 4, {255}); + } + + // upload texture on GPU + const GLenum target = GL_TEXTURE_2D; + glsafe(::glBindTexture(target, m_input.texture_id)); + + GLsizei w = m_tex_size.x(), h = m_tex_size.y(); + GLint xoffset = 0; // align to left + auto texture_w = m_input.size.x(); + GLint yoffset = m_input.size.y() * m_input.index; + // clear rest of texture + std::vector empty_data(texture_w * m_tex_size.y() * 4, {0}); + glsafe(::glTexSubImage2D(target, m_input.level, 0, yoffset, texture_w, h, m_input.format, m_input.type, empty_data.data())); + if (m_input.generate_origin_text) { // valid texture + glsafe(::glTexSubImage2D(target, m_input.level, xoffset, yoffset, w, h, m_input.format, m_input.type, m_result.data())); + } + // bind default texture + GLuint no_texture_id = 0; + glsafe(::glBindTexture(target, no_texture_id)); + + // show rendered texture + wxGetApp().plater()->canvas3D()->schedule_extra_frame(0); + + BOOST_LOG_TRIVIAL(info) + << "Generate Preview font('" << m_input.font_name << "' id:" << m_input.index << ") " + << "with text: '" << m_input.text << "' " + << "texture_size " << m_input.size.x() << " x " << m_input.size.y(); +} + +std::vector Slic3r::GUI::BackupFonts::backup_fonts; +void Slic3r::GUI::BackupFonts::generate_backup_fonts() { + auto language = wxGetApp().app_config->get("language"); + auto custom_back_font_name = wxGetApp().app_config->get("custom_back_font_name"); + if (backup_fonts.empty()) { + size_t idx = language.find('_'); + std::string lang = (idx == std::string::npos) ? language : language.substr(0, idx); + std::vector font_names; +#ifdef _WIN32 + font_names.emplace_back(wxString(L"宋体"));//chinese confirm + font_names.emplace_back(wxString::FromUTF8("MS Gothic")); // Japanese + font_names.emplace_back(wxString::FromUTF8("NanumGothic")); // Korean + font_names.emplace_back(wxString::FromUTF8("Arial")); // Arabic +#endif +#ifdef __APPLE__ + font_names.emplace_back(wxString::FromUTF8("Songti SC"));//chinese confirm + font_names.emplace_back(wxString::FromUTF8("SimSong")); // Japanese//mac special + font_names.emplace_back(wxString::FromUTF8("Nanum Gothic")); // Korean//mac need space + font_names.emplace_back(wxString::FromUTF8("Arial")); // Arabic +#endif +#ifdef __linux__ + font_names.emplace_back(wxString(L"宋体")); // chinese confirm + font_names.emplace_back(wxString::FromUTF8("MS Gothic")); // Japanese + font_names.emplace_back(wxString::FromUTF8("NanumGothic")); // Korean + font_names.emplace_back(wxString::FromUTF8("Arial")); // Arabic +#endif + if (!custom_back_font_name.empty()) { + font_names.emplace_back(wxString::FromUTF8(custom_back_font_name)); + } + for (int i = 0; i < font_names.size(); i++) { + backup_fonts.emplace_back(gener_font_with_cache(font_names[i], wxFontEncoding::wxFONTENCODING_SYSTEM)); + } + } +} + +Slic3r::Emboss::FontFileWithCache Slic3r::GUI::BackupFonts::gener_font_with_cache(const wxString &font_name, const wxFontEncoding &encoding) +{ + Emboss::FontFileWithCache font_file_with_cache; + if (!wxFontEnumerator::IsValidFacename(font_name)) + return font_file_with_cache; + // Select font + wxFont wx_font(wxFontInfo().FaceName(font_name).Encoding(encoding)); + if (!wx_font.IsOk()) return font_file_with_cache; + std::unique_ptr font_file = WxFontUtils::create_font_file(wx_font); + if (font_file == nullptr) + return font_file_with_cache; + + font_file_with_cache = Emboss::FontFileWithCache(std::move(font_file)); + return font_file_with_cache; +} + diff --git a/src/slic3r/GUI/Jobs/CreateFontNameImageJob.hpp b/src/slic3r/GUI/Jobs/CreateFontNameImageJob.hpp new file mode 100644 index 0000000..190199e --- /dev/null +++ b/src/slic3r/GUI/Jobs/CreateFontNameImageJob.hpp @@ -0,0 +1,89 @@ +#ifndef slic3r_CreateFontNameImageJob_hpp_ +#define slic3r_CreateFontNameImageJob_hpp_ + +#include +#include +#include +#include +#include +#include "JobNew.hpp" +#include "libslic3r/Point.hpp" // Vec2i32 +#include "libslic3r/Emboss.hpp" +namespace Slic3r::GUI { + +/// +/// Keep data for rasterization of text by font face +/// +struct FontImageData +{ + // Text to rasterize + std::string text; + // Define font face + wxString font_name; + wxFontEncoding encoding; + // texture for copy result to + // texture MUST BE initialized + GLuint texture_id; + // Index of face name, define place in texture + size_t index; + // Height of each text + // And Limit for width + Vec2i32 size; // in px + + // bigger value create darker image + // divide value 255 + unsigned char gray_level = 5; + + // texture meta data + GLenum format = GL_ALPHA, type = GL_UNSIGNED_BYTE; + GLint level = 0; + + // prevent opening too much files + // it is decreased in finalize phase + unsigned int *count_opened_font_files = nullptr; + + std::shared_ptr> cancel = nullptr; + std::shared_ptr is_created = nullptr; + bool generate_origin_text = false; +}; +class BackupFonts +{ +public: + static void generate_backup_fonts(); + static Slic3r::Emboss::FontFileWithCache gener_font_with_cache(const wxString &font_name, const wxFontEncoding& encoding); + static std::vector backup_fonts; +}; + +/// +/// Create image for face name +/// +class CreateFontImageJob : public JobNew +{ + FontImageData m_input; + std::vector m_result; + Point m_tex_size; +public: + CreateFontImageJob(FontImageData &&input); + /// + /// Rasterize text into image (result) + /// + /// Check for cancelation + void process(Ctl &ctl) override; + + /// + /// Copy image data into OpenGL texture + /// + /// + /// + void finalize(bool canceled, std::exception_ptr &) override; + + /// + /// Text used for generate preview for empty text + /// and when no glyph for given m_input.text + /// + static const std::string default_text; +}; + +} // namespace Slic3r::GUI + +#endif // slic3r_CreateFontNameImageJob_hpp_ diff --git a/src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.cpp b/src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.cpp new file mode 100644 index 0000000..2276f4f --- /dev/null +++ b/src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.cpp @@ -0,0 +1,158 @@ +#include "CreateFontStyleImagesJob.hpp" +#include "CreateFontNameImageJob.hpp" +// rasterization of ExPoly +#include "libslic3r/SLA/AGGRaster.hpp" +#include "slic3r/GUI/3DScene.hpp" // ::glsafe + +// ability to request new frame after finish rendering +#include "slic3r/GUI/GUI_App.hpp" +#include "slic3r/GUI/Plater.hpp" +#include "slic3r/GUI/GLCanvas3D.hpp" + +using namespace Slic3r; +using namespace Slic3r::Emboss; +using namespace Slic3r::GUI; +using namespace Slic3r::GUI::Emboss; + + +CreateFontStyleImagesJob::CreateFontStyleImagesJob(StyleManager::StyleImagesData &&input) + : m_input(std::move(input)), m_width(0), m_height(0) +{ + assert(m_input.result != nullptr); + assert(!m_input.styles.empty()); + assert(!m_input.text.empty()); + assert(m_input.max_size.x() > 1); + assert(m_input.max_size.y() > 1); + assert(m_input.ppm > 1e-5); +} + +void CreateFontStyleImagesJob::process(Ctl &ctl) +{ + // create shapes and calc size (bounding boxes) + std::vector name_shapes(m_input.styles.size()); + std::vector scales(m_input.styles.size()); + m_images = std::vector(m_input.styles.size()); + + auto was_canceled = []() { return false; }; + bool support_backup_fonts = GUI::wxGetApp().app_config->get_bool("support_backup_fonts"); + auto ft_fn = []() { return Slic3r::GUI::BackupFonts::backup_fonts; }; + for (auto &item : m_input.styles) { + size_t index = &item - &m_input.styles.front(); + ExPolygons &shapes = name_shapes[index]; + EmbossShape emboss_shape; + auto & ff = *item.font.font_file; + double standard_scale = get_text_shape_scale(item.prop, ff); + shapes = support_backup_fonts ? text2shapes(emboss_shape, item.font, m_input.text.c_str(), item.prop, was_canceled, ft_fn, standard_scale) + :text2shapes(emboss_shape, item.font, m_input.text.c_str(), item.prop, was_canceled); + // create image description + StyleManager::StyleImage &image = m_images[index]; + BoundingBox &bounding_box = image.bounding_box; + for (ExPolygon &shape : shapes) + bounding_box.merge(BoundingBox(shape.contour.points)); + for (ExPolygon &shape : shapes) shape.translate(-bounding_box.min); + + // calculate conversion from FontPoint to screen pixels by size of font + double scale = get_text_shape_scale(item.prop, *item.font.font_file) * m_input.ppm *0.5; + scales[index] = scale; + + //double scale = font_prop.size_in_mm * SCALING_FACTOR; + BoundingBoxf bb2(bounding_box.min.cast(), + bounding_box.max.cast()); + bb2.scale(scale); + image.tex_size.x = std::ceil(bb2.max.x() - bb2.min.x()); + image.tex_size.y = std::ceil(bb2.max.y() - bb2.min.y()); + + // crop image width + if (image.tex_size.x > m_input.max_size.x()) + image.tex_size.x = m_input.max_size.x(); + // crop image height + if (image.tex_size.y > m_input.max_size.y()) + image.tex_size.y = m_input.max_size.y(); + } + + // arrange bounding boxes + int offset_y = 0; + m_width = 0; + for (StyleManager::StyleImage &image : m_images) { + image.offset.y() = offset_y; + offset_y += image.tex_size.y+1; + if (m_width < image.tex_size.x) + m_width = image.tex_size.x; + } + m_height = offset_y; + for (StyleManager::StyleImage &image : m_images) { + const Point &o = image.offset; + const ImVec2 &s = image.tex_size; + image.uv0 = ImVec2(o.x() / (double) m_width, + o.y() / (double) m_height); + image.uv1 = ImVec2((o.x() + s.x) / (double) m_width, + (o.y() + s.y) / (double) m_height); + } + + // Set up result + m_pixels = std::vector(4 * m_width * m_height, {255}); + + // upload sub textures + for (StyleManager::StyleImage &image : m_images) { + sla::Resolution resolution(image.tex_size.x, image.tex_size.y); + size_t index = &image - &m_images.front(); + double pixel_dim = SCALING_FACTOR / scales[index]; + sla::PixelDim dim(pixel_dim, pixel_dim); + double gamma = 1.; + std::unique_ptr r = + sla::create_raster_grayscale_aa(resolution, dim, gamma); + for (const ExPolygon &shape : name_shapes[index]) r->draw(shape); + + // copy rastered data to pixels + sla::RasterEncoder encoder = [&offset = image.offset, &pix = m_pixels, w=m_width,h=m_height] + (const void *ptr, size_t width, size_t height, size_t num_components) { + // bigger value create darker image + unsigned char gray_level = 1; + size_t size {static_cast(w*h)}; + assert((offset.x() + width) <= (size_t)w); + assert((offset.y() + height) <= (size_t)h); + const unsigned char *ptr2 = (const unsigned char *) ptr; + for (size_t x = 0; x < width; ++x) + for (size_t y = 0; y < height; ++y) { + size_t index = (offset.y() + y)*w + offset.x() + x; + assert(index < size); + if (index >= size) continue; + pix[4*index+3] = ptr2[y * width + x] / gray_level; + } + return sla::EncodedRaster(); + }; + r->encode(encoder); + } +} + +void CreateFontStyleImagesJob::finalize(bool canceled, std::exception_ptr &) +{ + if (canceled) return; + // upload texture on GPU + GLuint tex_id; + GLenum target = GL_TEXTURE_2D, format = GL_RGBA, type = GL_UNSIGNED_BYTE; + GLint level = 0, border = 0; + glsafe(::glGenTextures(1, &tex_id)); + glsafe(::glBindTexture(target, tex_id)); + glsafe(::glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); + glsafe(::glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); + GLint w = m_width, h = m_height; + glsafe(::glTexImage2D(target, level, GL_RGBA, w, h, border, format, type, + (const void *) m_pixels.data())); + + // set up texture id + void *texture_id = (void *) (intptr_t) tex_id; + for (StyleManager::StyleImage &image : m_images) + image.texture_id = texture_id; + + // move to result + m_input.result->styles = std::move(m_input.styles); + m_input.result->images = std::move(m_images); + + // bind default texture + GLuint no_texture_id = 0; + glsafe(::glBindTexture(target, no_texture_id)); + + // show rendered texture + wxGetApp().plater()->canvas3D()->schedule_extra_frame(0); +} \ No newline at end of file diff --git a/src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.hpp b/src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.hpp new file mode 100644 index 0000000..3941a65 --- /dev/null +++ b/src/slic3r/GUI/Jobs/CreateFontStyleImagesJob.hpp @@ -0,0 +1,36 @@ +#ifndef slic3r_CreateFontStyleImagesJob_hpp_ +#define slic3r_CreateFontStyleImagesJob_hpp_ + +#include +#include +#include +#include "slic3r/Utils/EmbossStyleManager.hpp" +#include "JobNew.hpp" + +namespace Slic3r::GUI::Emboss { + +/// +/// Create texture with name of styles written by its style +/// NOTE: Access to glyph cache is possible only from job +/// +class CreateFontStyleImagesJob : public JobNew +{ + StyleManager::StyleImagesData m_input; + + // Output data + // texture size + int m_width, m_height; + // texture data + std::vector m_pixels; + // descriptors of sub textures + std::vector m_images; + +public: + CreateFontStyleImagesJob(StyleManager::StyleImagesData &&input); + void process(Ctl &ctl) override; + void finalize(bool canceled, std::exception_ptr &) override; +}; + +} // namespace Slic3r::GUI + +#endif // slic3r_CreateFontStyleImagesJob_hpp_ diff --git a/src/slic3r/GUI/Jobs/EmbossJob.cpp b/src/slic3r/GUI/Jobs/EmbossJob.cpp index 14e0cda..f72f2aa 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.cpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.cpp @@ -3,6 +3,7 @@ #include #include // +#include #include #include // load_obj for default mesh #include // use surface cuts @@ -25,7 +26,7 @@ //#include "slic3r/GUI/3DScene.hpp" #include "slic3r/GUI/Jobs/Worker.hpp" #include "slic3r/Utils/UndoRedo.hpp" - +#include // #define EXECUTE_UPDATE_ON_MAIN_THREAD // debug execution on main thread using namespace Slic3r::Emboss; namespace Slic3r { @@ -45,8 +46,10 @@ public: bool was_canceled(const JobNew::Ctl &ctl, const DataBase &base) { - if (base.cancel->load()) return true; - return ctl.was_canceled(); + if (base.cancel->load()) + return true; + auto flag = ctl.was_canceled(); + return flag; } bool _finalize(bool canceled, std::exception_ptr &eptr, const DataBase &input) @@ -178,6 +181,45 @@ bool is_valid(ModelVolumeType volume_type) return false; } + +void recreate_model_volume(ModelObject *model_object, int volume_idx, const TriangleMesh &mesh, Geometry::Transformation &text_tran, TextInfo &text_info) +{ + wxGetApp() .plater()->take_snapshot("Modify Text"); + + ModelVolume *model_volume = model_object->volumes[volume_idx]; + ModelVolume *new_model_volume = model_object->add_volume(mesh, false); + new_model_volume->calculate_convex_hull(); + new_model_volume->set_transformation(text_tran.get_matrix()); + new_model_volume->set_text_info(text_info); + new_model_volume->name = model_volume->name; + new_model_volume->set_type(model_volume->type()); + new_model_volume->config.apply(model_volume->config); + std::swap(model_object->volumes[volume_idx], model_object->volumes.back()); + model_object->delete_volume(model_object->volumes.size() - 1); + model_object->invalidate_bounding_box(); + wxGetApp().plater()->update(); +} + +void create_text_volume(Slic3r::ModelObject *model_object, const TriangleMesh &mesh, Geometry::Transformation &text_tran, TextInfo &text_info) +{ + wxGetApp().plater()->take_snapshot("create_text_volume"); + + ModelVolume *new_model_volume = model_object->add_volume(mesh, false); + new_model_volume->calculate_convex_hull(); + new_model_volume->set_transformation(text_tran.get_matrix()); + new_model_volume->set_text_info(text_info); + if (model_object->config.option("extruder")) { + new_model_volume->config.set_key_value("extruder", new ConfigOptionInt(model_object->config.extruder())); + } else { + new_model_volume->config.set_key_value("extruder", new ConfigOptionInt(1)); + model_object->config.set_key_value("extruder", new ConfigOptionInt(1)); + } + new_model_volume->name = "text_shape"; + + model_object->invalidate_bounding_box(); + wxGetApp().plater()->update(); +} + bool check(unsigned char gizmo_type) { return gizmo_type == (unsigned char) GLGizmosManager::Svg; } bool check(const CreateVolumeParams &input) @@ -339,6 +381,7 @@ void UpdateJob::update_volume(ModelVolume *volume, TriangleMesh &&mesh, const Da // update volume volume->set_mesh(std::move(mesh)); + volume->calculate_convex_hull(); volume->set_new_unique_id(); volume->calculate_convex_hull(); @@ -467,12 +510,16 @@ void CreateObjectJob::finalize(bool canceled, std::exception_ptr &eptr) // When cursor move and no one object is selected than // Manager::reset_all() So Gizmo could be closed before end of creation object GLCanvas3D * canvas = plater->get_view3D_canvas3D(); - GLGizmosManager &manager = canvas->get_gizmos_manager(); - if (manager.get_current_type() != m_input.gizmo_type) // GLGizmosManager::EType::svg - manager.open_gizmo(m_input.gizmo_type); + if (canvas) { + GLGizmosManager& manager = canvas->get_gizmos_manager(); + if (manager.get_current_type() != m_input.gizmo_type) { // GLGizmosManager::EType::svg + const auto svg_item_name = GLGizmosManager::convert_gizmo_type_to_string(static_cast(m_input.gizmo_type)); + canvas->force_main_toolbar_left_action(canvas->get_main_toolbar_item_id(svg_item_name)); + } - // redraw scene - canvas->reload_scene(true); + // redraw scene + canvas->reload_scene(true); + } } CreateSurfaceVolumeJob::CreateSurfaceVolumeJob(CreateSurfaceVolumeData &&input) : m_input(std::move(input)) {} @@ -495,7 +542,7 @@ CreateVolumeJob::CreateVolumeJob(DataCreateVolume &&input) : m_input(std::move(i void CreateVolumeJob::process(Ctl &ctl) { if (!check(m_input)) - throw std::runtime_error("Bad input data for EmbossCreateVolumeJob."); + throw JobException("Bad input data for EmbossCreateVolumeJob."); m_result = create_mesh(*m_input.base); } @@ -716,7 +763,7 @@ void create_volume( volume->calculate_convex_hull(); // set a default extruder value, since user can't add it manually - volume->config.set_key_value("extruder", new ConfigOptionInt(0)); + volume->config.set_key_value("extruder", new ConfigOptionInt(1)); // do not allow model reload from disk volume->source.is_from_builtin_objects = true; @@ -799,10 +846,15 @@ OrthoProject3d create_emboss_projection(bool is_outside, float emboss, Transform } indexed_triangle_set cut_surface_to_its(const ExPolygons &shapes, const Transform3d &tr, const SurfaceVolumeData::ModelSources &sources, DataBase &input) +{ + return cut_surface_to_its(shapes, input.shape.scale, tr, sources, input); +} + +indexed_triangle_set cut_surface_to_its(const ExPolygons &shapes, float scale, const Transform3d &tr, const SurfaceVolumeData::ModelSources &sources, DataBase &input) { assert(!sources.empty()); BoundingBox bb = get_extents(shapes); - double shape_scale = input.shape.scale; + double shape_scale = scale; const SurfaceVolumeData::ModelSource *biggest = &sources.front(); @@ -828,7 +880,8 @@ indexed_triangle_set cut_surface_to_its(const ExPolygons &shapes, const Transfor s_to_itss[source_index] = its_index; itss.emplace_back(std::move(its)); } - if (itss.empty()) return {}; + if (itss.empty()) + return {}; Transform3d tr_inv = biggest->tr.inverse(); Transform3d cut_projection_tr = tr_inv * tr; @@ -837,8 +890,10 @@ indexed_triangle_set cut_surface_to_its(const ExPolygons &shapes, const Transfor BoundingBoxf3 mesh_bb = bounding_box(itss[itss_index]); for (const SurfaceVolumeData::ModelSource &s : sources) { itss_index = s_to_itss[&s - &sources.front()]; - if (itss_index == std::numeric_limits::max()) continue; - if (&s == biggest) continue; + if (itss_index == std::numeric_limits::max()) + continue; + if (&s == biggest) + continue; Transform3d tr = s.tr * tr_inv; bool fix_reflected = true; @@ -864,7 +919,8 @@ indexed_triangle_set cut_surface_to_its(const ExPolygons &shapes, const Transfor shapes_data = shapes; // copy for (ExPolygon &shape : shapes_data) { shape.contour.reverse(); - for (Slic3r::Polygon &hole : shape.holes) hole.reverse(); + for (Slic3r::Polygon &hole : shape.holes) + hole.reverse(); } shapes_ptr = &shapes_data; } @@ -1037,7 +1093,11 @@ const GLVolume *find_closest(const Selection &selection, const Vec2d &screen_cen double center_sq_distance = std::numeric_limits::max(); for (unsigned int id : indices) { const GLVolume *gl_volume = selection.get_volume(id); - if (const ModelVolume *volume = get_model_volume(*gl_volume, objects); volume == nullptr || !volume->is_model_part()) continue; + const ModelVolume *volume = get_model_volume(*gl_volume, objects); + if (volume == nullptr || !volume->is_model_part()) continue; + if (volume->is_text()) { + continue; + } Slic3r::Polygon hull = CameraUtils::create_hull2d(camera, *gl_volume); Vec2d c = hull.centroid().cast(); Vec2d d = c - screen_center; @@ -1198,6 +1258,767 @@ SurfaceVolumeData::ModelSources create_sources(const ModelVolumePtrs &volumes, s return result; } +const GLVolume *find_glvoloume_render_screen_cs(const Selection &selection, const Vec2d &screen_center, const Camera &camera, const ModelObjectPtrs &objects, Vec2d *closest_center) +{ + return find_closest(selection, screen_center, camera, objects, closest_center); } -} // namespace Slic3r::GUI -} // namespace Slic3r \ No newline at end of file + +ProjectTransform calc_project_tran(DataBase &input, double real_scale) +{ + double scale = real_scale; // 1e-6 + double depth = (input.shape.projection.depth + input.shape.projection.embeded_depth) / scale; + auto projectZ = std::make_unique(depth); + float offset = input.is_outside ? -SAFE_SURFACE_OFFSET : (SAFE_SURFACE_OFFSET - input.shape.projection.depth); + if (input.from_surface.has_value()) offset += *input.from_surface; + Transform3d tr = Eigen::Translation(0., 0., static_cast(offset)) * Eigen::Scaling(scale); + ProjectTransform project_tr(std::move(projectZ), tr); + return project_tr; +} + +void create_all_char_mesh(DataBase &input, std::vector &result, EmbossShape &shape) +{ + shape = input.create_shape();//this will call letter2shapes + if (shape.shapes_with_ids.empty()) + return; + result.clear(); + auto first_project_tr = calc_project_tran(input, input.shape.scale); + + TextConfiguration text_configuration = input.get_text_configuration(); + bool support_backup_fonts = std::any_of(shape.text_scales.begin(), shape.text_scales.end(), [](float x) { return x > 0; }); + const char *text = input.get_text_configuration().text.c_str(); + wxString input_text = wxString::FromUTF8(input.get_text_configuration().text); + wxRegEx re("^ +$"); + bool is_all_space = re.Matches(input_text); + if (is_all_space) { return; } + if (input_text.size() != shape.shapes_with_ids.size()) { + BOOST_LOG_TRIVIAL(info) <<__FUNCTION__<< "error: input_text.size() != shape.shapes_with_ids.size()"; + } + for (int i = 0; i < shape.shapes_with_ids.size(); i++) { + auto &temp_shape = shape.shapes_with_ids[i]; + if (input_text[i] == ' ') { + result.emplace_back(TriangleMesh()); + continue; + } + if (temp_shape.expoly.empty()) + continue; + if (support_backup_fonts) { + if (i < shape.text_scales.size() && shape.text_scales[i] > 0) { + auto temp_scale = shape.text_scales[i]; + + auto temp_project_tr = calc_project_tran(input, temp_scale); + TriangleMesh mesh(polygons2model(temp_shape.expoly, temp_project_tr)); + result.emplace_back(mesh); + } else { + TriangleMesh mesh(polygons2model(temp_shape.expoly, first_project_tr)); + result.emplace_back(mesh); + } + } else { + TriangleMesh mesh(polygons2model(temp_shape.expoly, first_project_tr)); + result.emplace_back(mesh); + } + } +} + +float get_single_char_width(const std::vector &chars_mesh_result) +{ + for (int i = 0; i < chars_mesh_result.size(); ++i) { + auto box = chars_mesh_result[i].bounding_box(); + auto box_size = box.size(); + auto half_x_length = box_size[0] / 2.0f; + if (half_x_length > 0.01) { + return half_x_length; + } + } + return 0.f; +} + +bool calc_text_lengths(std::vector &text_lengths, const std::vector &chars_mesh_result) +{ + text_lengths.clear(); + auto single_char_width = get_single_char_width(chars_mesh_result); + if (single_char_width < 0.01) { return false; } + for (int i = 0; i < chars_mesh_result.size(); ++i) { + auto box = chars_mesh_result[i].bounding_box(); + auto box_size = box.size(); + auto half_x_length = box_size[0] / 2.0f; + if (half_x_length < 0.01) { + text_lengths.emplace_back(single_char_width + 1); + } else { + text_lengths.emplace_back(half_x_length + 1); + } + } + return true; +} + +void calc_position_points(std::vector &position_points, std::vector &text_lengths, float text_gap, const Vec3d &temp_pos_dir) +{ + auto text_num = text_lengths.size(); + if (text_num == 0) { + throw JobException("calc_position_points fail."); + return; + } + if (position_points.size() != text_lengths.size()) { position_points.resize(text_num); } + auto pos_dir = temp_pos_dir.normalized(); + + if (text_num % 2 == 1) { + position_points[text_num / 2] = Vec3d::Zero(); + for (int i = 0; i < text_num / 2; ++i) { + double left_gap = text_lengths[text_num / 2 - i - 1] + text_gap + text_lengths[text_num / 2 - i]; + if (left_gap < 0) + left_gap = 0; + + double right_gap = text_lengths[text_num / 2 + i + 1] + text_gap + text_lengths[text_num / 2 + i]; + if (right_gap < 0) + right_gap = 0; + + position_points[text_num / 2 - 1 - i] = position_points[text_num / 2 - i] - left_gap * pos_dir; + position_points[text_num / 2 + 1 + i] = position_points[text_num / 2 + i] + right_gap * pos_dir; + } + } else { + for (int i = 0; i < text_num / 2; ++i) { + double left_gap = i == 0 ? (text_lengths[text_num / 2 - i - 1] + text_gap / 2) : (text_lengths[text_num / 2 - i - 1] + text_gap + text_lengths[text_num / 2 - i]); + if (left_gap < 0) + left_gap = 0; + + double right_gap = i == 0 ? (text_lengths[text_num / 2 + i] + text_gap / 2) : (text_lengths[text_num / 2 + i] + text_gap + text_lengths[text_num / 2 + i - 1]); + if (right_gap < 0) + right_gap = 0; + + if (i == 0) { + position_points[text_num / 2 - 1 - i] = Vec3d::Zero() - left_gap * pos_dir; + position_points[text_num / 2 + i] = Vec3d::Zero() + right_gap * pos_dir; + continue; + } + + position_points[text_num / 2 - 1 - i] = position_points[text_num / 2 - i] - left_gap * pos_dir; + position_points[text_num / 2 + i] = position_points[text_num / 2 + i - 1] + right_gap * pos_dir; + } + } +} + +GenerateTextJob::GenerateTextJob(InputInfo &&input) : m_input(std::move(input)) {} +std::vector GenerateTextJob::debug_cut_points_in_world; +void GenerateTextJob::process(Ctl &ctl) +{ + auto canceled = was_canceled(ctl, *m_input.m_data_update.base); + if (canceled) + return; + create_all_char_mesh(*m_input.m_data_update.base, m_input.m_chars_mesh_result, m_input.m_text_shape); + if (m_input.m_chars_mesh_result.empty()) { + return; + } + if (!update_text_positions(m_input)) { + throw JobException("update_text_positions fail."); + } + if (!generate_text_points(m_input)) + throw JobException("generate_text_volume fail."); + GenerateTextJob::debug_cut_points_in_world = m_input.m_cut_points_in_world; + if (m_input.use_surface) { + if (m_input.m_text_shape.shapes_with_ids.empty()) + throw JobException(_u8L("Font doesn't have any shape for given text.").c_str()); + } + generate_mesh_according_points(m_input); +} + +void GenerateTextJob::finalize(bool canceled, std::exception_ptr &eptr) +{ + if (canceled || eptr) + return; + if (m_input.first_generate) { + create_text_volume(m_input.mo, m_input.m_final_text_mesh, m_input.m_final_text_tran_in_object, m_input.text_info); + auto model_object = m_input.mo; + m_input.m_volume_idx; + auto volume_idx = model_object->volumes.size() - 1; + ModelVolume * model_volume = model_object->volumes[volume_idx]; + auto add_to_selection = [model_volume](const ModelVolume *vol) { return vol == model_volume; }; + ObjectList * obj_list = wxGetApp().obj_list(); + int object_idx = wxGetApp().plater()->get_selected_object_idx(); + wxDataViewItemArray sel = obj_list->reorder_volumes_and_get_selection(object_idx, add_to_selection); + if (!sel.IsEmpty()) { + obj_list->select_item(sel.front()); + } + + obj_list->selection_changed(); + + GLCanvas3D * canvas = wxGetApp().plater()->get_view3D_canvas3D(); + GLGizmosManager &manager = canvas->get_gizmos_manager(); + if (manager.get_current_type() != GLGizmosManager::EType::Text) { + manager.open_gizmo(GLGizmosManager::EType::Text); + } + } else { + recreate_model_volume(m_input.mo, m_input.m_volume_idx, m_input.m_final_text_mesh, m_input.m_final_text_tran_in_object, m_input.text_info); + } +} + +bool GenerateTextJob::update_text_positions(InputInfo &input_info) +{ + if (input_info.m_chars_mesh_result.size() == 0) { + input_info.m_position_points.clear(); + return false; + } + std::vector text_lengths; + if (!calc_text_lengths(text_lengths, input_info.m_chars_mesh_result)) { + return false; + } + int text_num = input_info.m_chars_mesh_result.size(); // FIX by BBS 20250109 + input_info.m_position_points.clear(); + input_info.m_normal_points.clear(); + /*auto mouse_position_world = m_text_position_in_world.cast(); + auto mouse_normal_world = m_text_normal_in_world.cast();*/ + input_info.m_position_points.resize(text_num); + input_info.m_normal_points.resize(text_num); + + input_info.text_lengths = text_lengths; + input_info.m_surface_type = GenerateTextJob::SurfaceType::None; + if (input_info.text_surface_type == TextInfo::TextType::HORIZONAL) { + input_info.use_surface = false; + Vec3d mouse_normal_world = input_info.m_text_normal_in_world.cast(); + Vec3d world_pos_dir = input_info.m_cut_plane_dir_in_world.cross(mouse_normal_world); + auto inv_ = (input_info.m_model_object_in_world_tran.get_matrix_no_offset() * input_info.m_text_tran_in_object.get_matrix_no_offset()).inverse(); + Vec3d pos_dir = Vec3d::UnitX(); + auto mouse_normal_local = inv_ * mouse_normal_world; + mouse_normal_local.normalize(); + + calc_position_points(input_info.m_position_points, text_lengths, input_info.m_text_gap, pos_dir); + + for (int i = 0; i < text_num; ++i) { + input_info.m_normal_points[i] = mouse_normal_local; + } + return true; + } + input_info.m_surface_type = GenerateTextJob::SurfaceType::Surface; + return true; +} + +bool GenerateTextJob::generate_text_points(InputInfo &input_info) +{ + if (input_info.m_surface_type == GenerateTextJob::SurfaceType::None) { + return true; + } + auto &m_text_tran_in_object = input_info.m_text_tran_in_object; + auto mo = input_info.mo; + auto &m_volume_idx = input_info.m_volume_idx; + auto &m_position_points = input_info.m_position_points; + auto &m_normal_points = input_info.m_normal_points; + auto &m_cut_points_in_world = input_info.m_cut_points_in_world; + std::vector m_cut_points_in_local; + auto &m_text_cs_to_world_tran = input_info.m_text_tran_in_world.get_matrix(); + auto &m_chars_mesh_result = input_info.m_chars_mesh_result; + auto &m_text_position_in_world = input_info.m_text_position_in_world; + auto &m_text_normal_in_world = input_info.m_text_normal_in_world; + auto &m_text_gap = input_info.m_text_gap; + auto &m_model_object_in_world_tran = input_info.m_model_object_in_world_tran; + auto &text_lengths = input_info.text_lengths; + //auto hit_mesh_id = input_info.hit_mesh_id;//m_rr.mesh_id + //calc + TriangleMesh slice_meshs; + int mesh_index = 0; + for (int i = 0; i < mo->volumes.size(); ++i) { + ModelVolume *mv = mo->volumes[i]; + if (m_volume_idx == i) { + continue; + } + if (mv->is_text()) { + continue; + } + if (mv->is_model_part()) { + TriangleMesh vol_mesh(mv->mesh()); + vol_mesh.transform(mv->get_matrix()); + slice_meshs.merge(vol_mesh); + mesh_index++; + } + } + auto text_tran_in_object = m_text_tran_in_object; // important + input_info.slice_mesh = slice_meshs; + auto rotate_tran = Geometry::assemble_transform(Vec3d::Zero(), {-0.5 * M_PI, 0.0, 0.0}); + MeshSlicingParams slicing_params; + auto cut_tran = (text_tran_in_object.get_matrix() * rotate_tran); + slicing_params.trafo = cut_tran.inverse(); + // for debug + // its_write_obj(slice_meshs.its, "D:/debug_files/mesh.obj"); + // generate polygons + const Polygons temp_polys = slice_mesh(slice_meshs.its, 0, slicing_params); + Vec3d scale_click_pt(scale_(0), scale_(0), 0); + // for debug + // export_regions_to_svg(Point(scale_pt.x(), scale_pt.y()), temp_polys); + Polygons polys = union_(temp_polys); + + auto point_in_line_rectange = [](const Line &line, const Point &point, double &distance) { + distance = line.distance_to(point); + return distance < line.length() / 2; + }; + + int index = 0; + double min_distance = 1e12; + Polygon hit_ploy; + for (const Polygon poly : polys) { + if (poly.points.size() == 0) + continue; + Lines lines = poly.lines(); + for (int i = 0; i < lines.size(); ++i) { + Line line = lines[i]; + double distance = min_distance; + if (point_in_line_rectange(line, Point(scale_click_pt.x(), scale_click_pt.y()), distance)) { + if (distance < min_distance) { + min_distance = distance; + index = i; + hit_ploy = poly; + } + } + } + } + + if (hit_ploy.points.size() == 0) { + BOOST_LOG_TRIVIAL(info) << boost::format("Text: the hit polygon is null,") << "x:" << m_text_position_in_world.x() << ",y:" << m_text_position_in_world.y() + << ",z:" << m_text_position_in_world.z(); + throw JobException("The hit polygon is null,please try to regenerate after adjusting text position."); + return false; + } + int text_num = m_chars_mesh_result.size(); + + auto world_tran = m_model_object_in_world_tran * text_tran_in_object; + m_cut_points_in_world.clear(); + m_cut_points_in_world.reserve(hit_ploy.points.size()); + m_cut_points_in_local.clear(); + m_cut_points_in_local.reserve(hit_ploy.points.size()); + for (int i = 0; i < hit_ploy.points.size(); ++i) { + m_cut_points_in_local.emplace_back(rotate_tran * Vec3d(unscale_(hit_ploy.points[i].x()), unscale_(hit_ploy.points[i].y()), 0)); // m_text_cs_to_world_tran * + m_cut_points_in_world.emplace_back(world_tran.get_matrix() * m_cut_points_in_local.back()); + } + + Slic3r::Polygon_3D new_polygon(m_cut_points_in_local); + m_position_points.resize(text_num); + if (text_num % 2 == 1) { + m_position_points[text_num / 2] = Vec3d::Zero(); + std::vector lines = new_polygon.get_lines(); + Line_3D line = lines[index]; + auto min_dist = 1e6; + {// Find the nearest tangent point + for (int i = 0; i < lines.size(); i++) { + Line_3D temp_line = lines[i]; + Vec3d intersection_pt; + float proj_length; + auto pt = Vec3d::Zero(); + Linef3::get_point_projection_to_line(pt, temp_line.a, temp_line.vector(), intersection_pt, proj_length); + auto dist = (intersection_pt - pt).norm(); + if (min_dist > dist) { + min_dist = dist; + m_position_points[text_num / 2] = intersection_pt; + } + } + } + { + int index1 = index; + double left_length = (Vec3d::Zero() - line.a).cast().norm(); + int left_num = text_num / 2; + while (left_num > 0) { + double gap_length = (text_lengths[left_num] + m_text_gap + text_lengths[left_num - 1]); + if (gap_length < 0) gap_length = 0; + + while (gap_length > left_length) { + gap_length -= left_length; + if (index1 == 0) + index1 = lines.size() - 1; + else + --index1; + left_length = lines[index1].length(); + } + + Vec3d direction = lines[index1].vector(); + direction.normalize(); + double distance_to_a = (left_length - gap_length); + Line_3D new_line = lines[index1]; + + double norm_value = direction.cast().norm(); + double deta_x = distance_to_a * direction.x() / norm_value; + double deta_y = distance_to_a * direction.y() / norm_value; + double deta_z = distance_to_a * direction.z() / norm_value; + Vec3d new_pos = new_line.a + Vec3d(deta_x, deta_y, deta_z); + left_num--; + m_position_points[left_num] = new_pos; + left_length = distance_to_a; + } + } + + { + int index2 = index; + double right_length = (line.b - Vec3d::Zero()).cast().norm(); + int right_num = text_num / 2; + while (right_num > 0) { + double gap_length = (text_lengths[text_num - right_num] + m_text_gap + text_lengths[text_num - right_num - 1]); + if (gap_length < 0) gap_length = 0; + + while (gap_length > right_length) { + gap_length -= right_length; + if (index2 == lines.size() - 1) + index2 = 0; + else + ++index2; + right_length = lines[index2].length(); + } + + Line_3D line2 = lines[index2]; + line2.reverse(); + Vec3d direction = line2.vector(); + direction.normalize(); + double distance_to_b = (right_length - gap_length); + Line_3D new_line = lines[index2]; + + double norm_value = direction.cast().norm(); + double deta_x = distance_to_b * direction.x() / norm_value; + double deta_y = distance_to_b * direction.y() / norm_value; + double deta_z = distance_to_b * direction.z() / norm_value; + Vec3d new_pos = new_line.b + Vec3d(deta_x, deta_y, deta_z); + m_position_points[text_num - right_num] = new_pos; + right_length = distance_to_b; + right_num--; + } + } + } else { + for (int i = 0; i < text_num / 2; ++i) { + std::vector lines = new_polygon.get_lines(); + Line_3D line = lines[index]; + { + int index1 = index; + double left_length = (Vec3d::Zero() - line.a).cast().norm(); + int left_num = text_num / 2; + for (int i = 0; i < text_num / 2; ++i) { + double gap_length = 0; + if (i == 0) { + gap_length = m_text_gap / 2 + text_lengths[text_num / 2 - 1 - i]; + } else { + gap_length = text_lengths[text_num / 2 - i] + m_text_gap + text_lengths[text_num / 2 - 1 - i]; + } + if (gap_length < 0) gap_length = 0; + + while (gap_length > left_length) { + gap_length -= left_length; + if (index1 == 0) + index1 = lines.size() - 1; + else + --index1; + left_length = lines[index1].length(); + } + + Vec3d direction = lines[index1].vector(); + direction.normalize(); + double distance_to_a = (left_length - gap_length); + Line_3D new_line = lines[index1]; + + double norm_value = direction.cast().norm(); + double deta_x = distance_to_a * direction.x() / norm_value; + double deta_y = distance_to_a * direction.y() / norm_value; + double deta_z = distance_to_a * direction.z() / norm_value; + Vec3d new_pos = new_line.a + Vec3d(deta_x, deta_y, deta_z); + + m_position_points[text_num / 2 - 1 - i] = new_pos; + left_length = distance_to_a; + } + } + + { + int index2 = index; + double right_length = (line.b - Vec3d::Zero()).cast().norm(); + int right_num = text_num / 2; + double gap_length = 0; + for (int i = 0; i < text_num / 2; ++i) { + double gap_length = 0; + if (i == 0) { + gap_length = m_text_gap / 2 + text_lengths[text_num / 2 + i]; + } else { + gap_length = text_lengths[text_num / 2 + i] + m_text_gap + text_lengths[text_num / 2 + i - 1]; + } + if (gap_length < 0) gap_length = 0; + + while (gap_length > right_length) { + gap_length -= right_length; + if (index2 == lines.size() - 1) + index2 = 0; + else + ++index2; + right_length = lines[index2].length(); + } + + Line_3D line2 = lines[index2]; + line2.reverse(); + Vec3d direction = line2.vector(); + direction.normalize(); + double distance_to_b = (right_length - gap_length); + Line_3D new_line = lines[index2]; + + double norm_value = direction.cast().norm(); + double deta_x = distance_to_b * direction.x() / norm_value; + double deta_y = distance_to_b * direction.y() / norm_value; + double deta_z = distance_to_b * direction.z() / norm_value; + Vec3d new_pos = new_line.b + Vec3d(deta_x, deta_y, deta_z); + m_position_points[text_num / 2 + i] = new_pos; + right_length = distance_to_b; + } + } + } + } + + std::vector mesh_values(m_position_points.size(), 1e9); + m_normal_points.resize(m_position_points.size()); + auto point_in_triangle_delete_area = [](const Vec3d &point, const Vec3d &point0, const Vec3d &point1, const Vec3d &point2) { + Vec3d p0_p = point - point0; + Vec3d p0_p1 = point1 - point0; + Vec3d p0_p2 = point2 - point0; + Vec3d p_p0 = point0 - point; + Vec3d p_p1 = point1 - point; + Vec3d p_p2 = point2 - point; + + double s = p0_p1.cross(p0_p2).norm(); + double s0 = p_p0.cross(p_p1).norm(); + double s1 = p_p1.cross(p_p2).norm(); + double s2 = p_p2.cross(p_p0).norm(); + + return abs(s0 + s1 + s2 - s); + }; + bool is_mirrored = (m_model_object_in_world_tran * text_tran_in_object).is_left_handed(); + slice_meshs.transform(text_tran_in_object.get_matrix().inverse()); + TriangleMesh& mesh = slice_meshs; + std::vector debug_incides; + debug_incides.resize(m_position_points.size()); + for (int i = 0; i < m_position_points.size(); ++i) { + int debug_index = 0; + for (auto indice : mesh.its.indices) { + stl_vertex stl_point0 = mesh.its.vertices[indice[0]]; + stl_vertex stl_point1 = mesh.its.vertices[indice[1]]; + stl_vertex stl_point2 = mesh.its.vertices[indice[2]]; + + Vec3d point0 = stl_point0.cast(); + Vec3d point1 = stl_point1.cast(); + Vec3d point2 = stl_point2.cast(); + + double abs_area = point_in_triangle_delete_area(m_position_points[i], point0, point1, point2); + if (mesh_values[i] > abs_area) { + mesh_values[i] = abs_area; + debug_incides[i] = debug_index; + Vec3d s1 = point1 - point0; + Vec3d s2 = point2 - point0; + m_normal_points[i] = s1.cross(s2); + m_normal_points[i].normalize(); + if (is_mirrored) { + m_normal_points[i] = -m_normal_points[i]; + } + } + debug_index++; + } + } + return true; +} + + Geometry::Transformation GenerateTextJob::get_sub_mesh_tran(const Vec3d &position, const Vec3d &normal, const Vec3d &text_up_dir, float embeded_depth) +{ + double phi; + Vec3d rotation_axis; + Matrix3d rotation_matrix; + Geometry::rotation_from_two_vectors(Vec3d::UnitZ(), normal, rotation_axis, phi, &rotation_matrix); + Geometry::Transformation local_tran; + Transform3d temp_tran0(Transform3d::Identity()); + temp_tran0.rotate(Eigen::AngleAxisd(phi, rotation_axis.normalized())); + + auto project_on_plane = [](const Vec3d &dir, const Vec3d &plane_normal) -> Vec3d { return dir - (plane_normal.dot(dir) * plane_normal.dot(plane_normal)) * plane_normal; }; + + Vec3d old_text_dir = Vec3d::UnitY(); + old_text_dir = rotation_matrix * old_text_dir; + Vec3d new_text_dir = project_on_plane(text_up_dir, normal); + new_text_dir.normalize(); + Geometry::rotation_from_two_vectors(old_text_dir, new_text_dir, rotation_axis, phi, &rotation_matrix); + + if (abs(phi - PI) < EPSILON) + rotation_axis = normal; + + Transform3d temp_tran1(Transform3d::Identity()); + temp_tran1.rotate(Eigen::AngleAxisd(phi, rotation_axis.normalized())); + local_tran.set_matrix(temp_tran1 * temp_tran0); + + Vec3d offset = position - embeded_depth * normal; + local_tran.set_offset(offset); + return local_tran; +} + +void GenerateTextJob::get_text_mesh(TriangleMesh &result_mesh, std::vector &chars_mesh, int i, Geometry::Transformation &local_tran) +{ + if (chars_mesh.size() == 0) { + BOOST_LOG_TRIVIAL(info) << boost::format("check error:get_text_mesh"); + } + TriangleMesh mesh = chars_mesh[i]; // m_cur_font_name + auto box = mesh.bounding_box(); + mesh.translate(-box.center().x(), 0, 0); + + mesh.transform(local_tran.get_matrix()); + result_mesh = mesh; // mesh in object cs +} + +void GenerateTextJob::get_text_mesh(TriangleMesh & result_mesh, + EmbossShape & text_shape, + BoundingBoxes & line_qds, + SurfaceVolumeData::ModelSources &input_ms_es, + DataBase & input_db, + int i, + Geometry::Transformation &mv_tran, + Geometry::Transformation &local_tran_to_object_cs, + TriangleMesh & slice_mesh) +{ + ExPolygons glyph_shape = text_shape.shapes_with_ids[i].expoly; + const BoundingBox &glyph_bb = line_qds[i]; + Point offset(-glyph_bb.center().x(), 0); + for (ExPolygon &s : glyph_shape) { + s.translate(offset); + } + auto modify = local_tran_to_object_cs.get_matrix(); + Transform3d tr = mv_tran.get_matrix() * modify; + float text_scale = input_db.shape.scale; + if (i < text_shape.text_scales.size() && text_shape.text_scales[i] > 0) { + text_scale = text_shape.text_scales[i]; + } + indexed_triangle_set glyph_its = cut_surface_to_its(glyph_shape, text_scale, tr, input_ms_es, input_db); + if (glyph_its.empty()) { + BOOST_LOG_TRIVIAL(info) << boost::format("check error:get_text_mesh"); + } + // move letter in volume on the right position + its_transform(glyph_its, modify); + + // Improve: union instead of merge + //its_merge(result, std::move(glyph_its)); + result_mesh = TriangleMesh(glyph_its); +} + +void GenerateTextJob::generate_mesh_according_points(InputInfo &input_info) +{ + auto &m_position_points = input_info.m_position_points; + auto &m_normal_points = input_info.m_normal_points; + auto &m_cut_plane_dir_in_world = input_info.m_cut_plane_dir_in_world; + auto &m_chars_mesh_result = input_info.m_chars_mesh_result; + auto &m_embeded_depth = input_info.m_embeded_depth; + auto &m_thickness = input_info.m_thickness; + auto &m_model_object_in_world_tran = input_info.m_model_object_in_world_tran; + auto &m_text_tran_in_object = input_info.m_text_tran_in_object; + auto &mesh = input_info.m_final_text_mesh; + mesh.clear(); + auto text_tran_in_object = m_text_tran_in_object; // important + auto inv_text_cs_in_object_no_offset = (m_model_object_in_world_tran.get_matrix_no_offset() * text_tran_in_object.get_matrix_no_offset()).inverse(); + + ExPolygons ex_polygons; + std::vector qds; + int line_idx = 0; + SurfaceVolumeData::ModelSources ms_es; + DataBase input_db("", std::make_shared>(false)); + if (input_info.use_surface) { + EmbossShape &es = input_info.m_text_shape; + if (es.shapes_with_ids.empty()) + throw JobException(_u8L("Font doesn't have any shape for given text.").c_str()); + size_t count_lines = 1; // input1.text_lines.size(); + qds = create_line_bounds(es.shapes_with_ids, count_lines); + if (qds.empty()) { + return; + } + SurfaceVolumeData::ModelSource ms; + ms.mesh = std::make_shared (input_info.slice_mesh); + if (ms.mesh->empty()) { + return; + } + ms_es.push_back(ms); + input_db.is_outside = input_info.is_outside; + input_db.shape.projection.depth = m_thickness + m_embeded_depth; + input_db.shape.scale = input_info.shape_scale; + } + auto cut_plane_dir = inv_text_cs_in_object_no_offset * m_cut_plane_dir_in_world; + for (int i = 0; i < m_position_points.size(); ++i) { + auto position = m_position_points[i]; + auto normal = m_normal_points[i]; + TriangleMesh sub_mesh; + auto local_tran = get_sub_mesh_tran(position, normal, cut_plane_dir, m_embeded_depth); + if (input_info.use_surface) { + get_text_mesh(sub_mesh, input_info.m_text_shape, qds[line_idx], ms_es, input_db, i, text_tran_in_object, local_tran, input_info.slice_mesh); + } + else { + get_text_mesh(sub_mesh, m_chars_mesh_result, i, local_tran); + } + mesh.merge(sub_mesh); + } + if (mesh.its.empty()){ + throw JobException(_u8L("Text mesh ie empty.").c_str()); + return; + } + //ASCENT_CENTER = 1 / 2.5;// mesh.translate(Vec3f(0, -center.y(), 0)); // align vertical center +} + +CreateObjectTextJob::CreateObjectTextJob(CreateTextInput &&input) : m_input(std::move(input)) {} + +void CreateObjectTextJob::process(Ctl &ctl) { + create_all_char_mesh(*m_input.base, m_input.m_chars_mesh_result, m_input.m_text_shape); + if (m_input.m_chars_mesh_result.empty()) { + return; + } + std::vector text_lengths; + calc_text_lengths(text_lengths, m_input.m_chars_mesh_result); + calc_position_points(m_input.m_position_points, text_lengths, m_input.text_info.m_text_gap, Vec3d(1, 0, 0)); +} + +void CreateObjectTextJob::finalize(bool canceled, std::exception_ptr &eptr) { + if (canceled || eptr) return; + if (m_input.m_position_points.empty()) + return create_message("Can't create empty object."); + + TriangleMesh final_mesh; + for (int i = 0; i < m_input.m_position_points.size();i++) { + TriangleMesh sub_mesh; + auto position = m_input.m_position_points[i]; + auto local_tran = GenerateTextJob::get_sub_mesh_tran(position, Vec3d::UnitZ(), Vec3d(0, 1, 0), m_input.text_info.m_embeded_depth); + GenerateTextJob::get_text_mesh(sub_mesh, m_input.m_chars_mesh_result, i, local_tran); + final_mesh.merge(sub_mesh); + } + + GUI_App &app = wxGetApp(); + Plater * plater = app.plater(); + plater->take_snapshot("Add text object on plate"); + auto center = plater->get_partplate_list().get_curr_plate()->get_bounding_box().center(); + Model &model = plater->model(); + { + // INFO: inspiration for create object is from ObjectList::load_mesh_object() + ModelObject *new_object = model.add_object(); + new_object->name = _u8L("Text"); + new_object->add_instance(); // each object should have at list one instance + new_object->invalidate_bounding_box(); + + ModelVolume *new_volume = new_object->add_volume(std::move(final_mesh), false); + new_volume->calculate_convex_hull(); + new_volume->name = _u8L("Text"); + // set a default extruder value, since user can't add it manually + new_volume->config.set_key_value("extruder", new ConfigOptionInt(1)); + m_input.text_info.m_surface_type = TextInfo::TextType ::HORIZONAL; + new_volume->set_text_info(m_input.text_info); + // write emboss data into volume + m_input.base->write(*new_volume); + + // set transformation + Slic3r::Geometry::Transformation tr; + tr.set_offset(center); + new_object->instances.front()->set_transformation(tr); + new_object->ensure_on_bed(); + + Slic3r::save_object_mesh(*new_object); + new_object->get_model()->set_assembly_pos(new_object); + // Actualize right panel and set inside of selection + app.obj_list()->paste_objects_into_list({model.objects.size() - 1}); + } +#ifdef _DEBUG + check_model_ids_validity(model); +#endif /* _DEBUG */ + + // When add new object selection is empty. + // When cursor move and no one object is selected than + // Manager::reset_all() So Gizmo could be closed before end of creation object + GLCanvas3D * canvas = plater->get_view3D_canvas3D(); + GLGizmosManager &manager = canvas->get_gizmos_manager(); + if (manager.get_current_type() != GLGizmosManager::EType::Text) + manager.open_gizmo(GLGizmosManager::EType::Text); + + // redraw scene + canvas->reload_scene(true); +} + +}}} // namespace Slic3r \ No newline at end of file diff --git a/src/slic3r/GUI/Jobs/EmbossJob.hpp b/src/slic3r/GUI/Jobs/EmbossJob.hpp index 0732726..21d1480 100644 --- a/src/slic3r/GUI/Jobs/EmbossJob.hpp +++ b/src/slic3r/GUI/Jobs/EmbossJob.hpp @@ -49,7 +49,7 @@ public: /// /// Data object for store emboss params virtual void write(ModelVolume &volume) const; - + virtual TextConfiguration get_text_configuration() { return {}; } // Define projection move // True (raised) .. move outside from surface (MODEL_PART) // False (engraved).. move into object (NEGATIVE_VOLUME) @@ -176,7 +176,7 @@ struct SurfaceVolumeData // source volumes std::shared_ptr mesh; // Transformation of volume inside of object - Transform3d tr; + Transform3d tr{Transform3d::Identity()}; }; using ModelSources = std::vector; ModelSources sources; @@ -261,6 +261,30 @@ public: void process(Ctl &ctl) override; void finalize(bool canceled, std::exception_ptr &eptr) override; }; +struct CreateTextInput +{ + std::vector m_chars_mesh_result; + EmbossShape m_text_shape; + TextInfo text_info; + DataBasePtr base; + + std::vector m_position_points; +}; + +class CreateObjectTextJob : public JobNew +{ + CreateTextInput m_input; +public: + explicit CreateObjectTextJob(CreateTextInput &&input); + void process(Ctl &ctl) override; + void finalize(bool canceled, std::exception_ptr &eptr) override; +}; + +const GLVolume *find_glvoloume_render_screen_cs(const Selection &selection, const Vec2d &screen_center, const Camera &camera, const ModelObjectPtrs &objects, Vec2d *closest_center); +void create_all_char_mesh(DataBase &input, std::vector &result, EmbossShape &shape); +float get_single_char_width( const std::vector &chars_mesh_result); +bool calc_text_lengths(std::vector &text_lengths,const std::vector& chars_mesh_result); +void calc_position_points(std::vector &position_points, std::vector &text_lengths, float text_gap, const Vec3d &temp_pos_dir); struct Texture { @@ -304,6 +328,81 @@ public: void process(Ctl &ctl) override; void finalize(bool canceled, std::exception_ptr &eptr) override; }; + +void recreate_model_volume(Slic3r::ModelObject *model_object, int volume_idx, const TriangleMesh &mesh, Geometry::Transformation &text_tran, TextInfo &text_info); +void create_text_volume(Slic3r::ModelObject *model_object, const TriangleMesh &mesh, Geometry::Transformation &text_tran, TextInfo &text_info); +class GenerateTextJob : public JobNew +{ +public: + enum SurfaceType { + None, + Surface, + CharSurface, + }; + struct InputInfo + { + Geometry::Transformation m_text_tran_in_object; + Geometry::Transformation m_model_object_in_world_tran; + ModelObject * mo{nullptr}; + int m_volume_idx; + int hit_mesh_id; + std::vector m_position_points; + std::vector m_normal_points; + std::vector m_cut_points_in_world; + std::vector m_cut_points_in_local; + Geometry::Transformation m_text_tran_in_world; // Transform3d m_text_cs_to_world_tran; + //Transform3d m_object_cs_to_world_tran; + std::vector m_chars_mesh_result; + Vec3d m_text_position_in_world; + Vec3f m_text_normal_in_world; + float m_text_gap; + std::vector text_lengths; + + Vec3d m_cut_plane_dir_in_world; + float m_thickness = 2.f; + float m_embeded_depth = 0.f; + SurfaceType m_surface_type = SurfaceType::None; + TextInfo::TextType text_surface_type; + + TriangleMesh m_final_text_mesh; + Geometry::Transformation m_final_text_tran_in_object; + TextInfo text_info; + + TriangleMesh slice_mesh; + EmbossShape m_text_shape; + bool use_surface = false; + float shape_scale; + bool is_outside = true;//bool is_outside = (type == ModelVolumeType::MODEL_PART); + + bool first_generate = false; + Emboss::DataUpdate m_data_update; + + }; + static bool update_text_positions(InputInfo &input_info); + static bool generate_text_points(InputInfo &input_info); + static Geometry::Transformation get_sub_mesh_tran(const Vec3d &position, const Vec3d &normal, const Vec3d &text_up_dir, float embeded_depth); + static void get_text_mesh(TriangleMesh &result_mesh, std::vector &chars_mesh, int i, Geometry::Transformation& local_tran); + static void get_text_mesh(TriangleMesh & result_mesh, + EmbossShape & text_shape, + BoundingBoxes & line_qds, + SurfaceVolumeData::ModelSources& input_ms_es, + DataBase &input_db, + int i, + Geometry::Transformation &mv_tran, + Geometry::Transformation &local_tran_to_object_cs, + TriangleMesh & slice_mesh); + static void generate_mesh_according_points(InputInfo& input_info); + static std::vector debug_cut_points_in_world; + +public: + explicit GenerateTextJob(InputInfo &&input); + void process(Ctl &ctl) override; + void finalize(bool canceled, std::exception_ptr &eptr) override; + +private: + InputInfo m_input; +}; + static bool check(unsigned char gizmo_type); static bool check(const DataBase &input, bool check_fontfile, bool use_surface = false); static bool check(const CreateVolumeParams &input); @@ -323,6 +422,7 @@ static TriangleMesh try_create_mesh(DataBase &input); static TriangleMesh create_mesh(DataBase &input); static std::vector create_meshs(DataBase &input); static indexed_triangle_set cut_surface_to_its(const ExPolygons &shapes, const Transform3d &tr, const SurfaceVolumeData::ModelSources &sources, DataBase &input); +static indexed_triangle_set cut_surface_to_its(const ExPolygons &shapes, float scale, const Transform3d &tr, const SurfaceVolumeData::ModelSources &sources, DataBase &input); static TriangleMesh cut_per_glyph_surface(DataBase &input1, const SurfaceVolumeData &input2); static TriangleMesh cut_surface(DataBase &input1, const SurfaceVolumeData &input2); static void _update_volume(TriangleMesh &&mesh, const DataUpdate &data, const Transform3d *tr = nullptr); diff --git a/src/slic3r/GUI/Jobs/PrintJob.cpp b/src/slic3r/GUI/Jobs/PrintJob.cpp index cadc474..d6ba4c7 100644 --- a/src/slic3r/GUI/Jobs/PrintJob.cpp +++ b/src/slic3r/GUI/Jobs/PrintJob.cpp @@ -50,7 +50,6 @@ void PrintJob::prepare() if (&job_data) { std::string temp_file = Slic3r::resources_dir() + "/check_access_code.txt"; auto check_access_code_path = temp_file.c_str(); - BOOST_LOG_TRIVIAL(trace) << "sned_job: check_access_code_path = " << check_access_code_path; job_data._temp_path = fs::path(check_access_code_path); } @@ -140,7 +139,7 @@ wxString PrintJob::get_http_error_msg(unsigned int status, std::string body) ; } return wxEmptyString; -} +} void PrintJob::process() { @@ -256,6 +255,7 @@ void PrintJob::process() params.auto_bed_leveling = this->auto_bed_leveling; params.auto_flow_cali = this->auto_flow_cali; params.auto_offset_cali = this->auto_offset_cali; + params.task_ext_change_assist = this->task_ext_change_assist; if (m_print_type == "from_sdcard_view") { params.dst_file = m_dst_path; @@ -299,6 +299,7 @@ void PrintJob::process() std::regex pattern("_+"); params.project_name = std::regex_replace(mall_model_name, pattern, "_"); + params.project_name = truncate_string(params.project_name, 100); } catch (...) {} } @@ -356,8 +357,6 @@ void PrintJob::process() } } - - if (params.preset_name.empty() && m_print_type == "from_normal") { params.preset_name = wxString::Format("%s_plate_%d", m_project_name, curr_plate_idx).ToStdString(); } if (params.project_name.empty()) {params.project_name = m_project_name;} @@ -383,12 +382,12 @@ void PrintJob::process() bool is_try_lan_mode = false; bool is_try_lan_mode_failed = false; - auto update_fn = [this, + auto update_fn = [this, &is_try_lan_mode, &is_try_lan_mode_failed, - &msg, - &error_str, - &curr_percent, + &msg, + &error_str, + &curr_percent, &error_text, StagePercentPoint ](int stage, int code, std::string info) { @@ -471,7 +470,7 @@ void PrintJob::process() return was_canceled(); }; - + DeviceManager* dev = wxGetApp().getDeviceManager(); MachineObject* obj = dev->get_selected_machine(); @@ -593,7 +592,7 @@ void PrintJob::process() this->update_status(curr_percent, _L("Sending print job through cloud service")); result = m_agent->start_print(params, update_fn, cancel_fn, wait_fn); } - } + } } else { if (this->has_sdcard) { this->update_status(curr_percent, _L("Sending print job over LAN")); @@ -631,7 +630,7 @@ void PrintJob::process() if (result != QIDI_NETWORK_ERR_CANCELED) { this->show_error_info(msg_text, 0, "", ""); } - + BOOST_LOG_TRIVIAL(error) << "print_job: failed, result = " << result; } else { // wait for printer mqtt ready the same job id diff --git a/src/slic3r/GUI/Jobs/PrintJob.hpp b/src/slic3r/GUI/Jobs/PrintJob.hpp index 25609e3..75d8925 100644 --- a/src/slic3r/GUI/Jobs/PrintJob.hpp +++ b/src/slic3r/GUI/Jobs/PrintJob.hpp @@ -73,7 +73,7 @@ public: bool m_is_calibration_task = false; int m_print_from_sdc_plate_idx = 0; - + bool m_local_use_ssl_for_mqtt { true }; bool m_local_use_ssl_for_ftp { true }; bool task_bed_leveling; @@ -84,12 +84,13 @@ public: bool cloud_print_only { false }; bool has_sdcard { false }; bool task_use_ams { true }; + bool task_ext_change_assist { false }; int auto_bed_leveling{0}; int auto_flow_cali{0}; int auto_offset_cali{0}; - void set_print_config(std::string bed_type, bool bed_leveling, bool flow_cali, bool vabration_cali, bool record_timelapse, bool layer_inspect, + void set_print_config(std::string bed_type, bool bed_leveling, bool flow_cali, bool vabration_cali, bool record_timelapse, bool layer_inspect, bool ext_change_assist, int auto_bed_levelingt, int auto_flow_calit, int auto_offset_calit) @@ -100,10 +101,12 @@ public: task_vibration_cali = vabration_cali; task_record_timelapse = record_timelapse; task_layer_inspect = layer_inspect; + task_ext_change_assist = ext_change_assist; auto_bed_leveling = auto_bed_levelingt; auto_flow_cali = auto_flow_calit; auto_offset_cali = auto_offset_calit; + } int status_range() const override diff --git a/src/slic3r/GUI/Jobs/SendJob.cpp b/src/slic3r/GUI/Jobs/SendJob.cpp index 748400a..3215e67 100644 --- a/src/slic3r/GUI/Jobs/SendJob.cpp +++ b/src/slic3r/GUI/Jobs/SendJob.cpp @@ -40,7 +40,6 @@ void SendJob::prepare() if (&job_data) { std::string temp_file = Slic3r::resources_dir() + "/check_access_code.txt"; auto check_access_code_path = temp_file.c_str(); - BOOST_LOG_TRIVIAL(trace) << "sned_job: check_access_code_path = " << check_access_code_path; job_data._temp_path = fs::path(check_access_code_path); } } @@ -121,8 +120,8 @@ void SendJob::process() std::string http_body; - - + + // local print access params.dev_ip = m_dev_ip; params.username = "qdtp"; @@ -148,7 +147,7 @@ void SendJob::process() m_job_finished = true; return; } - + /* display info */ msg = _L("Sending gcode file over LAN"); @@ -316,7 +315,7 @@ void SendJob::process() else if (params.password.empty()) params.comments = "no_password"; - if (!params.password.empty() + if (!params.password.empty() && !params.dev_ip.empty() && this->has_sdcard) { // try to send local with record diff --git a/src/slic3r/GUI/Jobs/UpgradeNetworkJob.cpp b/src/slic3r/GUI/Jobs/UpgradeNetworkJob.cpp index 91330e3..5f7de83 100644 --- a/src/slic3r/GUI/Jobs/UpgradeNetworkJob.cpp +++ b/src/slic3r/GUI/Jobs/UpgradeNetworkJob.cpp @@ -62,7 +62,9 @@ void UpgradeNetworkJob::process() auto path_str = tmp_path.string() + wxString::Format(".%d%s", get_current_pid(), ".tmp").ToStdString(); tmp_path = fs::path(path_str); - BOOST_LOG_TRIVIAL(info) << "UpgradeNetworkJob: save netowrk_plugin to " << tmp_path.string(); +#if !QDT_RELEASE_TO_PUBLIC + BOOST_LOG_TRIVIAL(info) << "UpgradeNetworkJob: save network_plugin to " << PathSanitizer::sanitize(tmp_path); +#endif auto cancel_fn = [this]() { return was_canceled(); diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index b73490b..e031442 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -254,6 +254,7 @@ void KBShortcutsDialog::fill_shortcuts() {ctrl + "A", L("Select all objects")}, {ctrl + L("Shift+D"), L("Delete all")}, {ctrl + "Z", L("Undo")}, + {ctrl + "Shift+Z", L("Redo")}, {ctrl + "Y", L("Redo")}, { "M", L("Gizmo move") }, { "S", L("Gizmo scale") }, @@ -287,6 +288,7 @@ void KBShortcutsDialog::fill_shortcuts() {ctrl + "A", L("Select all objects")}, {ctrl + "K", L("Clone selected")}, {ctrl + "Z", L("Undo")}, + {ctrl + "Shift+Z", L("Redo")}, {ctrl + "Y", L("Redo")}, {L("Space"), L("Select the object/part and press space to change the name")}, {L("Mouse click"), L("Select the object/part and mouse click to change the name")}, diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index f123dfa..56fbc1f 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1,5 +1,5 @@ #include "MainFrame.hpp" - +#include "GLToolbar.hpp" #include #include #include @@ -26,6 +26,7 @@ #include "ProgressStatusBar.hpp" #include "3DScene.hpp" #include "ParamsDialog.hpp" +#include "UserPresetsDialog.hpp" #include "PrintHostDialogs.hpp" #include "wxExtensions.hpp" #include "GUI_ObjectList.hpp" @@ -508,16 +509,20 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_ j["split_to_objects"] = get_value("split_to_objects"); j["split_to_part"] = get_value("split_to_part"); j["custom_height"] = get_value("custom_height"); - j["move"] = get_value(get_name_from_gizmo_etype(GLGizmosManager::EType::Move)); - j["rotate"] = get_value(get_name_from_gizmo_etype(GLGizmosManager::EType::Rotate)); - j["scale"] = get_value(get_name_from_gizmo_etype(GLGizmosManager::EType::Scale)); - j["flatten"] = get_value(get_name_from_gizmo_etype(GLGizmosManager::EType::Flatten)); - j["cut"] = get_value(get_name_from_gizmo_etype(GLGizmosManager::EType::Cut)); - j["meshboolean"] = get_value(get_name_from_gizmo_etype(GLGizmosManager::EType::MeshBoolean)); - j["custom_support"] = get_value(get_name_from_gizmo_etype(GLGizmosManager::EType::FdmSupports)); - j["custom_seam"] = get_value(get_name_from_gizmo_etype(GLGizmosManager::EType::Seam)); - j["text_shape"] = get_value(get_name_from_gizmo_etype(GLGizmosManager::EType::Text)); - j["color_painting"] = get_value(get_name_from_gizmo_etype(GLGizmosManager::EType::MmuSegmentation)); + j["move"] = get_value(GLGizmosManager::convert_gizmo_type_to_string(GLGizmosManager::EType::Move)); + j["rotate"] = get_value(GLGizmosManager::convert_gizmo_type_to_string(GLGizmosManager::EType::Rotate)); + j["scale"] = get_value(GLGizmosManager::convert_gizmo_type_to_string(GLGizmosManager::EType::Scale)); + j["flatten"] = get_value(GLGizmosManager::convert_gizmo_type_to_string(GLGizmosManager::EType::Flatten)); + j["cut"] = get_value(GLGizmosManager::convert_gizmo_type_to_string(GLGizmosManager::EType::Cut)); + j["meshboolean"] = get_value(GLGizmosManager::convert_gizmo_type_to_string(GLGizmosManager::EType::MeshBoolean)); + j["custom_support"] = get_value(GLGizmosManager::convert_gizmo_type_to_string(GLGizmosManager::EType::FdmSupports)); + j["custom_fuzzyskin"] = get_value(GLGizmosManager::convert_gizmo_type_to_string(GLGizmosManager::EType::FuzzySkin)); + j["custom_seam"] = get_value(GLGizmosManager::convert_gizmo_type_to_string(GLGizmosManager::EType::Seam)); + j["text_shape"] = get_value(GLGizmosManager::convert_gizmo_type_to_string(GLGizmosManager::EType::Text)); + j["measure"] = get_value(GLGizmosManager::convert_gizmo_type_to_string(GLGizmosManager::EType::Measure)); + j["assembly"] = get_value(GLGizmosManager::convert_gizmo_type_to_string(GLGizmosManager::EType::Assembly)); + j["color_painting"] = get_value(GLGizmosManager::convert_gizmo_type_to_string(GLGizmosManager::EType::MmuSegmentation)); + j["brimears"] = get_value(GLGizmosManager::convert_gizmo_type_to_string(GLGizmosManager::EType::BrimEars)); j["assembly_view"] = get_value("assembly_view"); agent->track_event("key_func", j.dump()); @@ -530,11 +535,15 @@ DPIFrame(NULL, wxID_ANY, "", wxDefaultPosition, wxDefaultSize, BORDERLESS_FRAME_ j["scale_duration"] = get_value("Scale_duration"); j["flatten_duration"] = get_value("Lay on face_duration"); j["cut_duration"] = get_value("Cut_duration"); + j["brimears_duration"] = get_value("Brimears_duration"); j["meshboolean_duration"] = get_value("Mesh Boolean_duration"); j["custom_support_duration"] = get_value("Supports Painting_duration"); j["custom_seam_duration"] = get_value("Seam painting_duration"); j["text_shape_duration"] = get_value("Text shape_duration"); j["color_painting_duration"] = get_value("Color Painting_duration"); + j["fuzzyskin_painting_duration"] = get_value("FuzzySkin Painting_duration"); + j["assembly_duration"] = get_value("Assemble_duration"); + j["measure_duration"] = get_value("Measure_duration"); j["assembly_view_duration"] = get_value("assembly_view_duration"); agent->track_event("key_func_duration", j.dump()); @@ -2105,7 +2114,7 @@ wxBoxSizer* MainFrame::create_side_tools() /* Button * aux_btn = new Button(this, _L("Auxiliary")); - aux_btn->SetBackgroundColour(0x3B4446); + aux_btn->SetBackgroundColour("#3B4446"); aux_btn->Bind(wxEVT_BUTTON, [](auto e) { wxGetApp().sidebar().show_auxiliary_dialog(); }); @@ -2277,7 +2286,7 @@ void MainFrame::update_side_button_style() m_slice_btn->SetMinSize(wxSize(-1, FromDIP(24))); m_slice_btn->SetCornerRadius(FromDIP(12)); m_slice_btn->SetExtraSize(wxSize(FromDIP(38), FromDIP(10))); - m_slice_btn->SetBottomColour(wxColour(0x3B4446));*/ + m_slice_btn->SetBottomColour(wxColour("#3B4446"));*/ StateColor m_btn_bg_enable = StateColor( std::pair(wxColour(40, 90, 220), StateColor::Pressed), std::pair(wxColour(100, 150, 255), StateColor::Hovered), @@ -2734,20 +2743,11 @@ void MainFrame::init_menubar_as_editor() // QDS wxMenu *import_menu = new wxMenu(); -#ifndef __APPLE__ append_menu_item(import_menu, wxID_ANY, _L("Import 3MF/STL/STEP/SVG/OBJ/AMF") + dots + "\t" + ctrl + "I", _L("Load a model"), [this](wxCommandEvent&) { if (m_plater) { m_plater->add_file(); } }, "menu_import", nullptr, [this](){return can_add_models(); }, this); -#else - append_menu_item(import_menu, wxID_ANY, _L("Import 3MF/STL/STEP/SVG/OBJ/AMF") + dots + "\t" + ctrl + "I", _L("Load a model"), - [this](wxCommandEvent &) { - if (m_plater) { m_plater->add_file(); } - }, - "", nullptr, - [this](){return can_add_models(); }, this); -#endif append_menu_item(import_menu, wxID_ANY, _L("Import Configs") + dots /*+ "\tCtrl+I"*/, _L("Load configs"), [this](wxCommandEvent&) { load_config_file(); }, "menu_import", nullptr, [this](){return true; }, this); @@ -2780,7 +2780,7 @@ void MainFrame::init_menubar_as_editor() [this]() {return can_export_gcode(); }, this); append_menu_item(export_menu, wxID_ANY, _L("Export toolpaths as OBJ") + dots, _L("Export toolpaths as OBJ"), - [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->export_toolpaths_to_obj(); }, "", nullptr, + [this](wxCommandEvent&) { if (m_plater != nullptr) m_plater->export_toolpaths_to_obj(); }, "menu_export_toolpaths", nullptr, [this]() {return can_export_toolpaths(); }, this); append_menu_item( @@ -2815,6 +2815,14 @@ void MainFrame::init_menubar_as_editor() // fileMenu->AppendSeparator(); + append_menu_item( + fileMenu, wxID_ANY, _L("Batch Preset Management"), wxString::Format(_L("Batch Preset Management")), + [this](wxCommandEvent &) { + UserPresetsDialog dlg(this); + dlg.ShowModal(); + }, + "", nullptr); + #ifndef __APPLE__ append_menu_item(fileMenu, wxID_EXIT, _L("Quit"), wxString::Format(_L("Quit")), [this](wxCommandEvent&) { Close(false); }, "menu_exit", nullptr); @@ -3063,6 +3071,11 @@ void MainFrame::init_menubar_as_editor() }, this, [this]() { return m_tabpanel->GetSelection() == TabPosition::tp3DEditor || m_tabpanel->GetSelection() == TabPosition::tpPreview; }, [this]() { return wxGetApp().show_3d_navigator(); }, this); + append_menu_item( + viewMenu, wxID_ANY, _L("Reset Window Layout") + "\t" + ctrl + "W", _L("Reset to default window layout"), + [this](wxCommandEvent &) { m_plater->reset_window_layout(); }, "", this, + [this]() { return (m_tabpanel->GetSelection() == TabPosition::tp3DEditor || m_tabpanel->GetSelection() == TabPosition::tpPreview) && m_plater->is_sidebar_enabled(); }, + this); viewMenu->AppendSeparator(); append_menu_check_item(viewMenu, wxID_ANY, _L("Show Labels") + "\t" + ctrl + "E", _L("Show object labels in 3D scene"), [this](wxCommandEvent&) { m_plater->show_view3D_labels(!m_plater->are_view3D_labels_shown()); m_plater->get_current_canvas3D()->post_event(SimpleEvent(wxEVT_PAINT)); }, this, @@ -3630,6 +3643,16 @@ void MainFrame::update_menubar() const bool is_fff = plater()->printer_technology() == ptFFF; } +void MainFrame::update_calibration_button_status() +{ + Preset &printer_preset = wxGetApp().preset_bundle->printers.get_edited_preset(); + bool isQDT = printer_preset.is_qdt_vendor_preset(wxGetApp().preset_bundle); + bool is_multi_extruder = wxGetApp().preset_bundle->get_printer_extruder_count() > 1; + // Show calibration Menu for QDT printers if Develop Mode is on. + bool show_calibration = (!isQDT || wxGetApp().app_config->get("developer_mode") == "true") && !is_multi_extruder; + wxGetApp().mainframe->show_calibration_button(show_calibration); +} + void MainFrame::reslice_now() { if (m_plater) @@ -3717,7 +3740,9 @@ void MainFrame::load_config_file() BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " presets has been import,and size is" << cfiles.size(); NetworkAgent* agent = wxGetApp().getAgent(); if (agent) { +#if !QDT_RELEASE_TO_PUBLIC BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " user is: " << agent->get_user_id(); +#endif } } wxGetApp().preset_bundle->update_compatible(PresetSelectCompatibleType::Always); @@ -4156,7 +4181,7 @@ void MainFrame::remove_recent_project(size_t file_id, wxString const &filename) void MainFrame::load_url(wxString url) { - BOOST_LOG_TRIVIAL(trace) << "load_url:" << url; + BOOST_LOG_TRIVIAL(trace) << "load_url"; auto evt = new wxCommandEvent(EVT_LOAD_URL, this->GetId()); evt->SetString(url); wxQueueEvent(this, evt); @@ -4164,7 +4189,7 @@ void MainFrame::load_url(wxString url) void MainFrame::load_printer_url(wxString url) { - BOOST_LOG_TRIVIAL(trace) << "load_printer_url:" << url; + BOOST_LOG_TRIVIAL(trace) << "load_printer_url"; auto evt = new wxCommandEvent(EVT_LOAD_PRINTER_URL, this->GetId()); evt->SetString(url); wxQueueEvent(this, evt); diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 1883734..c7a4683 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -297,6 +297,7 @@ public: bool check_qdt_farm_client_installed(); void init_menubar_as_gcodeviewer(); void update_menubar(); + void update_calibration_button_status(); // Open item in menu by menu and item name (in actual language) void open_menubar_item(const wxString& menu_name,const wxString& item_name); #ifdef _WIN32 diff --git a/src/slic3r/GUI/MediaFilePanel.cpp b/src/slic3r/GUI/MediaFilePanel.cpp index ae99bdf..e28206c 100644 --- a/src/slic3r/GUI/MediaFilePanel.cpp +++ b/src/slic3r/GUI/MediaFilePanel.cpp @@ -326,7 +326,7 @@ void MediaFilePanel::SetMachineObject(MachineObject* obj) json j; j["code"] = err; j["dev_id"] = m_machine; - j["dev_ip"] = m_lan_ip; + j["dev_ip"] = ""; NetworkAgent* agent = wxGetApp().getAgent(); if (status == PrinterFileSystem::Failed && err != 0) { j["result"] = "failed"; @@ -357,7 +357,7 @@ void MediaFilePanel::SetMachineObject(MachineObject* obj) json j; j["code"] = result; j["dev_id"] = m_machine; - j["dev_ip"] = m_lan_ip; + j["dev_ip"] = ""; if (result > 1) { // download failed j["result"] = "failed"; @@ -521,7 +521,9 @@ void MediaFilePanel::fetchUrl(boost::weak_ptr wfs) url += "&cli_id=" + wxGetApp().app_config->get("slicer_uuid"); url += "&cli_ver=" + std::string(SLIC3R_VERSION); } +#if !QDT_RELEASE_TO_PUBLIC BOOST_LOG_TRIVIAL(info) << "MediaFilePanel::fetchUrl: camera_url: " << hide_passwd(url, {"?uid=", "authkey=", "passwd="}); +#endif CallAfter([=] { boost::shared_ptr fs(wfs.lock()); if (!fs || fs != m_image_grid->GetFileSystem()) return; diff --git a/src/slic3r/GUI/MediaPlayCtrl.cpp b/src/slic3r/GUI/MediaPlayCtrl.cpp index 61baf03..b239b3d 100644 --- a/src/slic3r/GUI/MediaPlayCtrl.cpp +++ b/src/slic3r/GUI/MediaPlayCtrl.cpp @@ -8,7 +8,8 @@ #include "MsgDialog.hpp" #include "DownloadProgressDialog.hpp" -#include +#include "slic3r/Utils/QDTUtil.hpp" + #include #include #include @@ -203,7 +204,7 @@ void MediaPlayCtrl::SetMachineObject(MachineObject* obj) return; } m_machine = machine; - BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl switch machine: " << m_machine; + BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl switch machine: " << QDTCrossTalk::Crosstalk_DevId(m_machine); m_disable_lan = false; m_failed_retry = 0; m_last_failed_codes.clear(); @@ -315,7 +316,11 @@ void MediaPlayCtrl::Play() url += "&dev_ver=" + m_dev_ver; url += "&cli_id=" + wxGetApp().app_config->get("slicer_uuid"); url += "&cli_ver=" + std::string(SLIC3R_VERSION); + +#if !QDT_RELEASE_TO_PUBLIC BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl: " << hide_passwd(hide_id_middle_string(url, url.find(m_lan_ip), m_lan_ip.length()), {m_lan_passwd}); +#endif + m_url = url; load(); m_button_play->SetIcon("media_stop"); @@ -365,11 +370,14 @@ void MediaPlayCtrl::Play() url += "&cli_id=" + wxGetApp().app_config->get("slicer_uuid"); url += "&cli_ver=" + std::string(SLIC3R_VERSION); } - BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl: " << hide_passwd(url, - {"?uid=", "authkey=", "passwd=", "license=", "token="}); + +#if !QDT_RELEASE_TO_PUBLIC + BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl: " << hide_passwd(url, {"?uid=", "channel=", "authkey=", "passwd=", "license=", "token="}); +#endif + CallAfter([this, m, url] { if (m != m_machine) { - BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl drop late ttcode for machine: " << m; + BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl drop late ttcode for machine: " << QDTCrossTalk::Crosstalk_DevId(m); return; } if (m_last_state == MEDIASTATE_IDLE) { @@ -446,7 +454,7 @@ void MediaPlayCtrl::Stop(wxString const &msg, wxString const &msg2) json j; j["stage"] = last_state; j["dev_id"] = m_machine; - j["dev_ip"] = m_lan_ip; + j["dev_ip"] = ""; j["result"] = "failed"; j["user_triggered"] = m_user_triggered; j["failed_retry"] = m_failed_retry; @@ -469,7 +477,7 @@ void MediaPlayCtrl::Stop(wxString const &msg, wxString const &msg2) if (last_state == wxMEDIASTATE_PLAYING && m_stat.size() == 4) { json j; j["dev_id"] = m_machine; - j["dev_ip"] = m_lan_ip; + j["dev_ip"] = ""; j["result"] = m_failed_code ? "failed" : "success"; j["tunnel"] = tunnel; j["code"] = m_failed_code; @@ -588,7 +596,11 @@ void MediaPlayCtrl::ToggleStream() url = "qidi:///rtsp___" + m_lan_user + ":" + m_lan_passwd + "@" + m_lan_ip + "/streaming/live/1?proto=rtsp"; url += "&device=" + into_u8(m_machine); url += "&dev_ver=" + m_dev_ver; + +#if !QDT_RELEASE_TO_PUBLIC BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl::ToggleStream: " << hide_passwd(hide_id_middle_string(url, url.find(m_lan_ip), m_lan_ip.length()), {m_lan_passwd}); +#endif + std::string file_url = data_dir() + "/cameratools/url.txt"; boost::nowide::ofstream file(file_url); auto url2 = encode_path(url.c_str()); @@ -610,8 +622,11 @@ void MediaPlayCtrl::ToggleStream() url += "&cli_id=" + wxGetApp().app_config->get("slicer_uuid"); url += "&cli_ver=" + std::string(SLIC3R_VERSION); } - BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl::ToggleStream: " << hide_passwd(url, - {"?uid=", "authkey=", "passwd=", "license=", "token="}); + +#if !QDT_RELEASE_TO_PUBLIC + BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl::ToggleStream: " << hide_passwd(url,{"?uid=", "authkey=", "passwd=", "license=", "token="}); +#endif + CallAfter([this, m, url] { if (m != m_machine) return; if (url.empty() || !boost::algorithm::starts_with(url, "qidi:///")) { @@ -678,7 +693,7 @@ void MediaPlayCtrl::onStateChanged(wxMediaEvent &event) json j; j["stage"] = std::to_string(m_last_state); j["dev_id"] = m_machine; - j["dev_ip"] = m_lan_ip; + j["dev_ip"] = ""; j["result"] = "success"; j["code"] = 0; auto tunnel = into_u8(wxURI(m_url).GetPath()).substr(1); @@ -775,7 +790,10 @@ void MediaPlayCtrl::media_proc() } wxString url = m_tasks.front(); if (m_tasks.size() >= 2 && !url.IsEmpty() && url[0] != '<' && m_tasks[1] == "") { + +#if !QDT_RELEASE_TO_PUBLIC BOOST_LOG_TRIVIAL(trace) << "MediaPlayCtrl: busy skip url: " << url; +#endif m_tasks.pop_front(); m_tasks.pop_front(); continue; @@ -834,7 +852,7 @@ bool MediaPlayCtrl::start_stream_service(bool *need_install) file_url2 = wxURI(file_url2).BuildURI(); try { std::string configs; - boost::filesystem::load_string_file(file_ff_cfg, configs); + load_string_file(file_ff_cfg, configs); std::vector configss; boost::algorithm::split(configss, configs, boost::algorithm::is_any_of("\r\n")); configss.erase(std::remove(configss.begin(), configss.end(), std::string()), configss.end()); diff --git a/src/slic3r/GUI/MeshUtils.cpp b/src/slic3r/GUI/MeshUtils.cpp index baaaac3..d2e5496 100644 --- a/src/slic3r/GUI/MeshUtils.cpp +++ b/src/slic3r/GUI/MeshUtils.cpp @@ -46,23 +46,37 @@ void MeshClipper::set_limiting_plane(const ClippingPlane& plane) } } -void MeshClipper::set_mesh(const TriangleMesh& mesh) +void MeshClipper::set_mesh(const indexed_triangle_set &mesh) { - if (m_mesh != &mesh) { + if (m_mesh.get() != &mesh) { m_mesh = &mesh; - reset(); + m_result.reset(); } } -void MeshClipper::set_negative_mesh(const TriangleMesh& mesh) +void MeshClipper::set_mesh(AnyPtr &&ptr) { - if (m_negative_mesh != &mesh) { - m_negative_mesh = &mesh; - reset(); + if (m_mesh.get() != ptr.get()) { + m_mesh = std::move(ptr); + m_result.reset(); } } +void MeshClipper::set_negative_mesh(const indexed_triangle_set &mesh) +{ + if (m_negative_mesh.get() != &mesh) { + m_negative_mesh = &mesh; + m_result.reset(); + } +} +void MeshClipper::set_negative_mesh(AnyPtr &&ptr) +{ + if (m_negative_mesh.get() != ptr.get()) { + m_negative_mesh = std::move(ptr); + m_result.reset(); + } +} void MeshClipper::set_transformation(const Geometry::Transformation& trafo) { @@ -208,10 +222,10 @@ void MeshClipper::recalculate_triangles() // if (m_csgmesh.empty()) { if (m_mesh) { - expolys = union_ex(slice_mesh(m_mesh->its, height_mesh, slicing_params)); + expolys = union_ex(slice_mesh(*m_mesh, height_mesh, slicing_params)); } if (m_negative_mesh && !m_negative_mesh->empty()) { - const ExPolygons neg_expolys = union_ex(slice_mesh(m_negative_mesh->its, height_mesh, slicing_params)); + const ExPolygons neg_expolys = union_ex(slice_mesh(*m_negative_mesh, height_mesh, slicing_params)); expolys = diff_ex(expolys, neg_expolys); } diff --git a/src/slic3r/GUI/MeshUtils.hpp b/src/slic3r/GUI/MeshUtils.hpp index 916380c..842bc3b 100644 --- a/src/slic3r/GUI/MeshUtils.hpp +++ b/src/slic3r/GUI/MeshUtils.hpp @@ -6,7 +6,7 @@ #include "libslic3r/Geometry.hpp" #include "libslic3r/SLA/IndexedMesh.hpp" #include "admesh/stl.h" - +#include "libslic3r/AnyPtr.hpp" #include "slic3r/GUI/GLModel.hpp" #include @@ -93,9 +93,11 @@ public: // Which mesh to cut. MeshClipper remembers const * to it, caller // must make sure that it stays valid. - void set_mesh(const TriangleMesh& mesh); + void set_mesh(const indexed_triangle_set &mesh); + void set_mesh(AnyPtr &&ptr); - void set_negative_mesh(const TriangleMesh &mesh); + void set_negative_mesh(const indexed_triangle_set &mesh); + void set_negative_mesh(AnyPtr &&ptr); // Inform the MeshClipper about the transformation that transforms the mesh // into world coordinates. @@ -115,8 +117,8 @@ private: void recalculate_triangles(); void reset(); Geometry::Transformation m_trafo; - const TriangleMesh * m_mesh = nullptr; - const TriangleMesh * m_negative_mesh = nullptr; + AnyPtr m_mesh; + AnyPtr m_negative_mesh; ClippingPlane m_plane; ClippingPlane m_limiting_plane = ClippingPlane::ClipsNothing(); diff --git a/src/slic3r/GUI/Monitor.cpp b/src/slic3r/GUI/Monitor.cpp index 9e956ce..69ab573 100644 --- a/src/slic3r/GUI/Monitor.cpp +++ b/src/slic3r/GUI/Monitor.cpp @@ -459,23 +459,6 @@ bool MonitorPanel::Show(bool show) return wxPanel::Show(show); } -void MonitorPanel::update_side_panel() -{ - Slic3r::DeviceManager *dev = Slic3r::GUI::wxGetApp().getDeviceManager(); - if (!dev) return; - - auto is_next_machine = false; - if (!dev->get_first_online_user_machine().empty()) { - wxCommandEvent* event = new wxCommandEvent(wxEVT_COMMAND_CHOICE_SELECTED); - event->SetString(dev->get_first_online_user_machine()); - wxQueueEvent(this, event); - is_next_machine = true; - return; - } - - if (!is_next_machine) { m_side_tools->set_none_printer_mode(); } -} - void MonitorPanel::show_status(int status) { if (!m_initialized) return; diff --git a/src/slic3r/GUI/Monitor.hpp b/src/slic3r/GUI/Monitor.hpp index 9b3b391..7d2846f 100644 --- a/src/slic3r/GUI/Monitor.hpp +++ b/src/slic3r/GUI/Monitor.hpp @@ -144,7 +144,6 @@ public: void update_hms_tag(); bool Show(bool show); - void update_side_panel(); void show_status(int status); std::string get_string_from_tab(PrinterTab tab); diff --git a/src/slic3r/GUI/MultiTaskManagerPage.cpp b/src/slic3r/GUI/MultiTaskManagerPage.cpp index a5b40e2..bb806ca 100644 --- a/src/slic3r/GUI/MultiTaskManagerPage.cpp +++ b/src/slic3r/GUI/MultiTaskManagerPage.cpp @@ -6,6 +6,7 @@ #include "Widgets/RadioBox.hpp" #include #include +#include "QDTUtil.hpp" namespace Slic3r { namespace GUI { @@ -176,7 +177,7 @@ void MultiTaskItem::update_info() void MultiTaskItem::onPause() { if (get_obj() && !get_obj()->can_resume()) { - BOOST_LOG_TRIVIAL(info) << "MultiTask: pause current print task dev_id =" << get_obj()->dev_id; + BOOST_LOG_TRIVIAL(info) << "MultiTask: pause current print task dev_id =" << QDTCrossTalk::Crosstalk_DevId(get_obj()->dev_id); get_obj()->command_task_pause(); m_button_pause->Hide(); m_button_resume->Show(); @@ -187,7 +188,7 @@ void MultiTaskItem::onPause() void MultiTaskItem::onResume() { if (get_obj() && get_obj()->can_resume()) { - BOOST_LOG_TRIVIAL(info) << "MultiTask: resume current print task dev_id =" << get_obj()->dev_id; + BOOST_LOG_TRIVIAL(info) << "MultiTask: resume current print task dev_id =" << QDTCrossTalk::Crosstalk_DevId(get_obj()->dev_id); get_obj()->command_task_resume(); m_button_pause->Show(); m_button_resume->Hide(); @@ -198,7 +199,7 @@ void MultiTaskItem::onResume() void MultiTaskItem::onStop() { if (get_obj()) { - BOOST_LOG_TRIVIAL(info) << "MultiTask: abort current print task dev_id =" << get_obj()->dev_id; + BOOST_LOG_TRIVIAL(info) << "MultiTask: abort current print task dev_id =" << QDTCrossTalk::Crosstalk_DevId(get_obj()->dev_id); get_obj()->command_task_abort(); m_button_pause->Hide(); m_button_resume->Hide(); @@ -536,7 +537,7 @@ LocalTaskManagerPage::LocalTaskManagerPage(wxWindow* parent) #ifdef __WINDOWS__ SetDoubleBuffered(true); #endif //__WINDOWS__ - SetBackgroundColour(wxColour(0xEEEEEE)); + SetBackgroundColour(wxColour("#EEEEEE")); m_main_panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); m_main_panel->SetBackgroundColour(*wxWHITE); m_main_sizer = new wxBoxSizer(wxVERTICAL); @@ -898,10 +899,10 @@ CloudTaskManagerPage::CloudTaskManagerPage(wxWindow* parent) #ifdef __WINDOWS__ SetDoubleBuffered(true); #endif //__WINDOWS__ - SetBackgroundColour(wxColour(0xEEEEEE)); + SetBackgroundColour(wxColour("#EEEEEE")); m_sort.set_role(SortItem::SR_SEND_TIME, true); - SetBackgroundColour(wxColour(0xEEEEEE)); + SetBackgroundColour(wxColour("#EEEEEE")); m_main_panel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); m_main_panel->SetBackgroundColour(*wxWHITE); m_main_sizer = new wxBoxSizer(wxVERTICAL); diff --git a/src/slic3r/GUI/NetworkTestDialog.cpp b/src/slic3r/GUI/NetworkTestDialog.cpp index c7c9829..c6dbaa6 100644 --- a/src/slic3r/GUI/NetworkTestDialog.cpp +++ b/src/slic3r/GUI/NetworkTestDialog.cpp @@ -269,7 +269,7 @@ wxBoxSizer* NetworkTestDialog::create_content_sizer(wxWindow* parent) }); btn_network_plugin->Bind(wxEVT_BUTTON, [this](wxCommandEvent &evt) { - start_test_plugin_download_thread(); + start_test_plugin_download_thread(); }); return sizer; @@ -687,7 +687,7 @@ void NetworkTestDialog::start_test_oss_download() } bool cancel = false; - BOOST_LOG_TRIVIAL(info) << "[test_storage_download] get_url = " << download_url; + //BOOST_LOG_TRIVIAL(info) << "[test_storage_download] get_url = " << download_url; // download Slic3r::Http http = Slic3r::Http::get(download_url); @@ -831,7 +831,7 @@ void NetworkTestDialog:: start_test_plugin_download(){ } bool cancel = false; - BOOST_LOG_TRIVIAL(info) << "[test_plugin_download] get_url = " << download_url; + //BOOST_LOG_TRIVIAL(info) << "[test_plugin_download] get_url = " << download_url; // download Slic3r::Http http = Slic3r::Http::get(download_url); @@ -937,8 +937,8 @@ void NetworkTestDialog::start_test_oss_upload_thread() void NetworkTestDialog:: start_test_plugin_download_thread(){ - test_job[TEST_PLUGIN_JOB] = new boost::thread([this] { - start_test_plugin_download(); + test_job[TEST_PLUGIN_JOB] = new boost::thread([this] { + start_test_plugin_download(); }); } diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp index a0db00a..c41d250 100644 --- a/src/slic3r/GUI/NotificationManager.cpp +++ b/src/slic3r/GUI/NotificationManager.cpp @@ -1301,6 +1301,7 @@ void NotificationManager::UpdatedItemsInfoNotification::add_type(InfoItemType ty // QDS //case InfoItemType::CustomSeam: text += format(("%1$d Object has custom seam.", "%1$d Objects have custom seam.", (*it).second), (*it).second) + "\n"; break; case InfoItemType::MmuSegmentation: text += format(_L_PLURAL("%1$d Object has color painting.", "%1$d Objects have color painting.",(*it).second), (*it).second) + "\n"; break; + case InfoItemType::FuzzySkin: text += format(_L_PLURAL("%1$d Object has fuzzy skin.", "%1$d Objects have fuzzy skin.", (*it).second), (*it).second) + "\n"; break; // QDS //case InfoItemType::Sinking: text += format(("%1$d Object has partial sinking.", "%1$d Objects have partial sinking.", (*it).second), (*it).second) + "\n"; break; case InfoItemType::CutConnectors: text += format(_L_PLURAL("%1$d object was loaded as a part of cut object.", "%1$d objects were loaded as parts of cut object", (*it).second), (*it).second) + "\n"; break; diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp index 04ede51..c89277d 100644 --- a/src/slic3r/GUI/NotificationManager.hpp +++ b/src/slic3r/GUI/NotificationManager.hpp @@ -154,6 +154,7 @@ enum class NotificationType QDTSliceMultiExtruderHeightOutside, QDTBedFilamentIncompatible, QDTMixUsePLAAndPETG, + QDTNozzleFilamentIncompatible, AssemblyWarning, AssemblyInfo, NotificationTypeCount diff --git a/src/slic3r/GUI/OG_CustomCtrl.cpp b/src/slic3r/GUI/OG_CustomCtrl.cpp index 2990dee..a0fc7b5 100644 --- a/src/slic3r/GUI/OG_CustomCtrl.cpp +++ b/src/slic3r/GUI/OG_CustomCtrl.cpp @@ -783,7 +783,9 @@ void OG_CustomCtrl::CtrlLine::render(wxDC& dc, wxCoord h_pos, wxCoord v_pos) } is_url_string = !suppress_hyperlinks && !og_line.label_path.empty(); // QDS - h_pos = draw_text(dc, wxPoint(h_pos, v_pos), label /* + ":" */, text_clr, icon_pos + ctrl->opt_group->label_width * ctrl->m_em_unit - h_pos, is_url_string, true); + wxCoord indent = og_line.subline ? lround(1.5 * ctrl->m_em_unit) : 0; + h_pos = draw_text(dc, wxPoint(h_pos + indent, v_pos), label /* + ":" */, text_clr, icon_pos + ctrl->opt_group->label_width * ctrl->m_em_unit - h_pos, is_url_string, true); + h_pos -= indent; } // If there's a widget, build it and set result to the correct position. diff --git a/src/slic3r/GUI/ObjectDataViewModel.cpp b/src/slic3r/GUI/ObjectDataViewModel.cpp index 64a7596..e1b8ed3 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.cpp +++ b/src/slic3r/GUI/ObjectDataViewModel.cpp @@ -63,6 +63,7 @@ struct InfoItemAtributes { const std::map INFO_ITEMS{ // info_item Type info_item Name info_item BitmapName { InfoItemType::CustomSupports, {L("Support painting"), "toolbar_support" }, }, + {InfoItemType::FuzzySkin,{L("Fuzzy skin"), "toolbar_fuzzyskin"},}, //{ InfoItemType::CustomSeam, {L("Paint-on seam"), "seam_" }, }, { InfoItemType::MmuSegmentation, {L("Color painting"), "mmu_segmentation"}, }, //{ InfoItemType::Sinking, {L("Sinking"), "objlist_sinking"}, }, @@ -247,6 +248,16 @@ void ObjectDataViewModelNode::set_support_icon(bool enable, bool force) m_support_icon = create_scaled_bitmap("dot"); } +void ObjectDataViewModelNode::set_fuzzyskin_icon(bool enable, bool force) +{ + if (!force && m_fuzzyskin_enable == enable) return; + m_fuzzyskin_enable = enable; + if ((m_type & itObject) && enable) + m_fuzzyskin_icon = create_scaled_bitmap("toolbar_fuzzyskin"); + else + m_fuzzyskin_icon = create_scaled_bitmap("dot"); +} + void ObjectDataViewModelNode::set_sinking_icon(bool enable, bool force) { if (!force && m_sink_enable == enable) @@ -343,6 +354,9 @@ bool ObjectDataViewModelNode::SetValue(const wxVariant& variant, unsigned col) case colSupportPaint: m_support_icon << variant; break; + case colFuzzySkin: + m_fuzzyskin_icon << variant; + break; case colSinking: m_sinking_icon << variant; break; @@ -1778,6 +1792,9 @@ void ObjectDataViewModel::GetValue(wxVariant &variant, const wxDataViewItem &ite case colSupportPaint: variant << node->m_support_icon; break; + case colFuzzySkin: + variant << node->m_fuzzyskin_icon; + break; case colSinking: variant << node->m_sinking_icon; break; @@ -2326,6 +2343,15 @@ bool ObjectDataViewModel::IsSupportPainted(wxDataViewItem& item) const return node->m_support_enable; } +bool ObjectDataViewModel::IsFuzzySkinPainted(wxDataViewItem &item) const +{ + ObjectDataViewModelNode *node = static_cast(item.GetID()); + if (!node) + return false; + + return node->m_fuzzyskin_enable; +} + bool ObjectDataViewModel::IsSinked(wxDataViewItem &item) const { ObjectDataViewModelNode *node = static_cast(item.GetID()); @@ -2354,6 +2380,15 @@ void ObjectDataViewModel::SetSupportPaintState(const bool painted, wxDataViewIte ItemChanged(obj_item); } +void ObjectDataViewModel::SetFuzzySkinPaintState(const bool painted, wxDataViewItem obj_item, bool force) +{ + ObjectDataViewModelNode *node = static_cast(obj_item.GetID()); + if (!node) return; + + node->set_fuzzyskin_icon(painted, force); + ItemChanged(obj_item); +} + void ObjectDataViewModel::SetSinkState(const bool painted, wxDataViewItem obj_item, bool force) { ObjectDataViewModelNode *node = static_cast(obj_item.GetID()); diff --git a/src/slic3r/GUI/ObjectDataViewModel.hpp b/src/slic3r/GUI/ObjectDataViewModel.hpp index 2136abf..9115f40 100644 --- a/src/slic3r/GUI/ObjectDataViewModel.hpp +++ b/src/slic3r/GUI/ObjectDataViewModel.hpp @@ -44,6 +44,7 @@ enum ColumnNumber colFilament , // extruder selection // QDS colSupportPaint , + colFuzzySkin , colColorPaint , colSinking , colEditing , // item editing @@ -68,6 +69,7 @@ enum class InfoItemType Undef, CustomSupports, //CustomSeam, + FuzzySkin, MmuSegmentation, //Sinking CutConnectors, @@ -97,6 +99,7 @@ class ObjectDataViewModelNode wxBitmap m_action_icon; // QDS wxBitmap m_support_icon; + wxBitmap m_fuzzyskin_icon; wxBitmap m_color_icon; wxBitmap m_sinking_icon; PrintIndicator m_printable {piUndef}; @@ -115,6 +118,7 @@ class ObjectDataViewModelNode bool m_action_enable = false; // can undo all settings // QDS bool m_support_enable = false; + bool m_fuzzyskin_enable = false; bool m_color_enable = false; bool m_sink_enable = false; @@ -260,6 +264,7 @@ public: // QDS bool HasColorPainting() const { return m_color_enable; } bool HasSupportPainting() const { return m_support_enable; } + bool HasFuzzySkinPainting() const { return m_fuzzyskin_enable; } bool HasSinking() const { return m_sink_enable; } bool IsActionEnabled() const { return m_action_enable; } void UpdateExtruderAndColorIcon(wxString extruder = ""); @@ -303,6 +308,7 @@ public: // QDS void set_color_icon(bool enable, bool force = false); void set_support_icon(bool enable,bool force = false); + void set_fuzzyskin_icon(bool enable, bool force = false); void set_sinking_icon(bool enable, bool force = false); // Set warning icon for node @@ -508,9 +514,11 @@ public: // QDS bool IsColorPainted(wxDataViewItem& item) const; bool IsSupportPainted(wxDataViewItem &item) const; + bool IsFuzzySkinPainted(wxDataViewItem &item) const; bool IsSinked(wxDataViewItem &item) const; void SetColorPaintState(const bool painted, wxDataViewItem obj_item,bool force = false); void SetSupportPaintState(const bool painted, wxDataViewItem obj_item,bool force = false); + void SetFuzzySkinPaintState(const bool painted, wxDataViewItem obj_item, bool force = false); void SetSinkState(const bool painted, wxDataViewItem obj_item,bool force = false); void SetAssociatedControl(wxDataViewCtrl* ctrl) { m_ctrl = ctrl; } diff --git a/src/slic3r/GUI/OpenGLManager.cpp b/src/slic3r/GUI/OpenGLManager.cpp index bcca692..5f4f0c5 100644 --- a/src/slic3r/GUI/OpenGLManager.cpp +++ b/src/slic3r/GUI/OpenGLManager.cpp @@ -793,6 +793,16 @@ bool OpenGLManager::is_gizmo_keep_screen_size_enabled() const return m_b_gizmo_keep_screen_size_enabled; } +void OpenGLManager::set_toolbar_rendering_style(uint8_t style) +{ + m_toolbar_rendering_style = style; +} + +uint8_t OpenGLManager::get_toolbar_rendering_style() const +{ + return m_toolbar_rendering_style; +} + std::string OpenGLManager::framebuffer_type_to_string(EFramebufferType type) { switch (type) diff --git a/src/slic3r/GUI/OpenGLManager.hpp b/src/slic3r/GUI/OpenGLManager.hpp index dc941ac..954081d 100644 --- a/src/slic3r/GUI/OpenGLManager.hpp +++ b/src/slic3r/GUI/OpenGLManager.hpp @@ -231,6 +231,7 @@ private: uint32_t m_vao{ 0 }; bool m_b_legacy_framebuffer_enabled{ true }; bool m_b_gizmo_keep_screen_size_enabled{ true }; + uint8_t m_toolbar_rendering_style{ 0 }; static GLInfo s_gl_info; #ifdef __APPLE__ // Part of hack to remove crash when closing the application on OSX 10.9.5 when building against newer wxWidgets @@ -280,6 +281,9 @@ public: bool is_fxaa_enabled() const; void blit_framebuffer(const std::string& source, const std::string& target); + void set_toolbar_rendering_style(uint8_t style); + uint8_t get_toolbar_rendering_style() const; + static bool init(); static bool are_compressed_textures_supported() { return s_compressed_textures_supported; } static bool can_multisample() { return s_multisample == EMultisampleState::Enabled; } diff --git a/src/slic3r/GUI/OptionsGroup.cpp b/src/slic3r/GUI/OptionsGroup.cpp index be13c20..ea00fe8 100644 --- a/src/slic3r/GUI/OptionsGroup.cpp +++ b/src/slic3r/GUI/OptionsGroup.cpp @@ -580,12 +580,13 @@ void OptionsGroup::clear(bool destroy_custom_ctrl) m_fields.clear(); } -Line OptionsGroup::create_single_option_line(const Option& option, const std::string& path/* = std::string()*/) const +Line OptionsGroup::create_single_option_line(const Option& option, const std::string& path/* = std::string()*/, bool subline) const { wxString tooltip = _(option.opt.tooltip); edit_tooltip(tooltip); Line retval{ _(option.opt.label), tooltip }; retval.label_path = path; + retval.subline = subline; retval.append_option(option); return retval; } diff --git a/src/slic3r/GUI/OptionsGroup.hpp b/src/slic3r/GUI/OptionsGroup.hpp index 6c4bc35..faab470 100644 --- a/src/slic3r/GUI/OptionsGroup.hpp +++ b/src/slic3r/GUI/OptionsGroup.hpp @@ -60,6 +60,7 @@ public: size_t full_width {0}; wxColour* full_Label_color {nullptr}; bool blink {false}; + bool subline {false}; widget_t widget {nullptr}; std::function near_label_widget{ nullptr }; wxWindow* near_label_widget_win {nullptr}; @@ -147,8 +148,8 @@ public: // delete all controls from the option group void clear(bool destroy_custom_ctrl = false); - Line create_single_option_line(const Option& option, const std::string& path = std::string()) const; - void append_single_option_line(const Option& option, const std::string& path = std::string()) { append_line(create_single_option_line(option, path)); } + Line create_single_option_line(const Option& option, const std::string& path = std::string(), bool subline = false) const; + void append_single_option_line(const Option& option, const std::string& path = std::string(), bool subline = false) { append_line(create_single_option_line(option, path, subline)); } void append_separator(); // return a non-owning pointer reference @@ -272,13 +273,13 @@ public: Line create_single_option_line(const Option& option, const std::string& path = std::string()) const { return OptionsGroup::create_single_option_line(option, path); } - void append_single_option_line(const Option& option, const std::string& path = std::string()) { - OptionsGroup::append_single_option_line(option, path); + void append_single_option_line(const Option& option, const std::string& path = std::string(), bool subline = false) { + OptionsGroup::append_single_option_line(option, path, subline); } - void append_single_option_line(const std::string title, const std::string& path = std::string(), int idx = -1) + void append_single_option_line(const std::string title, const std::string& path = std::string(), int idx = -1, bool subline = false) { Option option = get_option(title, idx); - append_single_option_line(option, path); + append_single_option_line(option, path, subline); } void on_change_OG(const t_config_option_key& opt_id, const boost::any& value) override; diff --git a/src/slic3r/GUI/PartPlate.cpp b/src/slic3r/GUI/PartPlate.cpp index a9fee2e..545c2b3 100644 --- a/src/slic3r/GUI/PartPlate.cpp +++ b/src/slic3r/GUI/PartPlate.cpp @@ -53,6 +53,7 @@ static const int PARTPLATE_ICON_SIZE = 16; static const int PARTPLATE_EDIT_PLATE_NAME_ICON_SIZE = 12; static const int PARTPLATE_PLATE_NAME_FIX_HEIGHT_SIZE = 20; static const int PARTPLATE_ICON_GAP_TOP = 3; +static const int PARTPLATE_NAME_EDIT_ICON_GAP_LEFT = 3; //B static const int PARTPLATE_ICON_GAP_LEFT = 10; static const int PARTPLATE_ICON_GAP_Y = 5; @@ -417,6 +418,15 @@ void PartPlate::calc_height_limit() { BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Unable to create height limit top lines\n"; } +int PartPlate::get_right_icon_offset_bed() { + if (&wxGetApp() && wxGetApp().plater()) { + auto offset = wxGetApp().plater()->get_right_icon_offset_bed(); + return offset == 0 ? PARTPLATE_ICON_GAP_LEFT : offset; + } else { + return PARTPLATE_ICON_GAP_LEFT; + } +} + void PartPlate::calc_vertex_for_plate_name(GLTexture &texture, GLModel &gl_model) { if (texture.get_width() > 0 && texture.get_height()) { @@ -428,10 +438,10 @@ void PartPlate::calc_vertex_for_plate_name(GLTexture &texture, GLModel &gl_model w = int(factor * (texture.get_width() * 16) / texture.get_height()); h = PARTPLATE_PLATE_NAME_FIX_HEIGHT_SIZE; Vec2d p = bed_ext[3] + Vec2d(0, PARTPLATE_PLATENAME_OFFSET_Y + h * texture.m_original_height / texture.get_height()); - poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + offset_x), scale_(p(1) - h )}); - poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + w - offset_x), scale_(p(1) - h )}); - poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + w - offset_x), scale_(p(1) )}); - poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + offset_x), scale_(p(1) )}); + poly.contour.append({scale_(p(0) + PARTPLATE_NAME_EDIT_ICON_GAP_LEFT + offset_x), scale_(p(1) - h)}); + poly.contour.append({scale_(p(0) + PARTPLATE_NAME_EDIT_ICON_GAP_LEFT + w - offset_x), scale_(p(1) - h)}); + poly.contour.append({scale_(p(0) + PARTPLATE_NAME_EDIT_ICON_GAP_LEFT + w - offset_x), scale_(p(1))}); + poly.contour.append({scale_(p(0) + PARTPLATE_NAME_EDIT_ICON_GAP_LEFT + offset_x), scale_(p(1))}); auto triangles = triangulate_expolygon_2f(poly, NORMALS_UP); gl_model.reset(); @@ -455,18 +465,18 @@ void PartPlate::calc_vertex_for_plate_name_edit_icon(GLTexture *texture, int ind if (texture && texture->get_width() > 0 && texture->get_height()) { w = int(factor * (texture->get_original_width() * 16) / texture->get_height()) + 1; - poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + w), scale_(p(1) - h )}); - poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + w + PARTPLATE_EDIT_PLATE_NAME_ICON_SIZE), scale_(p(1) - h)}); - poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + w + PARTPLATE_EDIT_PLATE_NAME_ICON_SIZE), scale_(p(1))}); - poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + w), scale_(p(1) )}); + poly.contour.append({scale_(p(0) + PARTPLATE_NAME_EDIT_ICON_GAP_LEFT + w), scale_(p(1) - h)}); + poly.contour.append({scale_(p(0) + PARTPLATE_NAME_EDIT_ICON_GAP_LEFT + w + PARTPLATE_EDIT_PLATE_NAME_ICON_SIZE), scale_(p(1) - h)}); + poly.contour.append({scale_(p(0) + PARTPLATE_NAME_EDIT_ICON_GAP_LEFT + w + PARTPLATE_EDIT_PLATE_NAME_ICON_SIZE), scale_(p(1))}); + poly.contour.append({scale_(p(0) + PARTPLATE_NAME_EDIT_ICON_GAP_LEFT + w), scale_(p(1))}); triangles = triangulate_expolygon_2f(poly, NORMALS_UP); } else { - poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + offset_x ), scale_(p(1) - h )}); - poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + offset_x + PARTPLATE_EDIT_PLATE_NAME_ICON_SIZE), scale_(p(1) - h)}); - poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + offset_x + PARTPLATE_EDIT_PLATE_NAME_ICON_SIZE), scale_(p(1))}); - poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + offset_x), scale_(p(1) )}); + poly.contour.append({scale_(p(0) + PARTPLATE_NAME_EDIT_ICON_GAP_LEFT + offset_x), scale_(p(1) - h)}); + poly.contour.append({scale_(p(0) + PARTPLATE_NAME_EDIT_ICON_GAP_LEFT + offset_x + PARTPLATE_EDIT_PLATE_NAME_ICON_SIZE), scale_(p(1) - h)}); + poly.contour.append({scale_(p(0) + PARTPLATE_NAME_EDIT_ICON_GAP_LEFT + offset_x + PARTPLATE_EDIT_PLATE_NAME_ICON_SIZE), scale_(p(1))}); + poly.contour.append({scale_(p(0) + PARTPLATE_NAME_EDIT_ICON_GAP_LEFT + offset_x), scale_(p(1))}); triangles = triangulate_expolygon_2f(poly, NORMALS_UP); } @@ -475,21 +485,6 @@ void PartPlate::calc_vertex_for_plate_name_edit_icon(GLTexture *texture, int ind BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Unable to generate geometry buffers for icons\n"; } -void PartPlate::calc_vertex_for_icons_background(int icon_count, GeometryBuffer &buffer) -{ - ExPolygon poly; - Vec2d & p = m_partplate_list->m_shape[2]; - - poly.contour.append({ scale_(p(0) + PARTPLATE_ICON_GAP_LEFT), scale_(p(1) - icon_count * (PARTPLATE_ICON_SIZE + PARTPLATE_ICON_GAP_Y) - PARTPLATE_ICON_GAP_TOP) }); - poly.contour.append({ scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + PARTPLATE_ICON_SIZE), scale_(p(1) - icon_count * (PARTPLATE_ICON_SIZE + PARTPLATE_ICON_GAP_Y)- PARTPLATE_ICON_GAP_TOP) }); - poly.contour.append({ scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + PARTPLATE_ICON_SIZE), scale_(p(1) - PARTPLATE_ICON_GAP_TOP)}); - poly.contour.append({ scale_(p(0) + PARTPLATE_ICON_GAP_LEFT), scale_(p(1) - PARTPLATE_ICON_GAP_TOP) }); - - auto triangles = triangulate_expolygon_2f(poly, NORMALS_UP); - if (!buffer.set_from_triangles(triangles, GROUND_Z)) - BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "Unable to generate geometry buffers for icons\n"; -} - bool PartPlate::calc_bed_3d_boundingbox(BoundingBoxf3 &box_in_plate_origin) { if (m_partplate_list && m_partplate_list->m_bed3d && !m_partplate_list->m_bed3d->get_model_filename().empty()) { auto cur_bed = m_partplate_list->m_bed3d; @@ -1394,6 +1389,85 @@ bool PartPlate::check_mixture_of_pla_and_petg(const DynamicPrintConfig &config) return true; } +bool PartPlate::check_compatible_of_nozzle_and_filament(const DynamicPrintConfig &config, const std::vector &filament_presets, std::string &error_msg) +{ + float nozzle_diameter = config.option("nozzle_diameter")->values[0]; + auto volume_type_opt = config.option("nozzle_volume_type"); + + auto get_filament_alias = [](std::string preset_name) -> std::string { + size_t at_pos = preset_name.find('@'); + std::string alias = preset_name.substr(0, at_pos); + size_t first = alias.find_first_not_of(' '); + if (first == std::string::npos) return ""; + size_t last = alias.find_last_not_of(' '); + return alias.substr(first, last - first + 1); + }; + + bool with_same_volume_type = std::all_of(volume_type_opt->values.begin(), volume_type_opt->values.end(), + [first_value = volume_type_opt->values[0]](int value) { return value == first_value; }); + + std::set selected_filament_alias; + for (auto &filament_preset : filament_presets) { selected_filament_alias.insert(get_filament_alias(filament_preset)); } + + auto get_incompatible_selected = [&](const NozzleVolumeType volume_type) -> std::set { + std::vector incompatible_filaments = Print::get_incompatible_filaments_by_nozzle(nozzle_diameter, volume_type); + std::set ret; + for (auto &filament : selected_filament_alias) { + if (std::find(incompatible_filaments.begin(), incompatible_filaments.end(), filament) != incompatible_filaments.end()) ret.insert(filament); + } + return ret; + }; + + auto get_nozzle_msg = [](const float nozzle_diameter, const NozzleVolumeType volume_type) -> std::string { + std::ostringstream oss; + oss << std::fixed << std::setprecision(1) << nozzle_diameter; + std::string nozzle_msg = oss.str(); + ((nozzle_msg += "mm ") += _u8L(get_nozzle_volume_type_string(volume_type))) += _u8L(" nozzle"); + return nozzle_msg; + }; + + auto get_incompatible_filament_msg = [](const std::set &incompatible_selected_filaments) -> std::string { + std::string filament_str; + size_t idx = 0; + for (const auto &filament : incompatible_selected_filaments) { + if (idx > 0) filament_str += ','; + filament_str += filament; + ++idx; + } + return filament_str; + }; + + error_msg.clear(); + + std::set nozzle_volumes(volume_type_opt->values.begin(), volume_type_opt->values.end()); + std::map> incompatible_selected_map; + + for (auto volume_type_value : nozzle_volumes) { + NozzleVolumeType volume_type = static_cast(volume_type_value); + auto incompatible_selected = get_incompatible_selected(volume_type); + if (!incompatible_selected.empty()) incompatible_selected_map[volume_type] = incompatible_selected; + } + + if (incompatible_selected_map.empty()) return true; + + if (incompatible_selected_map.size() == 1) { + auto elem = incompatible_selected_map.begin(); + NozzleVolumeType volume_type = elem->first; + auto incompatible_selected = elem->second; + error_msg = GUI::format(_L("It is not recommended to print the following filament(s) with %1%: %2%\n"), get_nozzle_msg(nozzle_diameter, volume_type), + get_incompatible_filament_msg(incompatible_selected)); + } else { + std::string warning_msg = _u8L("It is not recommended to use the following nozzle and filament combinations:\n"); + for (auto &elem : incompatible_selected_map) { + NozzleVolumeType volume_type = elem.first; + auto incompatible_selected = elem.second; + warning_msg += GUI::format(_L("%1% with %2%\n"),get_nozzle_msg(nozzle_diameter, volume_type), get_incompatible_filament_msg(incompatible_selected)); + } + error_msg = warning_msg; + } + return false; +} + /*Vec3d PartPlate::calculate_wipe_tower_size(const DynamicPrintConfig &config, const double w, const double wipe_volume, int plate_extruder_size, bool use_global_objects) const { Vec3d wipe_tower_size; @@ -2353,76 +2427,6 @@ void PartPlate::set_logo_box_by_bed(const BoundingBoxf3& box) } } -void PartPlate::generate_print_polygon(ExPolygon &print_polygon) -{ - auto compute_points = [&print_polygon](Vec2d& center, double radius, double start_angle, double stop_angle, int count) - { - double angle, angle_steps; - angle_steps = (stop_angle - start_angle) / (count - 1); - for(int j = 0; j < count; j++ ) - { - double angle = start_angle + j * angle_steps; - double x = center(0) + ::cos(angle) * radius; - double y = center(1) + ::sin(angle) * radius; - print_polygon.contour.append({ scale_(x), scale_(y) }); - } - }; - - int points_count = 8; - if (m_shape.size() == 4) - { - //rectangle case - for (int i = 0; i < 4; i++) - { - const Vec2d& p = m_shape[i]; - Vec2d center; - double start_angle, stop_angle, angle_steps, radius_x, radius_y, radius; - switch (i) { - case 0: - radius = 5.f; - center(0) = p(0) + radius; - center(1) = p(1) + radius; - start_angle = PI; - stop_angle = 1.5 * PI; - compute_points(center, radius, start_angle, stop_angle, points_count); - break; - case 1: - print_polygon.contour.append({ scale_(p(0)), scale_(p(1)) }); - break; - case 2: - radius_x = (int)(p(0)) % 10; - radius_y = (int)(p(1)) % 10; - radius = (radius_x > radius_y)?radius_y: radius_x; - if (radius < 5.0) - radius = 5.f; - center(0) = p(0) - radius; - center(1) = p(1) - radius; - start_angle = 0; - stop_angle = 0.5 * PI; - compute_points(center, radius, start_angle, stop_angle, points_count); - break; - case 3: - radius_x = (int)(p(0)) % 10; - radius_y = (int)(p(1)) % 10; - radius = (radius_x > radius_y)?radius_y: radius_x; - if (radius < 5.0) - radius = 5.f; - center(0) = p(0) + radius; - center(1) = p(1) - radius; - start_angle = 0.5 * PI; - stop_angle = PI; - compute_points(center, radius, start_angle, stop_angle, points_count); - break; - } - } - } - else { - for (const Vec2d& p : m_shape) { - print_polygon.contour.append({ scale_(p(0)), scale_(p(1)) }); - } - } -} - void PartPlate::generate_exclude_polygon(ExPolygon &exclude_polygon) { auto compute_exclude_points = [&exclude_polygon](Vec2d& center, double radius, double start_angle, double stop_angle, int count) @@ -2449,7 +2453,7 @@ void PartPlate::generate_exclude_polygon(ExPolygon &exclude_polygon) double start_angle, stop_angle, angle_steps, radius_x, radius_y, radius; switch (i) { case 0: - radius = 5.f; + radius = 8.f; center(0) = p(0) + radius; center(1) = p(1) + radius; start_angle = PI; @@ -3011,7 +3015,7 @@ void PartPlate::print() const BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << boost::format(": plate index %1%, pointer %2%, print_index %3% print pointer %4%") % m_plate_index % this % m_print_index % m_print; BOOST_LOG_TRIVIAL(trace) << boost::format("\t origin {%1%,%2%,%3%}, width %4%, depth %5%, height %6%") % m_origin.x() % m_origin.y() % m_origin.z() % m_width % m_depth % m_height; BOOST_LOG_TRIVIAL(trace) << boost::format("\t m_printable %1%, m_locked %2%, m_ready_for_slice %3%, m_slice_result_valid %4%, m_tmp_gcode_path %5%, set size %6%")\ - % m_printable % m_locked % m_ready_for_slice % m_slice_result_valid % m_tmp_gcode_path % obj_to_instance_set.size(); + % m_printable % m_locked % m_ready_for_slice % m_slice_result_valid % PathSanitizer::sanitize(m_tmp_gcode_path) % obj_to_instance_set.size(); /*for (std::set>::iterator it = obj_to_instance_set.begin(); it != obj_to_instance_set.end(); ++it) { int obj_id = it->first; int instance_id = it->second; @@ -3248,9 +3252,13 @@ void PartPlateList::generate_print_polygon(ExPolygon &print_polygon) print_polygon.contour.append({scale_(x), scale_(y)}); } }; - + bool use_rect_grid = false; + if (&wxGetApp() && wxGetApp().plater()) { + auto pm = wxGetApp().plater()->get_curr_printer_model(); + use_rect_grid = (pm && pm->use_rect_grid == "true") ? true : false; + } int points_count = 8; - if (m_shape.size() == 4) { + if (m_shape.size() == 4 && !use_rect_grid) { // rectangle case for (int i = 0; i < 4; i++) { const Vec2d &p = m_shape[i]; @@ -3258,7 +3266,7 @@ void PartPlateList::generate_print_polygon(ExPolygon &print_polygon) double start_angle, stop_angle, radius_x, radius_y, radius; switch (i) { case 0: - radius = 5.f; + radius = 8.f; center(0) = p(0) + radius; center(1) = p(1) + radius; start_angle = PI; @@ -3319,7 +3327,7 @@ void PartPlateList::generate_exclude_polygon(ExPolygon &exclude_polygon) double start_angle, stop_angle, radius; switch (i) { case 0: - radius = 5.f; + radius = 8.f; center(0) = p(0) + radius; center(1) = p(1) + radius; start_angle = PI; @@ -3424,11 +3432,11 @@ void PartPlateList::calc_vertex_for_number(int index, bool one_number, GLModel & #else // in the bottom Vec2d &p = m_shape[1]; float offset_x = one_number ? PARTPLATE_TEXT_OFFSET_X1 : PARTPLATE_TEXT_OFFSET_X2; - - poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + offset_x), scale_(p(1) + PARTPLATE_TEXT_OFFSET_Y)}); - poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + PARTPLATE_ICON_SIZE - offset_x), scale_(p(1) + PARTPLATE_TEXT_OFFSET_Y)}); - poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + PARTPLATE_ICON_SIZE - offset_x), scale_(p(1) + PARTPLATE_ICON_SIZE - PARTPLATE_TEXT_OFFSET_Y)}); - poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + offset_x), scale_(p(1) + PARTPLATE_ICON_SIZE - PARTPLATE_TEXT_OFFSET_Y)}); + auto right_icon_offset_bed = m_plate_list.size() > 0 ? m_plate_list[0]->get_right_icon_offset_bed() : PARTPLATE_ICON_GAP_LEFT; + poly.contour.append({scale_(p(0) + right_icon_offset_bed + offset_x), scale_(p(1) + PARTPLATE_TEXT_OFFSET_Y)}); + poly.contour.append({scale_(p(0) + right_icon_offset_bed + PARTPLATE_ICON_SIZE - offset_x), scale_(p(1) + PARTPLATE_TEXT_OFFSET_Y)}); + poly.contour.append({scale_(p(0) + right_icon_offset_bed + PARTPLATE_ICON_SIZE - offset_x), scale_(p(1) + PARTPLATE_ICON_SIZE - PARTPLATE_TEXT_OFFSET_Y)}); + poly.contour.append({scale_(p(0) + right_icon_offset_bed + offset_x), scale_(p(1) + PARTPLATE_ICON_SIZE - PARTPLATE_TEXT_OFFSET_Y)}); #endif auto triangles = triangulate_expolygon_2f(poly, NORMALS_UP); gl_model.reset(); @@ -3439,14 +3447,11 @@ void PartPlateList::calc_vertex_for_icons(int index, GLModel &gl_model) { ExPolygon poly; Vec2d & p = m_shape[2]; - - poly.contour.append( - {scale_(p(0) + PARTPLATE_ICON_GAP_LEFT), scale_(p(1) - index * (PARTPLATE_ICON_SIZE + PARTPLATE_ICON_GAP_Y) - PARTPLATE_ICON_GAP_TOP - PARTPLATE_ICON_SIZE)}); - poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + PARTPLATE_ICON_SIZE), - scale_(p(1) - index * (PARTPLATE_ICON_SIZE + PARTPLATE_ICON_GAP_Y) - PARTPLATE_ICON_GAP_TOP - PARTPLATE_ICON_SIZE)}); - poly.contour.append( - {scale_(p(0) + PARTPLATE_ICON_GAP_LEFT + PARTPLATE_ICON_SIZE), scale_(p(1) - index * (PARTPLATE_ICON_SIZE + PARTPLATE_ICON_GAP_Y) - PARTPLATE_ICON_GAP_TOP)}); - poly.contour.append({scale_(p(0) + PARTPLATE_ICON_GAP_LEFT), scale_(p(1) - index * (PARTPLATE_ICON_SIZE + PARTPLATE_ICON_GAP_Y) - PARTPLATE_ICON_GAP_TOP)}); + auto right_icon_offset_bed = m_plate_list.size() > 0 ? m_plate_list[0]->get_right_icon_offset_bed() : PARTPLATE_ICON_GAP_LEFT; + poly.contour.append({scale_(p(0) + right_icon_offset_bed), scale_(p(1) - index * (PARTPLATE_ICON_SIZE + PARTPLATE_ICON_GAP_Y) - PARTPLATE_ICON_GAP_TOP - PARTPLATE_ICON_SIZE)}); + poly.contour.append({scale_(p(0) + right_icon_offset_bed + PARTPLATE_ICON_SIZE), scale_(p(1) - index * (PARTPLATE_ICON_SIZE + PARTPLATE_ICON_GAP_Y) - PARTPLATE_ICON_GAP_TOP - PARTPLATE_ICON_SIZE)}); + poly.contour.append({scale_(p(0) + right_icon_offset_bed + PARTPLATE_ICON_SIZE), scale_(p(1) - index * (PARTPLATE_ICON_SIZE + PARTPLATE_ICON_GAP_Y) - PARTPLATE_ICON_GAP_TOP)}); + poly.contour.append({scale_(p(0) + right_icon_offset_bed), scale_(p(1) - index * (PARTPLATE_ICON_SIZE + PARTPLATE_ICON_GAP_Y) - PARTPLATE_ICON_GAP_TOP)}); auto triangles = triangulate_expolygon_2f(poly, NORMALS_UP); gl_model.reset(); @@ -5871,8 +5876,9 @@ int PartPlateList::store_to_3mf_structure(PlateDataPtrs& plate_data_list, bool w plate_data_item->objects_and_instances.emplace_back(it->first, it->second); } - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ <m_gcode_result->filename % with_slice_info %m_plate_list[i]->is_slice_result_valid()%plate_data_item->objects_and_instances.size(); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ <m_gcode_result->filename) % with_slice_info % m_plate_list[i]->is_slice_result_valid() % + plate_data_item->objects_and_instances.size(); if (with_slice_info) { if (m_plate_list[i]->get_slice_result() && m_plate_list[i]->is_slice_result_valid()) { @@ -5942,8 +5948,9 @@ int PartPlateList::load_from_3mf_structure(PlateDataPtrs& plate_data_list, int f { BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(":plate index %1% seems invalid, skip it")% plate_data_list[i]->plate_index; } - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": plate %1%, gcode_file %2%, is_sliced_valid %3%, toolpath_outside %4%, is_support_used %5% is_label_object_enabled %6%") - %i %plate_data_list[i]->gcode_file %plate_data_list[i]->is_sliced_valid %plate_data_list[i]->toolpath_outside %plate_data_list[i]->is_support_used %plate_data_list[i]->is_label_object_enabled; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": plate %1%, gcode_file %2%, is_sliced_valid %3%, toolpath_outside %4%, is_support_used %5% is_label_object_enabled %6%") % i % + PathSanitizer::sanitize(plate_data_list[i]->gcode_file) % plate_data_list[i]->is_sliced_valid % plate_data_list[i]->toolpath_outside % + plate_data_list[i]->is_support_used % plate_data_list[i]->is_label_object_enabled; //load object and instance from 3mf //just test for file correct or not, we will rebuild later /*for (std::vector>::iterator it = plate_data_list[i]->objects_and_instances.begin(); it != plate_data_list[i]->objects_and_instances.end(); ++it) @@ -5971,7 +5978,7 @@ int PartPlateList::load_from_3mf_structure(PlateDataPtrs& plate_data_list, int f gcode_result->warnings = plate_data_list[i]->warnings; gcode_result->filament_maps = plate_data_list[i]->filament_maps; if (m_plater && !plate_data_list[i]->thumbnail_file.empty()) { - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": plate %1%, load thumbnail from %2%.")%(i+1) %plate_data_list[i]->thumbnail_file; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": plate %1%, load thumbnail from %2%.") % (i + 1) % PathSanitizer::sanitize(plate_data_list[i]->thumbnail_file); if (boost::filesystem::exists(plate_data_list[i]->thumbnail_file)) { m_plate_list[index]->load_thumbnail_data(plate_data_list[i]->thumbnail_file, m_plate_list[index]->thumbnail_data); BOOST_LOG_TRIVIAL(info) << __FUNCTION__ <no_light_thumbnail_file.empty()) { if (boost::filesystem::exists(plate_data_list[i]->no_light_thumbnail_file)) { - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": plate %1%, load no_light_thumbnail_file from %2%.")%(i+1) %plate_data_list[i]->no_light_thumbnail_file; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": plate %1%, load no_light_thumbnail_file from %2%.") % (i + 1) % PathSanitizer::sanitize(plate_data_list[i]->no_light_thumbnail_file); m_plate_list[index]->load_thumbnail_data(plate_data_list[i]->no_light_thumbnail_file, m_plate_list[index]->no_light_thumbnail_data); } } @@ -5994,13 +6001,13 @@ int PartPlateList::load_from_3mf_structure(PlateDataPtrs& plate_data_list, int f }*/ if (m_plater && !plate_data_list[i]->top_file.empty()) { if (boost::filesystem::exists(plate_data_list[i]->top_file)) { - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": plate %1%, load top_thumbnail from %2%.")%(i+1) %plate_data_list[i]->top_file; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": plate %1%, load top_thumbnail from %2%.") % (i + 1) % PathSanitizer::sanitize(plate_data_list[i]->top_file); m_plate_list[index]->load_thumbnail_data(plate_data_list[i]->top_file, m_plate_list[index]->top_thumbnail_data); } } if (m_plater && !plate_data_list[i]->pick_file.empty()) { if (boost::filesystem::exists(plate_data_list[i]->pick_file)) { - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": plate %1%, load pick_thumbnail from %2%.")%(i+1) %plate_data_list[i]->pick_file; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": plate %1%, load pick_thumbnail from %2%.") % (i + 1) % PathSanitizer::sanitize(plate_data_list[i]->pick_file); m_plate_list[index]->load_thumbnail_data(plate_data_list[i]->pick_file, m_plate_list[index]->pick_thumbnail_data); } } @@ -6132,15 +6139,54 @@ void PartPlateList::init_bed_type_info() BedTextureInfo::TexturePart pei_part2(74, -10, 148, 12, "qdt_bed_pei_bottom.svg"); BedTextureInfo::TexturePart pte_part1(10, 52, 8.393f, 192, "qdt_bed_pte_left.svg"); BedTextureInfo::TexturePart pte_part2(74, -10, 148, 12, "qdt_bed_pte_bottom.svg"); - + auto bed_texture_maps = wxGetApp().plater()->get_bed_texture_maps(); + std::string bottom_texture_end_name = bed_texture_maps.find("bottom_texture_end_name") != bed_texture_maps.end() ? bed_texture_maps["bottom_texture_end_name"] : ""; + std::string bottom_texture_rect_str = bed_texture_maps.find("bottom_texture_rect") != bed_texture_maps.end() ? bed_texture_maps["bottom_texture_rect"] : ""; + std::string middle_texture_rect_str = bed_texture_maps.find("middle_texture_rect") != bed_texture_maps.end() ? bed_texture_maps["middle_texture_rect"] : ""; + std::array bottom_texture_rect = {0, 0, 0, 0}, middle_texture_rect = {0, 0, 0, 0}; + if (bottom_texture_rect_str.size() > 0) { + std::vector items; + boost::algorithm::erase_all(bottom_texture_rect_str, " "); + boost::split(items, bottom_texture_rect_str, boost::is_any_of(",")); + if (items.size() == 4) { + for (int i = 0; i < items.size(); i++) { + bottom_texture_rect[i] = std::atof(items[i].c_str()); + } + } + } + if (middle_texture_rect_str.size() > 0) { + std::vector items; + boost::algorithm::erase_all(middle_texture_rect_str, " "); + boost::split(items, middle_texture_rect_str, boost::is_any_of(",")); + if (items.size() == 4) { + for (int i = 0; i < items.size(); i++) { + middle_texture_rect[i] = std::atof(items[i].c_str()); + } + } + } auto is_single_extruder = wxGetApp().preset_bundle->get_printer_extruder_count() == 1; if (!is_single_extruder) { m_allow_bed_type_in_double_nozzle.clear(); pte_part1 = BedTextureInfo::TexturePart(57, 300, 236.12f, 10.f, "qdt_bed_pte_middle.svg"); + auto &middle_rect = middle_texture_rect; + if (middle_rect[2] > 0.f) { + pte_part1 = BedTextureInfo::TexturePart(middle_rect[0], middle_rect[1], middle_rect[2], middle_rect[3], "qdt_bed_pte_middle.svg"); + } pte_part2 = BedTextureInfo::TexturePart(45, -14.5, 70, 8, "qdt_bed_pte_left_bottom.svg"); - + auto &bottom_rect = bottom_texture_rect; + if (bottom_texture_end_name.size() > 0 && bottom_rect[2] > 0.f) { + std::string pte_part2_name = "qdt_bed_pte_bottom_" + bottom_texture_end_name + ".svg"; + pte_part2 = BedTextureInfo::TexturePart(bottom_rect[0], bottom_rect[1], bottom_rect[2], bottom_rect[3], pte_part2_name); + } pei_part1 = BedTextureInfo::TexturePart(57, 300, 236.12f, 10.f, "qdt_bed_pei_middle.svg"); + if (middle_rect[2] > 0.f) { + pei_part1 = BedTextureInfo::TexturePart(middle_rect[0], middle_rect[1], middle_rect[2], middle_rect[3], "qdt_bed_pte_middle.svg"); + } pei_part2 = BedTextureInfo::TexturePart(45, -14.5, 70, 8, "qdt_bed_pei_left_bottom.svg"); + if (bottom_texture_end_name.size() > 0 && bottom_rect[2] > 0.f) { + std::string pei_part2_name = "qdt_bed_pei_bottom_" + bottom_texture_end_name + ".svg"; + pei_part2 = BedTextureInfo::TexturePart(bottom_rect[0], bottom_rect[1], bottom_rect[2], bottom_rect[3], pei_part2_name); + } m_allow_bed_type_in_double_nozzle[(int) btPEI] = true; m_allow_bed_type_in_double_nozzle[(int) btPTE] = true; } @@ -6166,8 +6212,8 @@ void PartPlateList::init_bed_type_info() float base_width = 256;//standard 256*256 for single_extruder float base_height = 256; if (!is_single_extruder) {//standard 350*325 for double_extruder - base_width = 350; - base_height = 320; + base_width = bed_width; + base_height = bed_height; } float x_rate = bed_width / base_width; float y_rate = bed_height / base_height; diff --git a/src/slic3r/GUI/PartPlate.hpp b/src/slic3r/GUI/PartPlate.hpp index 8a79f81..6577133 100644 --- a/src/slic3r/GUI/PartPlate.hpp +++ b/src/slic3r/GUI/PartPlate.hpp @@ -152,16 +152,15 @@ private: void init(); bool valid_instance(int obj_id, int instance_id); - void generate_print_polygon(ExPolygon &print_polygon); void generate_exclude_polygon(ExPolygon &exclude_polygon); void generate_logo_polygon(ExPolygon &logo_polygon); void generate_logo_polygon(ExPolygon &logo_polygon,const BoundingBoxf3& box); void calc_bounding_boxes() const; void calc_height_limit(); + int get_right_icon_offset_bed(); void calc_vertex_for_plate_name(GLTexture &texture, GLModel &buffer); void calc_vertex_for_plate_name_edit_icon(GLTexture *texture, int index, GLModel &buffer); - void calc_vertex_for_icons_background(int icon_count, GeometryBuffer &buffer); bool calc_bed_3d_boundingbox(BoundingBoxf3 & box_in_plate_origin); void render_logo(bool bottom, bool render_cali = true); void render_logo_texture(GLTexture &logo_texture, GLModel &logo_buffer, bool bottom); @@ -318,6 +317,7 @@ public: bool check_filament_printable(const DynamicPrintConfig & config, wxString& error_message); bool check_tpu_printable_status(const DynamicPrintConfig & config, const std::vector &tpu_filaments); bool check_mixture_of_pla_and_petg(const DynamicPrintConfig & config); + bool check_compatible_of_nozzle_and_filament(const DynamicPrintConfig & config, const std::vector& filament_presets, std::string& error_msg); /* instance related operations*/ //judge whether instance is bound in plate or not diff --git a/src/slic3r/GUI/PartSkipCommon.hpp b/src/slic3r/GUI/PartSkipCommon.hpp new file mode 100644 index 0000000..bd158ed --- /dev/null +++ b/src/slic3r/GUI/PartSkipCommon.hpp @@ -0,0 +1,18 @@ +#ifndef PARTSKIPCOMMON_H +#define PARTSKIPCOMMON_H + + +namespace Slic3r { namespace GUI { + +enum PartState { + psUnCheck, + psChecked, + psSkipped +}; + + +typedef std::vector> PartsInfo; + +}} + +#endif // PARTSKIPCOMMON_H \ No newline at end of file diff --git a/src/slic3r/GUI/PartSkipDialog.cpp b/src/slic3r/GUI/PartSkipDialog.cpp new file mode 100644 index 0000000..698a7d8 --- /dev/null +++ b/src/slic3r/GUI/PartSkipDialog.cpp @@ -0,0 +1,1048 @@ +#include "GUI_Utils.hpp" +#include "GUI_App.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Widgets/CheckBox.hpp" +#include "Widgets/Label.hpp" + +#include "MsgDialog.hpp" +#include "Printer/PrinterFileSystem.h" +#include "PartSkipDialog.hpp" +#include "SkipPartCanvas.hpp" +#include "MediaPlayCtrl.h" + +namespace Slic3r { namespace GUI { + +extern wxString hide_passwd(wxString url, std::vector const &passwords); +extern void refresh_agora_url(char const *device, char const *dev_ver, char const *channel, void *context, void (*callback)(void *context, char const *url)); + +StateColor percent_bg(std::pair(wxColour(255, 255, 255), StateColor::Disabled), + std::pair(wxColour(255, 255, 255), StateColor::Pressed), + std::pair(wxColour(255, 255, 255), StateColor::Hovered), + std::pair(wxColour(255, 255, 255), StateColor::Enabled), + std::pair(wxColour(255, 255, 255), StateColor::Normal)); + +static StateColor zoom_bg(std::pair(wxColour(255, 255, 255), StateColor::Disabled), + std::pair(wxColour(206, 206, 206), StateColor::Pressed), + std::pair(wxColour(238, 238, 238), StateColor::Hovered), + std::pair(wxColour(255, 255, 255), StateColor::Enabled), + std::pair(wxColour(255, 255, 255), StateColor::Normal)); + +static StateColor zoom_bd(std::pair(wxColour(144, 144, 144), StateColor::Disabled), std::pair(wxColour(38, 46, 48), StateColor::Enabled)); +static StateColor zoom_text(std::pair(wxColour(144, 144, 144), StateColor::Disabled), std::pair(wxColour(38, 46, 48), StateColor::Enabled)); + +static StateColor btn_bg_white(std::pair(wxColour(206, 206, 206), StateColor::Pressed), + std::pair(wxColour(238, 238, 238), StateColor::Hovered), + std::pair(*wxWHITE, StateColor::Normal)); + +static StateColor btn_bg_gray(std::pair(wxColour(194, 194, 194), StateColor::Pressed), + std::pair(wxColour(194, 194, 194), StateColor::Hovered), + std::pair(wxColour(194, 194, 194), StateColor::Normal)); + +//y +static StateColor btn_bg_blue(std::pair(wxColour(206, 206, 206), StateColor::Disabled), + std::pair(wxColour(40, 90, 220), StateColor::Pressed), + std::pair(wxColour(100, 150, 255), StateColor::Hovered), + std::pair(wxColour(68, 121, 251), StateColor::Normal)); + +PartSkipDialog::PartSkipDialog(wxWindow *parent) : DPIDialog(parent, wxID_ANY, _L("Skip Objects"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX) +{ + std::time_t t = std::time(0); + std::stringstream buf; + buf << put_time(std::localtime(&t), "%a_%b_%d_%H_%M_%S/"); + m_timestamp = buf.str(); + + std::string icon_path = (boost::format("%1%/images/QIDIStudioTitle.ico") % Slic3r::resources_dir()).str(); + SetIcon(wxIcon(Slic3r::encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO)); + SetBackgroundColour(*wxWHITE); + + m_sizer = new wxBoxSizer(wxVERTICAL); + + m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL); + m_line_top->SetMinSize(wxSize(-1, 1)); + m_line_top->SetBackgroundColour(wxColour(0xA6, 0xa9, 0xAA)); + m_sizer->Add(m_line_top, 0, wxEXPAND | wxTOP, FromDIP(0)); + + m_simplebook = new wxSimplebook(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0); + m_simplebook->SetMinSize(wxSize(FromDIP(720), FromDIP(535))); + m_simplebook->SetMaxSize(wxSize(FromDIP(720), FromDIP(535))); + m_book_first_panel = new wxPanel(m_simplebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + m_book_third_panel = new wxPanel(m_simplebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + m_book_third_panel->SetBackgroundColour(*wxWHITE); + m_dlg_sizer = new wxBoxSizer(wxVERTICAL); + m_dlg_content_sizer = new wxBoxSizer(wxHORIZONTAL); + m_canvas_sizer = new wxBoxSizer(wxVERTICAL); + + // page 3 + wxGLAttributes canvasAttrs; + canvasAttrs.PlatformDefaults().Defaults().Stencil(8).EndList(); + m_canvas = new SkipPartCanvas(m_book_third_panel, canvasAttrs); + m_canvas->SetMinSize(wxSize(FromDIP(400), FromDIP(400))); + m_canvas->SetMaxSize(wxSize(FromDIP(400), FromDIP(400))); + + m_canvas_btn_sizer = new wxBoxSizer(wxHORIZONTAL); + m_canvas_btn_sizer->SetMinSize(wxSize(-1, FromDIP(28))); + + zoom_bg.setTakeFocusedAsHovered(false); + + m_zoom_out_btn = new Button(m_book_third_panel, wxEmptyString); + m_zoom_out_btn->SetIcon("canvas_zoom_out"); + m_zoom_out_btn->SetToolTip(_L("Zoom Out")); + m_zoom_out_btn->SetBackgroundColor(zoom_bg); + m_zoom_out_btn->SetBorderColor(wxColour(238, 238, 238)); + m_zoom_out_btn->SetCornerRadius(0); + m_zoom_out_btn->SetMinSize(wxSize(FromDIP(56), FromDIP(28))); + + m_percent_label = new Button(m_book_third_panel, _L("100 %")); + m_percent_label->SetBackgroundColor(percent_bg); + m_percent_label->SetBorderColor(wxColour(238, 238, 238)); + m_percent_label->SetMinSize(wxSize(FromDIP(56), FromDIP(28))); + m_percent_label->SetCornerRadius(0); + + m_zoom_in_btn = new Button(m_book_third_panel, wxEmptyString); + m_zoom_in_btn->SetIcon("canvas_zoom_in"); + m_zoom_in_btn->SetToolTip(_L("Zoom In")); + m_zoom_in_btn->SetBackgroundColor(zoom_bg); + m_zoom_in_btn->SetBorderColor(wxColour(238, 238, 238)); + m_zoom_in_btn->SetMinSize(wxSize(FromDIP(56), FromDIP(28))); + m_zoom_in_btn->SetCornerRadius(0); + + m_switch_drag_btn = new Button(m_book_third_panel, wxEmptyString); + m_switch_drag_btn->SetIcon("canvas_drag"); + m_switch_drag_btn->SetToolTip(_L("Drag")); + m_switch_drag_btn->SetBackgroundColor(*wxWHITE); + m_switch_drag_btn->SetBorderColor(wxColour(238, 238, 238)); + m_switch_drag_btn->SetMinSize(wxSize(FromDIP(56), FromDIP(28))); + m_switch_drag_btn->SetCornerRadius(0); + + m_canvas_btn_sizer->Add(m_zoom_out_btn, 0, wxEXPAND | wxLEFT, FromDIP(105)); + m_canvas_btn_sizer->Add(m_percent_label, 0, wxEXPAND, 0); + m_canvas_btn_sizer->Add(m_zoom_in_btn, 0, wxEXPAND, 0); + m_canvas_btn_sizer->Add(m_switch_drag_btn, 0, wxEXPAND | wxRIGHT, FromDIP(88)); + + m_list_sizer = new wxBoxSizer(wxVERTICAL); + m_list_sizer->SetMinSize(wxSize(FromDIP(267), FromDIP(422))); + + auto all_checkbox_sizer = new wxBoxSizer(wxHORIZONTAL); + m_all_checkbox = new CheckBox(m_book_third_panel, wxID_ANY); + m_all_checkbox->SetValue(false); + m_all_checkbox->SetMinSize(wxSize(FromDIP(18), FromDIP(18))); + m_all_checkbox->SetBackgroundColour(*wxWHITE); + m_all_label = new Label(m_book_third_panel, _L("Select All")); + m_all_label->Wrap(-1); + m_all_label->SetMinSize(wxSize(-1, FromDIP(18))); + m_all_label->SetBackgroundColour(*wxWHITE); + + m_all_label->SetMinSize(wxSize(267, -1)); + m_all_label->SetMaxSize(wxSize(267, -1)); + all_checkbox_sizer->Add(m_all_checkbox, 0, wxALIGN_CENTER_VERTICAL, 0); + all_checkbox_sizer->Add(m_all_label, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, FromDIP(8)); + + m_line = new wxPanel(m_book_third_panel, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(267), FromDIP(1)), wxTAB_TRAVERSAL); + m_line->SetMinSize(wxSize(FromDIP(267), 1)); + m_line->SetMaxSize(wxSize(FromDIP(267), 1)); + m_line->SetBackgroundColour(wxColor(238, 238, 238)); + + m_list_view = new wxScrolledWindow(m_book_third_panel, wxID_ANY, wxDefaultPosition, wxSize(267, -1), wxHSCROLL | wxVSCROLL); + m_list_view->SetScrollRate(5, 5); + m_list_view->SetMinSize(wxSize(FromDIP(267), FromDIP(378))); + m_list_view->SetMaxSize(wxSize(FromDIP(267), FromDIP(378))); + m_list_view->SetBackgroundColour(*wxWHITE); + + m_scroll_sizer = new wxBoxSizer(wxVERTICAL); + m_list_view->SetSizer(m_scroll_sizer); + m_list_view->Layout(); + + m_dlg_btn_sizer = new wxBoxSizer(wxHORIZONTAL); + m_dlg_btn_sizer->SetMinSize(wxSize(-1, FromDIP(54))); + + m_cnt_label = new Label(m_book_third_panel, wxEmptyString); + m_cnt_label->Wrap(-1); + m_cnt_label->SetBackgroundColour(*wxWHITE); + m_cnt_label->SetForegroundColour(wxColour(0, 174, 66)); + m_cnt_label->SetFont(Label::Head_16); + m_cnt_label->SetSize(wxSize(-1, FromDIP(20))); + m_cnt_label->SetMaxSize(wxSize(-1, FromDIP(20))); + + m_tot_label = new Label(m_book_third_panel, wxEmptyString); + m_tot_label->Wrap(-1); + m_tot_label->SetBackgroundColour(*wxWHITE); + m_tot_label->SetMinSize(wxSize(FromDIP(200), FromDIP(20))); + + m_apply_btn = new Button(m_book_third_panel, _L("Skip")); + m_apply_btn->SetBackgroundColor(btn_bg_gray); + m_apply_btn->SetTextColor(*wxWHITE); + // m_apply_btn->SetBorderColor(wxColour(38, 46, 48)); + m_apply_btn->SetFont(Label::Body_14); + m_apply_btn->SetSize(wxSize(FromDIP(80), FromDIP(32))); + m_apply_btn->SetMinSize(wxSize(FromDIP(80), FromDIP(32))); + m_apply_btn->SetCornerRadius(FromDIP(16)); + m_apply_btn->SetToolTip(wxEmptyString); + + m_canvas_sizer->Add(m_canvas, 0, wxLEFT | wxTOP | wxEXPAND, FromDIP(17)); + m_canvas_sizer->Add(m_canvas_btn_sizer, 0, wxTOP, FromDIP(8)); + m_list_sizer->Add(0, 0, 1, wxEXPAND, 0); + + m_list_sizer->Add(0, 0, 0, wxTOP, FromDIP(27)); + m_list_sizer->Add(all_checkbox_sizer, 0, wxLEFT, FromDIP(0)); + m_list_sizer->Add(m_line, 0, wxEXPAND | wxTOP | wxBOTTOM, FromDIP(12)); + m_list_sizer->Add(m_list_view, 0, wxEXPAND, 0); + m_list_sizer->Add(0, 0, 1, wxEXPAND, 0); + + m_dlg_content_sizer->Add(m_canvas_sizer, 0, wxEXPAND, FromDIP(0)); + m_dlg_content_sizer->Add(0, 0, 0, wxEXPAND | wxLEFT, FromDIP(22)); + m_dlg_content_sizer->Add(m_list_sizer, 0, wxRIGHT | wxEXPAND, FromDIP(14)); + + m_dlg_btn_sizer->Add(0, 0, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, FromDIP(21)); + m_dlg_btn_sizer->Add(m_cnt_label, 0, wxALIGN_CENTER_VERTICAL, FromDIP(0)); +#ifdef __WXMSW__ + m_dlg_btn_sizer->Add(m_tot_label, 0, wxLEFT | wxTOP | wxALIGN_CENTER_VERTICAL, FromDIP(2)); +#else + m_dlg_btn_sizer->Add(m_tot_label, 0, wxALIGN_CENTER_VERTICAL, 0); +#endif + m_dlg_btn_sizer->Add(0, 0, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL, FromDIP(0)); + m_dlg_btn_sizer->Add(m_apply_btn, 0, wxALIGN_CENTER_VERTICAL, FromDIP(0)); + m_dlg_btn_sizer->Add(0, 0, 0, wxLEFT | wxEXPAND | wxALIGN_CENTER_VERTICAL, FromDIP(24)); + + m_dlg_placeholder = new wxPanel(m_book_third_panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + m_dlg_placeholder->SetMinSize(wxSize(-1, FromDIP(15))); + m_dlg_placeholder->SetMaxSize(wxSize(-1, FromDIP(15))); + m_dlg_placeholder->SetBackgroundColour(*wxWHITE); + + m_dlg_sizer->Add(m_dlg_content_sizer, 1, wxEXPAND, FromDIP(0)); + // m_dlg_sizer->Add( 0, 0, 1, wxEXPAND, FromDIP(0)); + m_dlg_sizer->Add(m_dlg_btn_sizer, 0, wxEXPAND, FromDIP(0)); + m_dlg_sizer->Add(m_dlg_placeholder, 0, 0, wxEXPAND, 0); + + m_book_third_panel->SetSizer(m_dlg_sizer); + m_book_third_panel->Layout(); + m_dlg_sizer->Fit(m_book_third_panel); + + // page 2 + m_book_second_panel = new wxPanel(m_simplebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + m_book_second_panel->SetBackgroundColour(*wxWHITE); + m_book_second_sizer = new wxBoxSizer(wxVERTICAL); + m_book_second_btn_sizer = new wxBoxSizer(wxHORIZONTAL); + + m_retry_bitmap = new wxStaticBitmap(m_book_second_panel, -1, create_scaled_bitmap("partskip_retry", m_book_second_panel, 200), wxDefaultPosition, wxDefaultSize); + m_retry_label = new Label(m_book_second_panel, _L("Load skipping objects information failed. Please try again.")); + m_retry_label->Wrap(-1); + m_retry_label->SetBackgroundColour(*wxWHITE); + m_book_second_sizer->Add(0, 0, 1, wxEXPAND, 0); + m_book_second_sizer->Add(m_retry_bitmap, 0, wxALIGN_CENTER_HORIZONTAL, 0); + m_book_second_sizer->Add(m_retry_label, 0, wxALIGN_CENTER_HORIZONTAL, 0); + m_book_second_sizer->Add(0, 0, 1, wxEXPAND, 0); + + m_second_retry_btn = new Button(m_book_second_panel, _L("Retry")); + m_second_retry_btn->SetBackgroundColor(btn_bg_blue); + // m_second_retry_btn->SetBorderColor(wxColour(38, 46, 48)); + m_second_retry_btn->SetTextColor(*wxWHITE); + m_second_retry_btn->SetFont(Label::Body_14); + m_second_retry_btn->SetSize(wxSize(FromDIP(80), FromDIP(32))); + m_second_retry_btn->SetMinSize(wxSize(FromDIP(80), FromDIP(32))); + m_second_retry_btn->SetCornerRadius(FromDIP(16)); + m_second_retry_btn->Bind(wxEVT_BUTTON, &PartSkipDialog::OnRetryButton, this); + m_book_second_btn_sizer->Add(m_second_retry_btn, 0, wxALL, FromDIP(24)); + + m_book_second_sizer->Add(m_book_second_btn_sizer, 0, wxALIGN_RIGHT, 0); + m_book_second_panel->SetSizer(m_book_second_sizer); + m_book_second_panel->Layout(); + m_book_second_sizer->Fit(m_book_second_panel); + + // page 1 + m_book_first_panel = new wxPanel(m_simplebook, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL); + m_book_first_sizer = new wxBoxSizer(wxVERTICAL); + // m_book_first_sizer->SetMinSize(wxSize(FromDIP(720), FromDIP(500))); + + auto m_loading_sizer = new wxBoxSizer(wxHORIZONTAL); + std::vector list{"ams_rfid_1", "ams_rfid_2", "ams_rfid_3", "ams_rfid_4"}; + m_loading_icon = new AnimaIcon(m_book_first_panel, wxID_ANY, list, "refresh_printer", 100); + m_loading_icon->SetMinSize(wxSize(FromDIP(25), FromDIP(25))); + + m_loading_label = new Label(m_book_first_panel, _L("Loading ...")); + m_loading_label->Wrap(-1); + m_loading_label->SetBackgroundColour(*wxWHITE); + + m_loading_sizer->Add(m_loading_icon, 0, wxALIGN_CENTER_VERTICAL, FromDIP(0)); + m_loading_sizer->Add(m_loading_label, 0, wxLEFT | wxALIGN_CENTER_VERTICAL, FromDIP(5)); + + m_book_first_sizer->Add(0, 0, 1, wxEXPAND, 0); + m_book_first_sizer->Add(m_loading_sizer, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_CENTER_HORIZONTAL, 0); + m_book_first_sizer->Add(0, 0, 1, wxEXPAND, 0); + + m_book_first_panel->SetSizer(m_book_first_sizer); + m_book_first_panel->Layout(); + m_book_first_sizer->Fit(m_book_first_panel); + + m_simplebook->AddPage(m_book_first_panel, _("loading page"), false); + m_simplebook->AddPage(m_book_second_panel, _("retry page"), false); + m_simplebook->AddPage(m_book_third_panel, _("dialog page"), false); + m_sizer->Add(m_simplebook, 1, wxEXPAND | wxALL, 5); + + SetSizer(m_sizer); + m_zoom_in_btn->Bind(wxEVT_BUTTON, &PartSkipDialog::OnZoomIn, this); + m_zoom_out_btn->Bind(wxEVT_BUTTON, &PartSkipDialog::OnZoomOut, this); + m_switch_drag_btn->Bind(wxEVT_BUTTON, &PartSkipDialog::OnSwitchDrag, this); + m_canvas->Bind(EVT_ZOOM_PERCENT, &PartSkipDialog::OnZoomPercent, this); + m_canvas->Bind(EVT_CANVAS_PART, &PartSkipDialog::UpdatePartsStateFromCanvas, this); + + m_apply_btn->Bind(wxEVT_BUTTON, &PartSkipDialog::OnApplyDialog, this); + m_all_checkbox->Bind(wxEVT_TOGGLEBUTTON, &PartSkipDialog::OnAllCheckbox, this); + + Layout(); + Fit(); + CentreOnParent(); +} + +PartSkipDialog::~PartSkipDialog() {} + +void PartSkipDialog::on_dpi_changed(const wxRect &suggested_rect) +{ + m_canvas->LoadPickImage(m_local_paths[0]); + + m_loading_icon->SetMinSize(wxSize(FromDIP(25), FromDIP(25))); + m_retry_bitmap->SetBitmap(create_scaled_bitmap("partskip_retry", m_book_second_panel, 200)); + + m_percent_label->SetMinSize(wxSize(FromDIP(56), FromDIP(28))); + m_percent_label->SetMaxSize(wxSize(FromDIP(56), FromDIP(28))); + m_zoom_out_btn->SetMinSize(wxSize(FromDIP(56), FromDIP(28))); + m_zoom_out_btn->SetMaxSize(wxSize(FromDIP(56), FromDIP(28))); + m_zoom_in_btn->SetMinSize(wxSize(FromDIP(56), FromDIP(28))); + m_zoom_in_btn->SetMaxSize(wxSize(FromDIP(56), FromDIP(28))); + m_switch_drag_btn->SetMinSize(wxSize(FromDIP(56), FromDIP(28))); + m_switch_drag_btn->SetMaxSize(wxSize(FromDIP(56), FromDIP(28))); + m_percent_label->Rescale(); + m_zoom_out_btn->Rescale(); + m_zoom_in_btn->Rescale(); + m_switch_drag_btn->Rescale(); + + m_cnt_label->SetMaxSize(wxSize(-1, FromDIP(20))); + + m_tot_label->SetMinSize(wxSize(FromDIP(200), FromDIP(20))); + m_tot_label->SetLabel(m_tot_label->GetLabel()); + m_tot_label->Refresh(); + + m_line_top->SetMinSize(wxSize(-1, 1)); + m_line->SetMinSize(wxSize(FromDIP(267), 1)); + m_line->SetMaxSize(wxSize(FromDIP(267), 1)); + + m_apply_btn->SetMinSize(wxSize(FromDIP(80), FromDIP(32))); + m_apply_btn->SetCornerRadius(FromDIP(16)); + m_apply_btn->Rescale(); + + m_dlg_placeholder->SetMinSize(wxSize(-1, FromDIP(15))); + m_dlg_placeholder->SetMaxSize(wxSize(-1, FromDIP(15))); + + // m_second_retry_btn->SetSize(wxSize(-1, FromDIP(32))); + m_second_retry_btn->SetMinSize(wxSize(FromDIP(80), FromDIP(32))); + m_second_retry_btn->SetCornerRadius(FromDIP(16)); + m_second_retry_btn->Rescale(); + + m_all_checkbox->SetMinSize(wxSize(FromDIP(18), FromDIP(18))); + m_all_checkbox->Rescale(); + + for (auto it = m_scroll_sizer->GetChildren().begin(); it != m_scroll_sizer->GetChildren().end(); ++it) { + wxSizerItem *item = *it; + if (item && item->IsSizer()) { + wxSizer *sizer = item->GetSizer(); + auto check_item = sizer->GetItem((size_t) 0); + if (check_item && check_item->IsWindow()) { + wxWindow *window = check_item->GetWindow(); + CheckBox *checkbox = dynamic_cast(window); + checkbox->SetMinSize(wxSize(FromDIP(18), FromDIP(18))); + checkbox->Rescale(); + } + } + } + m_canvas_btn_sizer->SetMinSize(wxSize(-1, FromDIP(28))); + m_canvas_btn_sizer->Layout(); + m_canvas_sizer->Layout(); + m_dlg_content_sizer->Layout(); + m_dlg_sizer->Layout(); + + Fit(); + Layout(); +} + +std::string PartSkipDialog::create_tmp_path() +{ + boost::filesystem::path parent_path(temporary_dir()); + + std::stringstream buf; + buf << "/bamboo_task/"; + buf << m_timestamp; + if (m_obj) { + buf << m_obj->dev_id << "_"; + buf << m_obj->job_id_ << "/"; + } else { + buf << 1 << "_" << 1 << "/"; + } + std::string tmp_path = (parent_path / buf.str()).string(); + + if (!std::filesystem::exists(tmp_path + "Metadata/") && !fs::create_directories(tmp_path + "Metadata/")) { wxMessageBox("create file failed."); } + return tmp_path; +} + +bool PartSkipDialog::is_local_file_existed(const std::vector &local_paths) +{ + for (auto path : local_paths) { + if (!std::filesystem::exists(path)) { return false; } + } + return true; +} + +void PartSkipDialog::DownloadPartsFile() +{ + BOOST_LOG_TRIVIAL(info) << "part skip: create temp path begin."; + m_tmp_path = create_tmp_path(); // wxGetApp().app_config->get("download_path"); + BOOST_LOG_TRIVIAL(info) << "part skip: create temp path end."; + + m_local_paths.clear(); + m_target_paths.clear(); + + m_plate_idx = m_obj ? m_obj->m_plate_index : -1; + if (m_plate_idx < 0) { + m_plate_idx = 1; + BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << "part skip: printer plate index is invalid."; + } + + m_local_paths.push_back(m_tmp_path + "Metadata/pick_" + std::to_string(m_plate_idx) + ".png"); + m_local_paths.push_back(m_tmp_path + "Metadata/model_settings.config"); + m_local_paths.push_back(m_tmp_path + "Metadata/slice_info.config"); + + m_target_paths.push_back("Metadata/pick_" + std::to_string(m_plate_idx) + ".png"); + m_target_paths.push_back("Metadata/model_settings.config"); + m_target_paths.push_back("Metadata/slice_info.config"); + + if (!is_local_file_existed(m_local_paths)) { + if (!m_file_sys) { + m_file_sys = boost::make_shared(); + m_file_sys->Attached(); + m_file_sys->Bind(EVT_STATUS_CHANGED, &PartSkipDialog::OnFileSystemEvent, this); + m_file_sys->Bind(EVT_RAMDOWNLOAD, &PartSkipDialog::OnFileSystemResult, this); + m_file_sys->Start(); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "part skip: print file system start."; + } else { + m_file_sys->Retry(); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "part skip: print file system retry."; + } + } else { + m_file_sys->SendExistedFile(); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "part skip: local parts info file is existed."; + } +} +// actor +void PartSkipDialog::fetchUrl(boost::weak_ptr wfs) +{ + boost::shared_ptr fs(wfs.lock()); + if (!fs) return; + + DeviceManager *dm = GUI::wxGetApp().getDeviceManager(); + MachineObject *obj = dm->get_selected_machine(); + + if (obj == nullptr) { + fs->SetUrl("0"); + return; + } + std::string dev_ver = obj->get_ota_version(); + std::string dev_id = obj->dev_id; + // int remote_proto = obj->get_file_remote(); + + NetworkAgent *agent = wxGetApp().getAgent(); + std::string agent_version = agent ? agent->get_version() : ""; + + auto url_state = m_url_state; + if (obj->is_lan_mode_printer()) { url_state = URL_TCP; } + + if (agent) { + switch (url_state) { + case URL_TCP: { + std::string devIP = obj->dev_ip; + std::string accessCode = obj->get_access_code(); + std::string tcp_url = "qidi:///local/" + devIP + "?port=6000&user=" + "qdtp" + "&passwd=" + accessCode; + CallAfter([=] { + boost::shared_ptr fs(wfs.lock()); + if (!fs) return; + if (boost::algorithm::starts_with(tcp_url, "qidi:///")) { + fs->SetUrl(tcp_url); + } else { + fs->SetUrl("3"); + } + }); + break; + } + case URL_TUTK: { + std::string protocols[] = {"", "\"tutk\"", "\"agora\"", "\"tutk\",\"agora\""}; + agent->get_camera_url(obj->dev_id + "|" + dev_ver + "|" + protocols[3], [this, wfs, m = dev_id, v = agent->get_version(), dv = dev_ver](std::string url) { + if (boost::algorithm::starts_with(url, "qidi:///")) { + url += "&device=" + m; + url += "&net_ver=" + v; + url += "&dev_ver=" + dv; + url += "&refresh_url=" + boost::lexical_cast(&refresh_agora_url); + url += "&cli_id=" + wxGetApp().app_config->get("slicer_uuid"); + url += "&cli_ver=" + std::string(SLIC3R_VERSION); + } + CallAfter([=] { + boost::shared_ptr fs(wfs.lock()); + if (!fs) return; + if (boost::algorithm::starts_with(url, "qidi:///")) { + fs->SetUrl(url); + } else { + fs->SetUrl("3"); + } + }); + }); + break; + } + default: break; + } + } +} +// controller +void PartSkipDialog::OnFileSystemEvent(wxCommandEvent &e) +{ + e.Skip(); + auto wfs = boost::weak_ptr(m_file_sys); + boost::shared_ptr fs(wfs.lock()); + if (!fs) return; + + wxString msg; + int status = e.GetInt(); + // int extra = e.GetExtraLong(); + + switch (status) { + case PrinterFileSystem::Initializing: + case PrinterFileSystem::Connecting: break; + case PrinterFileSystem::ListSyncing: { + m_file_sys->GetPickImages(m_local_paths, m_target_paths); + break; + } + case PrinterFileSystem::Failed: { + m_file_sys->Stop(); + if (m_url_state == URL_TCP) { + m_url_state = URL_TUTK; + m_file_sys->SetUrl("3"); + m_file_sys->Retry(); + BOOST_LOG_TRIVIAL(info) << "part skip: print file system connnect failed first."; + } else { + m_file_sys->SendConnectFail(); + BOOST_LOG_TRIVIAL(info) << "part skip: print file system connnect failed second."; + } + break; + } + case PrinterFileSystem::Reconnecting: break; + } + if (e.GetInt() == PrinterFileSystem::Initializing) { + CallAfter([=] { + boost::shared_ptr fs(wfs.lock()); + if (!fs) return; + fetchUrl(boost::weak_ptr(fs)); + BOOST_LOG_TRIVIAL(info) << "part skip: fetch url, get parts info files from printer."; + }); + } +} + +// reseter: [TCP -> TUTK(TCP)] -> [TCP -> TUTK(TCP)] +void PartSkipDialog::OnFileSystemResult(wxCommandEvent &event) +{ + int result = event.GetInt(); + m_loading_icon->Stop(); + if (result == 0) { + InitDialogUI(); + SetSimplebookPage(2); + m_file_sys->Stop(); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "part skip: on file system result success."; + } else { + m_url_state = URL_TCP; + SetSimplebookPage(1); + m_file_sys->Stop(); + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "part skip: on file system result failed."; + } +} + +void PartSkipDialog::InitSchedule(MachineObject *obj) +{ + m_obj = obj; + SetSimplebookPage(0); + m_loading_icon->Play(); + DownloadPartsFile(); +} + +void PartSkipDialog::OnRetryButton(wxCommandEvent &event) +{ + event.Skip(); + InitSchedule(m_obj); + Refresh(); +} + +bool PartSkipDialog::is_drag_mode() { return m_is_drag == true; } + +PartsInfo PartSkipDialog::GetPartsInfo() +{ + PartsInfo parts_info; + for (auto [part_id, part_state] : this->m_parts_state) { parts_info.push_back(std::pair(part_id, part_state)); } + return parts_info; +} + +void PartSkipDialog::OnZoomIn(wxCommandEvent &event) +{ + m_canvas->ZoomIn(20); + UpdateZoomPercent(); +} + +void PartSkipDialog::OnZoomOut(wxCommandEvent &event) +{ + m_canvas->ZoomOut(20); + UpdateZoomPercent(); +} + +void PartSkipDialog::OnSwitchDrag(wxCommandEvent &event) +{ + if (this->is_drag_mode()) { + m_is_drag = false; + m_switch_drag_btn->SetBackgroundColor(*wxWHITE); + m_switch_drag_btn->SetIcon("canvas_drag"); + } else { + m_is_drag = true; + m_switch_drag_btn->SetBackgroundColor(wxColour(0, 174, 66)); + m_switch_drag_btn->SetIcon("canvas_drag_active"); + } + m_canvas->SwitchDrag(m_is_drag); +} + +void PartSkipDialog::OnZoomPercent(wxCommandEvent &event) +{ + m_zoom_percent = event.GetInt(); + if (m_zoom_percent >= 1000) { + m_zoom_percent = 1000; + m_zoom_in_btn->Enable(false); + m_zoom_in_btn->SetIcon("canvas_zoom_in_disable"); + } else if (m_zoom_percent <= 100) { + m_zoom_percent = 100; + m_zoom_out_btn->Enable(false); + m_zoom_out_btn->SetIcon("canvas_zoom_out_disable"); + } else { + m_zoom_in_btn->Enable(true); + m_zoom_out_btn->Enable(true); + m_zoom_in_btn->SetIcon("canvas_zoom_in"); + m_zoom_out_btn->SetIcon("canvas_zoom_out"); + } + + UpdateZoomPercent(); +} + +void PartSkipDialog::UpdatePartsStateFromCanvas(wxCommandEvent &event) +{ + int part_id = event.GetExtraLong(); + PartState part_state = PartState(event.GetInt()); + + m_parts_state[part_id] = part_state; + if (part_state == psUnCheck) { m_all_checkbox->SetValue(false); } + if (IsAllChecked()) { m_all_checkbox->SetValue(true); } + + UpdateApplyButtonStatus(); + UpdateDialogUI(); +} + +void PartSkipDialog::UpdateZoomPercent() { m_percent_label->SetLabel(wxString::Format(_L("%d%%"), m_zoom_percent)); } + +void PartSkipDialog::UpdateCountLabel() +{ + int check_cnt = 0; + int tot_cnt = 0; + for (auto [part_id, part_state] : m_parts_state) { + if (part_state == PartState::psChecked) check_cnt++; + if (part_state != PartState::psSkipped) tot_cnt++; + } + m_cnt_label->SetLabel(wxString::Format(_L("%d"), check_cnt)); + m_cnt_label->Fit(); + m_tot_label->SetLabel(wxString::Format(_L("/%d Selected"), tot_cnt)); + m_tot_label->Fit(); + m_dlg_btn_sizer->Layout(); +} + +bool PartSkipDialog::Show(bool show) +{ + if (show) { + wxGetApp().UpdateDlgDarkUI(this); + CentreOnParent(); + + Layout(); + Fit(); + } + return DPIDialog::Show(show); +} + +void PartSkipDialog::InitDialogUI() +{ + m_print_lock = true; + is_model_support_partskip = false; + BOOST_LOG_TRIVIAL(info) << "part skip: lock parts info from printer."; + m_scroll_sizer->Clear(true); + m_all_checkbox->SetValue(false); + m_parts_state.clear(); + m_parts_name.clear(); + + string pick_img = m_local_paths[0]; + string slice_info = m_local_paths[2]; + + m_switch_drag_btn->SetIcon("canvas_drag"); + m_switch_drag_btn->SetBackgroundColor(*wxWHITE); + m_is_drag = false; + m_canvas->SwitchDrag(false); + m_canvas->SetZoomPercent(100); + m_canvas->SetOffset(wxPoint(0, 0)); + + BOOST_LOG_TRIVIAL(info) << "part skip: load canvas pick image begin."; + m_canvas->LoadPickImage(pick_img); + BOOST_LOG_TRIVIAL(info) << "part skip: load canvas pick image end."; + ModelSettingHelper helper(slice_info); + + if (helper.Parse()) { + is_model_support_partskip = helper.GetLabelObjectEnabled(m_plate_idx); + auto parse_result = helper.GetPlateObjects(m_plate_idx); + for (const auto &part : parse_result) { + m_parts_state[part.identify_id] = part.state; + m_parts_name[part.identify_id] = part.name; + } + if (m_obj) { + std::vector partskip_ids = m_obj->m_partskip_ids; + for (auto part_id : partskip_ids) { m_parts_state[part_id] = PartState::psSkipped; } + } + + for (const auto &[part_id, part_state] : m_parts_state) { + auto line_sizer = new wxBoxSizer(wxHORIZONTAL); + auto checkbox = new CheckBox(m_list_view); + auto label = new Label(m_list_view, wxEmptyString); + + checkbox->Bind( + wxEVT_TOGGLEBUTTON, + [this, part_id = part_id](wxCommandEvent &event) { + m_parts_state[part_id] = event.IsChecked() ? PartState::psChecked : PartState::psUnCheck; + if (!event.IsChecked()) { + m_all_checkbox->SetValue(false); + } else if (IsAllChecked()) { + m_all_checkbox->SetValue(true); + } + m_canvas->UpdatePartsInfo(GetPartsInfo()); + UpdateCountLabel(); + UpdateApplyButtonStatus(); + event.Skip(); + }, + checkbox->GetId()); + + if (part_state == PartState::psChecked) { + checkbox->SetValue(true); + checkbox->Enable(true); + } else if (part_state == PartState::psUnCheck) { + checkbox->SetValue(false); + checkbox->Enable(true); + } else if (part_state == PartState::psSkipped) { + checkbox->SetValue(true); + checkbox->Enable(false); + } + label->SetLabel(wxString::FromUTF8(m_parts_name[part_id])); + label->SetBackgroundColour(*wxWHITE); + label->SetForegroundColour(wxColor(107, 107, 107)); + label->Wrap(-1); + label->SetMinSize(wxSize(-1, FromDIP(18))); + checkbox->SetBackgroundColour(*wxWHITE); + checkbox->SetMinSize(wxSize(FromDIP(18), FromDIP(18))); + checkbox->SetMaxSize(wxSize(FromDIP(18), FromDIP(18))); + + line_sizer->Add(checkbox, 0, wxALIGN_CENTER_VERTICAL, 0); + line_sizer->Add(label, 1, wxLEFT | wxALIGN_CENTER_VERTICAL, FromDIP(8)); + m_scroll_sizer->Add(line_sizer, 0, wxBOTTOM | wxEXPAND, FromDIP(12)); + } + m_canvas->UpdatePartsInfo(GetPartsInfo()); + BOOST_LOG_TRIVIAL(info) << "part skip: update canvas parts info."; + } + + m_scroll_sizer->Layout(); + m_list_view->FitInside(); + UpdateApplyButtonStatus(); + UpdateCountLabel(); + Refresh(); + m_print_lock = false; + BOOST_LOG_TRIVIAL(info) << "part skip: unlock parts info from printer."; +} + +void PartSkipDialog::UpdatePartsStateFromPrinter(MachineObject *obj) +{ + if (m_print_lock) { + BOOST_LOG_TRIVIAL(info) << "part skip: parts info from printer is locked."; + return; + } + m_obj = obj; + if (m_obj) { + bool update_flag = false; + std::vector partskip_ids = m_obj->m_partskip_ids; + for (auto part_id : partskip_ids) { + if (m_parts_state[part_id] != PartState::psSkipped) { + m_parts_state[part_id] = PartState::psSkipped; + update_flag = true; + } + } + if (update_flag) { + m_canvas->UpdatePartsInfo(GetPartsInfo()); + UpdateDialogUI(); + } + } +} + +void PartSkipDialog::UpdateDialogUI() +{ + if (m_parts_state.size() != m_scroll_sizer->GetItemCount()) { + BOOST_LOG_TRIVIAL(warning) << "part skip: m_parts_state and m_scroll_sizer mismatch."; + return; + } + + for (auto it = m_parts_state.begin(); it != m_parts_state.end(); ++it) { + int idx = std::distance(m_parts_state.begin(), it); + auto part_state = it->second; + + wxSizerItem *item = m_scroll_sizer->GetItem(idx); + if (item && item->IsSizer()) { + wxSizer *sizer = item->GetSizer(); + auto check_item = sizer->GetItem((size_t) 0); + + if (check_item && check_item->IsWindow()) { + wxWindow *window = check_item->GetWindow(); + CheckBox *checkbox = dynamic_cast(window); + if (part_state == PartState::psChecked) { + checkbox->SetValue(true); + } else if (part_state == PartState::psUnCheck) { + checkbox->SetValue(false); + } else { + checkbox->SetValue(true); + checkbox->Enable(false); + } + } + } + } + + UpdateCountLabel(); + Refresh(); +} + +void PartSkipDialog::SetSimplebookPage(int page) { m_simplebook->SetSelection(page); } + +bool PartSkipDialog::IsAllChecked() +{ + for (auto &[part_id, part_state] : m_parts_state) { + if (part_state == PartState::psUnCheck) return false; + } + return true; +} + +bool PartSkipDialog::IsAllCancled() +{ + for (auto &[part_id, part_state] : m_parts_state) { + if (part_state == PartState::psChecked) return false; + } + return true; +} + +void PartSkipDialog::OnAllCheckbox(wxCommandEvent &event) +{ + if (m_all_checkbox->GetValue()) { + for (auto &[part_id, part_state] : m_parts_state) { + if (part_state == PartState::psUnCheck) part_state = PartState::psChecked; + } + } else { + for (auto &[part_id, part_state] : m_parts_state) { + if (part_state == PartState::psChecked) part_state = PartState::psUnCheck; + } + } + UpdateApplyButtonStatus(); + m_canvas->UpdatePartsInfo(GetPartsInfo()); + UpdateDialogUI(); + event.Skip(); +} + +void PartSkipDialog::UpdateApplyButtonStatus() +{ + if (IsAllCancled()) { + m_apply_btn->SetBackgroundColor(btn_bg_gray); + m_apply_btn->SetToolTip(_L("Nothing selected")); + m_enable_apply_btn = false; + } else if (m_parts_state.size() > 64) { + m_apply_btn->SetBackgroundColor(btn_bg_gray); + m_apply_btn->SetToolTip(_L("Over 64 objects in single plate")); + m_enable_apply_btn = false; + } else if (!is_model_support_partskip) { + m_apply_btn->SetBackgroundColor(btn_bg_gray); + m_apply_btn->SetToolTip(_L("The current print job cannot be skipped")); + m_enable_apply_btn = false; + } else { + m_apply_btn->SetBackgroundColor(btn_bg_blue); + m_apply_btn->SetToolTip(wxEmptyString); + m_enable_apply_btn = true; + } +} + +void PartSkipDialog::OnApplyDialog(wxCommandEvent &event) +{ + event.Skip(); + + if (!m_enable_apply_btn) return; + + m_partskip_ids.clear(); + for (const auto &[part_id, part_state] : m_parts_state) { + if (part_state == PartState::psChecked) { m_partskip_ids.push_back(part_id); } + } + + bool all_skipped = true; + for (auto [part_id, part_state] : m_parts_state) { + if (part_state == PartState::psUnCheck) all_skipped = false; + } + + PartSkipConfirmDialog confirm_dialog(this); + if (all_skipped) { + confirm_dialog.SetMsgLabel(_L("Skipping all objects.")); + confirm_dialog.SetTipLabel(_L("The printing job will be stopped. Continue?")); + } else { + confirm_dialog.SetMsgLabel(wxString::Format(_L("Skipping %d objects."), m_partskip_ids.size())); + confirm_dialog.SetTipLabel(_L("This action cannot be undone. Continue?")); + } + + if (confirm_dialog.ShowModal() == wxID_OK) { + if (m_obj) { + BOOST_LOG_TRIVIAL(info) << "part skip: skipping " << m_partskip_ids.size() << " objects."; + + if (all_skipped) { + m_obj->command_task_abort(); + BOOST_LOG_TRIVIAL(info) << "part skip: command skip all parts, abort task."; + } else if (m_partskip_ids.size() > 0) { + m_obj->command_task_partskip(m_partskip_ids); + BOOST_LOG_TRIVIAL(info) << "part skip: command skip " << m_partskip_ids.size() << " parts."; + } + EndModal(wxID_OK); + } else { + BOOST_LOG_TRIVIAL(warning) << "part skip: machine object is null."; + } + } +} + +int PartSkipDialog::GetAllSkippedPartsNum() +{ + int skipped_cnt = 0; + for (auto &[part_id, part_state] : m_parts_state) { + if (part_state == PartState::psSkipped || part_state == PartState::psChecked) skipped_cnt++; + } + return skipped_cnt; +} + +PartSkipConfirmDialog::PartSkipConfirmDialog(wxWindow *parent) : DPIDialog(parent, wxID_ANY, _L("Skip Objects"), wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX) +{ + std::string icon_path = (boost::format("%1%/images/QIDIStudioTitle.ico") % Slic3r::resources_dir()).str(); + SetIcon(wxIcon(Slic3r::encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO)); + SetBackgroundColour(*wxWHITE); + SetMinSize(wxSize(FromDIP(480), FromDIP(215))); + SetSizeHints(wxDefaultSize, wxDefaultSize); + + wxBoxSizer *m_sizer; + m_sizer = new wxBoxSizer(wxVERTICAL); + + auto m_line_top = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(-1, 1), wxTAB_TRAVERSAL); + m_line_top->SetMinSize(wxSize(FromDIP(480), 1)); + m_line_top->SetMaxSize(wxSize(FromDIP(480), 1)); + m_line_top->SetBackgroundColour(wxColour(0xA6, 0xa9, 0xAA)); + m_sizer->Add(m_line_top, 0, wxEXPAND, 0); + m_sizer->Add(0, 0, 0, wxALL, FromDIP(15)); + + m_msg_label = new Label(this, _L("Skipping objects.")); + m_msg_label->Wrap(-1); + m_msg_label->SetBackgroundColour(*wxWHITE); + + m_tip_label = new Label(this, _L("This action cannot be undone. Continue?")); + m_tip_label->Wrap(-1); + m_tip_label->SetBackgroundColour(*wxWHITE); + m_tip_label->SetForegroundColour(wxColor(92, 92, 92)); + + m_sizer->Add(m_msg_label, 0, wxLEFT, FromDIP(29)); + m_sizer->Add(0, 0, 0, wxTOP, FromDIP(9)); + m_sizer->Add(m_tip_label, 0, wxLEFT, FromDIP(29)); + + wxBoxSizer *m_button_sizer; + m_button_sizer = new wxBoxSizer(wxHORIZONTAL); + m_button_sizer->SetMinSize(wxSize(FromDIP(480), FromDIP(54))); + m_button_sizer->Add(0, 0, 1, wxEXPAND, 0); + + StateColor btn_bg_white(std::pair(wxColour(206, 206, 206), StateColor::Pressed), std::pair(wxColour(238, 238, 238), StateColor::Hovered), + std::pair(*wxWHITE, StateColor::Normal)); + + //y + StateColor btn_bg_blue(std::pair(wxColour(206, 206, 206), StateColor::Disabled), + std::pair(wxColour(40, 90, 220), StateColor::Pressed), + std::pair(wxColour(100, 150, 255), StateColor::Hovered), + std::pair(wxColour(68, 121, 251), StateColor::Normal)); + + m_apply_button = new Button(this, _L("Continue")); + m_apply_button->SetBackgroundColor(btn_bg_blue); + m_apply_button->SetTextColor(*wxWHITE); + // m_apply_button->SetBorderColor(wxColour(38, 46, 48)); + m_apply_button->SetFont(Label::Body_14); + m_apply_button->SetSize(wxSize(FromDIP(80), FromDIP(32))); + m_apply_button->SetMinSize(wxSize(FromDIP(80), FromDIP(32))); + m_apply_button->SetCornerRadius(FromDIP(16)); + m_apply_button->Bind(wxEVT_BUTTON, [this](auto &e) { + EndModal(wxID_OK); + e.Skip(); + }); + + m_button_sizer->Add(m_apply_button, 0, wxRIGHT | wxBOTTOM, FromDIP(24)); + m_sizer->Add(m_button_sizer, 1, wxEXPAND, FromDIP(5)); + m_sizer->Fit(this); + + SetSizer(m_sizer); + Layout(); + Fit(); +} + +PartSkipConfirmDialog::~PartSkipConfirmDialog() {} + +bool PartSkipConfirmDialog::Show(bool show) +{ + if (show) { + wxGetApp().UpdateDlgDarkUI(this); + CentreOnParent(); + + Layout(); + Fit(); + } + return DPIDialog::Show(show); +} + +void PartSkipConfirmDialog::on_dpi_changed(const wxRect &suggested_rect) +{ + m_apply_button->SetMinSize(wxSize(FromDIP(80), FromDIP(32))); + m_apply_button->SetCornerRadius(FromDIP(16)); + m_apply_button->Rescale(); + Layout(); + Fit(); +} + +Button *PartSkipConfirmDialog::GetConfirmButton() { return m_apply_button; } + +void PartSkipConfirmDialog::SetMsgLabel(wxString msg) { m_msg_label->SetLabel(msg); } + +void PartSkipConfirmDialog::SetTipLabel(wxString msg) { m_tip_label->SetLabel(msg); } + +}} // namespace Slic3r::GUI diff --git a/src/slic3r/GUI/PartSkipDialog.hpp b/src/slic3r/GUI/PartSkipDialog.hpp new file mode 100644 index 0000000..4f0b5bf --- /dev/null +++ b/src/slic3r/GUI/PartSkipDialog.hpp @@ -0,0 +1,163 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Widgets/Label.hpp" +#include "Widgets/CheckBox.hpp" +#include "Widgets/Button.hpp" +#include "Widgets/AnimaController.hpp" +#include "DeviceManager.hpp" +#include "PartSkipCommon.hpp" +#include "Printer/PrinterFileSystem.h" +#include "I18N.hpp" +#include "GUI_Utils.hpp" + + +namespace Slic3r { namespace GUI { + +class SkipPartCanvas; + +enum URL_STATE { + URL_TCP, + URL_TUTK, +}; + +class PartSkipConfirmDialog : public DPIDialog +{ +private: +protected: + Label *m_msg_label; + Label *m_tip_label; + Button *m_apply_button; + +public: + PartSkipConfirmDialog(wxWindow *parent); + ~PartSkipConfirmDialog(); + + void on_dpi_changed(const wxRect &suggested_rect); + Button *GetConfirmButton(); + void SetMsgLabel(wxString msg); + void SetTipLabel(wxString msg); + bool Show(bool show); +}; + +class PartSkipDialog : public DPIDialog +{ +public: + PartSkipDialog(wxWindow *parent); + ~PartSkipDialog(); + void on_dpi_changed(const wxRect &suggested_rect); + bool Show(bool show); + + void UpdatePartsStateFromPrinter(MachineObject *obj_); + void SetSimplebookPage(int page); + void InitSchedule(MachineObject *obj_); + void InitDialogUI(); + int GetAllSkippedPartsNum(); + + MachineObject *m_obj{nullptr}; + + wxSimplebook *m_simplebook; + wxPanel *m_book_third_panel; + wxPanel *m_book_second_panel; + wxPanel *m_book_first_panel; + + SkipPartCanvas *m_canvas; + Button *m_zoom_in_btn; + Button *m_zoom_out_btn; + Button *m_switch_drag_btn; + CheckBox *m_all_checkbox; + Button *m_percent_label; + Label *m_all_label; + wxPanel *m_line; + wxPanel *m_line_top; + wxScrolledWindow *m_list_view; + + wxPanel *m_dlg_placeholder; + Label *m_cnt_label; + Label *m_tot_label; + + Button *m_apply_btn; + + Label *m_loading_label; + Label *m_retry_label; + ScalableBitmap *m_retry_icon; + wxStaticBitmap *m_retry_bitmap; + + wxBoxSizer *m_sizer; + wxBoxSizer *m_dlg_sizer; + wxBoxSizer *m_dlg_content_sizer; + wxBoxSizer *m_dlg_btn_sizer; + wxBoxSizer *m_canvas_sizer; + wxBoxSizer *m_canvas_btn_sizer; + wxBoxSizer *m_list_sizer; + wxBoxSizer *m_scroll_sizer; + wxBoxSizer *m_book_first_sizer; + wxBoxSizer *m_book_second_sizer; + wxBoxSizer *m_book_second_btn_sizer; + Button *m_second_retry_btn; + AnimaIcon *m_loading_icon; + +private: + int m_plate_idx{-1}; + int m_zoom_percent{100}; + bool m_is_drag{false}; + bool m_print_lock{true}; + bool m_enable_apply_btn{false}; + bool is_model_support_partskip{false}; + + std::map m_parts_state; + std::map m_parts_name; + std::vector m_partskip_ids; + + enum URL_STATE m_url_state = URL_STATE::URL_TCP; + + PartsInfo GetPartsInfo(); + bool is_drag_mode(); + + boost::shared_ptr m_file_sys; + bool m_file_sys_result{false}; + std::string m_timestamp; + std::string m_tmp_path; + std::vector m_local_paths; + std::vector m_target_paths; + std::string create_tmp_path(); + + bool is_local_file_existed(const std::vector &local_paths); + + void DownloadPartsFile(); + void OnFileSystemEvent(wxCommandEvent &event); + void OnFileSystemResult(wxCommandEvent &event); + void fetchUrl(boost::weak_ptr wfs); + + void OnZoomIn(wxCommandEvent &event); + void OnZoomOut(wxCommandEvent &event); + void OnSwitchDrag(wxCommandEvent &event); + void OnZoomPercent(wxCommandEvent &event); + void UpdatePartsStateFromCanvas(wxCommandEvent &event); + + void UpdateZoomPercent(); + void UpdateCountLabel(); + void UpdateDialogUI(); + void UpdateApplyButtonStatus(); + bool IsAllChecked(); + bool IsAllCancled(); + + void OnRetryButton(wxCommandEvent &event); + void OnAllCheckbox(wxCommandEvent &event); + void OnApplyDialog(wxCommandEvent &event); +}; + +}} // namespace Slic3r::GUI \ No newline at end of file diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 89a07ba..ec09321 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -203,10 +203,8 @@ wxDEFINE_EVENT(EVT_ADD_CUSTOM_FILAMENT, ColorEvent); wxDEFINE_EVENT(EVT_NOTICE_CHILDE_SIZE_CHANGED, SimpleEvent); wxDEFINE_EVENT(EVT_NOTICE_FULL_SCREEN_CHANGED, IntEvent); #define PRINTER_THUMBNAIL_SIZE (wxSize(FromDIP(48), FromDIP(48))) -#define PRINTER_THUMBNAIL_SIZE_SMALL (wxSize(FromDIP(32), FromDIP(32))) -#define PRINTER_PANEL_SIZE_SMALL (wxSize(FromDIP(98), FromDIP(68))) -#define PRINTER_PANEL_SIZE_WIDEN (wxSize(FromDIP(136), FromDIP(68))) -#define PRINTER_PANEL_SIZE (wxSize(FromDIP(98), FromDIP(98))) +#define PRINTER_PANEL_SIZE (wxSize(FromDIP(96), FromDIP(96))) +#define BTN_SYNC_SIZE (wxSize(FromDIP(96), FromDIP(98))) static string get_diameter_string(float diameter) { @@ -241,6 +239,21 @@ void Plater::show_illegal_characters_warning(wxWindow* parent) show_error(parent, _L("Invalid name, the following characters are not allowed:") + " <>:/\\|?*\"" +_L("(Including its escape characters)")); } +void Plater::mark_plate_toolbar_image_dirty() +{ + m_b_plate_toolbar_image_dirty = true; +} + +bool Plater::is_plate_toolbar_image_dirty() const +{ + return m_b_plate_toolbar_image_dirty; +} + +void Plater::clear_plate_toolbar_image_dirty() +{ + m_b_plate_toolbar_image_dirty = false; +} + static std::map bed_type_thumbnails = { {BedType::btPC, "bed_cool"}, {BedType::btEP, "bed_engineering"}, @@ -542,13 +555,12 @@ void Sidebar::priv::layout_printer(bool is_support_box, bool isDual) // double auto hsizer_extruder = new wxBoxSizer(wxHORIZONTAL); + hsizer_extruder->Add(left_extruder->sizer, 1, wxEXPAND, 0); hsizer_extruder->AddSpacer(FromDIP(4)); - hsizer_extruder->Add(left_extruder->sizer, 1, wxEXPAND); - hsizer_extruder->Add(right_extruder->sizer, 1, wxLEFT | wxEXPAND, FromDIP(4)); - hsizer_extruder->AddSpacer(FromDIP(4)); + hsizer_extruder->Add(right_extruder->sizer, 1, wxEXPAND, 0); // single - vsizer_printer->Add(hsizer_extruder, 0, wxEXPAND | wxLEFT, 0); + vsizer_printer->Add(hsizer_extruder, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(4)); vsizer_printer->Add(single_extruder->sizer, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(4)); vsizer_printer->AddSpacer(FromDIP(4)); @@ -743,14 +755,20 @@ static struct DynamicFilamentList : DynamicList void apply_on(Choice *c) override { + if (!c) { + return; + } if (items.empty()) update(true); auto cb = dynamic_cast(c->window); + if (!cb) { + return; + } int n = cb->GetSelection(); cb->Clear(); cb->Append(_L("Default")); for (auto i : items) { - cb->Append(i.first, *i.second); + cb->Append(i.first, i.second ? wxNullBitmap : * i.second); } if ((unsigned int)n < cb->GetCount()) cb->SetSelection(n); @@ -974,20 +992,22 @@ ExtruderGroup::ExtruderGroup(wxWindow * parent, int index, wxString const &title hsizer_ams->Add(btn_edit, 0, wxLEFT | wxALIGN_CENTER, FromDIP(2)); hsizer_ams->Add(ams_not_installed_msg, 0, wxALIGN_CENTER); - btn_up = new ScalableButton(this, wxID_ANY, "page_up", "", {-1, FromDIP(14)}); + btn_up = new ScalableButton(this, wxID_ANY, "page_up", "", {FromDIP(14), FromDIP(14)}, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, false, 14); btn_up->SetBackgroundColour(*wxWHITE); btn_up->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this, index](auto &evt) { if (page_cur > 0) --page_cur; update_ams(); }); - btn_down = new ScalableButton(this, wxID_ANY, "page_down", "", {-1, FromDIP(14)}); + btn_up->Hide(); + btn_down = new ScalableButton(this, wxID_ANY, "page_down", "", {FromDIP(14), FromDIP(14)}, wxDefaultPosition, wxBU_EXACTFIT | wxNO_BORDER, false, 14); btn_down->SetBackgroundColour(*wxWHITE); btn_down->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this, index](auto &evt) { if (page_cur + 1 < page_num) ++page_cur; update_ams(); }); + btn_down->Hide(); wxBoxSizer *hsizer_diameter = new wxBoxSizer(wxHORIZONTAL); @@ -1162,7 +1182,7 @@ bool Sidebar::priv::sync_extruder_list(bool &only_external_material) return false; } - std::string machine_print_name = obj->printer_type; + std::string machine_print_name = obj->get_show_printer_type(); PresetBundle *preset_bundle = wxGetApp().preset_bundle; std::string target_model_id = preset_bundle->printers.get_selected_preset().get_printer_type(preset_bundle); Preset* machine_preset = get_printer_preset(obj); @@ -1298,7 +1318,7 @@ void Sidebar::priv::update_sync_status(const MachineObject *obj) // 1. update printer status //y59 //const Preset &cur_preset = wxGetApp().preset_bundle->printers.get_edited_preset(); - //if (preset_bundle && preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle) == obj->printer_type) { + //if (preset_bundle && preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle) == obj->get_show_printer_type()) { std::string cur_preset_name = wxGetApp().get_tab(Preset::TYPE_PRINTER)->get_presets()->get_edited_preset().name; if (preset_bundle && cur_preset_name == wxGetApp().plater()->sidebar().box_list_preset_name) { panel_printer_preset->ShowBadge(true); @@ -1537,7 +1557,7 @@ Sidebar::Sidebar(Plater *parent) // add printer title scrolled_sizer->Add(p->m_panel_printer_title, 0, wxEXPAND | wxALL, 0); - p->m_panel_printer_title->Bind(wxEVT_LEFT_UP, [this] (auto & e) { + p->m_panel_printer_title->Bind(wxEVT_LEFT_DOWN, [this] (auto & e) { if (p->m_panel_printer_content->GetMaxHeight() == 0) p->m_panel_printer_content->SetMaxSize({-1, -1}); else @@ -1576,16 +1596,16 @@ Sidebar::Sidebar(Plater *parent) if (p->combo_printer->switch_to_tab()) p->editing_filament = 0; }); - p->btn_edit_printer = edit_btn; - ScalableBitmap bitmap_printer(p->panel_printer_preset, "printer_placeholder", 48); - p->image_printer = new wxStaticBitmap(p->panel_printer_preset, wxID_ANY, bitmap_printer.bmp(), wxDefaultPosition, PRINTER_THUMBNAIL_SIZE, 0); - p->image_printer->Bind(wxEVT_LEFT_DOWN, [this](auto &evt) { - p->combo_printer->wxEvtHandler::ProcessEvent(evt); - }); - - PlaterPresetComboBox *combo_printer = new PlaterPresetComboBox(p->panel_printer_preset, Preset::TYPE_PRINTER); - combo_printer->SetWindowStyle(combo_printer->GetWindowStyle() & ~wxALIGN_MASK | wxALIGN_CENTER_HORIZONTAL); - combo_printer->SetBorderWidth(0); + p->btn_edit_printer = edit_btn; + ScalableBitmap bitmap_printer(p->panel_printer_preset, "printer_placeholder", 48); + p->image_printer = new wxStaticBitmap(p->panel_printer_preset, wxID_ANY, bitmap_printer.bmp(), wxDefaultPosition, PRINTER_THUMBNAIL_SIZE, 0); + p->image_printer->Bind(wxEVT_LEFT_DOWN, [this](auto &evt) { + p->combo_printer->wxEvtHandler::ProcessEvent(evt); + }); + + PlaterPresetComboBox *combo_printer = new PlaterPresetComboBox(p->panel_printer_preset, Preset::TYPE_PRINTER); + combo_printer->SetWindowStyle(combo_printer->GetWindowStyle() & ~wxALIGN_MASK | wxALIGN_CENTER_HORIZONTAL); + combo_printer->SetBorderWidth(0); p->combo_printer = combo_printer; // y5 @@ -1599,12 +1619,12 @@ Sidebar::Sidebar(Plater *parent) // }); { - auto hovered = std::make_shared(); - for (wxWindow *w : std::initializer_list{p->panel_printer_preset, edit_btn, p->image_printer, combo_printer}) { - w->Bind(wxEVT_ENTER_WINDOW, [w, hovered, edit_btn](wxMouseEvent &evt) { *hovered = w; edit_btn->SetBitmap_("edit"); }); - w->Bind(wxEVT_LEAVE_WINDOW, [w, hovered, edit_btn](wxMouseEvent &evt) { if (*hovered == w) { edit_btn->SetBitmap_("dot"); *hovered = nullptr; } }); - } - } + auto hovered = std::make_shared(); + for (wxWindow *w : std::initializer_list{p->panel_printer_preset, edit_btn, p->image_printer, combo_printer}) { + w->Bind(wxEVT_ENTER_WINDOW, [w, hovered, edit_btn](wxMouseEvent &evt) { *hovered = w; edit_btn->SetBitmap_("edit"); }); + w->Bind(wxEVT_LEAVE_WINDOW, [w, hovered, edit_btn](wxMouseEvent &evt) { if (*hovered == w) { edit_btn->SetBitmap_("dot"); *hovered = nullptr; } }); + } + } // Bed type selection p->panel_printer_bed = new StaticBox(p->m_panel_printer_content); @@ -1668,6 +1688,7 @@ Sidebar::Sidebar(Plater *parent) bed_type_vsizer->AddStretchSpacer(1); p->panel_printer_bed->SetSizer(bed_type_vsizer); + AppConfig *app_config = wxGetApp().app_config; std::string str_bed_type = app_config->get("curr_bed_type"); int bed_type_value = atoi(str_bed_type.c_str()); @@ -1691,7 +1712,7 @@ Sidebar::Sidebar(Plater *parent) project_config.set_key_value("curr_bed_type", new ConfigOptionEnum(bed_type)); // Sync printer information - btn_sync = new Button(p->m_panel_printer_content, _L("Sync printer information"), "printer_sync", 0, 32); + btn_sync = new Button(p->m_panel_printer_content, _L("Sync info"), "printer_sync", 0, 32); //btn_sync->SetFont(Label::Body_8); btn_sync->SetToolTip(_L("Synchronize nozzle information and the number of BOX")); btn_sync->SetCornerRadius(8); @@ -1707,8 +1728,8 @@ Sidebar::Sidebar(Plater *parent) btn_sync->SetBorderColor(btn_sync_bd_col); btn_sync->SetCanFocus(false); btn_sync->SetPaddingSize({FromDIP(6), FromDIP(12)}); - btn_sync->SetMinSize(PRINTER_PANEL_SIZE); - btn_sync->SetMaxSize(PRINTER_PANEL_SIZE); + btn_sync->SetMinSize(BTN_SYNC_SIZE); + btn_sync->SetMaxSize(BTN_SYNC_SIZE); btn_sync->SetVertical(); btn_sync->Bind(wxEVT_UPDATE_UI, &Sidebar::update_sync_ams_btn_enable, this); btn_sync->Bind(wxEVT_BUTTON, [this](wxCommandEvent &e) { @@ -1747,7 +1768,7 @@ Sidebar::Sidebar(Plater *parent) p->m_panel_filament_title = new StaticBox(p->scrolled, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL | wxBORDER_NONE); p->m_panel_filament_title->SetBackgroundColor(title_bg); p->m_panel_filament_title->SetBackgroundColor2(0xF1F1F1); - p->m_panel_filament_title->Bind(wxEVT_LEFT_UP, [this](wxMouseEvent &e) { + p->m_panel_filament_title->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &e) { if (e.GetPosition().x > (p->m_flushing_volume_btn->IsShown() ? p->m_flushing_volume_btn->GetPosition().x : p->m_bpButton_add_filament->GetPosition().x)) return; @@ -2025,7 +2046,6 @@ Sidebar::Sidebar(Plater *parent) p->m_search_bar->ShowSearchButton(true); p->m_search_bar->ShowCancelButton(true); p->m_search_bar->SetDescriptiveText(_L("Search plate, object and part.")); - p->m_search_bar->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#000000"))); p->m_search_bar->Bind(wxEVT_SET_FOCUS, [this](wxFocusEvent&) { this->p->on_search_update(); @@ -2100,7 +2120,6 @@ void Sidebar::on_leave_image_printer_bed(wxMouseEvent &evt) { //} p->big_bed_image_popup->on_hide(); } - void Sidebar::on_change_color_mode(bool is_dark) { const ModelObjectPtrs &mos = wxGetApp().model().objects; for (int i = 0; i < mos.size(); i++) { @@ -2432,8 +2451,9 @@ void Sidebar::update_presets(Preset::Type preset_type) } Preset& printer_preset = wxGetApp().preset_bundle->printers.get_edited_preset(); - - bool isQDT = printer_preset.is_qdt_vendor_preset(wxGetApp().preset_bundle); + + //wxGetApp().mainframe->update_calibration_button_status(); + //B y58 wxGetApp().mainframe->show_calibration_button(false); @@ -2442,9 +2462,10 @@ void Sidebar::update_presets(Preset::Type preset_type) } else wxGetApp().plater()->get_current_canvas3D()->get_arrange_settings().align_to_y_axis = false; + // Update dual extrudes auto extruder_variants = printer_preset.config.option("extruder_variant_list"); - + bool isQDT = printer_preset.is_qdt_vendor_preset(wxGetApp().preset_bundle); bool is_dual_extruder = extruder_variants->size() == 2; //y65 @@ -2644,7 +2665,6 @@ bool Sidebar::reset_bed_type_combox_choices(bool is_sidebar_init) if (m_cur_image_bed_type != pm->id) { m_cur_image_bed_type = pm->id; } - for (auto item : bed_type_def->enum_labels) { index++; bool find = std::find(pm->not_support_bed_types.begin(), pm->not_support_bed_types.end(), item) != pm->not_support_bed_types.end(); @@ -2728,8 +2748,7 @@ void Sidebar::msw_rescale() p->single_extruder->Rescale(); p->btn_sync_printer->SetPaddingSize({FromDIP(6), FromDIP(12)}); - p->btn_sync_printer->SetMinSize(PRINTER_PANEL_SIZE); - p->btn_sync_printer->SetMaxSize(PRINTER_PANEL_SIZE); + p->btn_sync_printer->SetMinSize(BTN_SYNC_SIZE); p->panel_printer_bed->SetMinSize(PRINTER_PANEL_SIZE); p->btn_sync_printer->Rescale(); #if 0 @@ -3074,15 +3093,16 @@ std::map Sidebar::build_filament_ams_list(MachineObject tray_config.set_key_value("filament_type", new ConfigOptionStrings{tray.type}); tray_config.set_key_value("tray_name", new ConfigOptionStrings{ name }); tray_config.set_key_value("filament_colour", new ConfigOptionStrings{into_u8(wxColour("#" + tray.color).GetAsString(wxC2S_HTML_SYNTAX))}); + tray_config.set_key_value("filament_multi_colour", new ConfigOptionStrings{}); + tray_config.set_key_value("filament_colour_type", new ConfigOptionStrings{std::to_string(tray.ctype)}); tray_config.set_key_value("filament_exist", new ConfigOptionBools{tray.is_exists}); - tray_config.set_key_value("filament_multi_colors", new ConfigOptionStrings{}); std::optional info; if (wxGetApp().preset_bundle) { info = wxGetApp().preset_bundle->get_filament_by_filament_id(tray.setting_id); } tray_config.set_key_value("filament_is_support", new ConfigOptionBools{ info.has_value() ? info->is_support : false}); for (int i = 0; i < tray.cols.size(); ++i) { - tray_config.opt("filament_multi_colors")->values.push_back(into_u8(wxColour("#" + tray.cols[i]).GetAsString(wxC2S_HTML_SYNTAX))); + tray_config.opt("filament_multi_colour")->values.push_back(into_u8(wxColour("#" + tray.cols[i]).GetAsString(wxC2S_HTML_SYNTAX))); } return tray_config; }; @@ -3131,7 +3151,7 @@ bool Sidebar::need_auto_sync_extruder_list_after_connect_priner(const MachineObj if(!obj) return false; - std::string machine_print_name = obj->printer_type; + std::string machine_print_name = obj->get_show_printer_type(); PresetBundle *preset_bundle = wxGetApp().preset_bundle; std::string target_model_id = preset_bundle->printers.get_selected_preset().get_printer_type(preset_bundle); if (machine_print_name != target_model_id) { @@ -3349,7 +3369,7 @@ void Sidebar::sync_ams_list(bool is_from_big_sync_btn) }; { // badge ams filament clear_combos_filament_badge(); - { + if (sync_result.direct_sync) { for (auto &c : p->combos_filament) { badge_combox_filament(c); } @@ -3432,6 +3452,7 @@ std::map Sidebar::build_filament_box_list(std::vector Sidebar::build_filament_box_list(std::vector("filament_multi_colors")->values.push_back(into_u8(wxColour(color[i]).GetAsString(wxC2S_HTML_SYNTAX))); + //y68 + tray_config.set_key_value("filament_colour_type", new ConfigOptionStrings{"1"}); + + tray_config.set_key_value("filament_exist", new ConfigOptionBools{ true }); //default + tray_config.set_key_value("filament_multi_colour", new ConfigOptionStrings{}); + tray_config.opt("filament_multi_colour")->values.push_back(into_u8(wxColour(color[i]).GetAsString(wxC2S_HTML_SYNTAX))); filament_ams_list.emplace('A' + i, std::move(tray_config)); } @@ -3467,9 +3491,11 @@ std::map Sidebar::build_filament_box_list(std::vector("filament_multi_colors")->values.push_back(into_u8(wxColour(color.back()).GetAsString(wxC2S_HTML_SYNTAX))); + tray_config.set_key_value("filament_multi_colour", new ConfigOptionStrings{}); + tray_config.opt("filament_multi_colour")->values.push_back(into_u8(wxColour(color.back()).GetAsString(wxC2S_HTML_SYNTAX))); filament_ams_list.emplace('A' + count + 1, std::move(tray_config)); } @@ -4011,9 +4037,7 @@ void Sidebar::auto_calc_flushing_volumes_internal(const int modify_id, const int const auto& full_config = wxGetApp().preset_bundle->full_config(); auto& ams_multi_color_filament = preset_bundle->ams_multi_color_filment; size_t extruder_nums = preset_bundle->get_printer_extruder_count(); - bool is_multi_extruder = extruder_nums > 1; - NozzleVolumeType volume_type=NozzleVolumeType(full_config.option("nozzle_volume_type")->values[extruder_id]); - + int nozzle_flush_dataset = full_config.option("nozzle_flush_dataset")->values[extruder_id]; std::vector init_matrix = get_flush_volumes_matrix((project_config.option("flush_volumes_matrix"))->values, extruder_id, extruder_nums); const std::vector& min_flush_volumes = get_min_flush_volumes(full_config, extruder_id); @@ -4050,7 +4074,7 @@ void Sidebar::auto_calc_flushing_volumes_internal(const int modify_id, const int // from to modify int from_idx = i; if (from_idx != modify_id) { - Slic3r::FlushVolCalculator calculator(min_flush_volumes[from_idx], m_max_flush_volume, is_multi_extruder,volume_type); + Slic3r::FlushVolCalculator calculator(min_flush_volumes[from_idx], m_max_flush_volume, nozzle_flush_dataset); int flushing_volume = 0; bool is_from_support = is_support_filament(from_idx); bool is_to_support = is_support_filament(modify_id); @@ -4075,7 +4099,7 @@ void Sidebar::auto_calc_flushing_volumes_internal(const int modify_id, const int // modify to to int to_idx = i; if (to_idx != modify_id) { - Slic3r::FlushVolCalculator calculator(min_flush_volumes[modify_id], m_max_flush_volume, is_multi_extruder, volume_type); + Slic3r::FlushVolCalculator calculator(min_flush_volumes[modify_id], m_max_flush_volume, nozzle_flush_dataset); bool is_from_support = is_support_filament(modify_id); bool is_to_support = is_support_filament(to_idx); int flushing_volume = 0; @@ -4331,6 +4355,7 @@ public: bool show_render_statistic_dialog{ false }; bool show_wireframe{ false }; bool wireframe_enabled{ true }; + bool show_text_cs{false}; bool show_non_manifold_edges{false}; static const std::regex pattern_bundle; static const std::regex pattern_3mf; @@ -4813,7 +4838,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) , m_ui_jobs(this) , m_job_prepare_state(Job::JobPrepareState::PREPARE_STATE_DEFAULT) , delayed_scene_refresh(false) - , collapse_toolbar(GLToolbar::Normal, "Collapse") + , collapse_toolbar(GLToolbar::EType::Normal, "Collapse") //QDS :partplatelist construction , partplate_list(this->q, &model) { @@ -4822,7 +4847,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) m_aui_mgr.SetDockSizeConstraint(1, 1); // m_aui_mgr.GetArtProvider()->SetMetric(wxAUI_DOCKART_PANE_BORDER_SIZE, 0); // m_aui_mgr.GetArtProvider()->SetMetric(wxAUI_DOCKART_SASH_SIZE, 2); - m_aui_mgr.GetArtProvider()->SetMetric(wxAUI_DOCKART_CAPTION_SIZE, 0); + m_aui_mgr.GetArtProvider()->SetMetric(wxAUI_DOCKART_CAPTION_SIZE, wxGetApp().app_config->get_bool("enable_sidebar_floatable") ? 8 : 0); m_aui_mgr.GetArtProvider()->SetMetric(wxAUI_DOCKART_GRADIENT_TYPE, wxAUI_GRADIENT_NONE); this->q->SetFont(Slic3r::GUI::wxGetApp().normal_font()); @@ -4898,19 +4923,17 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame) }); update(); - // Orca: Make sidebar dockable - auto look = wxGetApp().app_config->get_bool("enable_sidebar_resizable"); m_aui_mgr.AddPane(sidebar, wxAuiPaneInfo() .Name("sidebar") .Left() .CloseButton(false) .TopDockable(false) .BottomDockable(false) - //.Floatable(true) + .Floatable(wxGetApp().app_config->get_bool("enable_sidebar_floatable")) .Resizable(wxGetApp().app_config->get_bool("enable_sidebar_resizable")) - .MinSize(wxSize(41 * wxGetApp().em_unit(), -1)) - .BestSize(wxSize(42 * wxGetApp().em_unit(), 90 * wxGetApp().em_unit()))); + .MinSize(wxSize(15 * wxGetApp().em_unit(), 90 * wxGetApp().em_unit())) + .BestSize(wxSize(42 * wxGetApp().em_unit(), 90 * wxGetApp().em_unit()))); auto *panel_sizer = new wxBoxSizer(wxHORIZONTAL); panel_sizer->Add(view3D, 1, wxEXPAND | wxALL, 0); @@ -5331,10 +5354,8 @@ void Plater::priv::update(unsigned int flags) // QDS assemble view this->assemble_view->reload_scene(false, flags); - if (current_panel && q->is_preview_shown()) { - q->force_update_all_plate_thumbnails(); - //update_fff_scene_only_shells(true); - } + // todo: better to mark thumbnail dirty here + q->mark_plate_toolbar_image_dirty(); if (force_background_processing_restart) this->restart_background_process(update_status); @@ -5386,6 +5407,34 @@ const VendorProfile::PrinterModel *Plater::get_curr_printer_model() return nullptr; } +std::map Plater::get_bed_texture_maps() +{ + auto pm = get_curr_printer_model(); + if (pm) { + std::map maps; + if (pm->bottom_texture_end_name.size() > 0) { + maps["bottom_texture_end_name"] = pm->bottom_texture_end_name; + } + if (pm->bottom_texture_rect.size() > 0) { + maps["bottom_texture_rect"] = pm->bottom_texture_rect; + } + if (pm->middle_texture_rect.size() > 0) { + maps["middle_texture_rect"] = pm->middle_texture_rect; + } + return maps; + } + return {}; +} + +int Plater::get_right_icon_offset_bed() +{ + auto pm = get_curr_printer_model(); + if (pm && pm->right_icon_offset_bed.size() > 0) { + return std::stoi(pm->right_icon_offset_bed); + } + return 0; +} + wxColour Plater::get_next_color_for_filament() { static int curr_color_filamenet = 0; @@ -5508,7 +5557,14 @@ void Plater::priv::collapse_sidebar(bool collapse) // Now update the tooltip in the toolbar. std::string new_tooltip = collapse ? _u8L("Expand sidebar") : _u8L("Collapse sidebar"); + new_tooltip += " [Shift+Tab]"; + #ifdef __APPLE__ + new_tooltip += "\n" + _u8L("Reset Window Layout") + "[command+W]"; +#else + new_tooltip += "\n" + _u8L("Reset Window Layout") + "[Ctrl+W]"; +#endif + int id = collapse_toolbar.get_item_id("collapse_sidebar"); collapse_toolbar.set_tooltip(id, new_tooltip); @@ -5889,33 +5945,33 @@ std::vector Plater::priv::load_files(const std::vector& input_ show_info(q, _L("The 3mf is not from QIDI Tech, load geometry data only."), _L("Load 3mf")); } //w18 - ////else if (load_config && (file_version.maj() != app_version.maj())) { - //// // version mismatch, only load geometries - //// load_config = false; - //// if (!load_model) { - //// // only load config case, return directly - //// show_info(q, _L("The Config can not be loaded."), _L("Load 3mf")); - //// q->skip_thumbnail_invalid = false; - //// return empty_result; - //// } - //// load_old_project = true; - //// // select view to 3D - //// q->select_view_3D("3D"); - //// // select plate 0 as default - //// q->select_plate(0); - //// if (load_type != LoadType::LoadGeometry) { - //// if (en_3mf_file_type == En3mfType::From_QDS) - //// show_info(q, _L("Due to the lower version of QIDI Studio, this 3mf file cannot be fully loaded. Please update QIDI Studio to the latest version"), _L("Load 3mf")); - //// else - //// show_info(q, _L("The 3mf is not from QIDI Tech, load geometry data only."), _L("Load 3mf")); - //// } - //// for (ModelObject *model_object : model.objects) { - //// model_object->config.reset(); - //// // Is there any modifier or advanced config data? - //// for (ModelVolume *model_volume : model_object->volumes) model_volume->config.reset(); - //// } + else if (load_config && (file_version.maj() != app_version.maj())) { + // version mismatch, only load geometries + load_config = false; + if (!load_model) { + // only load config case, return directly + show_info(q, _L("The Config can not be loaded."), _L("Load 3mf")); + q->skip_thumbnail_invalid = false; + return empty_result; + } + load_old_project = true; + // select view to 3D + q->select_view_3D("3D"); + // select plate 0 as default + q->select_plate(0); + if (load_type != LoadType::LoadGeometry) { + if (en_3mf_file_type == En3mfType::From_QDS) + show_info(q, _L("Due to the lower version of QIDI Studio, this 3mf file cannot be fully loaded. Please update QIDI Studio to the latest version"), _L("Load 3mf")); + else + show_info(q, _L("The 3mf is not from QIDI Tech, load geometry data only."), _L("Load 3mf")); + } + for (ModelObject *model_object : model.objects) { + model_object->config.reset(); + // Is there any modifier or advanced config data? + for (ModelVolume *model_volume : model_object->volumes) model_volume->config.reset(); + } + } //y54 - // // } // // else if (load_config && (file_version > app_version)) { // Semver cloud_ver; // if (wxGetApp().app_config->has("app", "cloud_version")) { @@ -5957,10 +6013,13 @@ std::vector Plater::priv::load_files(const std::vector& input_ //} else if (load_config && config_loaded.empty()) { load_config = false; - if (file_version.maj() == 0 && file_version.min() == 0 && file_version.patch() == 0) { - show_info(q, _L("The 3mf is not from QIDI Tech, load geometry data only."), _L("Load 3mf")); - } else { - show_info(q, _L("The 3mf is generated by old QIDI Studio, load geometry data only."), _L("Load 3mf")); + if (wxGetApp().app_config->get_bool("skip_non_qidi_3mf_warning") != true) { + TipsDialog dlg(q, _L("Load 3mf"), + file_version.maj() == 0 && file_version.min() == 0 && file_version.patch() == 0 + ? _L("The 3mf is not from QIDI Tech, load geometry data only.") + : _L("The 3mf is generated by old QIDI Studio, load geometry data only."), + "skip_non_qidi_3mf_warning", wxOK); + dlg.ShowModal(); } } else if (!load_config) { @@ -6081,6 +6140,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ if (!config.empty()) { Preset::normalize(config); PresetBundle *preset_bundle = wxGetApp().preset_bundle; + { // QDS: modify the prime tower params for old version file Semver old_version3(2, 0, 0); @@ -6283,11 +6343,42 @@ std::vector Plater::priv::load_files(const std::vector& input_ *wipe_tower_y = *file_wipe_tower_y; ConfigOptionStrings* filament_color = proj_cfg.opt("filament_colour"); - ConfigOptionInts* filament_map = proj_cfg.opt("filament_map", true); - if (filament_color && filament_color->size() != filament_map->size()) { - filament_map->values.resize(filament_color->size(), 1); + if (filament_color) { + size_t filament_count = filament_color->size(); + + // Sync filament map + ConfigOptionInts* filament_map = proj_cfg.opt("filament_map", true); + if (filament_map->size() != filament_count) { + filament_map->values.resize(filament_count, 1); + } + + // Sync filament multi colour + ConfigOptionStrings* filament_multi_color = proj_cfg.opt("filament_multi_colour", true); + if (filament_multi_color->size() != filament_count) { + filament_multi_color->values.resize(filament_count); + } + // If there is no multi-color data or color is not match, use single color as default value + for (size_t i = 0; i < filament_count; i++) { + std::vector colors = Slic3r::split_string(filament_multi_color->values[i], ' '); + if (i >= filament_multi_color->values.size() || colors.empty() || colors[0] != filament_color->values[i] ) { + filament_multi_color->values[i] = filament_color->values[i]; + } + } + // Sync filament colour type + ConfigOptionStrings* filament_color_type = proj_cfg.opt("filament_colour_type", true); + if (filament_color_type && filament_color_type->size() != filament_count) { + filament_color_type->values.resize(filament_count); + + for (size_t i = 0; i < filament_count; i++) { + if (i >= filament_color_type->values.size() || filament_color_type->values[i].empty()) { + filament_color_type->values[i] = "1"; + } + } + } } } + // Update filament combobox after loading config + wxGetApp().plater()->sidebar().update_presets(Preset::TYPE_FILAMENT); } } if (!silence) wxGetApp().app_config->update_config_dir(path.parent_path().string()); @@ -6307,7 +6398,7 @@ std::vector Plater::priv::load_files(const std::vector& input_ makerlab_name = in_out.ml_name; makerlab_id = in_out.ml_id; - if (!boost::iends_with(path.string(), ".obj")) { return; } + if (!boost::iends_with(path.string(), ".obj")) { return; } const std::vector extruder_colours = wxGetApp().plater()->get_extruder_colors_from_plater_config(); ObjColorDialog color_dlg(nullptr, in_out, extruder_colours); if (color_dlg.ShowModal() != wxID_OK) { @@ -7200,13 +7291,6 @@ void Plater::priv::delete_all_objects_from_model() view3D->get_canvas3d()->reset_sequential_print_clearance(); - if (assemble_view) { - const auto& p_camera = assemble_view->get_override_camera(); - if (p_camera) { - p_camera->requires_zoom_to_volumes = true; - } - } - m_ui_jobs.cancel_all(); // Stop and reset the Print content. @@ -7246,6 +7330,13 @@ void Plater::priv::reset(bool apply_presets_change) view3D->get_canvas3d()->reset_sequential_print_clearance(); + if (assemble_view) { + const auto& p_camera = assemble_view->get_override_camera(); + if (p_camera) { + p_camera->requires_zoom_to_volumes = true; + } + } + m_ui_jobs.cancel_all(); //QDS: clear the partplate list's object before object cleared @@ -7679,7 +7770,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool if (background_process.finished() && cur_plate && cur_plate->is_slice_result_valid()) { //ready_to_slice = false; - this->main_frame->update_slice_print_status(MainFrame::eEventSliceUpdate, false, true); + this->main_frame->update_slice_print_status(MainFrame::eEventSliceUpdate, false); } else if (!background_process.empty() && !background_process.running()) /* Do not update buttons if background process is running @@ -7689,7 +7780,7 @@ unsigned int Plater::priv::update_background_process(bool force_validation, bool { if (cur_plate->can_slice()) { //ready_to_slice = true; - this->main_frame->update_slice_print_status(MainFrame::eEventSliceUpdate, true, true); + this->main_frame->update_slice_print_status(MainFrame::eEventSliceUpdate, true); process_completed_with_error = -1; } else { @@ -7970,6 +8061,7 @@ bool Plater::priv::replace_volume_with_stl(int object_idx, int volume_idx, const else if (old_volume->source.is_converted_from_meters) new_volume->convert_from_meters(); new_volume->supported_facets.assign(old_volume->supported_facets); + new_volume->fuzzy_skin_facets.assign(old_volume->fuzzy_skin_facets); new_volume->seam_facets.assign(old_volume->seam_facets); new_volume->mmu_segmentation_facets.assign(old_volume->mmu_segmentation_facets); std::swap(old_model_object->volumes[volume_idx], old_model_object->volumes.back()); @@ -8602,7 +8694,6 @@ void Plater::priv::set_current_panel(wxPanel* panel, bool no_slice) preview->set_as_dirty(); }; - // Add sidebar and toolbar collapse logic if (panel == view3D || panel == preview) { this->enable_sidebar(!q->only_gcode_mode()); @@ -8616,9 +8707,6 @@ void Plater::priv::set_current_panel(wxPanel* panel, bool no_slice) preview->get_canvas3d()->enable_select_plate_toolbar(true); } } - else { - preview->get_canvas3d()->clear_select_plate_toolbar_render_flag(); - } if (current_panel == panel) { @@ -8883,7 +8971,6 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt) } auto idx = combo->get_filament_idx(); - bool flag = is_support_filament(idx); //! Because of The MSW and GTK version of wxBitmapComboBox derived from wxComboBox, //! but the OSX version derived from wxOwnerDrawnCombo. @@ -8930,23 +9017,23 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt) else physical_printers.unselect_printer(); - if (marker == PresetComboBox::LABEL_ITEM_PRINTER_MODELS) { - auto preset = wxGetApp().preset_bundle->get_similar_printer_preset(preset_name, {}); - if (preset == nullptr) { - MessageDialog dlg(this->sidebar, _L(""), _L("")); - dlg.ShowModal(); - } - preset->is_visible = true; // force visible - preset_name = preset->name; + if (marker == PresetComboBox::LABEL_ITEM_PRINTER_MODELS) { + auto preset = wxGetApp().preset_bundle->get_similar_printer_preset(preset_name, {}); + if (preset == nullptr) { + MessageDialog dlg(this->sidebar, _L(""), _L("")); + dlg.ShowModal(); } - std::string old_preset_name = wxGetApp().preset_bundle->printers.get_edited_preset().name; - - update_objects_position_when_select_preset([this, &preset_type, &preset_name]() { - wxWindowUpdateLocker noUpdates2(sidebar->filament_panel()); - wxGetApp().get_tab(preset_type)->select_preset(preset_name); - // update plater with new config - q->on_config_change(wxGetApp().preset_bundle->full_config()); - }); + preset->is_visible = true; // force visible + preset_name = preset->name; + } + std::string old_preset_name = wxGetApp().preset_bundle->printers.get_edited_preset().name; + + update_objects_position_when_select_preset([this, &preset_type, &preset_name]() { + wxWindowUpdateLocker noUpdates2(sidebar->filament_panel()); + wxGetApp().get_tab(preset_type)->select_preset(preset_name); + // update plater with new config + q->on_config_change(wxGetApp().preset_bundle->full_config()); + }); if (old_preset_name != preset_name && wxGetApp().app_config->get("auto_calculate_flush") == "all") { @@ -8959,7 +9046,7 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt) if (obj && obj->is_multi_extruders()) { PresetBundle *preset_bundle = wxGetApp().preset_bundle; Preset& cur_preset = preset_bundle->printers.get_edited_preset(); - if (cur_preset.get_printer_type(preset_bundle) == obj->printer_type) { + if (cur_preset.get_printer_type(preset_bundle) == obj->get_show_printer_type()) { double preset_nozzle_diameter = cur_preset.config.option("nozzle_diameter")->values[0]; bool same_nozzle_diameter = true; for (const Extder &extruder : obj->m_extder_data.extders) { @@ -9541,46 +9628,46 @@ void Plater::priv::on_action_print_plate(SimpleEvent&) int extruders_size = wxGetApp().plater()->get_partplate_list().get_curr_plate()->get_used_filaments().size(); std::string box_ip = wxGetApp().plater()->sidebar().box_list_printer_ip; - //y61 - bool has_diff = false; - if (!box_ip.empty()) { -#if QDT_RELEASE_TO_PUBLIC - QIDINetwork qidi; - wxString msg = ""; - GUI::Box_info filament_info; - filament_info = qidi.get_box_info(msg, box_ip); - GUI::Box_info cur_box_info; - cur_box_info = q->get_cur_box_info(); +// //y61 +// bool has_diff = false; +// if (!box_ip.empty()) { +// #if QDT_RELEASE_TO_PUBLIC +// QIDINetwork qidi; +// wxString msg = ""; +// GUI::Box_info filament_info; +// filament_info = qidi.get_box_info(msg, box_ip); +// GUI::Box_info cur_box_info; +// cur_box_info = q->get_cur_box_info(); - if (filament_info.filament_index != cur_box_info.filament_index - || filament_info.filament_vendor != cur_box_info.filament_vendor - || filament_info.filament_color_index != cur_box_info.filament_color_index - || filament_info.box_count != cur_box_info.box_count - || filament_info.auto_reload_detect != cur_box_info.auto_reload_detect) { - has_diff = true; - } -#endif - } +// if (filament_info.filament_index != cur_box_info.filament_index +// || filament_info.filament_vendor != cur_box_info.filament_vendor +// || filament_info.filament_color_index != cur_box_info.filament_color_index +// || filament_info.box_count != cur_box_info.box_count +// || filament_info.auto_reload_detect != cur_box_info.auto_reload_detect) { +// has_diff = true; +// } +// #endif +// } -//y65 +//y65 y68 bool is_can_change_color = preview->get_canvas3d()->get_gcode_viewer().get_layers_slider()->get_is_can_change_color(); if(extruders_size > 1 && box_ip.empty() && !is_can_change_color){ wxGetApp().plater()->sidebar().sync_box_list(); } - //y61 - else if(has_diff){ - MessageDialog dlg(nullptr, _L("The filament information in the BOX has changed. Please resynchronize it."), _L("BOX message has change"), wxYES); - if (dlg.ShowModal() == wxID_YES) { - wxGetApp().plater()->sidebar().sync_box_list(); - } - }else{ - //y40 - SelectMachineDialog* dlg = new SelectMachineDialog(q); - //y52 - dlg->prepare(partplate_list.get_curr_plate_index()); - dlg->ShowModal(); - } + // //y61 + // else if(has_diff){ + // MessageDialog dlg(nullptr, _L("The filament information in the BOX has changed. Please resynchronize it."), _L("BOX message has change"), wxYES); + // if (dlg.ShowModal() == wxID_YES) { + // wxGetApp().plater()->sidebar().sync_box_list(); + // } + // } + + //y40 + SelectMachineDialog* dlg = new SelectMachineDialog(q); + //y52 + dlg->prepare(partplate_list.get_curr_plate_index()); + dlg->ShowModal(); //if (!m_select_machine_dlg) m_select_machine_dlg = new SelectMachineDialog(q); //m_select_machine_dlg->set_print_type(PrintFromType::FROM_NORMAL); @@ -9589,11 +9676,12 @@ void Plater::priv::on_action_print_plate(SimpleEvent&) //record_start_print_preset("print_plate"); } +//y void Plater::priv::resetUploadCount() { UploadCount = 0; m_sending_interval = 0; -}; +} void Plater::priv::on_action_send_to_multi_machine(SimpleEvent&) { @@ -10078,6 +10166,8 @@ void Plater::priv::on_right_click(RBtnEvent& evt) if (model_volume != nullptr) { if (model_volume->is_svg()) { menu = menus.svg_part_menu(); + } else if (model_volume->is_text()) { + menu = menus.text_part_menu(); } else if (model_volume->is_cut_connector()) { menu = menus.cut_connector_menu(); } else { @@ -10312,7 +10402,7 @@ void Plater::priv::set_project_filename(const wxString& filename) full_path.replace_extension(""); m_project_folder = full_path.parent_path(); - BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << __LINE__ << " project folder is:" << m_project_folder.string(); + BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << __LINE__ << " project folder is:" << PathSanitizer::sanitize(m_project_folder); //QDS wxString project_name = from_u8(full_path.filename().string()); @@ -10531,6 +10621,7 @@ void Plater::set_print_job_plate_idx(int plate_idx) } } + int Plater::get_send_calibration_finished_event() { return EVT_SEND_CALIBRATION_FINISHED; @@ -10609,7 +10700,7 @@ bool Plater::priv::check_ams_status_impl(bool is_slice_all) return true; } PresetBundle *preset_bundle = wxGetApp().preset_bundle; - if (preset_bundle && preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle) == obj->printer_type) { + if (preset_bundle && preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle) == obj->get_show_printer_type()) { bool is_same_as_printer = true; auto nozzle_volumes_values = preset_bundle->project_config.option("nozzle_volume_type")->values; assert(obj->m_extder_data.extders.size() == 2 && nozzle_volumes_values.size() == 2); @@ -10699,7 +10790,7 @@ bool Plater::priv::get_machine_sync_status() return false; PresetBundle *preset_bundle = wxGetApp().preset_bundle; - return preset_bundle && preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle) == obj->printer_type; + return preset_bundle && preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle) == obj->get_show_printer_type(); } Camera& Plater::priv::get_current_camera() @@ -10735,19 +10826,21 @@ bool Plater::priv::init_collapse_toolbar() if (!collapse_toolbar.init(background_data)) return false; - collapse_toolbar.set_layout_type(GLToolbar::Layout::Vertical); - collapse_toolbar.set_horizontal_orientation(GLToolbar::Layout::HO_Right); - collapse_toolbar.set_vertical_orientation(GLToolbar::Layout::VO_Top); + collapse_toolbar.set_layout_type(ToolbarLayout::EType::Vertical); + collapse_toolbar.set_horizontal_orientation(ToolbarLayout::HO_Right); + collapse_toolbar.set_vertical_orientation(ToolbarLayout::VO_Top); collapse_toolbar.set_border(0.0f); collapse_toolbar.set_separator_size(5); collapse_toolbar.set_gap_size(2); collapse_toolbar.del_all_item(); + collapse_toolbar.set_position_mode(ToolbarLayout::EPositionMode::TopLeft); GLToolbarItem::Data item; item.name = "collapse_sidebar"; - // set collapse svg name - item.icon_filename = "collapse.svg"; + item.icon_filename_callback = [](bool is_dark_mode)->std::string { + return "collapse.svg"; + }; item.sprite_id = 0; item.left.action_callback = []() { wxGetApp().plater()->collapse_sidebar(!wxGetApp().plater()->is_sidebar_collapsed()); @@ -10986,20 +11079,18 @@ bool Plater::priv::can_delete_all() const bool Plater::priv::can_edit_text() const { - const Selection &selection = view3D->get_canvas3d()->get_selection(); - if (selection.is_single_full_instance()) - return true; - - if (selection.is_single_volume()) { - const GLVolume *gl_volume = selection.get_volume(*selection.get_volume_idxs().begin()); - int out_object_idx = gl_volume->object_idx(); - ModelObject * model_object = selection.get_model()->objects[out_object_idx]; - int out_volume_idx = gl_volume->volume_idx(); - ModelVolume * model_volume = model_object->volumes[out_volume_idx]; - if (model_volume) - return !model_volume->get_text_info().m_text.empty(); - } - return false; + if (wxGetApp().plater() == nullptr) + return false; + const Selection &selection = wxGetApp().plater()->get_selection(); + if (selection.volumes_count() != 1) + return false; + const GLVolume *gl_volume = selection.get_first_volume(); + if (gl_volume == nullptr) + return false; + const ModelVolume *mv = get_model_volume(*gl_volume, selection.get_model()->objects); + if (mv == nullptr) + return false; + return mv->is_text(); } bool Plater::priv::can_add_plate() const @@ -11276,7 +11367,8 @@ void Plater::priv::undo() auto it_current = std::lower_bound(snapshots.begin(), snapshots.end(), UndoRedo::Snapshot(this->undo_redo_stack().active_snapshot_time())); // QDS: undo-redo until modify record while (--it_current != snapshots.begin() && !snapshot_modifies_project(*it_current)); - if (it_current == snapshots.begin()) return; + if (it_current == snapshots.begin()) + return; if (get_current_canvas3D()->get_canvas_type() == GLCanvas3D::CanvasAssembleView) { if (it_current->snapshot_data.snapshot_type != UndoRedo::SnapshotType::GizmoAction && it_current->snapshot_data.snapshot_type != UndoRedo::SnapshotType::EnteringGizmo && @@ -11833,10 +11925,7 @@ bool Plater::try_sync_preset_with_connected_printer(int& nozzle_diameter) } float machine_nozzle_diameter = obj->m_extder_data.extders[0].current_nozzle_diameter; - std::string printer_type = obj->printer_type; - // handle p1p with upgraded kit - if (obj->is_support_upgrade_kit && obj->installed_upgrade_kit) - printer_type = "C12"; + std::string printer_type = obj->get_show_printer_type(); // can not find the preset for connected printer, return false Preset* machine_preset = get_printer_preset(obj); @@ -11894,7 +11983,7 @@ bool Plater::try_sync_preset_with_connected_printer(int& nozzle_diameter) int Plater::load_project(wxString const &filename2, wxString const& originfile) { - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "filename is: " << filename2 << "and originfile is: " << originfile; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "filename is: " << PathSanitizer::sanitize(filename2.mb_str()) << "and originfile is: " << PathSanitizer::sanitize(originfile.mb_str()); auto filename = filename2; auto check = [&filename, this] (bool yes_or_no) { if (!yes_or_no && !wxGetApp().check_and_save_current_preset_changes(_L("Load project"), @@ -12037,7 +12126,12 @@ int Plater::save_project(bool saveAs) return wxID_CANCEL; //QDS export 3mf without gcode - if (export_3mf(into_path(filename), SaveStrategy::SplitModel | SaveStrategy::ShareMesh | SaveStrategy::FullPathSources) < 0) { + auto save_strategy = SaveStrategy::SplitModel | SaveStrategy::ShareMesh; + bool full_pathnames = wxGetApp().app_config->get_bool("export_sources_full_pathnames"); + if (full_pathnames) { + save_strategy = save_strategy | SaveStrategy::FullPathSources; + } + if (export_3mf(into_path(filename), save_strategy) < 0) { MessageDialog(this, _L("Failed to save the project.\nPlease check whether the folder exists online or if other programs open the project file or if there is enough disk space."), _L("Save project"), wxOK | wxICON_WARNING).ShowModal(); return wxID_CANCEL; @@ -12424,6 +12518,8 @@ std::string Plater::double_to_str(const double value) void Plater::calib_pa(const Calib_Params ¶ms) { + model().calib_pa_pattern.reset(nullptr); + const auto calib_pa_name = wxString::Format(L"Pressure Advance Test"); if (new_project(false, true, calib_pa_name) == wxID_CANCEL) return; @@ -12674,6 +12770,14 @@ void Plater::_calib_pa_pattern(const Calib_Params ¶ms) // start with pattern centered on plate center_selection(); const Vec3d plate_center = get_partplate_list().get_curr_plate()->get_center_origin(); + + if ((plate_center.x() - (pa_pattern.print_size_x() / 2) < plate_origin.x()) || + (plate_center.y() - (pa_pattern.print_size_y() / 2) - pa_pattern.handle_spacing() < plate_origin.y())) { + MessageDialog dlg(this, _L("The current settings produce a model that is too large. Please narrow the range or increase the step size. (Model size is proportional to (End - Start) / Step size.)"), _L("Warning"), wxOK | wxICON_WARNING); + dlg.ShowModal(); + return; + } + giz_obj_manip.on_change("position", 0, plate_center.x() - (pa_pattern.print_size_x() / 2)); giz_obj_manip.on_change("position", 1, plate_center.y() - (pa_pattern.print_size_y() / 2) - pa_pattern.handle_spacing()); @@ -12908,6 +13012,9 @@ void Plater::_calib_pa_tower(const Calib_Params ¶ms) if (new_height < obj_bb.size().z()) { std::array plane_pts = get_cut_plane(obj_bb, new_height); cut(0, 0, plane_pts, ModelObjectCutAttribute::KeepLower); + } else { + MessageDialog dlg(this, _L("The current settings produce a model that is too large. Please narrow the range or increase the step size. (Model size is proportional to (End - Start) / Step size.)"), _L("Warning"), wxOK | wxICON_WARNING); + dlg.ShowModal(); } _calib_pa_select_added_objects();*/ @@ -13098,6 +13205,8 @@ void Plater::calib_flowrate(int pass,double input_value) void Plater::calib_temp(const Calib_Params ¶ms) { + model().calib_pa_pattern.reset(nullptr); + try { json js; js["cali_type"] = "third_cali_temp"; @@ -13159,7 +13268,10 @@ void Plater::calib_temp(const Calib_Params ¶ms) void Plater::calib_max_vol_speed(const Calib_Params ¶ms) { //w29 - /*try { + /* + model().calib_pa_pattern.reset(nullptr); + + try { json js; js["cali_type"] = "third_cali_max_flowrate"; std::string filament_id = wxGetApp().preset_bundle->filaments.get_edited_preset().filament_id; @@ -13200,6 +13312,7 @@ void Plater::calib_max_vol_speed(const Calib_Params ¶ms) filament_config->set_key_value("slow_down_layer_time", new ConfigOptionInts{0}); print_config->set_key_value("enable_overhang_speed", new ConfigOptionBoolsNullable{false}); + print_config->set_key_value("enable_height_slowdown", new ConfigOptionBoolsNullable{ false }); print_config->set_key_value("timelapse_type", new ConfigOptionEnum(tlTraditional)); print_config->set_key_value("wall_loops", new ConfigOptionInt(1)); print_config->set_key_value("top_shell_layers", new ConfigOptionInt(0)); @@ -13227,6 +13340,9 @@ void Plater::calib_max_vol_speed(const Calib_Params ¶ms) if (height < obj_bb.size().z()) { std::array plane_pts = get_cut_plane(obj_bb, height); cut(0, 0, plane_pts, ModelObjectCutAttribute::KeepLower); + } else { + MessageDialog dlg(this, _L("The current settings produce a model that is too large. Please narrow the range or increase the step size. (Model size is proportional to (End - Start) / Step size.)"), _L("Warning"), wxOK | wxICON_WARNING); + dlg.ShowModal(); } auto new_params = params; @@ -13325,6 +13441,8 @@ void Plater::calib_max_vol_speed(const Calib_Params ¶ms) void Plater::calib_retraction(const Calib_Params ¶ms) { + model().calib_pa_pattern.reset(nullptr); + try { json js; js["cali_type"] = "third_cali_retraction"; @@ -13368,6 +13486,9 @@ void Plater::calib_retraction(const Calib_Params ¶ms) if (height < obj_bb.size().z()) { std::array plane_pts = get_cut_plane(obj_bb, height); cut(0, 0, plane_pts, ModelObjectCutAttribute::KeepLower); + } else { + MessageDialog dlg(this, _L("The current settings produce a model that is too large. Please narrow the range or increase the step size. (Model size is proportional to (End - Start) / Step size.)"), _L("Warning"), wxOK | wxICON_WARNING); + dlg.ShowModal(); } p->background_process.fff_print()->set_calib_params(params); @@ -13375,6 +13496,8 @@ void Plater::calib_retraction(const Calib_Params ¶ms) void Plater::calib_VFA(const Calib_Params ¶ms) { + model().calib_pa_pattern.reset(nullptr); + try { json js; js["cali_type"] = "third_cali_VFA"; @@ -13397,6 +13520,7 @@ void Plater::calib_VFA(const Calib_Params ¶ms) filament_config->set_key_value("slow_down_layer_time", new ConfigOptionInts{0}); filament_config->set_key_value("filament_max_volumetric_speed", new ConfigOptionFloatsNullable{200}); print_config->set_key_value("enable_overhang_speed", new ConfigOptionBoolsNullable{false}); + print_config->set_key_value("enable_height_slowdown", new ConfigOptionBoolsNullable{ false }); print_config->set_key_value("timelapse_type", new ConfigOptionEnum(tlTraditional)); print_config->set_key_value("wall_loops", new ConfigOptionInt(1)); print_config->set_key_value("top_shell_layers", new ConfigOptionInt(0)); @@ -13419,6 +13543,9 @@ void Plater::calib_VFA(const Calib_Params ¶ms) if (height < obj_bb.size().z()) { std::array plane_pts = get_cut_plane(obj_bb, height); cut(0, 0, plane_pts, ModelObjectCutAttribute::KeepLower); + } else { + MessageDialog dlg(this, _L("The current settings produce a model that is too large. Please narrow the range or increase the step size. (Model size is proportional to (End - Start) / Step size.)"), _L("Warning"), wxOK | wxICON_WARNING); + dlg.ShowModal(); } p->background_process.fff_print()->set_calib_params(params); @@ -13506,6 +13633,8 @@ void Plater::load_gcode(const wxString& filename) current_print.apply(this->model(), wxGetApp().preset_bundle->full_config()); + current_print.apply_config_for_render(processor.export_config_for_render()); + //QDS: add cost info when drag in gcode auto& ps = current_result->print_statistics; double total_cost = 0.0; @@ -13613,8 +13742,6 @@ void Plater::force_update_all_plate_thumbnails() invalid_all_plate_thumbnails(); update_all_plate_thumbnails(true); } - get_preview_canvas3D()->clear_select_plate_toolbar_render_flag(); - get_preview_canvas3D()->update_plate_thumbnails(); } // QDS: backup @@ -14007,11 +14134,11 @@ bool Plater::load_svg(const wxArrayString &filenames, bool from_toolbar_or_file_ if (filenames.size() == 1) { const wxString &filename = filenames[0]; if (boost::iends_with(filenames[0].ToStdString(), ".svg")) { - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "," << __FILE__ << filename; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "," << __FILE__ << PathSanitizer::sanitize(filename.mb_str()); emboss_svg(filename, from_toolbar_or_file_menu); return true; } else { - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "," << __FILE__ << ",fail:" << filename; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << "," << __FILE__ << ",fail:" << PathSanitizer::sanitize(filename.mb_str()); } } else { @@ -15229,8 +15356,7 @@ Preset *get_printer_preset(MachineObject *obj) if (printer_nozzle_opt) printer_nozzle_vals = dynamic_cast(printer_nozzle_opt); std::string model_id = printer_it->get_current_printer_type(preset_bundle); - std::string printer_type = obj->printer_type; - if (obj->is_support_upgrade_kit && obj->installed_upgrade_kit) printer_type = "C12"; + std::string printer_type = obj->get_show_printer_type(); if (model_id.compare(printer_type) == 0 && printer_nozzle_vals && abs(printer_nozzle_vals->get_at(0) - machine_nozzle_diameter) < 1e-3) { printer_preset = &(*printer_it); } @@ -15260,7 +15386,7 @@ bool Plater::check_printer_initialized(MachineObject *obj, bool only_warning, bo if (!has_been_initialized) { if (popup_warning) { if (!only_warning) { - if (DeviceManager::get_printer_can_set_nozzle(obj->printer_type)) { + if (DeviceManager::get_printer_can_set_nozzle(obj->get_show_printer_type())) { MessageDialog dlg(wxGetApp().plater(), _L("The nozzle type is not set. Please set the nozzle and try again."), _L("Warning"), wxOK | wxICON_WARNING); dlg.ShowModal(); } else { @@ -15315,16 +15441,16 @@ TriangleMesh Plater::combine_mesh_fff(const ModelObject& mo, int instance_id, st csgmesh.reserve(2 * mo.volumes.size()); bool has_splitable_volume = csg::model_to_csgmesh(mo.const_volumes(), Transform3d::Identity(), std::back_inserter(csgmesh), csg::mpartsPositive | csg::mpartsNegative); - csg::BooleanFailReason fail_reason; - std::string fail_msg = check_boolean_possible(mo.const_volumes(), fail_reason); - if (fail_msg.empty() || fail_reason == csg::BooleanFailReason::NotBoundAVolume) { - try { - MeshBoolean::mcut::McutMeshPtr meshPtr = csg::perform_csgmesh_booleans_mcut(Range{std::begin(csgmesh), std::end(csgmesh)}); - mesh = MeshBoolean::mcut::mcut_to_triangle_mesh(*meshPtr); - if (mesh.its.indices.size() > 0) { - fail_msg = ""; - } - } catch (...) {} + csg::BooleanFailReason fail_reason; + std::string fail_msg = check_boolean_possible(mo.const_volumes(), fail_reason); + if (fail_msg.empty() || fail_reason == csg::BooleanFailReason::NotBoundAVolume) { + try { + MeshBoolean::mcut::McutMeshPtr meshPtr = csg::perform_csgmesh_booleans_mcut(Range{std::begin(csgmesh), std::end(csgmesh)}); + mesh = MeshBoolean::mcut::mcut_to_triangle_mesh(*meshPtr); + if (mesh.its.indices.size() > 0) { + fail_msg = ""; + } + } catch (...) {} #if 0 // if mcut fails, try again with CGAL if (mesh.empty()) { @@ -15711,8 +15837,8 @@ int Plater::export_3mf(const boost::filesystem::path& output_path, SaveStrategy const std::string path_u8 = into_u8(path); wxBusyCursor wait; - BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << boost::format(": path=%1%, backup=%2%, export_plate_idx=%3%, SaveStrategy=%4%") - %output_path.string()%(strategy & SaveStrategy::Backup)%export_plate_idx %(unsigned int)strategy; + BOOST_LOG_TRIVIAL(trace) << __FUNCTION__ << boost::format(": path=%1%, backup=%2%, export_plate_idx=%3%, SaveStrategy=%4%") % PathSanitizer::sanitize(output_path) % + (strategy & SaveStrategy::Backup) % export_plate_idx % (unsigned int) strategy; BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": path=%1%, backup=%2%, export_plate_idx=%3%, SaveStrategy=%4%") % std::string("") % (strategy & SaveStrategy::Backup) % export_plate_idx % (unsigned int)strategy; @@ -16405,7 +16531,7 @@ void Plater::print_job_finished(wxCommandEvent &evt) { //start print failed if (p) { - #ifdef __APPLE__ +#ifdef __APPLE__ p->hide_select_machine_dlg(); #else if (Slic3r::GUI::wxGetApp().get_inf_dialog_contect().empty()) { @@ -16420,7 +16546,6 @@ void Plater::print_job_finished(wxCommandEvent &evt) Slic3r::DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager(); if (!dev) return; - dev->set_selected_machine(evt.GetString().ToStdString()); p->main_frame->request_select_tab(MainFrame::TabPosition::tpMonitor); //jump to monitor and select device status panel MonitorPanel* curr_monitor = p->main_frame->m_monitor; @@ -16508,29 +16633,6 @@ bool Plater::undo_redo_string_getter(const bool is_undo, int idx, const char** o return false; } -bool Plater::on_filament_change(size_t filament_idx) -{ - auto& filament_presets = wxGetApp().preset_bundle->filament_presets; - if (filament_idx >= filament_presets.size()) - return false; - Slic3r::Preset* filament = wxGetApp().preset_bundle->filaments.find_preset(filament_presets[filament_idx]); - if (filament == nullptr) - return false; - std::string filament_type = filament->config.option("filament_type")->values[0]; - if (filament_type == "PVA") { - auto nozzle_diameters = p->config->option("nozzle_diameter")->values; - if (std::find(nozzle_diameters.begin(), nozzle_diameters.end(), 0.2) != nozzle_diameters.end()) { - wxString msg_text = _(L("It is not recommended to use PVA filaments with 0.2mm nozzles.")); - msg_text += "\n" + _(L("Are you sure to use them? \n")); - MessageDialog dialog(wxGetApp().plater(), msg_text, "", wxICON_WARNING | wxYES | wxNO); - if (dialog.ShowModal() == wxID_NO) { - return false; - } - } - } - return true; -} - int Plater::update_print_required_data(Slic3r::DynamicPrintConfig config, Slic3r::Model model, Slic3r::PlateDataPtrs plate_data_list, std::string file_name, std::string file_path) { return p->update_print_required_data(config, model, plate_data_list, file_name, file_path); @@ -16562,7 +16664,30 @@ bool Plater::search_string_getter(int idx, const char** label, const char** tool return false; } -// QDS. +bool Plater::on_filament_change(size_t filament_idx) +{ + auto& filament_presets = wxGetApp().preset_bundle->filament_presets; + if (filament_idx >= filament_presets.size()) + return false; + Slic3r::Preset* filament = wxGetApp().preset_bundle->filaments.find_preset(filament_presets[filament_idx]); + if (filament == nullptr) + return false; + std::string filament_type = filament->config.option("filament_type")->values[0]; + if (filament_type == "PVA") { + auto nozzle_diameters = p->config->option("nozzle_diameter")->values; + if (std::find(nozzle_diameters.begin(), nozzle_diameters.end(), 0.2) != nozzle_diameters.end()) { + wxString msg_text = _(L("It is not recommended to use PVA filaments with 0.2mm nozzles.")); + msg_text += "\n" + _(L("Are you sure to use them? \n")); + MessageDialog dialog(wxGetApp().plater(), msg_text, "", wxICON_WARNING | wxYES | wxNO); + if (dialog.ShowModal() == wxID_NO) { + return false; + } + } + } + return true; +} + +// BBS. void Plater::on_filament_count_change(size_t num_filaments) { // only update elements in plater @@ -16945,6 +17070,26 @@ std::vector Plater::get_extruder_colors_from_plater_config(const GC } } +std::vector Plater::get_filament_colors_render_info() const +{ + const Slic3r::DynamicPrintConfig* config = &wxGetApp().preset_bundle->project_config; + std::vector color_packs; + if (!config->has("filament_multi_colour")) return color_packs; + + color_packs = (config->option("filament_multi_colour"))->values; + return color_packs; +} + +std::vector Plater::get_filament_color_render_type() const +{ + const Slic3r::DynamicPrintConfig *config = &wxGetApp().preset_bundle->project_config; + std::vector ctype; + if (!config->has("filament_colour_type")) return ctype; + + ctype = (config->option("filament_colour_type"))->values; + return ctype; +} + /* Get vector of colors used for rendering of a Preview scene in "Color print" mode * It consists of extruder colors and colors, saved in model.custom_gcode_per_print_z */ @@ -17172,8 +17317,9 @@ void Plater::clear_before_change_mesh(int obj_idx) // may be different and they would make no sense. bool paint_removed = false; for (ModelVolume* mv : mo->volumes) { - paint_removed |= ! mv->supported_facets.empty() || ! mv->seam_facets.empty() || ! mv->mmu_segmentation_facets.empty(); + paint_removed |= !mv->supported_facets.empty() || !mv->fuzzy_skin_facets.empty() || !mv->seam_facets.empty() || !mv->mmu_segmentation_facets.empty(); mv->supported_facets.reset(); + mv->fuzzy_skin_facets.reset(); mv->seam_facets.reset(); mv->mmu_segmentation_facets.reset(); } @@ -17186,6 +17332,32 @@ void Plater::clear_before_change_mesh(int obj_idx) } } +void Plater::clear_before_change_mesh(int obj_idx, int vol_idx) +{ + ModelObject *mo = model().objects[obj_idx]; + + // If there are custom supports/seams/mmu segmentation, remove them. Fixed mesh + // may be different and they would make no sense. + bool paint_removed = false; + std::string model_name = ""; + for (size_t i = 0; i < mo->volumes.size(); i++) { + if (vol_idx != i) { continue; } + auto mv = mo->volumes[i]; + model_name = mv->name; + paint_removed |= !mv->supported_facets.empty() || !mv->fuzzy_skin_facets.empty() || !mv->seam_facets.empty() || !mv->mmu_segmentation_facets.empty(); + mv->supported_facets.reset(); + mv->fuzzy_skin_facets.reset(); + mv->seam_facets.reset(); + mv->mmu_segmentation_facets.reset(); + } + if (paint_removed) { + // snapshot_time is captured by copy so the lambda knows where to undo/redo to. + get_notification_manager()->push_notification(NotificationType::CustomSupportsAndSeamRemovedAfterRepair, + NotificationManager::NotificationLevel::PrintInfoNotificationLevel, + _u8L("Custom supports,seam_facets and color painting were removed before repairing,model name:") + model_name); + } +} + void Plater::changed_mesh(int obj_idx) { ModelObject* mo = model().objects[obj_idx]; @@ -17309,7 +17481,7 @@ void Plater::pop_warning_and_go_to_device_page(wxString printer_name, PrinterWar } else if (type == PrinterWarningType::EMPTY_FILAMENT) { content = _L("The filaments on the printer are all unknown types. Please go to the printer screen or software device page to set the filament type."); } - MessageDialog dlg(this, content, title, wxOK | wxICON_WARNING); + MessageDialog dlg(this, content, title, wxOK | wxFORWARD | wxICON_WARNING, _L("Device Page")); auto result = dlg.ShowModal(); if (result == wxFORWARD) { wxGetApp().mainframe->select_tab(size_t(MainFrame::tpMonitor)); @@ -17571,7 +17743,6 @@ int Plater::select_plate(int plate_index, bool need_slice) } const auto& preset_bundle = wxGetApp().preset_bundle; - if ((!ret) && (p->background_process.can_switch_print())) { //select successfully @@ -18290,7 +18461,9 @@ void Plater::show_object_info() t = model_object->instances[inst_idx]->get_matrix() * vol->get_matrix(); info_text += (boost::format(_utf8(L("Part name: %1%\n"))) % vol->name).str(); face_count = static_cast(vol->mesh().facets_count()); - size = vol->get_convex_hull().transformed_bounding_box(t).size(); + if (&vol->get_convex_hull()) { + size = vol->get_convex_hull().transformed_bounding_box(t).size(); + } } else { //int obj_idx, vol_idx; @@ -18579,6 +18752,7 @@ void Plater::show_status_message(std::string s) void Plater::edit_text() { auto &manager = get_view3D_canvas3D()->get_gizmos_manager(); + Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Edit existed text"); manager.open_gizmo(GLGizmosManager::Text); update(); } @@ -18680,6 +18854,14 @@ bool Plater::is_show_non_manifold_edges() { return p->show_non_manifold_edges; } +void Plater::toggle_text_cs() { + p->show_text_cs = !p->show_text_cs; +} + +bool Plater::is_show_text_cs() { + return p->show_text_cs; +} + void Plater::toggle_show_wireframe() { p->show_wireframe = !p->show_wireframe; } @@ -18764,8 +18946,9 @@ bool Plater::PopupObjectTableBySelection() wxMenu* Plater::plate_menu() { return p->menus.plate_menu(); } wxMenu* Plater::object_menu() { return p->menus.object_menu(); } wxMenu *Plater::part_menu() { return p->menus.part_menu(); } +wxMenu *Plater::text_part_menu() { return p->menus.text_part_menu(); } wxMenu *Plater::svg_part_menu() { return p->menus.svg_part_menu(); } -wxMenu* Plater::cut_connector_menu() { return p->menus.cut_connector_menu(); } +wxMenu* Plater::cut_connector_menu() { return p->menus.cut_connector_menu(); } wxMenu* Plater::sla_object_menu() { return p->menus.sla_object_menu(); } wxMenu* Plater::default_menu() { return p->menus.default_menu(); } wxMenu* Plater::instance_menu() { return p->menus.instance_menu(); } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index ae792af..57657ec 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -141,7 +141,7 @@ struct Box_msg { }; class Sidebar : public wxPanel -{ +{ ConfigOptionMode m_mode; Button * btn_sync{nullptr}; ScalableButton * ams_btn{nullptr}; @@ -404,7 +404,9 @@ public: void invalid_all_plate_thumbnails(); void force_update_all_plate_thumbnails(); - const VendorProfile::PrinterModel *get_curr_printer_model(); + const VendorProfile::PrinterModel * get_curr_printer_model(); + std::map get_bed_texture_maps(); + int get_right_icon_offset_bed(); static wxColour get_next_color_for_filament(); static wxString get_slice_warning_string(GCodeProcessorResult::SliceWarning& warning); @@ -528,6 +530,7 @@ public: void reslice_SLA_until_step(SLAPrintObjectStep step, const ModelObject &object, bool postpone_error_messages = false); void clear_before_change_mesh(int obj_idx); + void clear_before_change_mesh(int obj_idx, int vol_idx); void changed_mesh(int obj_idx); void changed_object(ModelObject &object); @@ -578,6 +581,7 @@ public: std::vector> get_extruders_colors(); // QDS void on_bed_type_change(BedType bed_type,bool is_gcode_file = false); + bool update_filament_colors_in_full_config(); void config_change_notification(const DynamicPrintConfig &config, const std::string& key); void on_config_change(const DynamicPrintConfig &config); @@ -586,6 +590,8 @@ public: // On activating the parent window. void on_activate(); std::vector get_extruder_colors_from_plater_config(const GCodeProcessorResult* const result = nullptr) const; + std::vector get_filament_colors_render_info() const; + std::vector get_filament_color_render_type() const; std::vector get_colors_for_color_print(const GCodeProcessorResult* const result = nullptr) const; void set_global_filament_map_mode(FilamentMapMode mode); @@ -594,7 +600,6 @@ public: FilamentMapMode get_global_filament_map_mode() const; void update_menus(); - wxString get_selected_printer_name_in_combox(); enum class PrinterWarningType { NOT_CONNECTED, @@ -887,6 +892,8 @@ public: void toggle_non_manifold_edges(); bool is_show_non_manifold_edges(); + void toggle_text_cs(); + bool is_show_text_cs(); void toggle_show_wireframe(); bool is_show_wireframe() const; void enable_wireframe(bool status); @@ -905,6 +912,7 @@ public: wxMenu* plate_menu(); wxMenu* object_menu(); wxMenu* part_menu(); + wxMenu* text_part_menu(); wxMenu* svg_part_menu(); wxMenu* cut_connector_menu(); wxMenu* sla_object_menu(); @@ -920,6 +928,7 @@ public: static bool has_illegal_filename_characters(const std::string& name); static void show_illegal_characters_warning(wxWindow* parent); + std::string get_preview_only_filename() { return m_preview_only_filename; }; bool last_arrange_job_is_finished() @@ -930,6 +939,10 @@ public: std::atomic m_arrange_running{false}; void reset_check_status() { m_check_status = 0; } + void mark_plate_toolbar_image_dirty(); + bool is_plate_toolbar_image_dirty() const; + void clear_plate_toolbar_image_dirty(); + //y void resetUploadCount(); //y58 @@ -968,6 +981,7 @@ private: std::string m_preview_only_filename; int m_valid_plates_count { 0 }; int m_check_status = 0; // 0 not check, 1 check success, 2 check failed + bool m_b_plate_toolbar_image_dirty{ true }; void suppress_snapshots(); void allow_snapshots(); diff --git a/src/slic3r/GUI/PrePrintChecker.cpp b/src/slic3r/GUI/PrePrintChecker.cpp index 315db1f..0e68239 100644 --- a/src/slic3r/GUI/PrePrintChecker.cpp +++ b/src/slic3r/GUI/PrePrintChecker.cpp @@ -1,7 +1,7 @@ #include "PrePrintChecker.hpp" #include "GUI_Utils.hpp" #include "I18N.hpp" - +#include namespace Slic3r { namespace GUI { @@ -94,7 +94,7 @@ void PrePrintChecker::clear() filamentList.clear(); } -void PrePrintChecker::add(PrintDialogStatus state, wxString msg, wxString tip) +void PrePrintChecker::add(PrintDialogStatus state, wxString msg, wxString tip, const wxString& wiki_url) { prePrintInfo info; @@ -124,12 +124,18 @@ void PrePrintChecker::add(PrintDialogStatus state, wxString msg, wxString tip) info.tips = wxEmptyString; } + info.wiki_url = wiki_url; + switch (info.type) { case prePrintInfoType::Filament: - filamentList.push_back(info); + if (std::find(filamentList.begin(), filamentList.end(), info) == filamentList.end()) { + filamentList.push_back(info); + } break; case prePrintInfoType::Printer: - printerList.push_back(info); + if (std::find(printerList.begin(), printerList.end(), info) == printerList.end()) { + printerList.push_back(info); + } break; default: break; } @@ -171,68 +177,64 @@ PrinterMsgPanel::PrinterMsgPanel(wxWindow *parent) this->SetSizer(m_sizer); } -void PrinterMsgPanel::SetLabelList(const std::vector &texts, const wxColour &colour) +static wxColour _GetLabelColour(const prePrintInfo& info) { - if (texts == m_last_texts) - return; - - m_last_texts = texts; - m_labels.clear(); - m_sizer->Clear(true); - std::set unique_texts; - - for (const wxString &text : texts) { - if (text.empty()) { - continue; - } - if (!unique_texts.insert(text).second) { - continue; - } - Label *label = new Label(this); - label->SetFont(::Label::Body_13); - label->SetForegroundColour(colour); - label->SetLabel(text); - label->Wrap(this->GetMinSize().GetWidth()); - label->Show(); - m_sizer->Add(label, 0, wxBOTTOM, FromDIP(4)); - m_labels.push_back(label); + if (info.level == Error) + { + return wxColour("#D01B1B"); } + else if (info.level == Warning) + { + return wxColour("#FF6F00"); + } + + return *wxBLACK; // Default colour for normal messages +} + +bool PrinterMsgPanel::UpdateInfos(const std::vector& infos) +{ + if (m_infos == infos) + { + return false; + } + m_infos = infos; + + m_sizer->Clear(true); + for (const prePrintInfo& info : infos) + { + if (!info.msg.empty()) + { + Label* label = new Label(this); + label->SetFont(::Label::Body_13); + label->SetForegroundColour(_GetLabelColour(info)); + + + if (info.wiki_url.empty()) + { + label->SetLabel(info.msg); + } + else + { + label->SetLabel(info.msg + " " + _L("Please refer to Wiki before use->")); + label->Bind(wxEVT_ENTER_WINDOW, [this](auto& e) { SetCursor(wxCURSOR_HAND); }); + label->Bind(wxEVT_LEAVE_WINDOW, [this](auto& e) { SetCursor(wxCURSOR_ARROW); }); + label->Bind(wxEVT_LEFT_DOWN, [info](wxMouseEvent& event) { wxLaunchDefaultBrowser(info.wiki_url); }); + } + + label->Wrap(this->GetMinSize().GetWidth()); + label->Show(); + m_sizer->Add(label, 0, wxBOTTOM, FromDIP(4)); + } + } + this->Show(); this->Layout(); + Fit(); + + return true; } -//void PrinterMsgPanel::SetLabelSingle(const wxString &texts, const wxColour &colour) -//{ -// Label *label = new Label(this); -// label->SetMinSize(wxSize(FromDIP(420), -1)); -// label->SetMaxSize(wxSize(FromDIP(420), -1)); -// label->SetFont(::Label::Body_13); -// label->SetForegroundColour(colour); -// label->SetLabel(texts); -// label->Wrap(FromDIP(-1)); -// label->Show(); -// m_sizer->Add(label, 0, wxBOTTOM, FromDIP(4)); -// m_labels.push_back(label); -// this->Layout(); -// Fit(); -//} - -wxString PrinterMsgPanel::GetLabel() { - if (!m_labels.empty() && m_labels[0] != nullptr) - return m_labels[0]->GetLabel(); - return wxEmptyString; -} - - -std::vector PrinterMsgPanel::GetLabelList() { - if (m_last_texts.empty()) - wxLogDebug(_L("No labels are currently stored.")); - return m_last_texts; -} - - - } }; diff --git a/src/slic3r/GUI/PrePrintChecker.hpp b/src/slic3r/GUI/PrePrintChecker.hpp index 96398cc..7487b52 100644 --- a/src/slic3r/GUI/PrePrintChecker.hpp +++ b/src/slic3r/GUI/PrePrintChecker.hpp @@ -22,7 +22,15 @@ struct prePrintInfo prePrintInfoType type; wxString msg; wxString tips; + wxString wiki_url; int index; + +public: + bool operator==(const prePrintInfo& other) const { + return level == other.level && type == other.type && + msg == other.msg && tips == other.tips && + wiki_url == other.wiki_url && index == other.index; + } }; enum PrintDialogStatus : unsigned int { @@ -58,6 +66,7 @@ enum PrintDialogStatus : unsigned int { PrintStatusBlankPlate, PrintStatusUnsupportedPrinter, PrintStatusInvalidMapping, + PrintStatusPrinterErrorEnd, // Errors for filament, Block Print @@ -68,6 +77,7 @@ enum PrintDialogStatus : unsigned int { PrintStatusAmsMappingMixInvalid, PrintStatusTPUUnsupportAutoCali, PrintStatusHasFilamentInBlackListError, + PrintStatusFilamentErrorEnd, PrintStatusErrorEnd,//->end error<- @@ -109,13 +119,20 @@ enum PrintDialogStatus : unsigned int { PrintStatusPublicInitFailed, PrintStatusPublicUploadFiled, - //y65 - PrintStatusPrinterOffline, + //y68 + QDTPrinterErrorBegin, PrintStatusPrinterNotStandby, PrintStatusPinrterPrinting, - PrinterNotConnectBox, - PrintStatusNeedUpgradingBox, + PrintStatusPrinterOffline, + QDTPrinterErrorEnd, + + //y68 + QDTBoxErrorBegin, BoxhasSomeProblem, + PrintStatusNeedUpgradingBox, + PrinterNotConnectBox, + QDTBoxErrorEnd, + }; class PrePrintChecker @@ -127,13 +144,16 @@ public: public: void clear(); /*auto merge*/ - void add(PrintDialogStatus state, wxString msg, wxString tip); + void add(PrintDialogStatus state, wxString msg, wxString tip, const wxString& wiki_url); static ::std::string get_print_status_info(PrintDialogStatus status); wxString get_pre_state_msg(PrintDialogStatus status); - static bool is_error(PrintDialogStatus status) { return (PrintStatusErrorBegin < status) && (PrintStatusErrorEnd > status); }; - static bool is_error_printer(PrintDialogStatus status) { return (PrintStatusPrinterErrorBegin < status) && (PrintStatusPrinterErrorEnd > status); }; - static bool is_error_filament(PrintDialogStatus status) { return (PrintStatusFilamentErrorBegin < status) && (PrintStatusFilamentErrorEnd > status); }; + + //y68 + static bool is_error(PrintDialogStatus status) { return ((PrintStatusErrorBegin < status) && (PrintStatusErrorEnd > status)) || ((QDTPrinterErrorBegin < status) && (QDTBoxErrorEnd > status)); }; + static bool is_error_printer(PrintDialogStatus status) { return ((PrintStatusPrinterErrorBegin < status) && (PrintStatusPrinterErrorEnd > status)) || ((QDTPrinterErrorBegin < status) && (QDTPrinterErrorEnd > status)); }; + static bool is_error_filament(PrintDialogStatus status) { return ((PrintStatusFilamentErrorBegin < status) && (PrintStatusFilamentErrorEnd > status)) || ((QDTBoxErrorBegin < status) || (QDTBoxErrorEnd > status)); }; + static bool is_warning(PrintDialogStatus status) { return (PrintStatusWarningBegin < status) && (PrintStatusWarningEnd > status); }; static bool is_warning_printer(PrintDialogStatus status) { return (PrintStatusPrinterWarningBegin < status) && (PrintStatusPrinterWarningEnd > status); }; static bool is_warning_filament(PrintDialogStatus status) { return (PrintStatusFilamentWarningBegin < status) && (PrintStatusFilamentWarningEnd > status); }; @@ -172,17 +192,13 @@ class PrinterMsgPanel : public wxPanel public: PrinterMsgPanel(wxWindow *parent); - void SetLabelList(const std::vector &texts, const wxColour &colour); - - // void SetLabelSingle(const wxString &texts,const wxColour& colour); - - wxString GetLabel(); - std::vector GetLabelList(); +public: + bool UpdateInfos(const std::vector& infos); private: - wxBoxSizer * m_sizer = nullptr; - std::vector