Merge branch 'master' of https://github.com/Prusa3d/PrusaSlicer
This commit is contained in:
commit
0b9b74e35e
41 changed files with 5144 additions and 348 deletions
3
deps/blosc-mods.patch
vendored
3
deps/blosc-mods.patch
vendored
|
@ -1,8 +1,9 @@
|
||||||
From 5669891dfaaa4c814f3ec667ca6bf4e693aea978 Mon Sep 17 00:00:00 2001
|
From 7cf6c014a36f1712efbdbe9bc52d2d4922b54673 Mon Sep 17 00:00:00 2001
|
||||||
From: tamasmeszaros <meszaros.q@gmail.com>
|
From: tamasmeszaros <meszaros.q@gmail.com>
|
||||||
Date: Wed, 30 Oct 2019 12:54:52 +0100
|
Date: Wed, 30 Oct 2019 12:54:52 +0100
|
||||||
Subject: [PATCH] Blosc 1.17 fixes and cmake config script
|
Subject: [PATCH] Blosc 1.17 fixes and cmake config script
|
||||||
|
|
||||||
|
Signed-off-by: tamasmeszaros <meszaros.q@gmail.com>
|
||||||
---
|
---
|
||||||
CMakeLists.txt | 105 +++++++++++++++++-----------------
|
CMakeLists.txt | 105 +++++++++++++++++-----------------
|
||||||
blosc/CMakeLists.txt | 118 +++++++++------------------------------
|
blosc/CMakeLists.txt | 118 +++++++++------------------------------
|
||||||
|
|
17
deps/deps-unix-common.cmake
vendored
17
deps/deps-unix-common.cmake
vendored
|
@ -55,14 +55,14 @@ find_package(Git REQUIRED)
|
||||||
|
|
||||||
ExternalProject_Add(dep_qhull
|
ExternalProject_Add(dep_qhull
|
||||||
EXCLUDE_FROM_ALL 1
|
EXCLUDE_FROM_ALL 1
|
||||||
URL "https://github.com/qhull/qhull/archive/v7.3.2.tar.gz"
|
#URL "https://github.com/qhull/qhull/archive/v7.3.2.tar.gz"
|
||||||
URL_HASH SHA256=619c8a954880d545194bc03359404ef36a1abd2dde03678089459757fd790cb0
|
#URL_HASH SHA256=619c8a954880d545194bc03359404ef36a1abd2dde03678089459757fd790cb0
|
||||||
|
GIT_REPOSITORY https://github.com/qhull/qhull.git
|
||||||
|
GIT_TAG 7afedcc73666e46a9f1d74632412ebecf53b1b30 # v7.3.2 plus the mac build patch
|
||||||
CMAKE_ARGS
|
CMAKE_ARGS
|
||||||
-DBUILD_SHARED_LIBS=OFF
|
-DBUILD_SHARED_LIBS=OFF
|
||||||
-DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local
|
-DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local
|
||||||
${DEP_CMAKE_OPTS}
|
${DEP_CMAKE_OPTS}
|
||||||
UPDATE_COMMAND ""
|
|
||||||
PATCH_COMMAND patch -p1 < ${CMAKE_CURRENT_SOURCE_DIR}/qhull-mods.patch
|
|
||||||
)
|
)
|
||||||
|
|
||||||
ExternalProject_Add(dep_blosc
|
ExternalProject_Add(dep_blosc
|
||||||
|
@ -80,8 +80,8 @@ ExternalProject_Add(dep_blosc
|
||||||
-DBUILD_TESTS=OFF
|
-DBUILD_TESTS=OFF
|
||||||
-DBUILD_BENCHMARKS=OFF
|
-DBUILD_BENCHMARKS=OFF
|
||||||
-DPREFER_EXTERNAL_ZLIB=ON
|
-DPREFER_EXTERNAL_ZLIB=ON
|
||||||
UPDATE_COMMAND ""
|
PATCH_COMMAND ${GIT_EXECUTABLE} reset --hard && git clean -df &&
|
||||||
PATCH_COMMAND ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/blosc-mods.patch
|
${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/blosc-mods.patch
|
||||||
)
|
)
|
||||||
|
|
||||||
ExternalProject_Add(dep_openexr
|
ExternalProject_Add(dep_openexr
|
||||||
|
@ -96,7 +96,6 @@ ExternalProject_Add(dep_openexr
|
||||||
-DPYILMBASE_ENABLE:BOOL=OFF
|
-DPYILMBASE_ENABLE:BOOL=OFF
|
||||||
-DOPENEXR_VIEWERS_ENABLE:BOOL=OFF
|
-DOPENEXR_VIEWERS_ENABLE:BOOL=OFF
|
||||||
-DOPENEXR_BUILD_UTILS:BOOL=OFF
|
-DOPENEXR_BUILD_UTILS:BOOL=OFF
|
||||||
UPDATE_COMMAND ""
|
|
||||||
)
|
)
|
||||||
|
|
||||||
ExternalProject_Add(dep_openvdb
|
ExternalProject_Add(dep_openvdb
|
||||||
|
@ -116,6 +115,6 @@ ExternalProject_Add(dep_openvdb
|
||||||
-DOPENVDB_CORE_STATIC=ON
|
-DOPENVDB_CORE_STATIC=ON
|
||||||
-DTBB_STATIC=ON
|
-DTBB_STATIC=ON
|
||||||
-DOPENVDB_BUILD_VDB_PRINT=ON
|
-DOPENVDB_BUILD_VDB_PRINT=ON
|
||||||
UPDATE_COMMAND ""
|
PATCH_COMMAND PATCH_COMMAND ${GIT_EXECUTABLE} checkout -f -- . && git clean -df &&
|
||||||
PATCH_COMMAND ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/openvdb-mods.patch
|
${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/openvdb-mods.patch
|
||||||
)
|
)
|
18
deps/deps-windows.cmake
vendored
18
deps/deps-windows.cmake
vendored
|
@ -218,15 +218,16 @@ find_package(Git REQUIRED)
|
||||||
|
|
||||||
ExternalProject_Add(dep_qhull
|
ExternalProject_Add(dep_qhull
|
||||||
EXCLUDE_FROM_ALL 1
|
EXCLUDE_FROM_ALL 1
|
||||||
URL "https://github.com/qhull/qhull/archive/v7.3.2.tar.gz"
|
#URL "https://github.com/qhull/qhull/archive/v7.3.2.tar.gz"
|
||||||
URL_HASH SHA256=619c8a954880d545194bc03359404ef36a1abd2dde03678089459757fd790cb0
|
#URL_HASH SHA256=619c8a954880d545194bc03359404ef36a1abd2dde03678089459757fd790cb0
|
||||||
|
GIT_REPOSITORY https://github.com/qhull/qhull.git
|
||||||
|
GIT_TAG 7afedcc73666e46a9f1d74632412ebecf53b1b30 # v7.3.2 plus the mac build patch
|
||||||
CMAKE_GENERATOR "${DEP_MSVC_GEN}"
|
CMAKE_GENERATOR "${DEP_MSVC_GEN}"
|
||||||
CMAKE_ARGS
|
CMAKE_ARGS
|
||||||
-DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local
|
-DCMAKE_INSTALL_PREFIX=${DESTDIR}/usr/local
|
||||||
-DBUILD_SHARED_LIBS=OFF
|
-DBUILD_SHARED_LIBS=OFF
|
||||||
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
-DCMAKE_POSITION_INDEPENDENT_CODE=ON
|
||||||
-DCMAKE_DEBUG_POSTFIX=d
|
-DCMAKE_DEBUG_POSTFIX=d
|
||||||
UPDATE_COMMAND ""
|
|
||||||
BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj
|
BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj
|
||||||
INSTALL_COMMAND ""
|
INSTALL_COMMAND ""
|
||||||
)
|
)
|
||||||
|
@ -287,8 +288,8 @@ ExternalProject_Add(dep_blosc
|
||||||
-DPREFER_EXTERNAL_ZLIB=ON
|
-DPREFER_EXTERNAL_ZLIB=ON
|
||||||
-DBLOSC_IS_SUBPROJECT:BOOL=ON
|
-DBLOSC_IS_SUBPROJECT:BOOL=ON
|
||||||
-DBLOSC_INSTALL:BOOL=ON
|
-DBLOSC_INSTALL:BOOL=ON
|
||||||
UPDATE_COMMAND ""
|
PATCH_COMMAND ${GIT_EXECUTABLE} checkout -f -- . && git clean -df &&
|
||||||
PATCH_COMMAND ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/blosc-mods.patch
|
${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/blosc-mods.patch
|
||||||
BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj
|
BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj
|
||||||
INSTALL_COMMAND ""
|
INSTALL_COMMAND ""
|
||||||
)
|
)
|
||||||
|
@ -310,7 +311,6 @@ ExternalProject_Add(dep_openexr
|
||||||
-DPYILMBASE_ENABLE:BOOL=OFF
|
-DPYILMBASE_ENABLE:BOOL=OFF
|
||||||
-DOPENEXR_VIEWERS_ENABLE:BOOL=OFF
|
-DOPENEXR_VIEWERS_ENABLE:BOOL=OFF
|
||||||
-DOPENEXR_BUILD_UTILS:BOOL=OFF
|
-DOPENEXR_BUILD_UTILS:BOOL=OFF
|
||||||
UPDATE_COMMAND ""
|
|
||||||
BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj
|
BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj
|
||||||
INSTALL_COMMAND ""
|
INSTALL_COMMAND ""
|
||||||
)
|
)
|
||||||
|
@ -323,7 +323,7 @@ ExternalProject_Add(dep_openvdb
|
||||||
#URL_HASH SHA256=dc337399dce8e1c9f21f20e97b1ce7e4933cb0a63bb3b8b734d8fcc464aa0c48
|
#URL_HASH SHA256=dc337399dce8e1c9f21f20e97b1ce7e4933cb0a63bb3b8b734d8fcc464aa0c48
|
||||||
GIT_REPOSITORY https://github.com/AcademySoftwareFoundation/openvdb.git
|
GIT_REPOSITORY https://github.com/AcademySoftwareFoundation/openvdb.git
|
||||||
GIT_TAG aebaf8d95be5e57fd33949281ec357db4a576c2e #v6.2.1
|
GIT_TAG aebaf8d95be5e57fd33949281ec357db4a576c2e #v6.2.1
|
||||||
DEPENDS dep_blosc dep_openexr #dep_tbb dep_boost
|
DEPENDS dep_blosc dep_openexr dep_tbb dep_boost
|
||||||
CMAKE_GENERATOR "${DEP_MSVC_GEN}"
|
CMAKE_GENERATOR "${DEP_MSVC_GEN}"
|
||||||
CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}"
|
CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}"
|
||||||
CMAKE_ARGS
|
CMAKE_ARGS
|
||||||
|
@ -339,8 +339,8 @@ ExternalProject_Add(dep_openvdb
|
||||||
-DTBB_STATIC=ON
|
-DTBB_STATIC=ON
|
||||||
-DOPENVDB_BUILD_VDB_PRINT=ON
|
-DOPENVDB_BUILD_VDB_PRINT=ON
|
||||||
BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj
|
BUILD_COMMAND msbuild /m /P:Configuration=Release INSTALL.vcxproj
|
||||||
UPDATE_COMMAND ""
|
PATCH_COMMAND ${GIT_EXECUTABLE} checkout -f -- . && git clean -df &&
|
||||||
PATCH_COMMAND ${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/openvdb-mods.patch
|
${GIT_EXECUTABLE} apply --whitespace=fix ${CMAKE_CURRENT_SOURCE_DIR}/openvdb-mods.patch
|
||||||
INSTALL_COMMAND ""
|
INSTALL_COMMAND ""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
3
deps/openvdb-mods.patch
vendored
3
deps/openvdb-mods.patch
vendored
|
@ -1,8 +1,9 @@
|
||||||
From e48f4a835fe7cb391f9f90945472bd367fb4c4f1 Mon Sep 17 00:00:00 2001
|
From dbe038fce8a15ddc9a5c83ec5156d7bc9e178015 Mon Sep 17 00:00:00 2001
|
||||||
From: tamasmeszaros <meszaros.q@gmail.com>
|
From: tamasmeszaros <meszaros.q@gmail.com>
|
||||||
Date: Wed, 16 Oct 2019 17:42:50 +0200
|
Date: Wed, 16 Oct 2019 17:42:50 +0200
|
||||||
Subject: [PATCH] Build fixes for PrusaSlicer integration
|
Subject: [PATCH] Build fixes for PrusaSlicer integration
|
||||||
|
|
||||||
|
Signed-off-by: tamasmeszaros <meszaros.q@gmail.com>
|
||||||
---
|
---
|
||||||
CMakeLists.txt | 3 -
|
CMakeLists.txt | 3 -
|
||||||
cmake/FindBlosc.cmake | 218 ---------------
|
cmake/FindBlosc.cmake | 218 ---------------
|
||||||
|
|
|
@ -1,109 +1,70 @@
|
||||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="180.7mm" height="180.6mm" viewBox="0 0 512.1 512">
|
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" width="180.5mm" height="180.6mm" viewBox="0 0 511.7 512">
|
||||||
<title>bed_texture_denser</title>
|
<title>MINI_bed_texture</title>
|
||||||
<path d="M510.6,510.9" transform="translate(0.7 0.4)" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/>
|
<path d="M510.6,510.9" transform="translate(0.4 0.4)" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/>
|
||||||
<path d="M.4,510.9" transform="translate(0.7 0.4)" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/>
|
<path d="M.4,510.9" transform="translate(0.4 0.4)" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/>
|
||||||
<line x1="511.3" y1="511.3" x2="511.3" y2="0.8" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/>
|
<line x1="511" y1="511.3" x2="511" y2="0.8" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/>
|
||||||
<path d="M.4.4" transform="translate(0.7 0.4)" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/>
|
<path d="M.4.4" transform="translate(0.4 0.4)" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/>
|
||||||
<path d="M510.6.4" transform="translate(0.7 0.4)" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/>
|
<path d="M510.6.4" transform="translate(0.4 0.4)" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/>
|
||||||
<line x1="1.1" y1="0.8" x2="1.1" y2="511.3" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/>
|
<line x1="0.8" y1="0.8" x2="0.8" y2="511.3" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/>
|
||||||
<line x1="1.1" y1="511.3" x2="1.1" y2="0.8" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/>
|
<line x1="0.8" y1="511.3" x2="0.8" y2="0.8" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/>
|
||||||
<line x1="511.3" y1="0.8" x2="511.3" y2="511.3" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/>
|
<line x1="511" y1="0.8" x2="511" y2="511.3" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/>
|
||||||
<line x1="1.1" y1="511.3" x2="511.3" y2="511.3" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/>
|
<line x1="0.8" y1="511.3" x2="511" y2="511.3" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/>
|
||||||
<line x1="1.1" y1="0.8" x2="511.3" y2="0.8" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/>
|
<line x1="0.8" y1="0.8" x2="511" y2="0.8" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-width: 1.5px"/>
|
||||||
<g>
|
<g>
|
||||||
<g>
|
<g>
|
||||||
<line x1="1.1" y1="383.6" x2="3.2" y2="383.6" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/>
|
<line x1="0.8" y1="383.6" x2="2.9" y2="383.6" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/>
|
||||||
<line x1="5.4" y1="383.6" x2="7" y2="383.6" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 0.5694814920425415,2.1355555057525635"/>
|
<line x1="5" y1="383.6" x2="6.6" y2="383.6" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 0.5694814920425415,2.1355555057525635"/>
|
||||||
<line x1="8.1" y1="383.6" x2="508.1" y2="383.6" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 4.271111011505127,2.1355555057525635,0.5694814920425415,2.1355555057525635"/>
|
<line x1="7.7" y1="383.6" x2="507.8" y2="383.6" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 4.271111011505127,2.1355555057525635,0.5694814920425415,2.1355555057525635"/>
|
||||||
<line x1="509.2" y1="383.6" x2="511.3" y2="383.6" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/>
|
<line x1="508.9" y1="383.6" x2="511" y2="383.6" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/>
|
||||||
</g>
|
</g>
|
||||||
<g>
|
<g>
|
||||||
<line x1="1.1" y1="256" x2="3.2" y2="256" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/>
|
<line x1="0.8" y1="256" x2="2.9" y2="256" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/>
|
||||||
<line x1="5.4" y1="256" x2="7" y2="256" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 0.5694814920425415,2.1355555057525635"/>
|
<line x1="5" y1="256" x2="6.6" y2="256" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 0.5694814920425415,2.1355555057525635"/>
|
||||||
<line x1="8.1" y1="256" x2="508.1" y2="256" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 4.271111011505127,2.1355555057525635,0.5694814920425415,2.1355555057525635"/>
|
<line x1="7.7" y1="256" x2="507.8" y2="256" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 4.271111011505127,2.1355555057525635,0.5694814920425415,2.1355555057525635"/>
|
||||||
<line x1="509.2" y1="256" x2="511.3" y2="256" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/>
|
<line x1="508.9" y1="256" x2="511" y2="256" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/>
|
||||||
</g>
|
</g>
|
||||||
<g>
|
<g>
|
||||||
<line x1="511.3" y1="128.4" x2="509.2" y2="128.4" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/>
|
<line x1="511" y1="128.4" x2="508.9" y2="128.4" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/>
|
||||||
<line x1="507.1" y1="128.4" x2="505.4" y2="128.4" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 0.5694814920425415,2.1355555057525635"/>
|
<line x1="506.7" y1="128.4" x2="505.1" y2="128.4" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 0.5694814920425415,2.1355555057525635"/>
|
||||||
<line x1="504.4" y1="128.4" x2="4.3" y2="128.4" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 4.271111011505127,2.1355555057525635,0.5694814920425415,2.1355555057525635"/>
|
<line x1="504" y1="128.4" x2="3.9" y2="128.4" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 4.271111011505127,2.1355555057525635,0.5694814920425415,2.1355555057525635"/>
|
||||||
<line x1="3.2" y1="128.4" x2="1.1" y2="128.4" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/>
|
<line x1="2.9" y1="128.4" x2="0.8" y2="128.4" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/>
|
||||||
</g>
|
</g>
|
||||||
<g>
|
<g>
|
||||||
<line x1="128.7" y1="511.3" x2="128.7" y2="509.1" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/>
|
<line x1="128.3" y1="511.3" x2="128.3" y2="509.1" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/>
|
||||||
<line x1="128.7" y1="507" x2="128.7" y2="505.4" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 0.569789707660675,2.1367111206054688"/>
|
<line x1="128.3" y1="507" x2="128.3" y2="505.4" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 0.569789707660675,2.1367111206054688"/>
|
||||||
<line x1="128.7" y1="504.3" x2="128.7" y2="3.9" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 4.2734222412109375,2.1367111206054688,0.569789707660675,2.1367111206054688"/>
|
<line x1="128.3" y1="504.3" x2="128.3" y2="3.9" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 4.2734222412109375,2.1367111206054688,0.569789707660675,2.1367111206054688"/>
|
||||||
<line x1="128.7" y1="2.9" x2="128.7" y2="0.8" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/>
|
<line x1="128.3" y1="2.9" x2="128.3" y2="0.8" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/>
|
||||||
</g>
|
</g>
|
||||||
<g>
|
<g>
|
||||||
<line x1="256.2" y1="0.8" x2="256.2" y2="2.9" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/>
|
<line x1="255.9" y1="0.8" x2="255.9" y2="2.9" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/>
|
||||||
<line x1="256.2" y1="5" x2="256.2" y2="6.7" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 0.569789707660675,2.1367111206054688"/>
|
<line x1="255.9" y1="5" x2="255.9" y2="6.7" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 0.569789707660675,2.1367111206054688"/>
|
||||||
<line x1="256.2" y1="7.7" x2="256.2" y2="508.1" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 4.2734222412109375,2.1367111206054688,0.569789707660675,2.1367111206054688"/>
|
<line x1="255.9" y1="7.7" x2="255.9" y2="508.1" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 4.2734222412109375,2.1367111206054688,0.569789707660675,2.1367111206054688"/>
|
||||||
<line x1="256.2" y1="509.1" x2="256.2" y2="511.3" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/>
|
<line x1="255.9" y1="509.1" x2="255.9" y2="511.3" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/>
|
||||||
</g>
|
</g>
|
||||||
<g>
|
<g>
|
||||||
<line x1="383.8" y1="482.3" x2="383.8" y2="480.2" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/>
|
<line x1="383.4" y1="484.8" x2="383.4" y2="482.6" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/>
|
||||||
<line x1="383.8" y1="478" x2="383.8" y2="476.4" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 0.5678567290306091,2.129462480545044"/>
|
<line x1="383.4" y1="480.5" x2="383.4" y2="478.9" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 0.5708136558532715,2.1405510902404785"/>
|
||||||
<line x1="383.8" y1="475.3" x2="383.8" y2="3.9" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 4.258924961090088,2.129462480545044,0.5678567290306091,2.129462480545044"/>
|
<line x1="383.4" y1="477.8" x2="383.4" y2="3.9" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round;stroke-dasharray: 4.281102180480957,2.1405510902404785,0.5708136558532715,2.1405510902404785"/>
|
||||||
<line x1="383.8" y1="2.9" x2="383.8" y2="0.8" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/>
|
<line x1="383.4" y1="2.9" x2="383.4" y2="0.8" style="fill: none;stroke: #fff;stroke-linecap: round;stroke-linejoin: round"/>
|
||||||
</g>
|
</g>
|
||||||
</g>
|
</g>
|
||||||
<g>
|
<g>
|
||||||
<path d="M277.3,489.1c4.6,0,7.4,2.8,7.4,8.1s-2.9,8.1-7.4,8.1-7.4-2.9-7.4-8.1S272.9,489.1,277.3,489.1Zm3.7,8.1c0-3.8-1.5-5.7-3.7-5.7s-3.8,1.9-3.8,5.7,1.3,5.6,3.8,5.6S281,500.9,281,497.2Z" transform="translate(0.7 0.4)" style="fill: #fff"/>
|
<path d="M277.3,489.1c4.6,0,7.4,2.8,7.4,8.1s-2.9,8.1-7.4,8.1-7.4-2.9-7.4-8.1S272.9,489.1,277.3,489.1Zm3.7,8.1c0-3.8-1.5-5.7-3.7-5.7s-3.8,1.9-3.8,5.7,1.3,5.6,3.8,5.6S281,500.9,281,497.2Z" transform="translate(0.4 0.4)" style="fill: #fff"/>
|
||||||
<path d="M293.1,499h-2.5v6h-3.3V489.3h6.1a7.3,7.3,0,0,1,3.2.6,4.1,4.1,0,0,1,2.6,4,4.4,4.4,0,0,1-3.1,4.3h0l3.5,6.8H296Zm-.1-2.4c1.5,0,2.7-.7,2.7-2.5a2.4,2.4,0,0,0-2.7-2.5h-2.4v5Z" transform="translate(0.7 0.4)" style="fill: #fff"/>
|
<path d="M293.1,499h-2.5v6h-3.3V489.3h6.1a7.3,7.3,0,0,1,3.2.6,4.1,4.1,0,0,1,2.6,4,4.4,4.4,0,0,1-3.1,4.3h0l3.5,6.8H296Zm-.1-2.4c1.5,0,2.7-.7,2.7-2.5a2.4,2.4,0,0,0-2.7-2.5h-2.4v5Z" transform="translate(0.4 0.4)" style="fill: #fff"/>
|
||||||
<path d="M302,489.3h3.4V505H302Z" transform="translate(0.7 0.4)" style="fill: #fff"/>
|
<path d="M302,489.3h3.4V505H302Z" transform="translate(0.4 0.4)" style="fill: #fff"/>
|
||||||
<path d="M311.6,497.2c0,4,1.4,5.6,3.8,5.6s3.4-1.3,3.6-3.5V499h-3.7v-2.4h6.8V505h-2.6v-2.2h-.1a5,5,0,0,1-4.6,2.5c-4.4,0-6.8-3.1-6.8-7.9s3-8.3,7.4-8.3,6.1,1.7,6.4,4.9h-3.4a2.8,2.8,0,0,0-3-2.5C313,491.5,311.6,493.5,311.6,497.2Z" transform="translate(0.7 0.4)" style="fill: #fff"/>
|
<path d="M311.6,497.2c0,4,1.4,5.6,3.8,5.6s3.4-1.3,3.6-3.5V499h-3.7v-2.4h6.8V505h-2.6v-2.2h-.1a5,5,0,0,1-4.6,2.5c-4.4,0-6.8-3.1-6.8-7.9s3-8.3,7.4-8.3,6.1,1.7,6.4,4.9h-3.4a2.8,2.8,0,0,0-3-2.5C313,491.5,311.6,493.5,311.6,497.2Z" transform="translate(0.4 0.4)" style="fill: #fff"/>
|
||||||
<path d="M325.1,489.3h3.4V505h-3.4Z" transform="translate(0.7 0.4)" style="fill: #fff"/>
|
<path d="M325.1,489.3h3.4V505h-3.4Z" transform="translate(0.4 0.4)" style="fill: #fff"/>
|
||||||
<path d="M331.7,489.3h3.7l5,8.5a16.8,16.8,0,0,1,1.2,2.3h0V489.3h3.1V505h-3.5l-5.3-8.7a12.8,12.8,0,0,1-1.1-2.4h-.1V505h-3Z" transform="translate(0.7 0.4)" style="fill: #fff"/>
|
<path d="M331.7,489.3h3.7l5,8.5a16.8,16.8,0,0,1,1.2,2.3h0V489.3h3.1V505h-3.5l-5.3-8.7a12.8,12.8,0,0,1-1.1-2.4h-.1V505h-3Z" transform="translate(0.4 0.4)" style="fill: #fff"/>
|
||||||
<path d="M356.8,501.5H351l-1.1,3.5h-3.2l5.4-15.7h3.8l5.6,15.7h-3.6Zm-3.5-7.1-1.5,4.6H356l-1.5-4.5c-.4-1.4-.6-2.3-.6-2.3h0A15.3,15.3,0,0,1,353.3,494.4Z" transform="translate(0.7 0.4)" style="fill: #fff"/>
|
<path d="M356.8,501.5H351l-1.1,3.5h-3.2l5.4-15.7h3.8l5.6,15.7h-3.6Zm-3.5-7.1-1.5,4.6H356l-1.5-4.5c-.4-1.4-.6-2.3-.6-2.3h0A15.3,15.3,0,0,1,353.3,494.4Z" transform="translate(0.4 0.4)" style="fill: #fff"/>
|
||||||
<path d="M363.4,489.3h3.4v13h6.8V505H363.4Z" transform="translate(0.7 0.4)" style="fill: #fff"/>
|
<path d="M363.4,489.3h3.4v13h6.8V505H363.4Z" transform="translate(0.4 0.4)" style="fill: #fff"/>
|
||||||
<path d="M384,499.6V505h-3.4V489.3h5.5c3.4,0,6,1.4,6,5s-2.8,5.3-6.3,5.3Zm2-2.5a2.5,2.5,0,0,0,2.8-2.7c0-1.9-1.1-2.7-2.8-2.7h-2v5.4Z" transform="translate(0.7 0.4)" style="fill: #fff"/>
|
<path d="M384,499.6V505h-3.4V489.3h5.5c3.4,0,6,1.4,6,5s-2.8,5.3-6.3,5.3Zm2-2.5a2.5,2.5,0,0,0,2.8-2.7c0-1.9-1.1-2.7-2.8-2.7h-2v5.4Z" transform="translate(0.4 0.4)" style="fill: #fff"/>
|
||||||
<path d="M400.1,499h-2.4v6h-3.4V489.3h6.1a7.3,7.3,0,0,1,3.2.6,3.9,3.9,0,0,1,2.6,4,4.5,4.5,0,0,1-3,4.3h0l3.5,6.8H403Zm-.1-2.4c1.5,0,2.8-.7,2.8-2.5a2.4,2.4,0,0,0-2.7-2.5h-2.4v5Z" transform="translate(0.7 0.4)" style="fill: #fff"/>
|
<path d="M400.1,499h-2.4v6h-3.4V489.3h6.1a7.3,7.3,0,0,1,3.2.6,3.9,3.9,0,0,1,2.6,4,4.5,4.5,0,0,1-3,4.3h0l3.5,6.8H403Zm-.1-2.4c1.5,0,2.8-.7,2.8-2.5a2.4,2.4,0,0,0-2.7-2.5h-2.4v5Z" transform="translate(0.4 0.4)" style="fill: #fff"/>
|
||||||
<path d="M408.7,489.3H412v9.1a5.6,5.6,0,0,0,.6,3.2,3.6,3.6,0,0,0,5,0c.6-.8.5-2.1.5-3.2v-9.1h3.3v10.2c0,3.9-2.5,5.8-6.4,5.8s-6.3-1.8-6.3-5.8Z" transform="translate(0.7 0.4)" style="fill: #fff"/>
|
<path d="M408.7,489.3H412v9.1a5.6,5.6,0,0,0,.6,3.2,3.6,3.6,0,0,0,5,0c.6-.8.5-2.1.5-3.2v-9.1h3.3v10.2c0,3.9-2.5,5.8-6.4,5.8s-6.3-1.8-6.3-5.8Z" transform="translate(0.4 0.4)" style="fill: #fff"/>
|
||||||
<path d="M432.4,493.5a2,2,0,0,0-2.3-2c-1.5,0-2.4.8-2.4,1.9s.6,1.7,2.1,1.9l2.4.5c2.8.5,4.1,2,4.1,4.3s-2.3,5.2-6.3,5.2-6.1-1.9-6.2-4.8h3.5a2.5,2.5,0,0,0,2.8,2.3c1.9,0,2.7-.9,2.7-2.1s-.5-1.7-2.2-2l-2.3-.4c-2.4-.5-4.1-1.9-4.1-4.4s2.2-4.8,5.9-4.8,5.7,2,5.8,4.4Z" transform="translate(0.7 0.4)" style="fill: #fff"/>
|
<path d="M432.4,493.5a2,2,0,0,0-2.3-2c-1.5,0-2.4.8-2.4,1.9s.6,1.7,2.1,1.9l2.4.5c2.8.5,4.1,2,4.1,4.3s-2.3,5.2-6.3,5.2-6.1-1.9-6.2-4.8h3.5a2.5,2.5,0,0,0,2.8,2.3c1.9,0,2.7-.9,2.7-2.1s-.5-1.7-2.2-2l-2.3-.4c-2.4-.5-4.1-1.9-4.1-4.4s2.2-4.8,5.9-4.8,5.7,2,5.8,4.4Z" transform="translate(0.4 0.4)" style="fill: #fff"/>
|
||||||
<path d="M447.4,501.5h-5.7l-1.2,3.5h-3.2l5.5-15.7h3.7l5.6,15.7h-3.5Zm-3.5-7.1-1.4,4.6h4.1l-1.4-4.5a11.3,11.3,0,0,1-.6-2.3h-.1A15.3,15.3,0,0,1,443.9,494.4Z" transform="translate(0.7 0.4)" style="fill: #fff"/>
|
<path d="M447.4,501.5h-5.7l-1.2,3.5h-3.2l5.5-15.7h3.7l5.6,15.7h-3.5Zm-3.5-7.1-1.4,4.6h4.1l-1.4-4.5a11.3,11.3,0,0,1-.6-2.3h-.1A15.3,15.3,0,0,1,443.9,494.4Z" transform="translate(0.4 0.4)" style="fill: #fff"/>
|
||||||
<path d="M458.7,489.3h4.9l2.8,8.4a15.7,15.7,0,0,1,.7,2.3h0l.7-2.3,2.8-8.4h4.9V505h-3.3V492.7h-.1a26.9,26.9,0,0,1-1,3.3l-2.9,9h-2.5l-2.9-9a26.9,26.9,0,0,1-1-3.3h0V505h-3.1Z" transform="translate(0.7 0.4)" style="fill: #fff"/>
|
<path d="M458.7,489.3h4.9l2.8,8.4a15.7,15.7,0,0,1,.7,2.3h0l.7-2.3,2.8-8.4h4.9V505h-3.3V492.7h-.1a26.9,26.9,0,0,1-1,3.3l-2.9,9h-2.5l-2.9-9a26.9,26.9,0,0,1-1-3.3h0V505h-3.1Z" transform="translate(0.4 0.4)" style="fill: #fff"/>
|
||||||
<path d="M478.6,489.3H482V505h-3.4Z" transform="translate(0.7 0.4)" style="fill: #fff"/>
|
<path d="M478.6,489.3H482V505h-3.4Z" transform="translate(0.4 0.4)" style="fill: #fff"/>
|
||||||
<path d="M485.2,489.3h3.7l5,8.5a16.8,16.8,0,0,1,1.2,2.3h0V489.3h3.1V505h-3.5l-5.3-8.7a12.8,12.8,0,0,1-1.1-2.4h-.1V505h-3Z" transform="translate(0.7 0.4)" style="fill: #fff"/>
|
<path d="M485.2,489.3h3.7l5,8.5a16.8,16.8,0,0,1,1.2,2.3h0V489.3h3.1V505h-3.5l-5.3-8.7a12.8,12.8,0,0,1-1.1-2.4h-.1V505h-3Z" transform="translate(0.4 0.4)" style="fill: #fff"/>
|
||||||
<path d="M501.3,489.3h3.4V505h-3.4Z" transform="translate(0.7 0.4)" style="fill: #fff"/>
|
<path d="M501.3,489.3h3.4V505h-3.4Z" transform="translate(0.4 0.4)" style="fill: #fff"/>
|
||||||
</g>
|
</g>
|
||||||
<line x1="0.4" y1="28.7" x2="511" y2="28.7" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line x1="0.4" y1="57.1" x2="511" y2="57.1" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line x1="0.4" y1="28.7" x2="511" y2="28.7" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line x1="0.1" y1="85.4" x2="510.7" y2="85.4" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line x1="0.1" y1="113.8" x2="510.7" y2="113.8" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line x1="0.9" y1="142.1" x2="511.5" y2="142.1" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<path d="M509.9,141.7" transform="translate(0.7 0.4)" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<path d="M-.7,141.7" transform="translate(0.7 0.4)" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line x1="1.5" y1="198.8" x2="512.1" y2="198.8" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line y1="170.5" x2="510.6" y2="170.5" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line x1="0.5" y1="227.2" x2="511.1" y2="227.2" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line x1="0.6" y1="283.9" x2="511.2" y2="283.9" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line x1="0.5" y1="28.7" x2="511.1" y2="28.7" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line x1="0.6" y1="312.2" x2="511.2" y2="312.2" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line x1="0.6" y1="340.6" x2="511.2" y2="340.6" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line x1="0.6" y1="368.9" x2="511.2" y2="368.9" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line x1="0.6" y1="397.2" x2="511.2" y2="397.2" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line x1="0.6" y1="425.6" x2="511.2" y2="425.6" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line x1="0.6" y1="453.9" x2="511.2" y2="453.9" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line x1="0.6" y1="482.3" x2="511.2" y2="482.3" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<path d="M481.9,511.4" transform="translate(0.7 0.4)" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<path d="M481.9.8" transform="translate(0.7 0.4)" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line x1="454.3" y1="1.2" x2="454.3" y2="482.3" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line x1="482.6" y1="1.2" x2="482.6" y2="482.3" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line x1="425.9" y1="0.8" x2="425.9" y2="482.3" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line x1="397.6" y1="0.8" x2="397.6" y2="482.3" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line x1="369.2" y1="1.7" x2="369.2" y2="482.3" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line x1="312.6" y1="2.2" x2="312.6" y2="482.3" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line x1="340.9" y1="0.8" x2="340.9" y2="482.3" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line x1="284.2" y1="1.3" x2="284.2" y2="482.3" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line x1="227.5" y1="1.3" x2="227.5" y2="511.9" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line x1="482.6" y1="1.3" x2="482.6" y2="482.3" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line x1="199.2" y1="1.3" x2="199.2" y2="511.9" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line x1="170.8" y1="1.3" x2="170.8" y2="511.9" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line x1="142.5" y1="1.3" x2="142.5" y2="511.9" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line x1="114.1" y1="1.3" x2="114.1" y2="511.9" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line x1="85.8" y1="1.3" x2="85.8" y2="511.9" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line x1="57.4" y1="1.3" x2="57.4" y2="511.9" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
<line x1="29.1" y1="1.3" x2="29.1" y2="511.9" style="fill: none;stroke: #fff;stroke-miterlimit: 10;stroke-width: 0.25px"/>
|
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 8.8 KiB |
45
resources/udev/90-3dconnexion.rules
Normal file
45
resources/udev/90-3dconnexion.rules
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
# See src/slic3r/GUI/Mouse3DController.cpp for the list of devices
|
||||||
|
|
||||||
|
# Logitech vendor devices
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c603", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c605", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c606", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c621", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c623", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c625", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c626", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c627", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c628", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c629", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62b", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62e", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c62f", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c631", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c632", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c633", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c635", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c636", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c640", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c652", MODE="0666"
|
||||||
|
|
||||||
|
# 3D Connexion vendor devices
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c603", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c605", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c606", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c621", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c623", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c625", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c626", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c627", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c628", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c629", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62b", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62e", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c62f", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c631", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c632", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c633", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c635", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c636", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c640", MODE="0666"
|
||||||
|
KERNEL=="hidraw*", ATTRS{idVendor}=="256f", ATTRS{idProduct}=="c652", MODE="0666"
|
|
@ -22,6 +22,8 @@ add_subdirectory(libslic3r)
|
||||||
|
|
||||||
if (SLIC3R_GUI)
|
if (SLIC3R_GUI)
|
||||||
add_subdirectory(imgui)
|
add_subdirectory(imgui)
|
||||||
|
add_subdirectory(hidapi)
|
||||||
|
include_directories(hidapi/include)
|
||||||
|
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
message(STATUS "WXWIN environment set to: $ENV{WXWIN}")
|
message(STATUS "WXWIN environment set to: $ENV{WXWIN}")
|
||||||
|
|
|
@ -184,10 +184,21 @@ extern void stl_mirror_xz(stl_file *stl);
|
||||||
|
|
||||||
extern void stl_get_size(stl_file *stl);
|
extern void stl_get_size(stl_file *stl);
|
||||||
|
|
||||||
|
// the following function is not used
|
||||||
|
/*
|
||||||
template<typename T>
|
template<typename T>
|
||||||
extern void stl_transform(stl_file *stl, T *trafo3x4)
|
extern void stl_transform(stl_file *stl, T *trafo3x4)
|
||||||
{
|
{
|
||||||
for (uint32_t i_face = 0; i_face < stl->stats.number_of_facets; ++ i_face) {
|
Eigen::Matrix<T, 3, 3, Eigen::DontAlign> trafo3x3;
|
||||||
|
for (int i = 0; i < 3; ++i)
|
||||||
|
{
|
||||||
|
for (int j = 0; j < 3; ++j)
|
||||||
|
{
|
||||||
|
trafo3x3(i, j) = (i * 4) + j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Eigen::Matrix<T, 3, 3, Eigen::DontAlign> r = trafo3x3.inverse().transpose();
|
||||||
|
for (uint32_t i_face = 0; i_face < stl->stats.number_of_facets; ++ i_face) {
|
||||||
stl_facet &face = stl->facet_start[i_face];
|
stl_facet &face = stl->facet_start[i_face];
|
||||||
for (int i_vertex = 0; i_vertex < 3; ++ i_vertex) {
|
for (int i_vertex = 0; i_vertex < 3; ++ i_vertex) {
|
||||||
stl_vertex &v_dst = face.vertex[i_vertex];
|
stl_vertex &v_dst = face.vertex[i_vertex];
|
||||||
|
@ -196,21 +207,18 @@ extern void stl_transform(stl_file *stl, T *trafo3x4)
|
||||||
v_dst(1) = T(trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6] * v_src(2) + trafo3x4[7]);
|
v_dst(1) = T(trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6] * v_src(2) + trafo3x4[7]);
|
||||||
v_dst(2) = T(trafo3x4[8] * v_src(0) + trafo3x4[9] * v_src(1) + trafo3x4[10] * v_src(2) + trafo3x4[11]);
|
v_dst(2) = T(trafo3x4[8] * v_src(0) + trafo3x4[9] * v_src(1) + trafo3x4[10] * v_src(2) + trafo3x4[11]);
|
||||||
}
|
}
|
||||||
stl_vertex &v_dst = face.normal;
|
face.normal = (r * face.normal.template cast<T>()).template cast<float>().eval();
|
||||||
stl_vertex v_src = v_dst;
|
}
|
||||||
v_dst(0) = T(trafo3x4[0] * v_src(0) + trafo3x4[1] * v_src(1) + trafo3x4[2] * v_src(2));
|
|
||||||
v_dst(1) = T(trafo3x4[4] * v_src(0) + trafo3x4[5] * v_src(1) + trafo3x4[6] * v_src(2));
|
|
||||||
v_dst(2) = T(trafo3x4[8] * v_src(0) + trafo3x4[9] * v_src(1) + trafo3x4[10] * v_src(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
stl_get_size(stl);
|
stl_get_size(stl);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline void stl_transform(stl_file *stl, const Eigen::Transform<T, 3, Eigen::Affine, Eigen::DontAlign>& t)
|
inline void stl_transform(stl_file *stl, const Eigen::Transform<T, 3, Eigen::Affine, Eigen::DontAlign>& t)
|
||||||
{
|
{
|
||||||
const Eigen::Matrix<double, 3, 3, Eigen::DontAlign> r = t.matrix().template block<3, 3>(0, 0);
|
const Eigen::Matrix<T, 3, 3, Eigen::DontAlign> r = t.matrix().template block<3, 3>(0, 0).inverse().transpose();
|
||||||
for (size_t i = 0; i < stl->stats.number_of_facets; ++ i) {
|
for (size_t i = 0; i < stl->stats.number_of_facets; ++ i) {
|
||||||
stl_facet &f = stl->facet_start[i];
|
stl_facet &f = stl->facet_start[i];
|
||||||
for (size_t j = 0; j < 3; ++j)
|
for (size_t j = 0; j < 3; ++j)
|
||||||
f.vertex[j] = (t * f.vertex[j].template cast<T>()).template cast<float>().eval();
|
f.vertex[j] = (t * f.vertex[j].template cast<T>()).template cast<float>().eval();
|
||||||
|
@ -223,12 +231,13 @@ inline void stl_transform(stl_file *stl, const Eigen::Transform<T, 3, Eigen::Aff
|
||||||
template<typename T>
|
template<typename T>
|
||||||
inline void stl_transform(stl_file *stl, const Eigen::Matrix<T, 3, 3, Eigen::DontAlign>& m)
|
inline void stl_transform(stl_file *stl, const Eigen::Matrix<T, 3, 3, Eigen::DontAlign>& m)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < stl->stats.number_of_facets; ++ i) {
|
const Eigen::Matrix<T, 3, 3, Eigen::DontAlign> r = m.inverse().transpose();
|
||||||
|
for (size_t i = 0; i < stl->stats.number_of_facets; ++ i) {
|
||||||
stl_facet &f = stl->facet_start[i];
|
stl_facet &f = stl->facet_start[i];
|
||||||
for (size_t j = 0; j < 3; ++j)
|
for (size_t j = 0; j < 3; ++j)
|
||||||
f.vertex[j] = (m * f.vertex[j].template cast<T>()).template cast<float>().eval();
|
f.vertex[j] = (m * f.vertex[j].template cast<T>()).template cast<float>().eval();
|
||||||
f.normal = (m * f.normal.template cast<T>()).template cast<float>().eval();
|
f.normal = (r * f.normal.template cast<T>()).template cast<float>().eval();
|
||||||
}
|
}
|
||||||
|
|
||||||
stl_get_size(stl);
|
stl_get_size(stl);
|
||||||
}
|
}
|
||||||
|
|
17
src/hidapi/CMakeLists.txt
Normal file
17
src/hidapi/CMakeLists.txt
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
|
||||||
|
if (WIN32)
|
||||||
|
set(HIDAPI_IMPL win/hid.c)
|
||||||
|
elseif (APPLE)
|
||||||
|
set(HIDAPI_IMPL mac/hid.c)
|
||||||
|
else ()
|
||||||
|
# Assume Linux or Unix other than Mac OS
|
||||||
|
set(HIDAPI_IMPL linux/hid.c)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
include_directories(include)
|
||||||
|
|
||||||
|
add_library(hidapi STATIC ${HIDAPI_IMPL})
|
||||||
|
|
||||||
|
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||||
|
target_link_libraries(hidapi udev)
|
||||||
|
endif()
|
395
src/hidapi/include/hidapi.h
Normal file
395
src/hidapi/include/hidapi.h
Normal file
|
@ -0,0 +1,395 @@
|
||||||
|
/*******************************************************
|
||||||
|
HIDAPI - Multi-Platform library for
|
||||||
|
communication with HID devices.
|
||||||
|
|
||||||
|
Alan Ott
|
||||||
|
Signal 11 Software
|
||||||
|
|
||||||
|
8/22/2009
|
||||||
|
|
||||||
|
Copyright 2009, All Rights Reserved.
|
||||||
|
|
||||||
|
At the discretion of the user of this library,
|
||||||
|
this software may be licensed under the terms of the
|
||||||
|
GNU General Public License v3, a BSD-Style license, or the
|
||||||
|
original HIDAPI license as outlined in the LICENSE.txt,
|
||||||
|
LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
|
||||||
|
files located at the root of the source distribution.
|
||||||
|
These files may also be found in the public source
|
||||||
|
code repository located at:
|
||||||
|
http://github.com/signal11/hidapi .
|
||||||
|
********************************************************/
|
||||||
|
|
||||||
|
/** @file
|
||||||
|
* @defgroup API hidapi API
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef HIDAPI_H__
|
||||||
|
#define HIDAPI_H__
|
||||||
|
|
||||||
|
#include <wchar.h>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define HID_API_EXPORT __declspec(dllexport)
|
||||||
|
#define HID_API_CALL
|
||||||
|
#else
|
||||||
|
#define HID_API_EXPORT /**< API export macro */
|
||||||
|
#define HID_API_CALL /**< API call macro */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
struct hid_device_;
|
||||||
|
typedef struct hid_device_ hid_device; /**< opaque hidapi structure */
|
||||||
|
|
||||||
|
/** hidapi info structure */
|
||||||
|
struct hid_device_info {
|
||||||
|
/** Platform-specific device path */
|
||||||
|
char *path;
|
||||||
|
/** Device Vendor ID */
|
||||||
|
unsigned short vendor_id;
|
||||||
|
/** Device Product ID */
|
||||||
|
unsigned short product_id;
|
||||||
|
/** Serial Number */
|
||||||
|
wchar_t *serial_number;
|
||||||
|
/** Device Release Number in binary-coded decimal,
|
||||||
|
also known as Device Version Number */
|
||||||
|
unsigned short release_number;
|
||||||
|
/** Manufacturer String */
|
||||||
|
wchar_t *manufacturer_string;
|
||||||
|
/** Product string */
|
||||||
|
wchar_t *product_string;
|
||||||
|
/** Usage Page for this Device/Interface
|
||||||
|
(Windows/Mac only). */
|
||||||
|
unsigned short usage_page;
|
||||||
|
/** Usage for this Device/Interface
|
||||||
|
(Windows/Mac only).*/
|
||||||
|
unsigned short usage;
|
||||||
|
/** The USB interface which this logical device
|
||||||
|
represents.
|
||||||
|
|
||||||
|
* Valid on both Linux implementations in all cases.
|
||||||
|
* Valid on the Windows implementation only if the device
|
||||||
|
contains more than one interface.
|
||||||
|
* Valid on the Mac implementation if and only if the device
|
||||||
|
is a USB HID device. */
|
||||||
|
int interface_number;
|
||||||
|
|
||||||
|
/** Pointer to the next device */
|
||||||
|
struct hid_device_info *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/** @brief Initialize the HIDAPI library.
|
||||||
|
|
||||||
|
This function initializes the HIDAPI library. Calling it is not
|
||||||
|
strictly necessary, as it will be called automatically by
|
||||||
|
hid_enumerate() and any of the hid_open_*() functions if it is
|
||||||
|
needed. This function should be called at the beginning of
|
||||||
|
execution however, if there is a chance of HIDAPI handles
|
||||||
|
being opened by different threads simultaneously.
|
||||||
|
|
||||||
|
@ingroup API
|
||||||
|
|
||||||
|
@returns
|
||||||
|
This function returns 0 on success and -1 on error.
|
||||||
|
*/
|
||||||
|
int HID_API_EXPORT HID_API_CALL hid_init(void);
|
||||||
|
|
||||||
|
/** @brief Finalize the HIDAPI library.
|
||||||
|
|
||||||
|
This function frees all of the static data associated with
|
||||||
|
HIDAPI. It should be called at the end of execution to avoid
|
||||||
|
memory leaks.
|
||||||
|
|
||||||
|
@ingroup API
|
||||||
|
|
||||||
|
@returns
|
||||||
|
This function returns 0 on success and -1 on error.
|
||||||
|
*/
|
||||||
|
int HID_API_EXPORT HID_API_CALL hid_exit(void);
|
||||||
|
|
||||||
|
/** @brief Enumerate the HID Devices.
|
||||||
|
|
||||||
|
This function returns a linked list of all the HID devices
|
||||||
|
attached to the system which match vendor_id and product_id.
|
||||||
|
If @p vendor_id is set to 0 then any vendor matches.
|
||||||
|
If @p product_id is set to 0 then any product matches.
|
||||||
|
If @p vendor_id and @p product_id are both set to 0, then
|
||||||
|
all HID devices will be returned.
|
||||||
|
|
||||||
|
@ingroup API
|
||||||
|
@param vendor_id The Vendor ID (VID) of the types of device
|
||||||
|
to open.
|
||||||
|
@param product_id The Product ID (PID) of the types of
|
||||||
|
device to open.
|
||||||
|
|
||||||
|
@returns
|
||||||
|
This function returns a pointer to a linked list of type
|
||||||
|
struct #hid_device_info, containing information about the HID devices
|
||||||
|
attached to the system, or NULL in the case of failure. Free
|
||||||
|
this linked list by calling hid_free_enumeration().
|
||||||
|
*/
|
||||||
|
struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id);
|
||||||
|
|
||||||
|
/** @brief Free an enumeration Linked List
|
||||||
|
|
||||||
|
This function frees a linked list created by hid_enumerate().
|
||||||
|
|
||||||
|
@ingroup API
|
||||||
|
@param devs Pointer to a list of struct_device returned from
|
||||||
|
hid_enumerate().
|
||||||
|
*/
|
||||||
|
void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs);
|
||||||
|
|
||||||
|
/** @brief Open a HID device using a Vendor ID (VID), Product ID
|
||||||
|
(PID) and optionally a serial number.
|
||||||
|
|
||||||
|
If @p serial_number is NULL, the first device with the
|
||||||
|
specified VID and PID is opened.
|
||||||
|
|
||||||
|
@ingroup API
|
||||||
|
@param vendor_id The Vendor ID (VID) of the device to open.
|
||||||
|
@param product_id The Product ID (PID) of the device to open.
|
||||||
|
@param serial_number The Serial Number of the device to open
|
||||||
|
(Optionally NULL).
|
||||||
|
|
||||||
|
@returns
|
||||||
|
This function returns a pointer to a #hid_device object on
|
||||||
|
success or NULL on failure.
|
||||||
|
*/
|
||||||
|
HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number);
|
||||||
|
|
||||||
|
/** @brief Open a HID device by its path name.
|
||||||
|
|
||||||
|
The path name be determined by calling hid_enumerate(), or a
|
||||||
|
platform-specific path name can be used (eg: /dev/hidraw0 on
|
||||||
|
Linux).
|
||||||
|
|
||||||
|
@ingroup API
|
||||||
|
@param path The path name of the device to open
|
||||||
|
|
||||||
|
@returns
|
||||||
|
This function returns a pointer to a #hid_device object on
|
||||||
|
success or NULL on failure.
|
||||||
|
*/
|
||||||
|
HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path);
|
||||||
|
|
||||||
|
/** @brief Write an Output report to a HID device.
|
||||||
|
|
||||||
|
The first byte of @p data[] must contain the Report ID. For
|
||||||
|
devices which only support a single report, this must be set
|
||||||
|
to 0x0. The remaining bytes contain the report data. Since
|
||||||
|
the Report ID is mandatory, calls to hid_write() will always
|
||||||
|
contain one more byte than the report contains. For example,
|
||||||
|
if a hid report is 16 bytes long, 17 bytes must be passed to
|
||||||
|
hid_write(), the Report ID (or 0x0, for devices with a
|
||||||
|
single report), followed by the report data (16 bytes). In
|
||||||
|
this example, the length passed in would be 17.
|
||||||
|
|
||||||
|
hid_write() will send the data on the first OUT endpoint, if
|
||||||
|
one exists. If it does not, it will send the data through
|
||||||
|
the Control Endpoint (Endpoint 0).
|
||||||
|
|
||||||
|
@ingroup API
|
||||||
|
@param dev A device handle returned from hid_open().
|
||||||
|
@param data The data to send, including the report number as
|
||||||
|
the first byte.
|
||||||
|
@param length The length in bytes of the data to send.
|
||||||
|
|
||||||
|
@returns
|
||||||
|
This function returns the actual number of bytes written and
|
||||||
|
-1 on error.
|
||||||
|
*/
|
||||||
|
int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length);
|
||||||
|
|
||||||
|
/** @brief Read an Input report from a HID device with timeout.
|
||||||
|
|
||||||
|
Input reports are returned
|
||||||
|
to the host through the INTERRUPT IN endpoint. The first byte will
|
||||||
|
contain the Report number if the device uses numbered reports.
|
||||||
|
|
||||||
|
@ingroup API
|
||||||
|
@param dev A device handle returned from hid_open().
|
||||||
|
@param data A buffer to put the read data into.
|
||||||
|
@param length The number of bytes to read. For devices with
|
||||||
|
multiple reports, make sure to read an extra byte for
|
||||||
|
the report number.
|
||||||
|
@param milliseconds timeout in milliseconds or -1 for blocking wait.
|
||||||
|
|
||||||
|
@returns
|
||||||
|
This function returns the actual number of bytes read and
|
||||||
|
-1 on error. If no packet was available to be read within
|
||||||
|
the timeout period, this function returns 0.
|
||||||
|
*/
|
||||||
|
int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds);
|
||||||
|
|
||||||
|
/** @brief Read an Input report from a HID device.
|
||||||
|
|
||||||
|
Input reports are returned
|
||||||
|
to the host through the INTERRUPT IN endpoint. The first byte will
|
||||||
|
contain the Report number if the device uses numbered reports.
|
||||||
|
|
||||||
|
@ingroup API
|
||||||
|
@param dev A device handle returned from hid_open().
|
||||||
|
@param data A buffer to put the read data into.
|
||||||
|
@param length The number of bytes to read. For devices with
|
||||||
|
multiple reports, make sure to read an extra byte for
|
||||||
|
the report number.
|
||||||
|
|
||||||
|
@returns
|
||||||
|
This function returns the actual number of bytes read and
|
||||||
|
-1 on error. If no packet was available to be read and
|
||||||
|
the handle is in non-blocking mode, this function returns 0.
|
||||||
|
*/
|
||||||
|
int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length);
|
||||||
|
|
||||||
|
/** @brief Set the device handle to be non-blocking.
|
||||||
|
|
||||||
|
In non-blocking mode calls to hid_read() will return
|
||||||
|
immediately with a value of 0 if there is no data to be
|
||||||
|
read. In blocking mode, hid_read() will wait (block) until
|
||||||
|
there is data to read before returning.
|
||||||
|
|
||||||
|
Nonblocking can be turned on and off at any time.
|
||||||
|
|
||||||
|
@ingroup API
|
||||||
|
@param dev A device handle returned from hid_open().
|
||||||
|
@param nonblock enable or not the nonblocking reads
|
||||||
|
- 1 to enable nonblocking
|
||||||
|
- 0 to disable nonblocking.
|
||||||
|
|
||||||
|
@returns
|
||||||
|
This function returns 0 on success and -1 on error.
|
||||||
|
*/
|
||||||
|
int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock);
|
||||||
|
|
||||||
|
/** @brief Send a Feature report to the device.
|
||||||
|
|
||||||
|
Feature reports are sent over the Control endpoint as a
|
||||||
|
Set_Report transfer. The first byte of @p data[] must
|
||||||
|
contain the Report ID. For devices which only support a
|
||||||
|
single report, this must be set to 0x0. The remaining bytes
|
||||||
|
contain the report data. Since the Report ID is mandatory,
|
||||||
|
calls to hid_send_feature_report() will always contain one
|
||||||
|
more byte than the report contains. For example, if a hid
|
||||||
|
report is 16 bytes long, 17 bytes must be passed to
|
||||||
|
hid_send_feature_report(): the Report ID (or 0x0, for
|
||||||
|
devices which do not use numbered reports), followed by the
|
||||||
|
report data (16 bytes). In this example, the length passed
|
||||||
|
in would be 17.
|
||||||
|
|
||||||
|
@ingroup API
|
||||||
|
@param dev A device handle returned from hid_open().
|
||||||
|
@param data The data to send, including the report number as
|
||||||
|
the first byte.
|
||||||
|
@param length The length in bytes of the data to send, including
|
||||||
|
the report number.
|
||||||
|
|
||||||
|
@returns
|
||||||
|
This function returns the actual number of bytes written and
|
||||||
|
-1 on error.
|
||||||
|
*/
|
||||||
|
int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length);
|
||||||
|
|
||||||
|
/** @brief Get a feature report from a HID device.
|
||||||
|
|
||||||
|
Set the first byte of @p data[] to the Report ID of the
|
||||||
|
report to be read. Make sure to allow space for this
|
||||||
|
extra byte in @p data[]. Upon return, the first byte will
|
||||||
|
still contain the Report ID, and the report data will
|
||||||
|
start in data[1].
|
||||||
|
|
||||||
|
@ingroup API
|
||||||
|
@param dev A device handle returned from hid_open().
|
||||||
|
@param data A buffer to put the read data into, including
|
||||||
|
the Report ID. Set the first byte of @p data[] to the
|
||||||
|
Report ID of the report to be read, or set it to zero
|
||||||
|
if your device does not use numbered reports.
|
||||||
|
@param length The number of bytes to read, including an
|
||||||
|
extra byte for the report ID. The buffer can be longer
|
||||||
|
than the actual report.
|
||||||
|
|
||||||
|
@returns
|
||||||
|
This function returns the number of bytes read plus
|
||||||
|
one for the report ID (which is still in the first
|
||||||
|
byte), or -1 on error.
|
||||||
|
*/
|
||||||
|
int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length);
|
||||||
|
|
||||||
|
/** @brief Close a HID device.
|
||||||
|
|
||||||
|
@ingroup API
|
||||||
|
@param dev A device handle returned from hid_open().
|
||||||
|
*/
|
||||||
|
void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev);
|
||||||
|
|
||||||
|
/** @brief Get The Manufacturer String from a HID device.
|
||||||
|
|
||||||
|
@ingroup API
|
||||||
|
@param dev A device handle returned from hid_open().
|
||||||
|
@param string A wide string buffer to put the data into.
|
||||||
|
@param maxlen The length of the buffer in multiples of wchar_t.
|
||||||
|
|
||||||
|
@returns
|
||||||
|
This function returns 0 on success and -1 on error.
|
||||||
|
*/
|
||||||
|
int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen);
|
||||||
|
|
||||||
|
/** @brief Get The Product String from a HID device.
|
||||||
|
|
||||||
|
@ingroup API
|
||||||
|
@param dev A device handle returned from hid_open().
|
||||||
|
@param string A wide string buffer to put the data into.
|
||||||
|
@param maxlen The length of the buffer in multiples of wchar_t.
|
||||||
|
|
||||||
|
@returns
|
||||||
|
This function returns 0 on success and -1 on error.
|
||||||
|
*/
|
||||||
|
int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen);
|
||||||
|
|
||||||
|
/** @brief Get The Serial Number String from a HID device.
|
||||||
|
|
||||||
|
@ingroup API
|
||||||
|
@param dev A device handle returned from hid_open().
|
||||||
|
@param string A wide string buffer to put the data into.
|
||||||
|
@param maxlen The length of the buffer in multiples of wchar_t.
|
||||||
|
|
||||||
|
@returns
|
||||||
|
This function returns 0 on success and -1 on error.
|
||||||
|
*/
|
||||||
|
int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen);
|
||||||
|
|
||||||
|
/** @brief Get a string from a HID device, based on its string index.
|
||||||
|
|
||||||
|
@ingroup API
|
||||||
|
@param dev A device handle returned from hid_open().
|
||||||
|
@param string_index The index of the string to get.
|
||||||
|
@param string A wide string buffer to put the data into.
|
||||||
|
@param maxlen The length of the buffer in multiples of wchar_t.
|
||||||
|
|
||||||
|
@returns
|
||||||
|
This function returns 0 on success and -1 on error.
|
||||||
|
*/
|
||||||
|
int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen);
|
||||||
|
|
||||||
|
/** @brief Get a string describing the last error which occurred.
|
||||||
|
|
||||||
|
@ingroup API
|
||||||
|
@param dev A device handle returned from hid_open().
|
||||||
|
|
||||||
|
@returns
|
||||||
|
This function returns a string containing the last error
|
||||||
|
which occurred or NULL if none has occurred.
|
||||||
|
*/
|
||||||
|
HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *dev);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
797
src/hidapi/linux/hid.c
Normal file
797
src/hidapi/linux/hid.c
Normal file
|
@ -0,0 +1,797 @@
|
||||||
|
/*******************************************************
|
||||||
|
HIDAPI - Multi-Platform library for
|
||||||
|
communication with HID devices.
|
||||||
|
|
||||||
|
Alan Ott
|
||||||
|
Signal 11 Software
|
||||||
|
|
||||||
|
8/22/2009
|
||||||
|
Linux Version - 6/2/2009
|
||||||
|
|
||||||
|
Copyright 2009, All Rights Reserved.
|
||||||
|
|
||||||
|
At the discretion of the user of this library,
|
||||||
|
this software may be licensed under the terms of the
|
||||||
|
GNU General Public License v3, a BSD-Style license, or the
|
||||||
|
original HIDAPI license as outlined in the LICENSE.txt,
|
||||||
|
LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
|
||||||
|
files located at the root of the source distribution.
|
||||||
|
These files may also be found in the public source
|
||||||
|
code repository located at:
|
||||||
|
http://github.com/signal11/hidapi .
|
||||||
|
********************************************************/
|
||||||
|
|
||||||
|
/* C */
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <locale.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
/* Unix */
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
#include <sys/utsname.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <poll.h>
|
||||||
|
|
||||||
|
/* Linux */
|
||||||
|
#include <linux/hidraw.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/input.h>
|
||||||
|
#include <libudev.h>
|
||||||
|
|
||||||
|
#include "hidapi.h"
|
||||||
|
|
||||||
|
/* Definitions from linux/hidraw.h. Since these are new, some distros
|
||||||
|
may not have header files which contain them. */
|
||||||
|
#ifndef HIDIOCSFEATURE
|
||||||
|
#define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len)
|
||||||
|
#endif
|
||||||
|
#ifndef HIDIOCGFEATURE
|
||||||
|
#define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/* USB HID device property names */
|
||||||
|
const char *device_string_names[] = {
|
||||||
|
"manufacturer",
|
||||||
|
"product",
|
||||||
|
"serial",
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Symbolic names for the properties above */
|
||||||
|
enum device_string_id {
|
||||||
|
DEVICE_STRING_MANUFACTURER,
|
||||||
|
DEVICE_STRING_PRODUCT,
|
||||||
|
DEVICE_STRING_SERIAL,
|
||||||
|
|
||||||
|
DEVICE_STRING_COUNT,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct hid_device_ {
|
||||||
|
int device_handle;
|
||||||
|
int blocking;
|
||||||
|
int uses_numbered_reports;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static __u32 kernel_version = 0;
|
||||||
|
|
||||||
|
static __u32 detect_kernel_version(void)
|
||||||
|
{
|
||||||
|
struct utsname name;
|
||||||
|
int major, minor, release;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
uname(&name);
|
||||||
|
ret = sscanf(name.release, "%d.%d.%d", &major, &minor, &release);
|
||||||
|
if (ret == 3) {
|
||||||
|
return KERNEL_VERSION(major, minor, release);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = sscanf(name.release, "%d.%d", &major, &minor);
|
||||||
|
if (ret == 2) {
|
||||||
|
return KERNEL_VERSION(major, minor, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Couldn't determine kernel version from version string \"%s\"\n", name.release);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static hid_device *new_hid_device(void)
|
||||||
|
{
|
||||||
|
hid_device *dev = calloc(1, sizeof(hid_device));
|
||||||
|
dev->device_handle = -1;
|
||||||
|
dev->blocking = 1;
|
||||||
|
dev->uses_numbered_reports = 0;
|
||||||
|
|
||||||
|
return dev;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* The caller must free the returned string with free(). */
|
||||||
|
static wchar_t *utf8_to_wchar_t(const char *utf8)
|
||||||
|
{
|
||||||
|
wchar_t *ret = NULL;
|
||||||
|
|
||||||
|
if (utf8) {
|
||||||
|
size_t wlen = mbstowcs(NULL, utf8, 0);
|
||||||
|
if ((size_t) -1 == wlen) {
|
||||||
|
return wcsdup(L"");
|
||||||
|
}
|
||||||
|
ret = calloc(wlen+1, sizeof(wchar_t));
|
||||||
|
mbstowcs(ret, utf8, wlen+1);
|
||||||
|
ret[wlen] = 0x0000;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get an attribute value from a udev_device and return it as a whar_t
|
||||||
|
string. The returned string must be freed with free() when done.*/
|
||||||
|
static wchar_t *copy_udev_string(struct udev_device *dev, const char *udev_name)
|
||||||
|
{
|
||||||
|
return utf8_to_wchar_t(udev_device_get_sysattr_value(dev, udev_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* uses_numbered_reports() returns 1 if report_descriptor describes a device
|
||||||
|
which contains numbered reports. */
|
||||||
|
static int uses_numbered_reports(__u8 *report_descriptor, __u32 size) {
|
||||||
|
unsigned int i = 0;
|
||||||
|
int size_code;
|
||||||
|
int data_len, key_size;
|
||||||
|
|
||||||
|
while (i < size) {
|
||||||
|
int key = report_descriptor[i];
|
||||||
|
|
||||||
|
/* Check for the Report ID key */
|
||||||
|
if (key == 0x85/*Report ID*/) {
|
||||||
|
/* This device has a Report ID, which means it uses
|
||||||
|
numbered reports. */
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//printf("key: %02hhx\n", key);
|
||||||
|
|
||||||
|
if ((key & 0xf0) == 0xf0) {
|
||||||
|
/* This is a Long Item. The next byte contains the
|
||||||
|
length of the data section (value) for this key.
|
||||||
|
See the HID specification, version 1.11, section
|
||||||
|
6.2.2.3, titled "Long Items." */
|
||||||
|
if (i+1 < size)
|
||||||
|
data_len = report_descriptor[i+1];
|
||||||
|
else
|
||||||
|
data_len = 0; /* malformed report */
|
||||||
|
key_size = 3;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* This is a Short Item. The bottom two bits of the
|
||||||
|
key contain the size code for the data section
|
||||||
|
(value) for this key. Refer to the HID
|
||||||
|
specification, version 1.11, section 6.2.2.2,
|
||||||
|
titled "Short Items." */
|
||||||
|
size_code = key & 0x3;
|
||||||
|
switch (size_code) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
case 2:
|
||||||
|
data_len = size_code;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
data_len = 4;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* Can't ever happen since size_code is & 0x3 */
|
||||||
|
data_len = 0;
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
key_size = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Skip over this key and it's associated data */
|
||||||
|
i += data_len + key_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Didn't find a Report ID key. Device doesn't use numbered reports. */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The caller is responsible for free()ing the (newly-allocated) character
|
||||||
|
* strings pointed to by serial_number_utf8 and product_name_utf8 after use.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
parse_uevent_info(const char *uevent, int *bus_type,
|
||||||
|
unsigned short *vendor_id, unsigned short *product_id,
|
||||||
|
char **serial_number_utf8, char **product_name_utf8)
|
||||||
|
{
|
||||||
|
char *tmp = strdup(uevent);
|
||||||
|
char *saveptr = NULL;
|
||||||
|
char *line;
|
||||||
|
char *key;
|
||||||
|
char *value;
|
||||||
|
|
||||||
|
int found_id = 0;
|
||||||
|
int found_serial = 0;
|
||||||
|
int found_name = 0;
|
||||||
|
|
||||||
|
line = strtok_r(tmp, "\n", &saveptr);
|
||||||
|
while (line != NULL) {
|
||||||
|
/* line: "KEY=value" */
|
||||||
|
key = line;
|
||||||
|
value = strchr(line, '=');
|
||||||
|
if (!value) {
|
||||||
|
goto next_line;
|
||||||
|
}
|
||||||
|
*value = '\0';
|
||||||
|
value++;
|
||||||
|
|
||||||
|
if (strcmp(key, "HID_ID") == 0) {
|
||||||
|
/**
|
||||||
|
* type vendor product
|
||||||
|
* HID_ID=0003:000005AC:00008242
|
||||||
|
**/
|
||||||
|
int ret = sscanf(value, "%x:%hx:%hx", bus_type, vendor_id, product_id);
|
||||||
|
if (ret == 3) {
|
||||||
|
found_id = 1;
|
||||||
|
}
|
||||||
|
} else if (strcmp(key, "HID_NAME") == 0) {
|
||||||
|
/* The caller has to free the product name */
|
||||||
|
*product_name_utf8 = strdup(value);
|
||||||
|
found_name = 1;
|
||||||
|
} else if (strcmp(key, "HID_UNIQ") == 0) {
|
||||||
|
/* The caller has to free the serial number */
|
||||||
|
*serial_number_utf8 = strdup(value);
|
||||||
|
found_serial = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
next_line:
|
||||||
|
line = strtok_r(NULL, "\n", &saveptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(tmp);
|
||||||
|
return (found_id && found_name && found_serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int get_device_string(hid_device *dev, enum device_string_id key, wchar_t *string, size_t maxlen)
|
||||||
|
{
|
||||||
|
struct udev *udev;
|
||||||
|
struct udev_device *udev_dev, *parent, *hid_dev;
|
||||||
|
struct stat s;
|
||||||
|
int ret = -1;
|
||||||
|
char *serial_number_utf8 = NULL;
|
||||||
|
char *product_name_utf8 = NULL;
|
||||||
|
|
||||||
|
/* Create the udev object */
|
||||||
|
udev = udev_new();
|
||||||
|
if (!udev) {
|
||||||
|
printf("Can't create udev\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the dev_t (major/minor numbers) from the file handle. */
|
||||||
|
ret = fstat(dev->device_handle, &s);
|
||||||
|
if (-1 == ret)
|
||||||
|
return ret;
|
||||||
|
/* Open a udev device from the dev_t. 'c' means character device. */
|
||||||
|
udev_dev = udev_device_new_from_devnum(udev, 'c', s.st_rdev);
|
||||||
|
if (udev_dev) {
|
||||||
|
hid_dev = udev_device_get_parent_with_subsystem_devtype(
|
||||||
|
udev_dev,
|
||||||
|
"hid",
|
||||||
|
NULL);
|
||||||
|
if (hid_dev) {
|
||||||
|
unsigned short dev_vid;
|
||||||
|
unsigned short dev_pid;
|
||||||
|
int bus_type;
|
||||||
|
size_t retm;
|
||||||
|
|
||||||
|
ret = parse_uevent_info(
|
||||||
|
udev_device_get_sysattr_value(hid_dev, "uevent"),
|
||||||
|
&bus_type,
|
||||||
|
&dev_vid,
|
||||||
|
&dev_pid,
|
||||||
|
&serial_number_utf8,
|
||||||
|
&product_name_utf8);
|
||||||
|
|
||||||
|
if (bus_type == BUS_BLUETOOTH) {
|
||||||
|
switch (key) {
|
||||||
|
case DEVICE_STRING_MANUFACTURER:
|
||||||
|
wcsncpy(string, L"", maxlen);
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
case DEVICE_STRING_PRODUCT:
|
||||||
|
retm = mbstowcs(string, product_name_utf8, maxlen);
|
||||||
|
ret = (retm == (size_t)-1)? -1: 0;
|
||||||
|
break;
|
||||||
|
case DEVICE_STRING_SERIAL:
|
||||||
|
retm = mbstowcs(string, serial_number_utf8, maxlen);
|
||||||
|
ret = (retm == (size_t)-1)? -1: 0;
|
||||||
|
break;
|
||||||
|
case DEVICE_STRING_COUNT:
|
||||||
|
default:
|
||||||
|
ret = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* This is a USB device. Find its parent USB Device node. */
|
||||||
|
parent = udev_device_get_parent_with_subsystem_devtype(
|
||||||
|
udev_dev,
|
||||||
|
"usb",
|
||||||
|
"usb_device");
|
||||||
|
if (parent) {
|
||||||
|
const char *str;
|
||||||
|
const char *key_str = NULL;
|
||||||
|
|
||||||
|
if (key >= 0 && key < DEVICE_STRING_COUNT) {
|
||||||
|
key_str = device_string_names[key];
|
||||||
|
} else {
|
||||||
|
ret = -1;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
str = udev_device_get_sysattr_value(parent, key_str);
|
||||||
|
if (str) {
|
||||||
|
/* Convert the string from UTF-8 to wchar_t */
|
||||||
|
retm = mbstowcs(string, str, maxlen);
|
||||||
|
ret = (retm == (size_t)-1)? -1: 0;
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
end:
|
||||||
|
free(serial_number_utf8);
|
||||||
|
free(product_name_utf8);
|
||||||
|
|
||||||
|
udev_device_unref(udev_dev);
|
||||||
|
/* parent and hid_dev don't need to be (and can't be) unref'd.
|
||||||
|
I'm not sure why, but they'll throw double-free() errors. */
|
||||||
|
udev_unref(udev);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HID_API_EXPORT hid_init(void)
|
||||||
|
{
|
||||||
|
const char *locale;
|
||||||
|
|
||||||
|
/* Set the locale if it's not set. */
|
||||||
|
locale = setlocale(LC_CTYPE, NULL);
|
||||||
|
if (!locale)
|
||||||
|
setlocale(LC_CTYPE, "");
|
||||||
|
|
||||||
|
kernel_version = detect_kernel_version();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HID_API_EXPORT hid_exit(void)
|
||||||
|
{
|
||||||
|
/* Nothing to do for this in the Linux/hidraw implementation. */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct hid_device_info HID_API_EXPORT *hid_enumerate(unsigned short vendor_id, unsigned short product_id)
|
||||||
|
{
|
||||||
|
struct udev *udev;
|
||||||
|
struct udev_enumerate *enumerate;
|
||||||
|
struct udev_list_entry *devices, *dev_list_entry;
|
||||||
|
|
||||||
|
struct hid_device_info *root = NULL; /* return object */
|
||||||
|
struct hid_device_info *cur_dev = NULL;
|
||||||
|
struct hid_device_info *prev_dev = NULL; /* previous device */
|
||||||
|
|
||||||
|
hid_init();
|
||||||
|
|
||||||
|
/* Create the udev object */
|
||||||
|
udev = udev_new();
|
||||||
|
if (!udev) {
|
||||||
|
printf("Can't create udev\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create a list of the devices in the 'hidraw' subsystem. */
|
||||||
|
enumerate = udev_enumerate_new(udev);
|
||||||
|
udev_enumerate_add_match_subsystem(enumerate, "hidraw");
|
||||||
|
udev_enumerate_scan_devices(enumerate);
|
||||||
|
devices = udev_enumerate_get_list_entry(enumerate);
|
||||||
|
/* For each item, see if it matches the vid/pid, and if so
|
||||||
|
create a udev_device record for it */
|
||||||
|
udev_list_entry_foreach(dev_list_entry, devices) {
|
||||||
|
const char *sysfs_path;
|
||||||
|
const char *dev_path;
|
||||||
|
const char *str;
|
||||||
|
struct udev_device *raw_dev; /* The device's hidraw udev node. */
|
||||||
|
struct udev_device *hid_dev; /* The device's HID udev node. */
|
||||||
|
struct udev_device *usb_dev; /* The device's USB udev node. */
|
||||||
|
struct udev_device *intf_dev; /* The device's interface (in the USB sense). */
|
||||||
|
unsigned short dev_vid;
|
||||||
|
unsigned short dev_pid;
|
||||||
|
char *serial_number_utf8 = NULL;
|
||||||
|
char *product_name_utf8 = NULL;
|
||||||
|
int bus_type;
|
||||||
|
int result;
|
||||||
|
|
||||||
|
/* Get the filename of the /sys entry for the device
|
||||||
|
and create a udev_device object (dev) representing it */
|
||||||
|
sysfs_path = udev_list_entry_get_name(dev_list_entry);
|
||||||
|
raw_dev = udev_device_new_from_syspath(udev, sysfs_path);
|
||||||
|
dev_path = udev_device_get_devnode(raw_dev);
|
||||||
|
|
||||||
|
hid_dev = udev_device_get_parent_with_subsystem_devtype(
|
||||||
|
raw_dev,
|
||||||
|
"hid",
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
if (!hid_dev) {
|
||||||
|
/* Unable to find parent hid device. */
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = parse_uevent_info(
|
||||||
|
udev_device_get_sysattr_value(hid_dev, "uevent"),
|
||||||
|
&bus_type,
|
||||||
|
&dev_vid,
|
||||||
|
&dev_pid,
|
||||||
|
&serial_number_utf8,
|
||||||
|
&product_name_utf8);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
/* parse_uevent_info() failed for at least one field. */
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bus_type != BUS_USB && bus_type != BUS_BLUETOOTH) {
|
||||||
|
/* We only know how to handle USB and BT devices. */
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check the VID/PID against the arguments */
|
||||||
|
if ((vendor_id == 0x0 || vendor_id == dev_vid) &&
|
||||||
|
(product_id == 0x0 || product_id == dev_pid)) {
|
||||||
|
struct hid_device_info *tmp;
|
||||||
|
|
||||||
|
/* VID/PID match. Create the record. */
|
||||||
|
tmp = malloc(sizeof(struct hid_device_info));
|
||||||
|
if (cur_dev) {
|
||||||
|
cur_dev->next = tmp;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
root = tmp;
|
||||||
|
}
|
||||||
|
prev_dev = cur_dev;
|
||||||
|
cur_dev = tmp;
|
||||||
|
|
||||||
|
/* Fill out the record */
|
||||||
|
cur_dev->next = NULL;
|
||||||
|
cur_dev->path = dev_path? strdup(dev_path): NULL;
|
||||||
|
|
||||||
|
/* VID/PID */
|
||||||
|
cur_dev->vendor_id = dev_vid;
|
||||||
|
cur_dev->product_id = dev_pid;
|
||||||
|
|
||||||
|
/* Serial Number */
|
||||||
|
cur_dev->serial_number = utf8_to_wchar_t(serial_number_utf8);
|
||||||
|
|
||||||
|
/* Release Number */
|
||||||
|
cur_dev->release_number = 0x0;
|
||||||
|
|
||||||
|
/* Interface Number */
|
||||||
|
cur_dev->interface_number = -1;
|
||||||
|
|
||||||
|
switch (bus_type) {
|
||||||
|
case BUS_USB:
|
||||||
|
/* The device pointed to by raw_dev contains information about
|
||||||
|
the hidraw device. In order to get information about the
|
||||||
|
USB device, get the parent device with the
|
||||||
|
subsystem/devtype pair of "usb"/"usb_device". This will
|
||||||
|
be several levels up the tree, but the function will find
|
||||||
|
it. */
|
||||||
|
usb_dev = udev_device_get_parent_with_subsystem_devtype(
|
||||||
|
raw_dev,
|
||||||
|
"usb",
|
||||||
|
"usb_device");
|
||||||
|
|
||||||
|
if (!usb_dev) {
|
||||||
|
/* Free this device */
|
||||||
|
free(cur_dev->serial_number);
|
||||||
|
free(cur_dev->path);
|
||||||
|
free(cur_dev);
|
||||||
|
|
||||||
|
/* Take it off the device list. */
|
||||||
|
if (prev_dev) {
|
||||||
|
prev_dev->next = NULL;
|
||||||
|
cur_dev = prev_dev;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cur_dev = root = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Manufacturer and Product strings */
|
||||||
|
cur_dev->manufacturer_string = copy_udev_string(usb_dev, device_string_names[DEVICE_STRING_MANUFACTURER]);
|
||||||
|
cur_dev->product_string = copy_udev_string(usb_dev, device_string_names[DEVICE_STRING_PRODUCT]);
|
||||||
|
|
||||||
|
/* Release Number */
|
||||||
|
str = udev_device_get_sysattr_value(usb_dev, "bcdDevice");
|
||||||
|
cur_dev->release_number = (str)? strtol(str, NULL, 16): 0x0;
|
||||||
|
|
||||||
|
/* Get a handle to the interface's udev node. */
|
||||||
|
intf_dev = udev_device_get_parent_with_subsystem_devtype(
|
||||||
|
raw_dev,
|
||||||
|
"usb",
|
||||||
|
"usb_interface");
|
||||||
|
if (intf_dev) {
|
||||||
|
str = udev_device_get_sysattr_value(intf_dev, "bInterfaceNumber");
|
||||||
|
cur_dev->interface_number = (str)? strtol(str, NULL, 16): -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case BUS_BLUETOOTH:
|
||||||
|
/* Manufacturer and Product strings */
|
||||||
|
cur_dev->manufacturer_string = wcsdup(L"");
|
||||||
|
cur_dev->product_string = utf8_to_wchar_t(product_name_utf8);
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
/* Unknown device type - this should never happen, as we
|
||||||
|
* check for USB and Bluetooth devices above */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
next:
|
||||||
|
free(serial_number_utf8);
|
||||||
|
free(product_name_utf8);
|
||||||
|
udev_device_unref(raw_dev);
|
||||||
|
/* hid_dev, usb_dev and intf_dev don't need to be (and can't be)
|
||||||
|
unref()d. It will cause a double-free() error. I'm not
|
||||||
|
sure why. */
|
||||||
|
}
|
||||||
|
/* Free the enumerator and udev objects. */
|
||||||
|
udev_enumerate_unref(enumerate);
|
||||||
|
udev_unref(udev);
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HID_API_EXPORT hid_free_enumeration(struct hid_device_info *devs)
|
||||||
|
{
|
||||||
|
struct hid_device_info *d = devs;
|
||||||
|
while (d) {
|
||||||
|
struct hid_device_info *next = d->next;
|
||||||
|
free(d->path);
|
||||||
|
free(d->serial_number);
|
||||||
|
free(d->manufacturer_string);
|
||||||
|
free(d->product_string);
|
||||||
|
free(d);
|
||||||
|
d = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hid_device * hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
|
||||||
|
{
|
||||||
|
struct hid_device_info *devs, *cur_dev;
|
||||||
|
const char *path_to_open = NULL;
|
||||||
|
hid_device *handle = NULL;
|
||||||
|
|
||||||
|
devs = hid_enumerate(vendor_id, product_id);
|
||||||
|
cur_dev = devs;
|
||||||
|
while (cur_dev) {
|
||||||
|
if (cur_dev->vendor_id == vendor_id &&
|
||||||
|
cur_dev->product_id == product_id) {
|
||||||
|
if (serial_number) {
|
||||||
|
if (wcscmp(serial_number, cur_dev->serial_number) == 0) {
|
||||||
|
path_to_open = cur_dev->path;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
path_to_open = cur_dev->path;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cur_dev = cur_dev->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path_to_open) {
|
||||||
|
/* Open the device */
|
||||||
|
handle = hid_open_path(path_to_open);
|
||||||
|
}
|
||||||
|
|
||||||
|
hid_free_enumeration(devs);
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
hid_device * HID_API_EXPORT hid_open_path(const char *path)
|
||||||
|
{
|
||||||
|
hid_device *dev = NULL;
|
||||||
|
|
||||||
|
hid_init();
|
||||||
|
|
||||||
|
dev = new_hid_device();
|
||||||
|
|
||||||
|
/* OPEN HERE */
|
||||||
|
dev->device_handle = open(path, O_RDWR);
|
||||||
|
|
||||||
|
/* If we have a good handle, return it. */
|
||||||
|
if (dev->device_handle > 0) {
|
||||||
|
|
||||||
|
/* Get the report descriptor */
|
||||||
|
int res, desc_size = 0;
|
||||||
|
struct hidraw_report_descriptor rpt_desc;
|
||||||
|
|
||||||
|
memset(&rpt_desc, 0x0, sizeof(rpt_desc));
|
||||||
|
|
||||||
|
/* Get Report Descriptor Size */
|
||||||
|
res = ioctl(dev->device_handle, HIDIOCGRDESCSIZE, &desc_size);
|
||||||
|
if (res < 0)
|
||||||
|
perror("HIDIOCGRDESCSIZE");
|
||||||
|
|
||||||
|
|
||||||
|
/* Get Report Descriptor */
|
||||||
|
rpt_desc.size = desc_size;
|
||||||
|
res = ioctl(dev->device_handle, HIDIOCGRDESC, &rpt_desc);
|
||||||
|
if (res < 0) {
|
||||||
|
perror("HIDIOCGRDESC");
|
||||||
|
} else {
|
||||||
|
/* Determine if this device uses numbered reports. */
|
||||||
|
dev->uses_numbered_reports =
|
||||||
|
uses_numbered_reports(rpt_desc.value,
|
||||||
|
rpt_desc.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dev;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Unable to open any devices. */
|
||||||
|
free(dev);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int HID_API_EXPORT hid_write(hid_device *dev, const unsigned char *data, size_t length)
|
||||||
|
{
|
||||||
|
int bytes_written;
|
||||||
|
|
||||||
|
bytes_written = write(dev->device_handle, data, length);
|
||||||
|
|
||||||
|
return bytes_written;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int HID_API_EXPORT hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
|
||||||
|
{
|
||||||
|
int bytes_read;
|
||||||
|
|
||||||
|
if (milliseconds >= 0) {
|
||||||
|
/* Milliseconds is either 0 (non-blocking) or > 0 (contains
|
||||||
|
a valid timeout). In both cases we want to call poll()
|
||||||
|
and wait for data to arrive. Don't rely on non-blocking
|
||||||
|
operation (O_NONBLOCK) since some kernels don't seem to
|
||||||
|
properly report device disconnection through read() when
|
||||||
|
in non-blocking mode. */
|
||||||
|
int ret;
|
||||||
|
struct pollfd fds;
|
||||||
|
|
||||||
|
fds.fd = dev->device_handle;
|
||||||
|
fds.events = POLLIN;
|
||||||
|
fds.revents = 0;
|
||||||
|
ret = poll(&fds, 1, milliseconds);
|
||||||
|
if (ret == -1 || ret == 0) {
|
||||||
|
/* Error or timeout */
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Check for errors on the file descriptor. This will
|
||||||
|
indicate a device disconnection. */
|
||||||
|
if (fds.revents & (POLLERR | POLLHUP | POLLNVAL))
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes_read = read(dev->device_handle, data, length);
|
||||||
|
if (bytes_read < 0 && (errno == EAGAIN || errno == EINPROGRESS))
|
||||||
|
bytes_read = 0;
|
||||||
|
|
||||||
|
if (bytes_read >= 0 &&
|
||||||
|
kernel_version != 0 &&
|
||||||
|
kernel_version < KERNEL_VERSION(2,6,34) &&
|
||||||
|
dev->uses_numbered_reports) {
|
||||||
|
/* Work around a kernel bug. Chop off the first byte. */
|
||||||
|
memmove(data, data+1, bytes_read);
|
||||||
|
bytes_read--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HID_API_EXPORT hid_read(hid_device *dev, unsigned char *data, size_t length)
|
||||||
|
{
|
||||||
|
return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int HID_API_EXPORT hid_set_nonblocking(hid_device *dev, int nonblock)
|
||||||
|
{
|
||||||
|
/* Do all non-blocking in userspace using poll(), since it looks
|
||||||
|
like there's a bug in the kernel in some versions where
|
||||||
|
read() will not return -1 on disconnection of the USB device */
|
||||||
|
|
||||||
|
dev->blocking = !nonblock;
|
||||||
|
return 0; /* Success */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int HID_API_EXPORT hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
|
||||||
|
res = ioctl(dev->device_handle, HIDIOCSFEATURE(length), data);
|
||||||
|
if (res < 0)
|
||||||
|
perror("ioctl (SFEATURE)");
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HID_API_EXPORT hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length)
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
|
||||||
|
res = ioctl(dev->device_handle, HIDIOCGFEATURE(length), data);
|
||||||
|
if (res < 0)
|
||||||
|
perror("ioctl (GFEATURE)");
|
||||||
|
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void HID_API_EXPORT hid_close(hid_device *dev)
|
||||||
|
{
|
||||||
|
if (!dev)
|
||||||
|
return;
|
||||||
|
close(dev->device_handle);
|
||||||
|
free(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen)
|
||||||
|
{
|
||||||
|
return get_device_string(dev, DEVICE_STRING_MANUFACTURER, string, maxlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
int HID_API_EXPORT_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen)
|
||||||
|
{
|
||||||
|
return get_device_string(dev, DEVICE_STRING_PRODUCT, string, maxlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen)
|
||||||
|
{
|
||||||
|
return get_device_string(dev, DEVICE_STRING_SERIAL, string, maxlen);
|
||||||
|
}
|
||||||
|
|
||||||
|
int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
1121
src/hidapi/mac/hid.c
Normal file
1121
src/hidapi/mac/hid.c
Normal file
File diff suppressed because it is too large
Load diff
956
src/hidapi/win/hid.c
Normal file
956
src/hidapi/win/hid.c
Normal file
|
@ -0,0 +1,956 @@
|
||||||
|
/*******************************************************
|
||||||
|
HIDAPI - Multi-Platform library for
|
||||||
|
communication with HID devices.
|
||||||
|
|
||||||
|
Alan Ott
|
||||||
|
Signal 11 Software
|
||||||
|
|
||||||
|
8/22/2009
|
||||||
|
|
||||||
|
Copyright 2009, All Rights Reserved.
|
||||||
|
|
||||||
|
At the discretion of the user of this library,
|
||||||
|
this software may be licensed under the terms of the
|
||||||
|
GNU General Public License v3, a BSD-Style license, or the
|
||||||
|
original HIDAPI license as outlined in the LICENSE.txt,
|
||||||
|
LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
|
||||||
|
files located at the root of the source distribution.
|
||||||
|
These files may also be found in the public source
|
||||||
|
code repository located at:
|
||||||
|
http://github.com/signal11/hidapi .
|
||||||
|
********************************************************/
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#ifndef _NTDEF_
|
||||||
|
typedef LONG NTSTATUS;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
#include <ntdef.h>
|
||||||
|
#include <winbase.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __CYGWIN__
|
||||||
|
#include <ntdef.h>
|
||||||
|
#define _wcsdup wcsdup
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* The maximum number of characters that can be passed into the
|
||||||
|
HidD_Get*String() functions without it failing.*/
|
||||||
|
#define MAX_STRING_WCHARS 0xFFF
|
||||||
|
|
||||||
|
/*#define HIDAPI_USE_DDK*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
#include <setupapi.h>
|
||||||
|
#include <winioctl.h>
|
||||||
|
#ifdef HIDAPI_USE_DDK
|
||||||
|
#include <hidsdi.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Copied from inc/ddk/hidclass.h, part of the Windows DDK. */
|
||||||
|
#define HID_OUT_CTL_CODE(id) \
|
||||||
|
CTL_CODE(FILE_DEVICE_KEYBOARD, (id), METHOD_OUT_DIRECT, FILE_ANY_ACCESS)
|
||||||
|
#define IOCTL_HID_GET_FEATURE HID_OUT_CTL_CODE(100)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include "hidapi.h"
|
||||||
|
|
||||||
|
#undef MIN
|
||||||
|
#define MIN(x,y) ((x) < (y)? (x): (y))
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
/* Thanks Microsoft, but I know how to use strncpy(). */
|
||||||
|
#pragma warning(disable:4996)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef HIDAPI_USE_DDK
|
||||||
|
/* Since we're not building with the DDK, and the HID header
|
||||||
|
files aren't part of the SDK, we have to define all this
|
||||||
|
stuff here. In lookup_functions(), the function pointers
|
||||||
|
defined below are set. */
|
||||||
|
typedef struct _HIDD_ATTRIBUTES{
|
||||||
|
ULONG Size;
|
||||||
|
USHORT VendorID;
|
||||||
|
USHORT ProductID;
|
||||||
|
USHORT VersionNumber;
|
||||||
|
} HIDD_ATTRIBUTES, *PHIDD_ATTRIBUTES;
|
||||||
|
|
||||||
|
typedef USHORT USAGE;
|
||||||
|
typedef struct _HIDP_CAPS {
|
||||||
|
USAGE Usage;
|
||||||
|
USAGE UsagePage;
|
||||||
|
USHORT InputReportByteLength;
|
||||||
|
USHORT OutputReportByteLength;
|
||||||
|
USHORT FeatureReportByteLength;
|
||||||
|
USHORT Reserved[17];
|
||||||
|
USHORT fields_not_used_by_hidapi[10];
|
||||||
|
} HIDP_CAPS, *PHIDP_CAPS;
|
||||||
|
typedef void* PHIDP_PREPARSED_DATA;
|
||||||
|
#define HIDP_STATUS_SUCCESS 0x110000
|
||||||
|
|
||||||
|
typedef BOOLEAN (__stdcall *HidD_GetAttributes_)(HANDLE device, PHIDD_ATTRIBUTES attrib);
|
||||||
|
typedef BOOLEAN (__stdcall *HidD_GetSerialNumberString_)(HANDLE device, PVOID buffer, ULONG buffer_len);
|
||||||
|
typedef BOOLEAN (__stdcall *HidD_GetManufacturerString_)(HANDLE handle, PVOID buffer, ULONG buffer_len);
|
||||||
|
typedef BOOLEAN (__stdcall *HidD_GetProductString_)(HANDLE handle, PVOID buffer, ULONG buffer_len);
|
||||||
|
typedef BOOLEAN (__stdcall *HidD_SetFeature_)(HANDLE handle, PVOID data, ULONG length);
|
||||||
|
typedef BOOLEAN (__stdcall *HidD_GetFeature_)(HANDLE handle, PVOID data, ULONG length);
|
||||||
|
typedef BOOLEAN (__stdcall *HidD_GetIndexedString_)(HANDLE handle, ULONG string_index, PVOID buffer, ULONG buffer_len);
|
||||||
|
typedef BOOLEAN (__stdcall *HidD_GetPreparsedData_)(HANDLE handle, PHIDP_PREPARSED_DATA *preparsed_data);
|
||||||
|
typedef BOOLEAN (__stdcall *HidD_FreePreparsedData_)(PHIDP_PREPARSED_DATA preparsed_data);
|
||||||
|
typedef NTSTATUS (__stdcall *HidP_GetCaps_)(PHIDP_PREPARSED_DATA preparsed_data, HIDP_CAPS *caps);
|
||||||
|
typedef BOOLEAN (__stdcall *HidD_SetNumInputBuffers_)(HANDLE handle, ULONG number_buffers);
|
||||||
|
|
||||||
|
static HidD_GetAttributes_ HidD_GetAttributes;
|
||||||
|
static HidD_GetSerialNumberString_ HidD_GetSerialNumberString;
|
||||||
|
static HidD_GetManufacturerString_ HidD_GetManufacturerString;
|
||||||
|
static HidD_GetProductString_ HidD_GetProductString;
|
||||||
|
static HidD_SetFeature_ HidD_SetFeature;
|
||||||
|
static HidD_GetFeature_ HidD_GetFeature;
|
||||||
|
static HidD_GetIndexedString_ HidD_GetIndexedString;
|
||||||
|
static HidD_GetPreparsedData_ HidD_GetPreparsedData;
|
||||||
|
static HidD_FreePreparsedData_ HidD_FreePreparsedData;
|
||||||
|
static HidP_GetCaps_ HidP_GetCaps;
|
||||||
|
static HidD_SetNumInputBuffers_ HidD_SetNumInputBuffers;
|
||||||
|
|
||||||
|
static HMODULE lib_handle = NULL;
|
||||||
|
static BOOLEAN initialized = FALSE;
|
||||||
|
#endif /* HIDAPI_USE_DDK */
|
||||||
|
|
||||||
|
struct hid_device_ {
|
||||||
|
HANDLE device_handle;
|
||||||
|
BOOL blocking;
|
||||||
|
USHORT output_report_length;
|
||||||
|
size_t input_report_length;
|
||||||
|
void *last_error_str;
|
||||||
|
DWORD last_error_num;
|
||||||
|
BOOL read_pending;
|
||||||
|
char *read_buf;
|
||||||
|
OVERLAPPED ol;
|
||||||
|
};
|
||||||
|
|
||||||
|
static hid_device *new_hid_device()
|
||||||
|
{
|
||||||
|
hid_device *dev = (hid_device*) calloc(1, sizeof(hid_device));
|
||||||
|
dev->device_handle = INVALID_HANDLE_VALUE;
|
||||||
|
dev->blocking = TRUE;
|
||||||
|
dev->output_report_length = 0;
|
||||||
|
dev->input_report_length = 0;
|
||||||
|
dev->last_error_str = NULL;
|
||||||
|
dev->last_error_num = 0;
|
||||||
|
dev->read_pending = FALSE;
|
||||||
|
dev->read_buf = NULL;
|
||||||
|
memset(&dev->ol, 0, sizeof(dev->ol));
|
||||||
|
dev->ol.hEvent = CreateEvent(NULL, FALSE, FALSE /*initial state f=nonsignaled*/, NULL);
|
||||||
|
|
||||||
|
return dev;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_hid_device(hid_device *dev)
|
||||||
|
{
|
||||||
|
CloseHandle(dev->ol.hEvent);
|
||||||
|
CloseHandle(dev->device_handle);
|
||||||
|
LocalFree(dev->last_error_str);
|
||||||
|
free(dev->read_buf);
|
||||||
|
free(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void register_error(hid_device *dev, const char *op)
|
||||||
|
{
|
||||||
|
WCHAR *ptr, *msg;
|
||||||
|
|
||||||
|
FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||||
|
FORMAT_MESSAGE_FROM_SYSTEM |
|
||||||
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
|
NULL,
|
||||||
|
GetLastError(),
|
||||||
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
|
(LPVOID)&msg, 0/*sz*/,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
/* Get rid of the CR and LF that FormatMessage() sticks at the
|
||||||
|
end of the message. Thanks Microsoft! */
|
||||||
|
ptr = msg;
|
||||||
|
while (*ptr) {
|
||||||
|
if (*ptr == '\r') {
|
||||||
|
*ptr = 0x0000;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ptr++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Store the message off in the Device entry so that
|
||||||
|
the hid_error() function can pick it up. */
|
||||||
|
LocalFree(dev->last_error_str);
|
||||||
|
dev->last_error_str = msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef HIDAPI_USE_DDK
|
||||||
|
static int lookup_functions()
|
||||||
|
{
|
||||||
|
lib_handle = LoadLibraryA("hid.dll");
|
||||||
|
if (lib_handle) {
|
||||||
|
#define RESOLVE(x) x = (x##_)GetProcAddress(lib_handle, #x); if (!x) return -1;
|
||||||
|
RESOLVE(HidD_GetAttributes);
|
||||||
|
RESOLVE(HidD_GetSerialNumberString);
|
||||||
|
RESOLVE(HidD_GetManufacturerString);
|
||||||
|
RESOLVE(HidD_GetProductString);
|
||||||
|
RESOLVE(HidD_SetFeature);
|
||||||
|
RESOLVE(HidD_GetFeature);
|
||||||
|
RESOLVE(HidD_GetIndexedString);
|
||||||
|
RESOLVE(HidD_GetPreparsedData);
|
||||||
|
RESOLVE(HidD_FreePreparsedData);
|
||||||
|
RESOLVE(HidP_GetCaps);
|
||||||
|
RESOLVE(HidD_SetNumInputBuffers);
|
||||||
|
#undef RESOLVE
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static HANDLE open_device(const char *path, BOOL open_rw)
|
||||||
|
{
|
||||||
|
HANDLE handle;
|
||||||
|
DWORD desired_access = (open_rw)? (GENERIC_WRITE | GENERIC_READ): 0;
|
||||||
|
DWORD share_mode = FILE_SHARE_READ|FILE_SHARE_WRITE;
|
||||||
|
|
||||||
|
handle = CreateFileA(path,
|
||||||
|
desired_access,
|
||||||
|
share_mode,
|
||||||
|
NULL,
|
||||||
|
OPEN_EXISTING,
|
||||||
|
FILE_FLAG_OVERLAPPED,/*FILE_ATTRIBUTE_NORMAL,*/
|
||||||
|
0);
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HID_API_EXPORT hid_init(void)
|
||||||
|
{
|
||||||
|
#ifndef HIDAPI_USE_DDK
|
||||||
|
if (!initialized) {
|
||||||
|
if (lookup_functions() < 0) {
|
||||||
|
hid_exit();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
initialized = TRUE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HID_API_EXPORT hid_exit(void)
|
||||||
|
{
|
||||||
|
#ifndef HIDAPI_USE_DDK
|
||||||
|
if (lib_handle)
|
||||||
|
FreeLibrary(lib_handle);
|
||||||
|
lib_handle = NULL;
|
||||||
|
initialized = FALSE;
|
||||||
|
#endif
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id)
|
||||||
|
{
|
||||||
|
BOOL res;
|
||||||
|
struct hid_device_info *root = NULL; /* return object */
|
||||||
|
struct hid_device_info *cur_dev = NULL;
|
||||||
|
|
||||||
|
/* Windows objects for interacting with the driver. */
|
||||||
|
GUID InterfaceClassGuid = {0x4d1e55b2, 0xf16f, 0x11cf, {0x88, 0xcb, 0x00, 0x11, 0x11, 0x00, 0x00, 0x30} };
|
||||||
|
SP_DEVINFO_DATA devinfo_data;
|
||||||
|
SP_DEVICE_INTERFACE_DATA device_interface_data;
|
||||||
|
SP_DEVICE_INTERFACE_DETAIL_DATA_A *device_interface_detail_data = NULL;
|
||||||
|
HDEVINFO device_info_set = INVALID_HANDLE_VALUE;
|
||||||
|
int device_index = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (hid_init() < 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Initialize the Windows objects. */
|
||||||
|
memset(&devinfo_data, 0x0, sizeof(devinfo_data));
|
||||||
|
devinfo_data.cbSize = sizeof(SP_DEVINFO_DATA);
|
||||||
|
device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
|
||||||
|
|
||||||
|
/* Get information for all the devices belonging to the HID class. */
|
||||||
|
device_info_set = SetupDiGetClassDevsA(&InterfaceClassGuid, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
|
||||||
|
|
||||||
|
/* Iterate over each device in the HID class, looking for the right one. */
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
HANDLE write_handle = INVALID_HANDLE_VALUE;
|
||||||
|
DWORD required_size = 0;
|
||||||
|
HIDD_ATTRIBUTES attrib;
|
||||||
|
|
||||||
|
res = SetupDiEnumDeviceInterfaces(device_info_set,
|
||||||
|
NULL,
|
||||||
|
&InterfaceClassGuid,
|
||||||
|
device_index,
|
||||||
|
&device_interface_data);
|
||||||
|
|
||||||
|
if (!res) {
|
||||||
|
/* A return of FALSE from this function means that
|
||||||
|
there are no more devices. */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Call with 0-sized detail size, and let the function
|
||||||
|
tell us how long the detail struct needs to be. The
|
||||||
|
size is put in &required_size. */
|
||||||
|
res = SetupDiGetDeviceInterfaceDetailA(device_info_set,
|
||||||
|
&device_interface_data,
|
||||||
|
NULL,
|
||||||
|
0,
|
||||||
|
&required_size,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
/* Allocate a long enough structure for device_interface_detail_data. */
|
||||||
|
device_interface_detail_data = (SP_DEVICE_INTERFACE_DETAIL_DATA_A*) malloc(required_size);
|
||||||
|
device_interface_detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A);
|
||||||
|
|
||||||
|
/* Get the detailed data for this device. The detail data gives us
|
||||||
|
the device path for this device, which is then passed into
|
||||||
|
CreateFile() to get a handle to the device. */
|
||||||
|
res = SetupDiGetDeviceInterfaceDetailA(device_info_set,
|
||||||
|
&device_interface_data,
|
||||||
|
device_interface_detail_data,
|
||||||
|
required_size,
|
||||||
|
NULL,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
if (!res) {
|
||||||
|
/* register_error(dev, "Unable to call SetupDiGetDeviceInterfaceDetail");
|
||||||
|
Continue to the next device. */
|
||||||
|
goto cont;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure this device is of Setup Class "HIDClass" and has a
|
||||||
|
driver bound to it. */
|
||||||
|
for (i = 0; ; i++) {
|
||||||
|
char driver_name[256];
|
||||||
|
|
||||||
|
/* Populate devinfo_data. This function will return failure
|
||||||
|
when there are no more interfaces left. */
|
||||||
|
res = SetupDiEnumDeviceInfo(device_info_set, i, &devinfo_data);
|
||||||
|
if (!res)
|
||||||
|
goto cont;
|
||||||
|
|
||||||
|
res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data,
|
||||||
|
SPDRP_CLASS, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL);
|
||||||
|
if (!res)
|
||||||
|
goto cont;
|
||||||
|
|
||||||
|
if ((strcmp(driver_name, "HIDClass") == 0) ||
|
||||||
|
(strcmp(driver_name, "Mouse") == 0) ||
|
||||||
|
(strcmp(driver_name, "Keyboard") == 0)) {
|
||||||
|
/* See if there's a driver bound. */
|
||||||
|
res = SetupDiGetDeviceRegistryPropertyA(device_info_set, &devinfo_data,
|
||||||
|
SPDRP_DRIVER, NULL, (PBYTE)driver_name, sizeof(driver_name), NULL);
|
||||||
|
if (res)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//wprintf(L"HandleName: %s\n", device_interface_detail_data->DevicePath);
|
||||||
|
|
||||||
|
/* Open a handle to the device */
|
||||||
|
write_handle = open_device(device_interface_detail_data->DevicePath, FALSE);
|
||||||
|
|
||||||
|
/* Check validity of write_handle. */
|
||||||
|
if (write_handle == INVALID_HANDLE_VALUE) {
|
||||||
|
/* Unable to open the device. */
|
||||||
|
//register_error(dev, "CreateFile");
|
||||||
|
goto cont_close;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Get the Vendor ID and Product ID for this device. */
|
||||||
|
attrib.Size = sizeof(HIDD_ATTRIBUTES);
|
||||||
|
HidD_GetAttributes(write_handle, &attrib);
|
||||||
|
//wprintf(L"Product/Vendor: %x %x\n", attrib.ProductID, attrib.VendorID);
|
||||||
|
|
||||||
|
/* Check the VID/PID to see if we should add this
|
||||||
|
device to the enumeration list. */
|
||||||
|
if ((vendor_id == 0x0 || attrib.VendorID == vendor_id) &&
|
||||||
|
(product_id == 0x0 || attrib.ProductID == product_id)) {
|
||||||
|
|
||||||
|
#define WSTR_LEN 512
|
||||||
|
const char *str;
|
||||||
|
struct hid_device_info *tmp;
|
||||||
|
PHIDP_PREPARSED_DATA pp_data = NULL;
|
||||||
|
HIDP_CAPS caps;
|
||||||
|
BOOLEAN res;
|
||||||
|
NTSTATUS nt_res;
|
||||||
|
wchar_t wstr[WSTR_LEN]; /* TODO: Determine Size */
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
/* VID/PID match. Create the record. */
|
||||||
|
tmp = (struct hid_device_info*) calloc(1, sizeof(struct hid_device_info));
|
||||||
|
if (cur_dev) {
|
||||||
|
cur_dev->next = tmp;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
root = tmp;
|
||||||
|
}
|
||||||
|
cur_dev = tmp;
|
||||||
|
|
||||||
|
/* Get the Usage Page and Usage for this device. */
|
||||||
|
res = HidD_GetPreparsedData(write_handle, &pp_data);
|
||||||
|
if (res) {
|
||||||
|
nt_res = HidP_GetCaps(pp_data, &caps);
|
||||||
|
if (nt_res == HIDP_STATUS_SUCCESS) {
|
||||||
|
cur_dev->usage_page = caps.UsagePage;
|
||||||
|
cur_dev->usage = caps.Usage;
|
||||||
|
}
|
||||||
|
|
||||||
|
HidD_FreePreparsedData(pp_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fill out the record */
|
||||||
|
cur_dev->next = NULL;
|
||||||
|
str = device_interface_detail_data->DevicePath;
|
||||||
|
if (str) {
|
||||||
|
len = strlen(str);
|
||||||
|
cur_dev->path = (char*) calloc(len+1, sizeof(char));
|
||||||
|
strncpy(cur_dev->path, str, len+1);
|
||||||
|
cur_dev->path[len] = '\0';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
cur_dev->path = NULL;
|
||||||
|
|
||||||
|
/* Serial Number */
|
||||||
|
res = HidD_GetSerialNumberString(write_handle, wstr, sizeof(wstr));
|
||||||
|
wstr[WSTR_LEN-1] = 0x0000;
|
||||||
|
if (res) {
|
||||||
|
cur_dev->serial_number = _wcsdup(wstr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Manufacturer String */
|
||||||
|
res = HidD_GetManufacturerString(write_handle, wstr, sizeof(wstr));
|
||||||
|
wstr[WSTR_LEN-1] = 0x0000;
|
||||||
|
if (res) {
|
||||||
|
cur_dev->manufacturer_string = _wcsdup(wstr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Product String */
|
||||||
|
res = HidD_GetProductString(write_handle, wstr, sizeof(wstr));
|
||||||
|
wstr[WSTR_LEN-1] = 0x0000;
|
||||||
|
if (res) {
|
||||||
|
cur_dev->product_string = _wcsdup(wstr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* VID/PID */
|
||||||
|
cur_dev->vendor_id = attrib.VendorID;
|
||||||
|
cur_dev->product_id = attrib.ProductID;
|
||||||
|
|
||||||
|
/* Release Number */
|
||||||
|
cur_dev->release_number = attrib.VersionNumber;
|
||||||
|
|
||||||
|
/* Interface Number. It can sometimes be parsed out of the path
|
||||||
|
on Windows if a device has multiple interfaces. See
|
||||||
|
http://msdn.microsoft.com/en-us/windows/hardware/gg487473 or
|
||||||
|
search for "Hardware IDs for HID Devices" at MSDN. If it's not
|
||||||
|
in the path, it's set to -1. */
|
||||||
|
cur_dev->interface_number = -1;
|
||||||
|
if (cur_dev->path) {
|
||||||
|
char *interface_component = strstr(cur_dev->path, "&mi_");
|
||||||
|
if (interface_component) {
|
||||||
|
char *hex_str = interface_component + 4;
|
||||||
|
char *endptr = NULL;
|
||||||
|
cur_dev->interface_number = strtol(hex_str, &endptr, 16);
|
||||||
|
if (endptr == hex_str) {
|
||||||
|
/* The parsing failed. Set interface_number to -1. */
|
||||||
|
cur_dev->interface_number = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cont_close:
|
||||||
|
CloseHandle(write_handle);
|
||||||
|
cont:
|
||||||
|
/* We no longer need the detail data. It can be freed */
|
||||||
|
free(device_interface_detail_data);
|
||||||
|
|
||||||
|
device_index++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Close the device information handle. */
|
||||||
|
SetupDiDestroyDeviceInfoList(device_info_set);
|
||||||
|
|
||||||
|
return root;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs)
|
||||||
|
{
|
||||||
|
/* TODO: Merge this with the Linux version. This function is platform-independent. */
|
||||||
|
struct hid_device_info *d = devs;
|
||||||
|
while (d) {
|
||||||
|
struct hid_device_info *next = d->next;
|
||||||
|
free(d->path);
|
||||||
|
free(d->serial_number);
|
||||||
|
free(d->manufacturer_string);
|
||||||
|
free(d->product_string);
|
||||||
|
free(d);
|
||||||
|
d = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number)
|
||||||
|
{
|
||||||
|
/* TODO: Merge this functions with the Linux version. This function should be platform independent. */
|
||||||
|
struct hid_device_info *devs, *cur_dev;
|
||||||
|
const char *path_to_open = NULL;
|
||||||
|
hid_device *handle = NULL;
|
||||||
|
|
||||||
|
devs = hid_enumerate(vendor_id, product_id);
|
||||||
|
cur_dev = devs;
|
||||||
|
while (cur_dev) {
|
||||||
|
if (cur_dev->vendor_id == vendor_id &&
|
||||||
|
cur_dev->product_id == product_id) {
|
||||||
|
if (serial_number) {
|
||||||
|
if (wcscmp(serial_number, cur_dev->serial_number) == 0) {
|
||||||
|
path_to_open = cur_dev->path;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
path_to_open = cur_dev->path;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cur_dev = cur_dev->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path_to_open) {
|
||||||
|
/* Open the device */
|
||||||
|
handle = hid_open_path(path_to_open);
|
||||||
|
}
|
||||||
|
|
||||||
|
hid_free_enumeration(devs);
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path)
|
||||||
|
{
|
||||||
|
hid_device *dev;
|
||||||
|
HIDP_CAPS caps;
|
||||||
|
PHIDP_PREPARSED_DATA pp_data = NULL;
|
||||||
|
BOOLEAN res;
|
||||||
|
NTSTATUS nt_res;
|
||||||
|
|
||||||
|
if (hid_init() < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
dev = new_hid_device();
|
||||||
|
|
||||||
|
/* Open a handle to the device */
|
||||||
|
dev->device_handle = open_device(path, TRUE);
|
||||||
|
|
||||||
|
/* Check validity of write_handle. */
|
||||||
|
if (dev->device_handle == INVALID_HANDLE_VALUE) {
|
||||||
|
/* System devices, such as keyboards and mice, cannot be opened in
|
||||||
|
read-write mode, because the system takes exclusive control over
|
||||||
|
them. This is to prevent keyloggers. However, feature reports
|
||||||
|
can still be sent and received. Retry opening the device, but
|
||||||
|
without read/write access. */
|
||||||
|
dev->device_handle = open_device(path, FALSE);
|
||||||
|
|
||||||
|
/* Check the validity of the limited device_handle. */
|
||||||
|
if (dev->device_handle == INVALID_HANDLE_VALUE) {
|
||||||
|
/* Unable to open the device, even without read-write mode. */
|
||||||
|
register_error(dev, "CreateFile");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the Input Report buffer size to 64 reports. */
|
||||||
|
res = HidD_SetNumInputBuffers(dev->device_handle, 64);
|
||||||
|
if (!res) {
|
||||||
|
register_error(dev, "HidD_SetNumInputBuffers");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the Input Report length for the device. */
|
||||||
|
res = HidD_GetPreparsedData(dev->device_handle, &pp_data);
|
||||||
|
if (!res) {
|
||||||
|
register_error(dev, "HidD_GetPreparsedData");
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
nt_res = HidP_GetCaps(pp_data, &caps);
|
||||||
|
if (nt_res != HIDP_STATUS_SUCCESS) {
|
||||||
|
register_error(dev, "HidP_GetCaps");
|
||||||
|
goto err_pp_data;
|
||||||
|
}
|
||||||
|
dev->output_report_length = caps.OutputReportByteLength;
|
||||||
|
dev->input_report_length = caps.InputReportByteLength;
|
||||||
|
HidD_FreePreparsedData(pp_data);
|
||||||
|
|
||||||
|
dev->read_buf = (char*) malloc(dev->input_report_length);
|
||||||
|
|
||||||
|
return dev;
|
||||||
|
|
||||||
|
err_pp_data:
|
||||||
|
HidD_FreePreparsedData(pp_data);
|
||||||
|
err:
|
||||||
|
free_hid_device(dev);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HID_API_EXPORT HID_API_CALL hid_write(hid_device *dev, const unsigned char *data, size_t length)
|
||||||
|
{
|
||||||
|
DWORD bytes_written;
|
||||||
|
BOOL res;
|
||||||
|
|
||||||
|
OVERLAPPED ol;
|
||||||
|
unsigned char *buf;
|
||||||
|
memset(&ol, 0, sizeof(ol));
|
||||||
|
|
||||||
|
/* Make sure the right number of bytes are passed to WriteFile. Windows
|
||||||
|
expects the number of bytes which are in the _longest_ report (plus
|
||||||
|
one for the report number) bytes even if the data is a report
|
||||||
|
which is shorter than that. Windows gives us this value in
|
||||||
|
caps.OutputReportByteLength. If a user passes in fewer bytes than this,
|
||||||
|
create a temporary buffer which is the proper size. */
|
||||||
|
if (length >= dev->output_report_length) {
|
||||||
|
/* The user passed the right number of bytes. Use the buffer as-is. */
|
||||||
|
buf = (unsigned char *) data;
|
||||||
|
} else {
|
||||||
|
/* Create a temporary buffer and copy the user's data
|
||||||
|
into it, padding the rest with zeros. */
|
||||||
|
buf = (unsigned char *) malloc(dev->output_report_length);
|
||||||
|
memcpy(buf, data, length);
|
||||||
|
memset(buf + length, 0, dev->output_report_length - length);
|
||||||
|
length = dev->output_report_length;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = WriteFile(dev->device_handle, buf, length, NULL, &ol);
|
||||||
|
|
||||||
|
if (!res) {
|
||||||
|
if (GetLastError() != ERROR_IO_PENDING) {
|
||||||
|
/* WriteFile() failed. Return error. */
|
||||||
|
register_error(dev, "WriteFile");
|
||||||
|
bytes_written = -1;
|
||||||
|
goto end_of_function;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait here until the write is done. This makes
|
||||||
|
hid_write() synchronous. */
|
||||||
|
res = GetOverlappedResult(dev->device_handle, &ol, &bytes_written, TRUE/*wait*/);
|
||||||
|
if (!res) {
|
||||||
|
/* The Write operation failed. */
|
||||||
|
register_error(dev, "WriteFile");
|
||||||
|
bytes_written = -1;
|
||||||
|
goto end_of_function;
|
||||||
|
}
|
||||||
|
|
||||||
|
end_of_function:
|
||||||
|
if (buf != data)
|
||||||
|
free(buf);
|
||||||
|
|
||||||
|
return bytes_written;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds)
|
||||||
|
{
|
||||||
|
DWORD bytes_read = 0;
|
||||||
|
size_t copy_len = 0;
|
||||||
|
BOOL res;
|
||||||
|
|
||||||
|
/* Copy the handle for convenience. */
|
||||||
|
HANDLE ev = dev->ol.hEvent;
|
||||||
|
|
||||||
|
if (!dev->read_pending) {
|
||||||
|
/* Start an Overlapped I/O read. */
|
||||||
|
dev->read_pending = TRUE;
|
||||||
|
memset(dev->read_buf, 0, dev->input_report_length);
|
||||||
|
ResetEvent(ev);
|
||||||
|
res = ReadFile(dev->device_handle, dev->read_buf, dev->input_report_length, &bytes_read, &dev->ol);
|
||||||
|
|
||||||
|
if (!res) {
|
||||||
|
if (GetLastError() != ERROR_IO_PENDING) {
|
||||||
|
/* ReadFile() has failed.
|
||||||
|
Clean up and return error. */
|
||||||
|
CancelIo(dev->device_handle);
|
||||||
|
dev->read_pending = FALSE;
|
||||||
|
goto end_of_function;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (milliseconds >= 0) {
|
||||||
|
/* See if there is any data yet. */
|
||||||
|
res = WaitForSingleObject(ev, milliseconds);
|
||||||
|
if (res != WAIT_OBJECT_0) {
|
||||||
|
/* There was no data this time. Return zero bytes available,
|
||||||
|
but leave the Overlapped I/O running. */
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Either WaitForSingleObject() told us that ReadFile has completed, or
|
||||||
|
we are in non-blocking mode. Get the number of bytes read. The actual
|
||||||
|
data has been copied to the data[] array which was passed to ReadFile(). */
|
||||||
|
res = GetOverlappedResult(dev->device_handle, &dev->ol, &bytes_read, TRUE/*wait*/);
|
||||||
|
|
||||||
|
/* Set pending back to false, even if GetOverlappedResult() returned error. */
|
||||||
|
dev->read_pending = FALSE;
|
||||||
|
|
||||||
|
if (res && bytes_read > 0) {
|
||||||
|
if (dev->read_buf[0] == 0x0) {
|
||||||
|
/* If report numbers aren't being used, but Windows sticks a report
|
||||||
|
number (0x0) on the beginning of the report anyway. To make this
|
||||||
|
work like the other platforms, and to make it work more like the
|
||||||
|
HID spec, we'll skip over this byte. */
|
||||||
|
bytes_read--;
|
||||||
|
copy_len = length > bytes_read ? bytes_read : length;
|
||||||
|
memcpy(data, dev->read_buf+1, copy_len);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/* Copy the whole buffer, report number and all. */
|
||||||
|
copy_len = length > bytes_read ? bytes_read : length;
|
||||||
|
memcpy(data, dev->read_buf, copy_len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
end_of_function:
|
||||||
|
if (!res) {
|
||||||
|
register_error(dev, "GetOverlappedResult");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return copy_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HID_API_EXPORT HID_API_CALL hid_read(hid_device *dev, unsigned char *data, size_t length)
|
||||||
|
{
|
||||||
|
return hid_read_timeout(dev, data, length, (dev->blocking)? -1: 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *dev, int nonblock)
|
||||||
|
{
|
||||||
|
dev->blocking = !nonblock;
|
||||||
|
return 0; /* Success */
|
||||||
|
}
|
||||||
|
|
||||||
|
int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *dev, const unsigned char *data, size_t length)
|
||||||
|
{
|
||||||
|
BOOL res = HidD_SetFeature(dev->device_handle, (PVOID)data, length);
|
||||||
|
if (!res) {
|
||||||
|
register_error(dev, "HidD_SetFeature");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *dev, unsigned char *data, size_t length)
|
||||||
|
{
|
||||||
|
BOOL res;
|
||||||
|
#if 0
|
||||||
|
res = HidD_GetFeature(dev->device_handle, data, length);
|
||||||
|
if (!res) {
|
||||||
|
register_error(dev, "HidD_GetFeature");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0; /* HidD_GetFeature() doesn't give us an actual length, unfortunately */
|
||||||
|
#else
|
||||||
|
DWORD bytes_returned;
|
||||||
|
|
||||||
|
OVERLAPPED ol;
|
||||||
|
memset(&ol, 0, sizeof(ol));
|
||||||
|
|
||||||
|
res = DeviceIoControl(dev->device_handle,
|
||||||
|
IOCTL_HID_GET_FEATURE,
|
||||||
|
data, length,
|
||||||
|
data, length,
|
||||||
|
&bytes_returned, &ol);
|
||||||
|
|
||||||
|
if (!res) {
|
||||||
|
if (GetLastError() != ERROR_IO_PENDING) {
|
||||||
|
/* DeviceIoControl() failed. Return error. */
|
||||||
|
register_error(dev, "Send Feature Report DeviceIoControl");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait here until the write is done. This makes
|
||||||
|
hid_get_feature_report() synchronous. */
|
||||||
|
res = GetOverlappedResult(dev->device_handle, &ol, &bytes_returned, TRUE/*wait*/);
|
||||||
|
if (!res) {
|
||||||
|
/* The operation failed. */
|
||||||
|
register_error(dev, "Send Feature Report GetOverLappedResult");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* bytes_returned does not include the first byte which contains the
|
||||||
|
report ID. The data buffer actually contains one more byte than
|
||||||
|
bytes_returned. */
|
||||||
|
bytes_returned++;
|
||||||
|
|
||||||
|
return bytes_returned;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void HID_API_EXPORT HID_API_CALL hid_close(hid_device *dev)
|
||||||
|
{
|
||||||
|
if (!dev)
|
||||||
|
return;
|
||||||
|
CancelIo(dev->device_handle);
|
||||||
|
free_hid_device(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
int HID_API_EXPORT_CALL HID_API_CALL hid_get_manufacturer_string(hid_device *dev, wchar_t *string, size_t maxlen)
|
||||||
|
{
|
||||||
|
BOOL res;
|
||||||
|
|
||||||
|
res = HidD_GetManufacturerString(dev->device_handle, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS));
|
||||||
|
if (!res) {
|
||||||
|
register_error(dev, "HidD_GetManufacturerString");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HID_API_EXPORT_CALL HID_API_CALL hid_get_product_string(hid_device *dev, wchar_t *string, size_t maxlen)
|
||||||
|
{
|
||||||
|
BOOL res;
|
||||||
|
|
||||||
|
res = HidD_GetProductString(dev->device_handle, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS));
|
||||||
|
if (!res) {
|
||||||
|
register_error(dev, "HidD_GetProductString");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HID_API_EXPORT_CALL HID_API_CALL hid_get_serial_number_string(hid_device *dev, wchar_t *string, size_t maxlen)
|
||||||
|
{
|
||||||
|
BOOL res;
|
||||||
|
|
||||||
|
res = HidD_GetSerialNumberString(dev->device_handle, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS));
|
||||||
|
if (!res) {
|
||||||
|
register_error(dev, "HidD_GetSerialNumberString");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int HID_API_EXPORT_CALL HID_API_CALL hid_get_indexed_string(hid_device *dev, int string_index, wchar_t *string, size_t maxlen)
|
||||||
|
{
|
||||||
|
BOOL res;
|
||||||
|
|
||||||
|
res = HidD_GetIndexedString(dev->device_handle, string_index, string, sizeof(wchar_t) * MIN(maxlen, MAX_STRING_WCHARS));
|
||||||
|
if (!res) {
|
||||||
|
register_error(dev, "HidD_GetIndexedString");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
HID_API_EXPORT const wchar_t * HID_API_CALL hid_error(hid_device *dev)
|
||||||
|
{
|
||||||
|
return (wchar_t*)dev->last_error_str;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*#define PICPGM*/
|
||||||
|
/*#define S11*/
|
||||||
|
#define P32
|
||||||
|
#ifdef S11
|
||||||
|
unsigned short VendorID = 0xa0a0;
|
||||||
|
unsigned short ProductID = 0x0001;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef P32
|
||||||
|
unsigned short VendorID = 0x04d8;
|
||||||
|
unsigned short ProductID = 0x3f;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef PICPGM
|
||||||
|
unsigned short VendorID = 0x04d8;
|
||||||
|
unsigned short ProductID = 0x0033;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
int __cdecl main(int argc, char* argv[])
|
||||||
|
{
|
||||||
|
int res;
|
||||||
|
unsigned char buf[65];
|
||||||
|
|
||||||
|
UNREFERENCED_PARAMETER(argc);
|
||||||
|
UNREFERENCED_PARAMETER(argv);
|
||||||
|
|
||||||
|
/* Set up the command buffer. */
|
||||||
|
memset(buf,0x00,sizeof(buf));
|
||||||
|
buf[0] = 0;
|
||||||
|
buf[1] = 0x81;
|
||||||
|
|
||||||
|
|
||||||
|
/* Open the device. */
|
||||||
|
int handle = open(VendorID, ProductID, L"12345");
|
||||||
|
if (handle < 0)
|
||||||
|
printf("unable to open device\n");
|
||||||
|
|
||||||
|
|
||||||
|
/* Toggle LED (cmd 0x80) */
|
||||||
|
buf[1] = 0x80;
|
||||||
|
res = write(handle, buf, 65);
|
||||||
|
if (res < 0)
|
||||||
|
printf("Unable to write()\n");
|
||||||
|
|
||||||
|
/* Request state (cmd 0x81) */
|
||||||
|
buf[1] = 0x81;
|
||||||
|
write(handle, buf, 65);
|
||||||
|
if (res < 0)
|
||||||
|
printf("Unable to write() (2)\n");
|
||||||
|
|
||||||
|
/* Read requested state */
|
||||||
|
read(handle, buf, 65);
|
||||||
|
if (res < 0)
|
||||||
|
printf("Unable to read()\n");
|
||||||
|
|
||||||
|
/* Print out the returned buffer. */
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
printf("buf[%d]: %d\n", i, buf[i]);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /* extern "C" */
|
||||||
|
#endif
|
|
@ -6,9 +6,6 @@
|
||||||
#include "Geometry.hpp"
|
#include "Geometry.hpp"
|
||||||
#include "GCode/PrintExtents.hpp"
|
#include "GCode/PrintExtents.hpp"
|
||||||
#include "GCode/WipeTower.hpp"
|
#include "GCode/WipeTower.hpp"
|
||||||
#if ENABLE_THUMBNAIL_GENERATOR
|
|
||||||
#include "GCode/ThumbnailData.hpp"
|
|
||||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
|
||||||
#include "ShortestPath.hpp"
|
#include "ShortestPath.hpp"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
|
|
||||||
|
@ -695,7 +692,7 @@ std::vector<std::pair<coordf_t, std::vector<GCode::LayerToPrint>>> GCode::collec
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_THUMBNAIL_GENERATOR
|
#if ENABLE_THUMBNAIL_GENERATOR
|
||||||
void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_data, const std::vector<ThumbnailData>* thumbnail_data)
|
void GCode::do_export(Print* print, const char* path, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb)
|
||||||
#else
|
#else
|
||||||
void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_data)
|
void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_data)
|
||||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||||
|
@ -725,7 +722,7 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
|
||||||
try {
|
try {
|
||||||
m_placeholder_parser_failed_templates.clear();
|
m_placeholder_parser_failed_templates.clear();
|
||||||
#if ENABLE_THUMBNAIL_GENERATOR
|
#if ENABLE_THUMBNAIL_GENERATOR
|
||||||
this->_do_export(*print, file, thumbnail_data);
|
this->_do_export(*print, file, thumbnail_cb);
|
||||||
#else
|
#else
|
||||||
this->_do_export(*print, file);
|
this->_do_export(*print, file);
|
||||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||||
|
@ -793,9 +790,9 @@ void GCode::do_export(Print *print, const char *path, GCodePreviewData *preview_
|
||||||
}
|
}
|
||||||
|
|
||||||
#if ENABLE_THUMBNAIL_GENERATOR
|
#if ENABLE_THUMBNAIL_GENERATOR
|
||||||
void GCode::_do_export(Print& print, FILE* file, const std::vector<ThumbnailData>* thumbnail_data)
|
void GCode::_do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thumbnail_cb)
|
||||||
#else
|
#else
|
||||||
void GCode::_do_export(Print &print, FILE *file)
|
void GCode::_do_export(Print& print, FILE* file)
|
||||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||||
{
|
{
|
||||||
PROFILE_FUNC();
|
PROFILE_FUNC();
|
||||||
|
@ -812,46 +809,46 @@ void GCode::_do_export(Print &print, FILE *file)
|
||||||
// shall be adjusted as well to produce a G-code block compatible with the particular firmware flavor.
|
// shall be adjusted as well to produce a G-code block compatible with the particular firmware flavor.
|
||||||
if (print.config().gcode_flavor.value == gcfMarlin) {
|
if (print.config().gcode_flavor.value == gcfMarlin) {
|
||||||
m_normal_time_estimator.set_max_acceleration((float)print.config().machine_max_acceleration_extruding.values[0]);
|
m_normal_time_estimator.set_max_acceleration((float)print.config().machine_max_acceleration_extruding.values[0]);
|
||||||
m_normal_time_estimator.set_retract_acceleration((float)print.config().machine_max_acceleration_retracting.values[0]);
|
m_normal_time_estimator.set_retract_acceleration((float)print.config().machine_max_acceleration_retracting.values[0]);
|
||||||
m_normal_time_estimator.set_minimum_feedrate((float)print.config().machine_min_extruding_rate.values[0]);
|
m_normal_time_estimator.set_minimum_feedrate((float)print.config().machine_min_extruding_rate.values[0]);
|
||||||
m_normal_time_estimator.set_minimum_travel_feedrate((float)print.config().machine_min_travel_rate.values[0]);
|
m_normal_time_estimator.set_minimum_travel_feedrate((float)print.config().machine_min_travel_rate.values[0]);
|
||||||
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, (float)print.config().machine_max_acceleration_x.values[0]);
|
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, (float)print.config().machine_max_acceleration_x.values[0]);
|
||||||
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, (float)print.config().machine_max_acceleration_y.values[0]);
|
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, (float)print.config().machine_max_acceleration_y.values[0]);
|
||||||
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, (float)print.config().machine_max_acceleration_z.values[0]);
|
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, (float)print.config().machine_max_acceleration_z.values[0]);
|
||||||
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, (float)print.config().machine_max_acceleration_e.values[0]);
|
m_normal_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, (float)print.config().machine_max_acceleration_e.values[0]);
|
||||||
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, (float)print.config().machine_max_feedrate_x.values[0]);
|
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, (float)print.config().machine_max_feedrate_x.values[0]);
|
||||||
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, (float)print.config().machine_max_feedrate_y.values[0]);
|
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, (float)print.config().machine_max_feedrate_y.values[0]);
|
||||||
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, (float)print.config().machine_max_feedrate_z.values[0]);
|
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, (float)print.config().machine_max_feedrate_z.values[0]);
|
||||||
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, (float)print.config().machine_max_feedrate_e.values[0]);
|
m_normal_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, (float)print.config().machine_max_feedrate_e.values[0]);
|
||||||
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, (float)print.config().machine_max_jerk_x.values[0]);
|
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, (float)print.config().machine_max_jerk_x.values[0]);
|
||||||
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, (float)print.config().machine_max_jerk_y.values[0]);
|
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, (float)print.config().machine_max_jerk_y.values[0]);
|
||||||
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, (float)print.config().machine_max_jerk_z.values[0]);
|
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, (float)print.config().machine_max_jerk_z.values[0]);
|
||||||
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, (float)print.config().machine_max_jerk_e.values[0]);
|
m_normal_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, (float)print.config().machine_max_jerk_e.values[0]);
|
||||||
|
|
||||||
if (m_silent_time_estimator_enabled)
|
if (m_silent_time_estimator_enabled)
|
||||||
{
|
{
|
||||||
m_silent_time_estimator.reset();
|
m_silent_time_estimator.reset();
|
||||||
m_silent_time_estimator.set_dialect(print.config().gcode_flavor);
|
m_silent_time_estimator.set_dialect(print.config().gcode_flavor);
|
||||||
/* "Stealth mode" values can be just a copy of "normal mode" values
|
/* "Stealth mode" values can be just a copy of "normal mode" values
|
||||||
* (when they aren't input for a printer preset).
|
* (when they aren't input for a printer preset).
|
||||||
* Thus, use back value from values, instead of second one, which could be absent
|
* Thus, use back value from values, instead of second one, which could be absent
|
||||||
*/
|
*/
|
||||||
m_silent_time_estimator.set_max_acceleration((float)print.config().machine_max_acceleration_extruding.values.back());
|
m_silent_time_estimator.set_max_acceleration((float)print.config().machine_max_acceleration_extruding.values.back());
|
||||||
m_silent_time_estimator.set_retract_acceleration((float)print.config().machine_max_acceleration_retracting.values.back());
|
m_silent_time_estimator.set_retract_acceleration((float)print.config().machine_max_acceleration_retracting.values.back());
|
||||||
m_silent_time_estimator.set_minimum_feedrate((float)print.config().machine_min_extruding_rate.values.back());
|
m_silent_time_estimator.set_minimum_feedrate((float)print.config().machine_min_extruding_rate.values.back());
|
||||||
m_silent_time_estimator.set_minimum_travel_feedrate((float)print.config().machine_min_travel_rate.values.back());
|
m_silent_time_estimator.set_minimum_travel_feedrate((float)print.config().machine_min_travel_rate.values.back());
|
||||||
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, (float)print.config().machine_max_acceleration_x.values.back());
|
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::X, (float)print.config().machine_max_acceleration_x.values.back());
|
||||||
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, (float)print.config().machine_max_acceleration_y.values.back());
|
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Y, (float)print.config().machine_max_acceleration_y.values.back());
|
||||||
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, (float)print.config().machine_max_acceleration_z.values.back());
|
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::Z, (float)print.config().machine_max_acceleration_z.values.back());
|
||||||
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, (float)print.config().machine_max_acceleration_e.values.back());
|
m_silent_time_estimator.set_axis_max_acceleration(GCodeTimeEstimator::E, (float)print.config().machine_max_acceleration_e.values.back());
|
||||||
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, (float)print.config().machine_max_feedrate_x.values.back());
|
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::X, (float)print.config().machine_max_feedrate_x.values.back());
|
||||||
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, (float)print.config().machine_max_feedrate_y.values.back());
|
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Y, (float)print.config().machine_max_feedrate_y.values.back());
|
||||||
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, (float)print.config().machine_max_feedrate_z.values.back());
|
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::Z, (float)print.config().machine_max_feedrate_z.values.back());
|
||||||
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, (float)print.config().machine_max_feedrate_e.values.back());
|
m_silent_time_estimator.set_axis_max_feedrate(GCodeTimeEstimator::E, (float)print.config().machine_max_feedrate_e.values.back());
|
||||||
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, (float)print.config().machine_max_jerk_x.values.back());
|
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::X, (float)print.config().machine_max_jerk_x.values.back());
|
||||||
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, (float)print.config().machine_max_jerk_y.values.back());
|
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Y, (float)print.config().machine_max_jerk_y.values.back());
|
||||||
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, (float)print.config().machine_max_jerk_z.values.back());
|
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::Z, (float)print.config().machine_max_jerk_z.values.back());
|
||||||
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, (float)print.config().machine_max_jerk_e.values.back());
|
m_silent_time_estimator.set_axis_max_jerk(GCodeTimeEstimator::E, (float)print.config().machine_max_jerk_e.values.back());
|
||||||
if (print.config().single_extruder_multi_material) {
|
if (print.config().single_extruder_multi_material) {
|
||||||
// As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they
|
// As of now the fields are shown at the UI dialog in the same combo box as the ramming values, so they
|
||||||
// are considered to be active for the single extruder multi-material printers only.
|
// are considered to be active for the single extruder multi-material printers only.
|
||||||
|
@ -909,7 +906,8 @@ void GCode::_do_export(Print &print, FILE *file)
|
||||||
std::sort(zs.begin(), zs.end());
|
std::sort(zs.begin(), zs.end());
|
||||||
m_layer_count += (unsigned int)(object->copies().size() * (std::unique(zs.begin(), zs.end()) - zs.begin()));
|
m_layer_count += (unsigned int)(object->copies().size() * (std::unique(zs.begin(), zs.end()) - zs.begin()));
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
// Print all objects with the same print_z together.
|
// Print all objects with the same print_z together.
|
||||||
std::vector<coordf_t> zs;
|
std::vector<coordf_t> zs;
|
||||||
for (auto object : print.objects()) {
|
for (auto object : print.objects()) {
|
||||||
|
@ -927,7 +925,7 @@ void GCode::_do_export(Print &print, FILE *file)
|
||||||
m_enable_cooling_markers = true;
|
m_enable_cooling_markers = true;
|
||||||
this->apply_print_config(print.config());
|
this->apply_print_config(print.config());
|
||||||
this->set_extruders(print.extruders());
|
this->set_extruders(print.extruders());
|
||||||
|
|
||||||
// Initialize colorprint.
|
// Initialize colorprint.
|
||||||
m_colorprint_heights = cast<float>(print.config().colorprint_heights.values);
|
m_colorprint_heights = cast<float>(print.config().colorprint_heights.values);
|
||||||
|
|
||||||
|
@ -936,31 +934,31 @@ void GCode::_do_export(Print &print, FILE *file)
|
||||||
// get the minimum cross-section used in the print
|
// get the minimum cross-section used in the print
|
||||||
std::vector<double> mm3_per_mm;
|
std::vector<double> mm3_per_mm;
|
||||||
for (auto object : print.objects()) {
|
for (auto object : print.objects()) {
|
||||||
for (size_t region_id = 0; region_id < object->region_volumes.size(); ++ region_id) {
|
for (size_t region_id = 0; region_id < object->region_volumes.size(); ++region_id) {
|
||||||
const PrintRegion* region = print.regions()[region_id];
|
const PrintRegion* region = print.regions()[region_id];
|
||||||
for (auto layer : object->layers()) {
|
for (auto layer : object->layers()) {
|
||||||
const LayerRegion* layerm = layer->regions()[region_id];
|
const LayerRegion* layerm = layer->regions()[region_id];
|
||||||
if (region->config().get_abs_value("perimeter_speed" ) == 0 ||
|
if (region->config().get_abs_value("perimeter_speed") == 0 ||
|
||||||
region->config().get_abs_value("small_perimeter_speed" ) == 0 ||
|
region->config().get_abs_value("small_perimeter_speed") == 0 ||
|
||||||
region->config().get_abs_value("external_perimeter_speed" ) == 0 ||
|
region->config().get_abs_value("external_perimeter_speed") == 0 ||
|
||||||
region->config().get_abs_value("bridge_speed" ) == 0)
|
region->config().get_abs_value("bridge_speed") == 0)
|
||||||
mm3_per_mm.push_back(layerm->perimeters.min_mm3_per_mm());
|
mm3_per_mm.push_back(layerm->perimeters.min_mm3_per_mm());
|
||||||
if (region->config().get_abs_value("infill_speed" ) == 0 ||
|
if (region->config().get_abs_value("infill_speed") == 0 ||
|
||||||
region->config().get_abs_value("solid_infill_speed" ) == 0 ||
|
region->config().get_abs_value("solid_infill_speed") == 0 ||
|
||||||
region->config().get_abs_value("top_solid_infill_speed" ) == 0 ||
|
region->config().get_abs_value("top_solid_infill_speed") == 0 ||
|
||||||
region->config().get_abs_value("bridge_speed" ) == 0)
|
region->config().get_abs_value("bridge_speed") == 0)
|
||||||
mm3_per_mm.push_back(layerm->fills.min_mm3_per_mm());
|
mm3_per_mm.push_back(layerm->fills.min_mm3_per_mm());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (object->config().get_abs_value("support_material_speed" ) == 0 ||
|
if (object->config().get_abs_value("support_material_speed") == 0 ||
|
||||||
object->config().get_abs_value("support_material_interface_speed" ) == 0)
|
object->config().get_abs_value("support_material_interface_speed") == 0)
|
||||||
for (auto layer : object->support_layers())
|
for (auto layer : object->support_layers())
|
||||||
mm3_per_mm.push_back(layer->support_fills.min_mm3_per_mm());
|
mm3_per_mm.push_back(layer->support_fills.min_mm3_per_mm());
|
||||||
}
|
}
|
||||||
print.throw_if_canceled();
|
print.throw_if_canceled();
|
||||||
// filter out 0-width segments
|
// filter out 0-width segments
|
||||||
mm3_per_mm.erase(std::remove_if(mm3_per_mm.begin(), mm3_per_mm.end(), [](double v) { return v < 0.000001; }), mm3_per_mm.end());
|
mm3_per_mm.erase(std::remove_if(mm3_per_mm.begin(), mm3_per_mm.end(), [](double v) { return v < 0.000001; }), mm3_per_mm.end());
|
||||||
if (! mm3_per_mm.empty()) {
|
if (!mm3_per_mm.empty()) {
|
||||||
// In order to honor max_print_speed we need to find a target volumetric
|
// In order to honor max_print_speed we need to find a target volumetric
|
||||||
// speed that we can use throughout the print. So we define this target
|
// speed that we can use throughout the print. So we define this target
|
||||||
// volumetric speed as the volumetric speed produced by printing the
|
// volumetric speed as the volumetric speed produced by printing the
|
||||||
|
@ -973,7 +971,7 @@ void GCode::_do_export(Print &print, FILE *file)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
print.throw_if_canceled();
|
print.throw_if_canceled();
|
||||||
|
|
||||||
m_cooling_buffer = make_unique<CoolingBuffer>(*this);
|
m_cooling_buffer = make_unique<CoolingBuffer>(*this);
|
||||||
if (print.config().spiral_vase.value)
|
if (print.config().spiral_vase.value)
|
||||||
m_spiral_vase = make_unique<SpiralVase>(print.config());
|
m_spiral_vase = make_unique<SpiralVase>(print.config());
|
||||||
|
@ -991,11 +989,12 @@ void GCode::_do_export(Print &print, FILE *file)
|
||||||
|
|
||||||
#if ENABLE_THUMBNAIL_GENERATOR
|
#if ENABLE_THUMBNAIL_GENERATOR
|
||||||
// Write thumbnails using base64 encoding
|
// Write thumbnails using base64 encoding
|
||||||
if (thumbnail_data != nullptr)
|
if (thumbnail_cb != nullptr)
|
||||||
{
|
{
|
||||||
const size_t max_row_length = 78;
|
const size_t max_row_length = 78;
|
||||||
|
ThumbnailsList thumbnails;
|
||||||
for (const ThumbnailData& data : *thumbnail_data)
|
thumbnail_cb(thumbnails, print.full_print_config().option<ConfigOptionPoints>("thumbnails")->values, true, true, false);
|
||||||
|
for (const ThumbnailData& data : thumbnails)
|
||||||
{
|
{
|
||||||
if (data.is_valid())
|
if (data.is_valid())
|
||||||
{
|
{
|
||||||
|
|
|
@ -17,6 +17,9 @@
|
||||||
#include "GCodeTimeEstimator.hpp"
|
#include "GCodeTimeEstimator.hpp"
|
||||||
#include "EdgeGrid.hpp"
|
#include "EdgeGrid.hpp"
|
||||||
#include "GCode/Analyzer.hpp"
|
#include "GCode/Analyzer.hpp"
|
||||||
|
#if ENABLE_THUMBNAIL_GENERATOR
|
||||||
|
#include "GCode/ThumbnailData.hpp"
|
||||||
|
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -30,9 +33,6 @@ namespace Slic3r {
|
||||||
// Forward declarations.
|
// Forward declarations.
|
||||||
class GCode;
|
class GCode;
|
||||||
class GCodePreviewData;
|
class GCodePreviewData;
|
||||||
#if ENABLE_THUMBNAIL_GENERATOR
|
|
||||||
struct ThumbnailData;
|
|
||||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
|
||||||
|
|
||||||
class AvoidCrossingPerimeters {
|
class AvoidCrossingPerimeters {
|
||||||
public:
|
public:
|
||||||
|
@ -167,7 +167,7 @@ public:
|
||||||
// throws std::runtime_exception on error,
|
// throws std::runtime_exception on error,
|
||||||
// throws CanceledException through print->throw_if_canceled().
|
// throws CanceledException through print->throw_if_canceled().
|
||||||
#if ENABLE_THUMBNAIL_GENERATOR
|
#if ENABLE_THUMBNAIL_GENERATOR
|
||||||
void do_export(Print* print, const char* path, GCodePreviewData* preview_data = nullptr, const std::vector<ThumbnailData>* thumbnail_data = nullptr);
|
void do_export(Print* print, const char* path, GCodePreviewData* preview_data = nullptr, ThumbnailsGeneratorCallback thumbnail_cb = nullptr);
|
||||||
#else
|
#else
|
||||||
void do_export(Print *print, const char *path, GCodePreviewData *preview_data = nullptr);
|
void do_export(Print *print, const char *path, GCodePreviewData *preview_data = nullptr);
|
||||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||||
|
@ -199,7 +199,7 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
#if ENABLE_THUMBNAIL_GENERATOR
|
#if ENABLE_THUMBNAIL_GENERATOR
|
||||||
void _do_export(Print& print, FILE* file, const std::vector<ThumbnailData>* thumbnail_data);
|
void _do_export(Print& print, FILE* file, ThumbnailsGeneratorCallback thumbnail_cb);
|
||||||
#else
|
#else
|
||||||
void _do_export(Print &print, FILE *file);
|
void _do_export(Print &print, FILE *file);
|
||||||
#endif //ENABLE_THUMBNAIL_GENERATOR
|
#endif //ENABLE_THUMBNAIL_GENERATOR
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#if ENABLE_THUMBNAIL_GENERATOR
|
#if ENABLE_THUMBNAIL_GENERATOR
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include "libslic3r/Point.hpp"
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
@ -20,6 +21,9 @@ struct ThumbnailData
|
||||||
bool is_valid() const;
|
bool is_valid() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef std::vector<ThumbnailData> ThumbnailsList;
|
||||||
|
typedef std::function<void(ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool transparent_background)> ThumbnailsGeneratorCallback;
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
||||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||||
|
|
|
@ -1538,7 +1538,7 @@ void Print::process()
|
||||||
// write error into the G-code, cannot execute post-processing scripts).
|
// write error into the G-code, cannot execute post-processing scripts).
|
||||||
// It is up to the caller to show an error message.
|
// It is up to the caller to show an error message.
|
||||||
#if ENABLE_THUMBNAIL_GENERATOR
|
#if ENABLE_THUMBNAIL_GENERATOR
|
||||||
std::string Print::export_gcode(const std::string& path_template, GCodePreviewData* preview_data, const std::vector<ThumbnailData>* thumbnail_data)
|
std::string Print::export_gcode(const std::string& path_template, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb)
|
||||||
#else
|
#else
|
||||||
std::string Print::export_gcode(const std::string &path_template, GCodePreviewData *preview_data)
|
std::string Print::export_gcode(const std::string &path_template, GCodePreviewData *preview_data)
|
||||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||||
|
@ -1559,7 +1559,7 @@ std::string Print::export_gcode(const std::string &path_template, GCodePreviewDa
|
||||||
// The following line may die for multiple reasons.
|
// The following line may die for multiple reasons.
|
||||||
GCode gcode;
|
GCode gcode;
|
||||||
#if ENABLE_THUMBNAIL_GENERATOR
|
#if ENABLE_THUMBNAIL_GENERATOR
|
||||||
gcode.do_export(this, path.c_str(), preview_data, thumbnail_data);
|
gcode.do_export(this, path.c_str(), preview_data, thumbnail_cb);
|
||||||
#else
|
#else
|
||||||
gcode.do_export(this, path.c_str(), preview_data);
|
gcode.do_export(this, path.c_str(), preview_data);
|
||||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||||
|
|
|
@ -11,6 +11,9 @@
|
||||||
#include "Slicing.hpp"
|
#include "Slicing.hpp"
|
||||||
#include "GCode/ToolOrdering.hpp"
|
#include "GCode/ToolOrdering.hpp"
|
||||||
#include "GCode/WipeTower.hpp"
|
#include "GCode/WipeTower.hpp"
|
||||||
|
#if ENABLE_THUMBNAIL_GENERATOR
|
||||||
|
#include "GCode/ThumbnailData.hpp"
|
||||||
|
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
|
@ -19,9 +22,6 @@ class PrintObject;
|
||||||
class ModelObject;
|
class ModelObject;
|
||||||
class GCode;
|
class GCode;
|
||||||
class GCodePreviewData;
|
class GCodePreviewData;
|
||||||
#if ENABLE_THUMBNAIL_GENERATOR
|
|
||||||
struct ThumbnailData;
|
|
||||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
|
||||||
|
|
||||||
// Print step IDs for keeping track of the print state.
|
// Print step IDs for keeping track of the print state.
|
||||||
enum PrintStep {
|
enum PrintStep {
|
||||||
|
@ -311,7 +311,7 @@ public:
|
||||||
// Exports G-code into a file name based on the path_template, returns the file path of the generated G-code file.
|
// Exports G-code into a file name based on the path_template, returns the file path of the generated G-code file.
|
||||||
// If preview_data is not null, the preview_data is filled in for the G-code visualization (not used by the command line Slic3r).
|
// If preview_data is not null, the preview_data is filled in for the G-code visualization (not used by the command line Slic3r).
|
||||||
#if ENABLE_THUMBNAIL_GENERATOR
|
#if ENABLE_THUMBNAIL_GENERATOR
|
||||||
std::string export_gcode(const std::string& path_template, GCodePreviewData* preview_data, const std::vector<ThumbnailData>* thumbnail_data = nullptr);
|
std::string export_gcode(const std::string& path_template, GCodePreviewData* preview_data, ThumbnailsGeneratorCallback thumbnail_cb = nullptr);
|
||||||
#else
|
#else
|
||||||
std::string export_gcode(const std::string &path_template, GCodePreviewData *preview_data);
|
std::string export_gcode(const std::string &path_template, GCodePreviewData *preview_data);
|
||||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||||
|
|
|
@ -1522,9 +1522,9 @@ bool PrintObject::update_layer_height_profile(const ModelObject &model_object, c
|
||||||
layer_height_profile.clear();
|
layer_height_profile.clear();
|
||||||
|
|
||||||
if (layer_height_profile.empty()) {
|
if (layer_height_profile.empty()) {
|
||||||
//layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes);
|
//layer_height_profile = layer_height_profile_adaptive(slicing_parameters, model_object.layer_config_ranges, model_object.volumes);
|
||||||
layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_config_ranges);
|
layer_height_profile = layer_height_profile_from_ranges(slicing_parameters, model_object.layer_config_ranges);
|
||||||
updated = true;
|
updated = true;
|
||||||
}
|
}
|
||||||
return updated;
|
return updated;
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,40 +224,59 @@ std::vector<coordf_t> layer_height_profile_from_ranges(
|
||||||
|
|
||||||
// Based on the work of @platsch
|
// Based on the work of @platsch
|
||||||
// Fill layer_height_profile by heights ensuring a prescribed maximum cusp height.
|
// Fill layer_height_profile by heights ensuring a prescribed maximum cusp height.
|
||||||
|
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
std::vector<double> layer_height_profile_adaptive(const SlicingParameters& slicing_params,
|
||||||
|
const ModelObject& object, float cusp_value)
|
||||||
|
#else
|
||||||
std::vector<coordf_t> layer_height_profile_adaptive(
|
std::vector<coordf_t> layer_height_profile_adaptive(
|
||||||
const SlicingParameters &slicing_params,
|
const SlicingParameters &slicing_params,
|
||||||
const t_layer_config_ranges & /* layer_config_ranges */,
|
const t_layer_config_ranges & /* layer_config_ranges */,
|
||||||
const ModelVolumePtrs &volumes)
|
const ModelVolumePtrs &volumes)
|
||||||
|
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
{
|
{
|
||||||
|
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
// 1) Initialize the SlicingAdaptive class with the object meshes.
|
// 1) Initialize the SlicingAdaptive class with the object meshes.
|
||||||
SlicingAdaptive as;
|
SlicingAdaptive as;
|
||||||
as.set_slicing_parameters(slicing_params);
|
as.set_slicing_parameters(slicing_params);
|
||||||
for (const ModelVolume *volume : volumes)
|
as.set_object(object);
|
||||||
|
#else
|
||||||
|
// 1) Initialize the SlicingAdaptive class with the object meshes.
|
||||||
|
SlicingAdaptive as;
|
||||||
|
as.set_slicing_parameters(slicing_params);
|
||||||
|
for (const ModelVolume* volume : volumes)
|
||||||
if (volume->is_model_part())
|
if (volume->is_model_part())
|
||||||
as.add_mesh(&volume->mesh());
|
as.add_mesh(&volume->mesh());
|
||||||
|
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
|
||||||
as.prepare();
|
as.prepare();
|
||||||
|
|
||||||
// 2) Generate layers using the algorithm of @platsch
|
// 2) Generate layers using the algorithm of @platsch
|
||||||
// loop until we have at least one layer and the max slice_z reaches the object height
|
// loop until we have at least one layer and the max slice_z reaches the object height
|
||||||
//FIXME make it configurable
|
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
// Cusp value: A maximum allowed distance from a corner of a rectangular extrusion to a chrodal line, in mm.
|
double cusp_value = 0.2;
|
||||||
const coordf_t cusp_value = 0.2; // $self->config->get_value('cusp_value');
|
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
|
||||||
std::vector<coordf_t> layer_height_profile;
|
std::vector<double> layer_height_profile;
|
||||||
layer_height_profile.push_back(0.);
|
layer_height_profile.push_back(0.0);
|
||||||
layer_height_profile.push_back(slicing_params.first_object_layer_height);
|
layer_height_profile.push_back(slicing_params.first_object_layer_height);
|
||||||
if (slicing_params.first_object_layer_height_fixed()) {
|
if (slicing_params.first_object_layer_height_fixed()) {
|
||||||
layer_height_profile.push_back(slicing_params.first_object_layer_height);
|
layer_height_profile.push_back(slicing_params.first_object_layer_height);
|
||||||
layer_height_profile.push_back(slicing_params.first_object_layer_height);
|
layer_height_profile.push_back(slicing_params.first_object_layer_height);
|
||||||
}
|
}
|
||||||
coordf_t slice_z = slicing_params.first_object_layer_height;
|
double slice_z = slicing_params.first_object_layer_height;
|
||||||
coordf_t height = slicing_params.first_object_layer_height;
|
|
||||||
int current_facet = 0;
|
int current_facet = 0;
|
||||||
|
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
while (slice_z <= slicing_params.object_print_z_height()) {
|
||||||
|
double height = 999.0;
|
||||||
|
#else
|
||||||
|
double height = slicing_params.first_object_layer_height;
|
||||||
while ((slice_z - height) <= slicing_params.object_print_z_height()) {
|
while ((slice_z - height) <= slicing_params.object_print_z_height()) {
|
||||||
height = 999;
|
height = 999.0;
|
||||||
|
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
// Slic3r::debugf "\n Slice layer: %d\n", $id;
|
// Slic3r::debugf "\n Slice layer: %d\n", $id;
|
||||||
// determine next layer height
|
// determine next layer height
|
||||||
coordf_t cusp_height = as.cusp_height(slice_z, cusp_value, current_facet);
|
double cusp_height = as.cusp_height((float)slice_z, cusp_value, current_facet);
|
||||||
|
|
||||||
// check for horizontal features and object size
|
// check for horizontal features and object size
|
||||||
/*
|
/*
|
||||||
if($self->config->get_value('match_horizontal_surfaces')) {
|
if($self->config->get_value('match_horizontal_surfaces')) {
|
||||||
|
@ -303,19 +322,113 @@ std::vector<coordf_t> layer_height_profile_adaptive(
|
||||||
layer_height_profile.push_back(slice_z);
|
layer_height_profile.push_back(slice_z);
|
||||||
layer_height_profile.push_back(height);
|
layer_height_profile.push_back(height);
|
||||||
slice_z += height;
|
slice_z += height;
|
||||||
|
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
layer_height_profile.push_back(slice_z);
|
layer_height_profile.push_back(slice_z);
|
||||||
layer_height_profile.push_back(height);
|
layer_height_profile.push_back(height);
|
||||||
|
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
}
|
}
|
||||||
|
|
||||||
coordf_t last = std::max(slicing_params.first_object_layer_height, layer_height_profile[layer_height_profile.size() - 2]);
|
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
double z_gap = slicing_params.object_print_z_height() - layer_height_profile[layer_height_profile.size() - 2];
|
||||||
|
if (z_gap > 0.0)
|
||||||
|
{
|
||||||
|
layer_height_profile.push_back(slicing_params.object_print_z_height());
|
||||||
|
layer_height_profile.push_back(clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, z_gap));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
double last = std::max(slicing_params.first_object_layer_height, layer_height_profile[layer_height_profile.size() - 2]);
|
||||||
layer_height_profile.push_back(last);
|
layer_height_profile.push_back(last);
|
||||||
layer_height_profile.push_back(slicing_params.first_object_layer_height);
|
layer_height_profile.push_back(slicing_params.first_object_layer_height);
|
||||||
layer_height_profile.push_back(slicing_params.object_print_z_height());
|
layer_height_profile.push_back(slicing_params.object_print_z_height());
|
||||||
layer_height_profile.push_back(slicing_params.first_object_layer_height);
|
layer_height_profile.push_back(slicing_params.first_object_layer_height);
|
||||||
|
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
|
||||||
return layer_height_profile;
|
return layer_height_profile;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<double> smooth_height_profile(const std::vector<double>& profile, const SlicingParameters& slicing_params, const HeightProfileSmoothingParams& smoothing_params)
|
||||||
|
{
|
||||||
|
auto gauss_blur = [&slicing_params](const std::vector<double>& profile, const HeightProfileSmoothingParams& smoothing_params) -> std::vector<double> {
|
||||||
|
auto gauss_kernel = [] (unsigned int radius) -> std::vector<double> {
|
||||||
|
unsigned int size = 2 * radius + 1;
|
||||||
|
std::vector<double> ret;
|
||||||
|
ret.reserve(size);
|
||||||
|
|
||||||
|
// Reworked from static inline int getGaussianKernelSize(float sigma) taken from opencv-4.1.2\modules\features2d\src\kaze\AKAZEFeatures.cpp
|
||||||
|
double sigma = 0.3 * (double)(radius - 1) + 0.8;
|
||||||
|
double two_sq_sigma = 2.0 * sigma * sigma;
|
||||||
|
double inv_root_two_pi_sq_sigma = 1.0 / ::sqrt(M_PI * two_sq_sigma);
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i < size; ++i)
|
||||||
|
{
|
||||||
|
double x = (double)i - (double)radius;
|
||||||
|
ret.push_back(inv_root_two_pi_sq_sigma * ::exp(-x * x / two_sq_sigma));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
// skip first layer ?
|
||||||
|
size_t skip_count = slicing_params.first_object_layer_height_fixed() ? 4 : 0;
|
||||||
|
|
||||||
|
// not enough data to smmoth
|
||||||
|
if ((int)profile.size() - (int)skip_count < 6)
|
||||||
|
return profile;
|
||||||
|
|
||||||
|
unsigned int radius = std::max(smoothing_params.radius, (unsigned int)1);
|
||||||
|
std::vector<double> kernel = gauss_kernel(radius);
|
||||||
|
int two_radius = 2 * (int)radius;
|
||||||
|
|
||||||
|
std::vector<double> ret;
|
||||||
|
size_t size = profile.size();
|
||||||
|
ret.reserve(size);
|
||||||
|
|
||||||
|
// leave first layer untouched
|
||||||
|
for (size_t i = 0; i < skip_count; ++i)
|
||||||
|
{
|
||||||
|
ret.push_back(profile[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// smooth the rest of the profile by biasing a gaussian blur
|
||||||
|
// the bias moves the smoothed profile closer to the min_layer_height
|
||||||
|
double delta_h = slicing_params.max_layer_height - slicing_params.min_layer_height;
|
||||||
|
double inv_delta_h = (delta_h != 0.0) ? 1.0 / delta_h : 1.0;
|
||||||
|
|
||||||
|
double max_dz_band = (double)radius * slicing_params.layer_height;
|
||||||
|
for (size_t i = skip_count; i < size; i += 2)
|
||||||
|
{
|
||||||
|
double zi = profile[i];
|
||||||
|
double hi = profile[i + 1];
|
||||||
|
ret.push_back(zi);
|
||||||
|
ret.push_back(0.0);
|
||||||
|
double& height = ret.back();
|
||||||
|
int begin = std::max((int)i - two_radius, (int)skip_count);
|
||||||
|
int end = std::min((int)i + two_radius, (int)size - 2);
|
||||||
|
double weight_total = 0.0;
|
||||||
|
for (int j = begin; j <= end; j += 2)
|
||||||
|
{
|
||||||
|
int kernel_id = radius + (j - (int)i) / 2;
|
||||||
|
double dz = std::abs(zi - profile[j]);
|
||||||
|
if (dz * slicing_params.layer_height <= max_dz_band)
|
||||||
|
{
|
||||||
|
double dh = std::abs(slicing_params.max_layer_height - profile[j + 1]);
|
||||||
|
double weight = kernel[kernel_id] * sqrt(dh * inv_delta_h);
|
||||||
|
height += weight * profile[j + 1];
|
||||||
|
weight_total += weight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
height = clamp(slicing_params.min_layer_height, slicing_params.max_layer_height, (weight_total != 0.0) ? height /= weight_total : hi);
|
||||||
|
if (smoothing_params.keep_min)
|
||||||
|
height = std::min(height, hi);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
return gauss_blur(profile, smoothing_params);
|
||||||
|
}
|
||||||
|
|
||||||
void adjust_layer_height_profile(
|
void adjust_layer_height_profile(
|
||||||
const SlicingParameters &slicing_params,
|
const SlicingParameters &slicing_params,
|
||||||
std::vector<coordf_t> &layer_height_profile,
|
std::vector<coordf_t> &layer_height_profile,
|
||||||
|
@ -609,7 +722,11 @@ int generate_layer_height_texture(
|
||||||
const Vec3crd &color1 = palette_raw[idx1];
|
const Vec3crd &color1 = palette_raw[idx1];
|
||||||
const Vec3crd &color2 = palette_raw[idx2];
|
const Vec3crd &color2 = palette_raw[idx2];
|
||||||
coordf_t z = cell_to_z * coordf_t(cell);
|
coordf_t z = cell_to_z * coordf_t(cell);
|
||||||
assert(z >= lo && z <= hi);
|
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
assert((lo - EPSILON <= z) && (z <= hi + EPSILON));
|
||||||
|
#else
|
||||||
|
assert(z >= lo && z <= hi);
|
||||||
|
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
// Intensity profile to visualize the layers.
|
// Intensity profile to visualize the layers.
|
||||||
coordf_t intensity = cos(M_PI * 0.7 * (mid - z) / h);
|
coordf_t intensity = cos(M_PI * 0.7 * (mid - z) / h);
|
||||||
// Color mapping from layer height to RGB.
|
// Color mapping from layer height to RGB.
|
||||||
|
|
|
@ -18,8 +18,12 @@ namespace Slic3r
|
||||||
|
|
||||||
class PrintConfig;
|
class PrintConfig;
|
||||||
class PrintObjectConfig;
|
class PrintObjectConfig;
|
||||||
|
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
class ModelObject;
|
||||||
|
#else
|
||||||
class ModelVolume;
|
class ModelVolume;
|
||||||
typedef std::vector<ModelVolume*> ModelVolumePtrs;
|
typedef std::vector<ModelVolume*> ModelVolumePtrs;
|
||||||
|
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
|
||||||
// Parameters to guide object slicing and support generation.
|
// Parameters to guide object slicing and support generation.
|
||||||
// The slicing parameters account for a raft and whether the 1st object layer is printed with a normal or a bridging flow
|
// The slicing parameters account for a raft and whether the 1st object layer is printed with a normal or a bridging flow
|
||||||
|
@ -138,11 +142,29 @@ extern std::vector<coordf_t> layer_height_profile_from_ranges(
|
||||||
const SlicingParameters &slicing_params,
|
const SlicingParameters &slicing_params,
|
||||||
const t_layer_config_ranges &layer_config_ranges);
|
const t_layer_config_ranges &layer_config_ranges);
|
||||||
|
|
||||||
|
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
extern std::vector<double> layer_height_profile_adaptive(
|
||||||
|
const SlicingParameters& slicing_params,
|
||||||
|
const ModelObject& object, float cusp_value);
|
||||||
|
|
||||||
|
struct HeightProfileSmoothingParams
|
||||||
|
{
|
||||||
|
unsigned int radius;
|
||||||
|
bool keep_min;
|
||||||
|
|
||||||
|
HeightProfileSmoothingParams() : radius(5), keep_min(false) {}
|
||||||
|
HeightProfileSmoothingParams(unsigned int radius, bool keep_min) : radius(radius), keep_min(keep_min) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
extern std::vector<double> smooth_height_profile(
|
||||||
|
const std::vector<double>& profile, const SlicingParameters& slicing_params,
|
||||||
|
const HeightProfileSmoothingParams& smoothing_params);
|
||||||
|
#else
|
||||||
extern std::vector<coordf_t> layer_height_profile_adaptive(
|
extern std::vector<coordf_t> layer_height_profile_adaptive(
|
||||||
const SlicingParameters &slicing_params,
|
const SlicingParameters &slicing_params,
|
||||||
const t_layer_config_ranges &layer_config_ranges,
|
const t_layer_config_ranges &layer_config_ranges,
|
||||||
const ModelVolumePtrs &volumes);
|
const ModelVolumePtrs &volumes);
|
||||||
|
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
|
||||||
enum LayerHeightEditActionType : unsigned int {
|
enum LayerHeightEditActionType : unsigned int {
|
||||||
LAYER_HEIGHT_EDIT_ACTION_INCREASE = 0,
|
LAYER_HEIGHT_EDIT_ACTION_INCREASE = 0,
|
||||||
|
|
|
@ -1,16 +1,22 @@
|
||||||
#include "libslic3r.h"
|
#include "libslic3r.h"
|
||||||
|
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
#include "Model.hpp"
|
||||||
|
#else
|
||||||
#include "TriangleMesh.hpp"
|
#include "TriangleMesh.hpp"
|
||||||
|
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
#include "SlicingAdaptive.hpp"
|
#include "SlicingAdaptive.hpp"
|
||||||
|
|
||||||
namespace Slic3r
|
namespace Slic3r
|
||||||
{
|
{
|
||||||
|
|
||||||
|
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
void SlicingAdaptive::clear()
|
void SlicingAdaptive::clear()
|
||||||
{
|
{
|
||||||
m_meshes.clear();
|
m_meshes.clear();
|
||||||
m_faces.clear();
|
m_faces.clear();
|
||||||
m_face_normal_z.clear();
|
m_face_normal_z.clear();
|
||||||
}
|
}
|
||||||
|
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
|
||||||
std::pair<float, float> face_z_span(const stl_facet *f)
|
std::pair<float, float> face_z_span(const stl_facet *f)
|
||||||
{
|
{
|
||||||
|
@ -21,21 +27,42 @@ std::pair<float, float> face_z_span(const stl_facet *f)
|
||||||
|
|
||||||
void SlicingAdaptive::prepare()
|
void SlicingAdaptive::prepare()
|
||||||
{
|
{
|
||||||
// 1) Collect faces of all meshes.
|
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
int nfaces_total = 0;
|
if (m_object == nullptr)
|
||||||
for (std::vector<const TriangleMesh*>::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh)
|
return;
|
||||||
|
|
||||||
|
m_faces.clear();
|
||||||
|
m_face_normal_z.clear();
|
||||||
|
|
||||||
|
m_mesh = m_object->raw_mesh();
|
||||||
|
const ModelInstance* first_instance = m_object->instances.front();
|
||||||
|
m_mesh.transform(first_instance->get_matrix(), first_instance->is_left_handed());
|
||||||
|
for (stl_facet& facet : m_mesh.stl.facet_start)
|
||||||
|
{
|
||||||
|
facet.normal.normalize();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1) Collect faces from mesh.
|
||||||
|
m_faces.reserve(m_mesh.stl.stats.number_of_facets);
|
||||||
|
for (const stl_facet& face : m_mesh.stl.facet_start)
|
||||||
|
m_faces.emplace_back(&face);
|
||||||
|
#else
|
||||||
|
// 1) Collect faces of all meshes.
|
||||||
|
int nfaces_total = 0;
|
||||||
|
for (std::vector<const TriangleMesh*>::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh)
|
||||||
nfaces_total += (*it_mesh)->stl.stats.number_of_facets;
|
nfaces_total += (*it_mesh)->stl.stats.number_of_facets;
|
||||||
m_faces.reserve(nfaces_total);
|
m_faces.reserve(nfaces_total);
|
||||||
for (std::vector<const TriangleMesh*>::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh)
|
for (std::vector<const TriangleMesh*>::const_iterator it_mesh = m_meshes.begin(); it_mesh != m_meshes.end(); ++ it_mesh)
|
||||||
for (const stl_facet &face : (*it_mesh)->stl.facet_start)
|
for (const stl_facet& face : (*it_mesh)->stl.facet_start)
|
||||||
m_faces.emplace_back(&face);
|
m_faces.emplace_back(&face);
|
||||||
|
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
|
||||||
// 2) Sort faces lexicographically by their Z span.
|
// 2) Sort faces lexicographically by their Z span.
|
||||||
std::sort(m_faces.begin(), m_faces.end(), [](const stl_facet *f1, const stl_facet *f2) {
|
std::sort(m_faces.begin(), m_faces.end(), [](const stl_facet *f1, const stl_facet *f2) {
|
||||||
std::pair<float, float> span1 = face_z_span(f1);
|
std::pair<float, float> span1 = face_z_span(f1);
|
||||||
std::pair<float, float> span2 = face_z_span(f2);
|
std::pair<float, float> span2 = face_z_span(f2);
|
||||||
return span1 < span2;
|
return span1 < span2;
|
||||||
});
|
});
|
||||||
|
|
||||||
// 3) Generate Z components of the facet normals.
|
// 3) Generate Z components of the facet normals.
|
||||||
m_face_normal_z.assign(m_faces.size(), 0.f);
|
m_face_normal_z.assign(m_faces.size(), 0.f);
|
||||||
|
@ -45,14 +72,14 @@ void SlicingAdaptive::prepare()
|
||||||
|
|
||||||
float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet)
|
float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet)
|
||||||
{
|
{
|
||||||
float height = m_slicing_params.max_layer_height;
|
float height = (float)m_slicing_params.max_layer_height;
|
||||||
bool first_hit = false;
|
bool first_hit = false;
|
||||||
|
|
||||||
// find all facets intersecting the slice-layer
|
// find all facets intersecting the slice-layer
|
||||||
int ordered_id = current_facet;
|
int ordered_id = current_facet;
|
||||||
for (; ordered_id < int(m_faces.size()); ++ ordered_id) {
|
for (; ordered_id < int(m_faces.size()); ++ ordered_id) {
|
||||||
std::pair<float, float> zspan = face_z_span(m_faces[ordered_id]);
|
std::pair<float, float> zspan = face_z_span(m_faces[ordered_id]);
|
||||||
// facet's minimum is higher than slice_z -> end loop
|
// facet's minimum is higher than slice_z -> end loop
|
||||||
if (zspan.first >= z)
|
if (zspan.first >= z)
|
||||||
break;
|
break;
|
||||||
// facet's maximum is higher than slice_z -> store the first event for next cusp_height call to begin at this point
|
// facet's maximum is higher than slice_z -> store the first event for next cusp_height call to begin at this point
|
||||||
|
@ -77,8 +104,8 @@ float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet
|
||||||
// check for sloped facets inside the determined layer and correct height if necessary
|
// check for sloped facets inside the determined layer and correct height if necessary
|
||||||
if (height > m_slicing_params.min_layer_height) {
|
if (height > m_slicing_params.min_layer_height) {
|
||||||
for (; ordered_id < int(m_faces.size()); ++ ordered_id) {
|
for (; ordered_id < int(m_faces.size()); ++ ordered_id) {
|
||||||
std::pair<float, float> zspan = face_z_span(m_faces[ordered_id]);
|
std::pair<float, float> zspan = face_z_span(m_faces[ordered_id]);
|
||||||
// facet's minimum is higher than slice_z + height -> end loop
|
// facet's minimum is higher than slice_z + height -> end loop
|
||||||
if (zspan.first >= z + height)
|
if (zspan.first >= z + height)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -117,24 +144,25 @@ float SlicingAdaptive::cusp_height(float z, float cusp_value, int ¤t_facet
|
||||||
return height;
|
return height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
// Returns the distance to the next horizontal facet in Z-dir
|
// Returns the distance to the next horizontal facet in Z-dir
|
||||||
// to consider horizontal object features in slice thickness
|
// to consider horizontal object features in slice thickness
|
||||||
float SlicingAdaptive::horizontal_facet_distance(float z)
|
float SlicingAdaptive::horizontal_facet_distance(float z)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < m_faces.size(); ++ i) {
|
for (size_t i = 0; i < m_faces.size(); ++ i) {
|
||||||
std::pair<float, float> zspan = face_z_span(m_faces[i]);
|
std::pair<float, float> zspan = face_z_span(m_faces[i]);
|
||||||
// facet's minimum is higher than max forward distance -> end loop
|
// facet's minimum is higher than max forward distance -> end loop
|
||||||
if (zspan.first > z + m_slicing_params.max_layer_height)
|
if (zspan.first > z + m_slicing_params.max_layer_height)
|
||||||
break;
|
break;
|
||||||
// min_z == max_z -> horizontal facet
|
// min_z == max_z -> horizontal facet
|
||||||
if (zspan.first > z && zspan.first == zspan.second)
|
if ((zspan.first > z) && (zspan.first == zspan.second))
|
||||||
return zspan.first - z;
|
return zspan.first - z;
|
||||||
}
|
}
|
||||||
|
|
||||||
// objects maximum?
|
// objects maximum?
|
||||||
return (z + m_slicing_params.max_layer_height > m_slicing_params.object_print_z_height()) ?
|
return (z + (float)m_slicing_params.max_layer_height > (float)m_slicing_params.object_print_z_height()) ?
|
||||||
std::max<float>(m_slicing_params.object_print_z_height() - z, 0.f) :
|
std::max((float)m_slicing_params.object_print_z_height() - z, 0.f) : (float)m_slicing_params.max_layer_height;
|
||||||
m_slicing_params.max_layer_height;
|
|
||||||
}
|
}
|
||||||
|
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
|
||||||
}; // namespace Slic3r
|
}; // namespace Slic3r
|
||||||
|
|
|
@ -5,29 +5,49 @@
|
||||||
|
|
||||||
#include "Slicing.hpp"
|
#include "Slicing.hpp"
|
||||||
#include "admesh/stl.h"
|
#include "admesh/stl.h"
|
||||||
|
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
#include "TriangleMesh.hpp"
|
||||||
|
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
|
||||||
namespace Slic3r
|
namespace Slic3r
|
||||||
{
|
{
|
||||||
|
|
||||||
|
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
class ModelVolume;
|
||||||
|
#else
|
||||||
class TriangleMesh;
|
class TriangleMesh;
|
||||||
|
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
|
||||||
class SlicingAdaptive
|
class SlicingAdaptive
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void clear();
|
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
void set_slicing_parameters(SlicingParameters params) { m_slicing_params = params; }
|
void clear();
|
||||||
void add_mesh(const TriangleMesh *mesh) { m_meshes.push_back(mesh); }
|
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
void prepare();
|
void set_slicing_parameters(SlicingParameters params) { m_slicing_params = params; }
|
||||||
|
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
void set_object(const ModelObject& object) { m_object = &object; }
|
||||||
|
#else
|
||||||
|
void add_mesh(const TriangleMesh* mesh) { m_meshes.push_back(mesh); }
|
||||||
|
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
void prepare();
|
||||||
float cusp_height(float z, float cusp_value, int ¤t_facet);
|
float cusp_height(float z, float cusp_value, int ¤t_facet);
|
||||||
float horizontal_facet_distance(float z);
|
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
float horizontal_facet_distance(float z);
|
||||||
|
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
SlicingParameters m_slicing_params;
|
SlicingParameters m_slicing_params;
|
||||||
|
|
||||||
std::vector<const TriangleMesh*> m_meshes;
|
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
// Collected faces of all meshes, sorted by raising Z of the bottom most face.
|
const ModelObject* m_object;
|
||||||
|
TriangleMesh m_mesh;
|
||||||
|
#else
|
||||||
|
std::vector<const TriangleMesh*> m_meshes;
|
||||||
|
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
// Collected faces of all meshes, sorted by raising Z of the bottom most face.
|
||||||
std::vector<const stl_facet*> m_faces;
|
std::vector<const stl_facet*> m_faces;
|
||||||
// Z component of face normals, normalized.
|
// Z component of face normals, normalized.
|
||||||
std::vector<float> m_face_normal_z;
|
std::vector<float> m_face_normal_z;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -42,4 +42,7 @@
|
||||||
#define ENABLE_THUMBNAIL_GENERATOR_DEBUG (0 && ENABLE_THUMBNAIL_GENERATOR)
|
#define ENABLE_THUMBNAIL_GENERATOR_DEBUG (0 && ENABLE_THUMBNAIL_GENERATOR)
|
||||||
#define ENABLE_THUMBNAIL_GENERATOR_PNG_TO_GCODE (1 && ENABLE_THUMBNAIL_GENERATOR)
|
#define ENABLE_THUMBNAIL_GENERATOR_PNG_TO_GCODE (1 && ENABLE_THUMBNAIL_GENERATOR)
|
||||||
|
|
||||||
|
// Enable adaptive layer height profile
|
||||||
|
#define ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE (1 && ENABLE_2_2_0_ALPHA1)
|
||||||
|
|
||||||
#endif // _technologies_h_
|
#endif // _technologies_h_
|
||||||
|
|
|
@ -194,7 +194,7 @@ inline typename CONTAINER_TYPE::size_type next_idx_modulo(typename CONTAINER_TYP
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename CONTAINER_TYPE>
|
template<typename CONTAINER_TYPE>
|
||||||
inline typename const CONTAINER_TYPE::value_type& prev_value_modulo(typename CONTAINER_TYPE::size_type idx, const CONTAINER_TYPE &container)
|
inline const typename CONTAINER_TYPE::value_type& prev_value_modulo(typename CONTAINER_TYPE::size_type idx, const CONTAINER_TYPE &container)
|
||||||
{
|
{
|
||||||
return container[prev_idx_modulo(idx, container.size())];
|
return container[prev_idx_modulo(idx, container.size())];
|
||||||
}
|
}
|
||||||
|
@ -206,7 +206,7 @@ inline typename CONTAINER_TYPE::value_type& prev_value_modulo(typename CONTAINER
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename CONTAINER_TYPE>
|
template<typename CONTAINER_TYPE>
|
||||||
inline typename const CONTAINER_TYPE::value_type& next_value_modulo(typename CONTAINER_TYPE::size_type idx, const CONTAINER_TYPE &container)
|
inline const typename CONTAINER_TYPE::value_type& next_value_modulo(typename CONTAINER_TYPE::size_type idx, const CONTAINER_TYPE &container)
|
||||||
{
|
{
|
||||||
return container[next_idx_modulo(idx, container.size())];
|
return container[next_idx_modulo(idx, container.size())];
|
||||||
}
|
}
|
||||||
|
|
|
@ -136,6 +136,8 @@ set(SLIC3R_GUI_SOURCES
|
||||||
GUI/ProgressStatusBar.cpp
|
GUI/ProgressStatusBar.cpp
|
||||||
GUI/PrintHostDialogs.cpp
|
GUI/PrintHostDialogs.cpp
|
||||||
GUI/PrintHostDialogs.hpp
|
GUI/PrintHostDialogs.hpp
|
||||||
|
GUI/Mouse3DController.cpp
|
||||||
|
GUI/Mouse3DController.hpp
|
||||||
Utils/Http.cpp
|
Utils/Http.cpp
|
||||||
Utils/Http.hpp
|
Utils/Http.hpp
|
||||||
Utils/FixModelByWin10.cpp
|
Utils/FixModelByWin10.cpp
|
||||||
|
@ -170,7 +172,7 @@ add_library(libslic3r_gui STATIC ${SLIC3R_GUI_SOURCES})
|
||||||
|
|
||||||
encoding_check(libslic3r_gui)
|
encoding_check(libslic3r_gui)
|
||||||
|
|
||||||
target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui ${GLEW_LIBRARIES})
|
target_link_libraries(libslic3r_gui libslic3r avrdude cereal imgui ${GLEW_LIBRARIES} hidapi)
|
||||||
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
|
if (SLIC3R_PCH AND NOT SLIC3R_SYNTAXONLY)
|
||||||
add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE)
|
add_precompiled_header(libslic3r_gui pchheader.hpp FORCEINCLUDE)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
|
@ -271,6 +271,80 @@ void AppConfig::set_recent_projects(const std::vector<std::string>& recent_proje
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AppConfig::set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone)
|
||||||
|
{
|
||||||
|
std::string key = std::string("mouse_device:") + name;
|
||||||
|
auto it = m_storage.find(key);
|
||||||
|
if (it == m_storage.end())
|
||||||
|
it = m_storage.insert(std::map<std::string, std::map<std::string, std::string>>::value_type(key, std::map<std::string, std::string>())).first;
|
||||||
|
|
||||||
|
it->second.clear();
|
||||||
|
it->second["translation_speed"] = std::to_string(translation_speed);
|
||||||
|
it->second["translation_deadzone"] = std::to_string(translation_deadzone);
|
||||||
|
it->second["rotation_speed"] = std::to_string(rotation_speed);
|
||||||
|
it->second["rotation_deadzone"] = std::to_string(rotation_deadzone);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AppConfig::get_mouse_device_translation_speed(const std::string& name, double& speed)
|
||||||
|
{
|
||||||
|
std::string key = std::string("mouse_device:") + name;
|
||||||
|
auto it = m_storage.find(key);
|
||||||
|
if (it == m_storage.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto it_val = it->second.find("translation_speed");
|
||||||
|
if (it_val == it->second.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
speed = ::atof(it_val->second.c_str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AppConfig::get_mouse_device_translation_deadzone(const std::string& name, double& deadzone)
|
||||||
|
{
|
||||||
|
std::string key = std::string("mouse_device:") + name;
|
||||||
|
auto it = m_storage.find(key);
|
||||||
|
if (it == m_storage.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto it_val = it->second.find("translation_deadzone");
|
||||||
|
if (it_val == it->second.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
deadzone = ::atof(it_val->second.c_str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AppConfig::get_mouse_device_rotation_speed(const std::string& name, float& speed)
|
||||||
|
{
|
||||||
|
std::string key = std::string("mouse_device:") + name;
|
||||||
|
auto it = m_storage.find(key);
|
||||||
|
if (it == m_storage.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto it_val = it->second.find("rotation_speed");
|
||||||
|
if (it_val == it->second.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
speed = (float)::atof(it_val->second.c_str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AppConfig::get_mouse_device_rotation_deadzone(const std::string& name, float& deadzone)
|
||||||
|
{
|
||||||
|
std::string key = std::string("mouse_device:") + name;
|
||||||
|
auto it = m_storage.find(key);
|
||||||
|
if (it == m_storage.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto it_val = it->second.find("rotation_deadzone");
|
||||||
|
if (it_val == it->second.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
deadzone = (float)::atof(it_val->second.c_str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void AppConfig::update_config_dir(const std::string &dir)
|
void AppConfig::update_config_dir(const std::string &dir)
|
||||||
{
|
{
|
||||||
this->set("recent", "config_directory", dir);
|
this->set("recent", "config_directory", dir);
|
||||||
|
|
|
@ -131,8 +131,15 @@ public:
|
||||||
std::vector<std::string> get_recent_projects() const;
|
std::vector<std::string> get_recent_projects() const;
|
||||||
void set_recent_projects(const std::vector<std::string>& recent_projects);
|
void set_recent_projects(const std::vector<std::string>& recent_projects);
|
||||||
|
|
||||||
|
void set_mouse_device(const std::string& name, double translation_speed, double translation_deadzone, float rotation_speed, float rotation_deadzone);
|
||||||
|
bool get_mouse_device_translation_speed(const std::string& name, double& speed);
|
||||||
|
bool get_mouse_device_translation_deadzone(const std::string& name, double& deadzone);
|
||||||
|
bool get_mouse_device_rotation_speed(const std::string& name, float& speed);
|
||||||
|
bool get_mouse_device_rotation_deadzone(const std::string& name, float& deadzone);
|
||||||
|
|
||||||
static const std::string SECTION_FILAMENTS;
|
static const std::string SECTION_FILAMENTS;
|
||||||
static const std::string SECTION_MATERIALS;
|
static const std::string SECTION_MATERIALS;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Map of section, name -> value
|
// Map of section, name -> value
|
||||||
std::map<std::string, std::map<std::string, std::string>> m_storage;
|
std::map<std::string, std::map<std::string, std::string>> m_storage;
|
||||||
|
|
|
@ -20,9 +20,6 @@
|
||||||
#include "libslic3r/Utils.hpp"
|
#include "libslic3r/Utils.hpp"
|
||||||
#include "libslic3r/GCode/PostProcessor.hpp"
|
#include "libslic3r/GCode/PostProcessor.hpp"
|
||||||
#include "libslic3r/GCode/PreviewData.hpp"
|
#include "libslic3r/GCode/PreviewData.hpp"
|
||||||
#if ENABLE_THUMBNAIL_GENERATOR
|
|
||||||
#include "libslic3r/GCode/ThumbnailData.hpp"
|
|
||||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
|
||||||
#include "libslic3r/libslic3r.h"
|
#include "libslic3r/libslic3r.h"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
@ -91,7 +88,7 @@ void BackgroundSlicingProcess::process_fff()
|
||||||
m_print->process();
|
m_print->process();
|
||||||
wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_slicing_completed_id));
|
wxQueueEvent(GUI::wxGetApp().mainframe->m_plater, new wxCommandEvent(m_event_slicing_completed_id));
|
||||||
#if ENABLE_THUMBNAIL_GENERATOR
|
#if ENABLE_THUMBNAIL_GENERATOR
|
||||||
m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data, m_thumbnail_data);
|
m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data, m_thumbnail_cb);
|
||||||
#else
|
#else
|
||||||
m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data);
|
m_fff_print->export_gcode(m_temp_output_path, m_gcode_preview_data);
|
||||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||||
|
@ -139,9 +136,12 @@ void BackgroundSlicingProcess::process_sla()
|
||||||
m_sla_print->export_raster(zipper);
|
m_sla_print->export_raster(zipper);
|
||||||
|
|
||||||
#if ENABLE_THUMBNAIL_GENERATOR
|
#if ENABLE_THUMBNAIL_GENERATOR
|
||||||
if (m_thumbnail_data != nullptr)
|
if (m_thumbnail_cb != nullptr)
|
||||||
{
|
{
|
||||||
for (const ThumbnailData& data : *m_thumbnail_data)
|
ThumbnailsList thumbnails;
|
||||||
|
m_thumbnail_cb(thumbnails, current_print()->full_print_config().option<ConfigOptionPoints>("thumbnails")->values, true, true, false);
|
||||||
|
// m_thumbnail_cb(thumbnails, current_print()->full_print_config().option<ConfigOptionPoints>("thumbnails")->values, true, false, false); // renders also supports and pad
|
||||||
|
for (const ThumbnailData& data : thumbnails)
|
||||||
{
|
{
|
||||||
if (data.is_valid())
|
if (data.is_valid())
|
||||||
write_thumbnail(zipper, data);
|
write_thumbnail(zipper, data);
|
||||||
|
@ -461,9 +461,12 @@ void BackgroundSlicingProcess::prepare_upload()
|
||||||
Zipper zipper{source_path.string()};
|
Zipper zipper{source_path.string()};
|
||||||
m_sla_print->export_raster(zipper, m_upload_job.upload_data.upload_path.string());
|
m_sla_print->export_raster(zipper, m_upload_job.upload_data.upload_path.string());
|
||||||
#if ENABLE_THUMBNAIL_GENERATOR
|
#if ENABLE_THUMBNAIL_GENERATOR
|
||||||
if (m_thumbnail_data != nullptr)
|
if (m_thumbnail_cb != nullptr)
|
||||||
{
|
{
|
||||||
for (const ThumbnailData& data : *m_thumbnail_data)
|
ThumbnailsList thumbnails;
|
||||||
|
m_thumbnail_cb(thumbnails, current_print()->full_print_config().option<ConfigOptionPoints>("thumbnails")->values, true, true, false);
|
||||||
|
// m_thumbnail_cb(thumbnails, current_print()->full_print_config().option<ConfigOptionPoints>("thumbnails")->values, true, false, false); // renders also supports and pad
|
||||||
|
for (const ThumbnailData& data : thumbnails)
|
||||||
{
|
{
|
||||||
if (data.is_valid())
|
if (data.is_valid())
|
||||||
write_thumbnail(zipper, data);
|
write_thumbnail(zipper, data);
|
||||||
|
|
|
@ -17,9 +17,6 @@ namespace Slic3r {
|
||||||
|
|
||||||
class DynamicPrintConfig;
|
class DynamicPrintConfig;
|
||||||
class GCodePreviewData;
|
class GCodePreviewData;
|
||||||
#if ENABLE_THUMBNAIL_GENERATOR
|
|
||||||
struct ThumbnailData;
|
|
||||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
|
||||||
class Model;
|
class Model;
|
||||||
class SLAPrint;
|
class SLAPrint;
|
||||||
|
|
||||||
|
@ -53,7 +50,7 @@ public:
|
||||||
void set_sla_print(SLAPrint *print) { m_sla_print = print; }
|
void set_sla_print(SLAPrint *print) { m_sla_print = print; }
|
||||||
void set_gcode_preview_data(GCodePreviewData *gpd) { m_gcode_preview_data = gpd; }
|
void set_gcode_preview_data(GCodePreviewData *gpd) { m_gcode_preview_data = gpd; }
|
||||||
#if ENABLE_THUMBNAIL_GENERATOR
|
#if ENABLE_THUMBNAIL_GENERATOR
|
||||||
void set_thumbnail_data(const std::vector<ThumbnailData>* data) { m_thumbnail_data = data; }
|
void set_thumbnail_cb(ThumbnailsGeneratorCallback cb) { m_thumbnail_cb = cb; }
|
||||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||||
|
|
||||||
// The following wxCommandEvent will be sent to the UI thread / Platter window, when the slicing is finished
|
// The following wxCommandEvent will be sent to the UI thread / Platter window, when the slicing is finished
|
||||||
|
@ -159,8 +156,8 @@ private:
|
||||||
// Data structure, to which the G-code export writes its annotations.
|
// Data structure, to which the G-code export writes its annotations.
|
||||||
GCodePreviewData *m_gcode_preview_data = nullptr;
|
GCodePreviewData *m_gcode_preview_data = nullptr;
|
||||||
#if ENABLE_THUMBNAIL_GENERATOR
|
#if ENABLE_THUMBNAIL_GENERATOR
|
||||||
// Data structures, used to write thumbnails into gcode.
|
// Callback function, used to write thumbnails into gcode.
|
||||||
const std::vector<ThumbnailData>* m_thumbnail_data = nullptr;
|
ThumbnailsGeneratorCallback m_thumbnail_cb = nullptr;
|
||||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||||
// Temporary G-code, there is one defined for the BackgroundSlicingProcess, differentiated from the other processes by a process ID.
|
// Temporary G-code, there is one defined for the BackgroundSlicingProcess, differentiated from the other processes by a process ID.
|
||||||
std::string m_temp_output_path;
|
std::string m_temp_output_path;
|
||||||
|
|
|
@ -91,10 +91,16 @@ void Camera::select_next_type()
|
||||||
|
|
||||||
void Camera::set_target(const Vec3d& target)
|
void Camera::set_target(const Vec3d& target)
|
||||||
{
|
{
|
||||||
m_target = target;
|
BoundingBoxf3 test_box = m_scene_box;
|
||||||
m_target(0) = clamp(m_scene_box.min(0), m_scene_box.max(0), m_target(0));
|
test_box.translate(-m_scene_box.center());
|
||||||
m_target(1) = clamp(m_scene_box.min(1), m_scene_box.max(1), m_target(1));
|
// We may let this factor be customizable
|
||||||
m_target(2) = clamp(m_scene_box.min(2), m_scene_box.max(2), m_target(2));
|
static const double ScaleFactor = 1.5;
|
||||||
|
test_box.scale(ScaleFactor);
|
||||||
|
test_box.translate(m_scene_box.center());
|
||||||
|
|
||||||
|
m_target(0) = clamp(test_box.min(0), test_box.max(0), target(0));
|
||||||
|
m_target(1) = clamp(test_box.min(1), test_box.max(1), target(1));
|
||||||
|
m_target(2) = clamp(test_box.min(2), test_box.max(2), target(2));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Camera::set_theta(float theta, bool apply_limit)
|
void Camera::set_theta(float theta, bool apply_limit)
|
||||||
|
@ -109,20 +115,20 @@ void Camera::set_theta(float theta, bool apply_limit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Camera::set_zoom(double zoom, const BoundingBoxf3& max_box, int canvas_w, int canvas_h)
|
void Camera::update_zoom(double delta_zoom)
|
||||||
{
|
{
|
||||||
zoom = std::max(std::min(zoom, 4.0), -4.0) / 10.0;
|
set_zoom(m_zoom / (1.0 - std::max(std::min(delta_zoom, 4.0), -4.0) * 0.1));
|
||||||
zoom = m_zoom / (1.0 - zoom);
|
}
|
||||||
|
|
||||||
|
void Camera::set_zoom(double zoom)
|
||||||
|
{
|
||||||
// Don't allow to zoom too far outside the scene.
|
// Don't allow to zoom too far outside the scene.
|
||||||
double zoom_min = calc_zoom_to_bounding_box_factor(max_box, canvas_w, canvas_h);
|
double zoom_min = calc_zoom_to_bounding_box_factor(m_scene_box, (int)m_viewport[2], (int)m_viewport[3]);
|
||||||
if (zoom_min > 0.0)
|
if (zoom_min > 0.0)
|
||||||
zoom = std::max(zoom, zoom_min * 0.7);
|
zoom = std::max(zoom, zoom_min * 0.7);
|
||||||
|
|
||||||
// Don't allow to zoom too close to the scene.
|
// Don't allow to zoom too close to the scene.
|
||||||
zoom = std::min(zoom, 100.0);
|
m_zoom = std::min(zoom, 100.0);
|
||||||
|
|
||||||
m_zoom = zoom;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Camera::select_view(const std::string& direction)
|
bool Camera::select_view(const std::string& direction)
|
||||||
|
|
|
@ -70,8 +70,8 @@ public:
|
||||||
void set_theta(float theta, bool apply_limit);
|
void set_theta(float theta, bool apply_limit);
|
||||||
|
|
||||||
double get_zoom() const { return m_zoom; }
|
double get_zoom() const { return m_zoom; }
|
||||||
void set_zoom(double zoom, const BoundingBoxf3& max_box, int canvas_w, int canvas_h);
|
void update_zoom(double delta_zoom);
|
||||||
void set_zoom(double zoom) { m_zoom = zoom; }
|
void set_zoom(double zoom);
|
||||||
|
|
||||||
const BoundingBoxf3& get_scene_box() const { return m_scene_box; }
|
const BoundingBoxf3& get_scene_box() const { return m_scene_box; }
|
||||||
void set_scene_box(const BoundingBoxf3& box) { m_scene_box = box; }
|
void set_scene_box(const BoundingBoxf3& box) { m_scene_box = box; }
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
#include "GUI_App.hpp"
|
#include "GUI_App.hpp"
|
||||||
#include "GUI_ObjectList.hpp"
|
#include "GUI_ObjectList.hpp"
|
||||||
#include "GUI_ObjectManipulation.hpp"
|
#include "GUI_ObjectManipulation.hpp"
|
||||||
|
#include "Mouse3DController.hpp"
|
||||||
#include "I18N.hpp"
|
#include "I18N.hpp"
|
||||||
|
|
||||||
#if ENABLE_RETINA_GL
|
#if ENABLE_RETINA_GL
|
||||||
|
@ -130,6 +131,9 @@ GLCanvas3D::LayersEditing::LayersEditing()
|
||||||
, m_object_max_z(0.f)
|
, m_object_max_z(0.f)
|
||||||
, m_slicing_parameters(nullptr)
|
, m_slicing_parameters(nullptr)
|
||||||
, m_layer_height_profile_modified(false)
|
, m_layer_height_profile_modified(false)
|
||||||
|
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
, m_adaptive_cusp(0.2f)
|
||||||
|
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
, state(Unknown)
|
, state(Unknown)
|
||||||
, band_width(2.0f)
|
, band_width(2.0f)
|
||||||
, strength(0.005f)
|
, strength(0.005f)
|
||||||
|
@ -150,7 +154,9 @@ GLCanvas3D::LayersEditing::~LayersEditing()
|
||||||
}
|
}
|
||||||
|
|
||||||
const float GLCanvas3D::LayersEditing::THICKNESS_BAR_WIDTH = 70.0f;
|
const float GLCanvas3D::LayersEditing::THICKNESS_BAR_WIDTH = 70.0f;
|
||||||
|
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
const float GLCanvas3D::LayersEditing::THICKNESS_RESET_BUTTON_HEIGHT = 22.0f;
|
const float GLCanvas3D::LayersEditing::THICKNESS_RESET_BUTTON_HEIGHT = 22.0f;
|
||||||
|
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
|
||||||
bool GLCanvas3D::LayersEditing::init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename)
|
bool GLCanvas3D::LayersEditing::init(const std::string& vertex_shader_filename, const std::string& fragment_shader_filename)
|
||||||
{
|
{
|
||||||
|
@ -217,13 +223,103 @@ void GLCanvas3D::LayersEditing::render_overlay(const GLCanvas3D& canvas) const
|
||||||
if (!m_enabled)
|
if (!m_enabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
static const ImVec4 orange(0.757f, 0.404f, 0.216f, 1.0f);
|
||||||
|
|
||||||
|
const Size& cnv_size = canvas.get_canvas_size();
|
||||||
|
float canvas_w = (float)cnv_size.get_width();
|
||||||
|
float canvas_h = (float)cnv_size.get_height();
|
||||||
|
|
||||||
|
ImGuiWrapper& imgui = *wxGetApp().imgui();
|
||||||
|
imgui.set_next_window_pos(canvas_w - imgui.get_style_scaling() * THICKNESS_BAR_WIDTH, canvas_h, ImGuiCond_Always, 1.0f, 1.0f);
|
||||||
|
imgui.set_next_window_bg_alpha(0.5f);
|
||||||
|
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
|
||||||
|
|
||||||
|
imgui.begin(_(L("Layer height profile")), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse);
|
||||||
|
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, orange);
|
||||||
|
imgui.text(_(L("Left mouse button:")));
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
ImGui::SameLine();
|
||||||
|
imgui.text(_(L("Add detail")));
|
||||||
|
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, orange);
|
||||||
|
imgui.text(_(L("Right mouse button:")));
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
ImGui::SameLine();
|
||||||
|
imgui.text(_(L("Remove detail")));
|
||||||
|
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, orange);
|
||||||
|
imgui.text(_(L("Shift + Left mouse button:")));
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
ImGui::SameLine();
|
||||||
|
imgui.text(_(L("Reset to base")));
|
||||||
|
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, orange);
|
||||||
|
imgui.text(_(L("Shift + Right mouse button:")));
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
ImGui::SameLine();
|
||||||
|
imgui.text(_(L("Smoothing")));
|
||||||
|
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, orange);
|
||||||
|
imgui.text(_(L("Mouse wheel:")));
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
ImGui::SameLine();
|
||||||
|
imgui.text(_(L("Increase/decrease edit area")));
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
if (imgui.button(_(L("Adaptive"))))
|
||||||
|
wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), Event<float>(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, m_adaptive_cusp));
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
float text_align = ImGui::GetCursorPosX();
|
||||||
|
imgui.text(_(L("Cusp (mm)")));
|
||||||
|
ImGui::SameLine();
|
||||||
|
float widget_align = ImGui::GetCursorPosX();
|
||||||
|
ImGui::PushItemWidth(120.0f);
|
||||||
|
m_adaptive_cusp = std::min(m_adaptive_cusp, (float)m_slicing_parameters->max_layer_height);
|
||||||
|
ImGui::SliderFloat("", &m_adaptive_cusp, 0.0f, (float)m_slicing_parameters->max_layer_height, "%.2f");
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
if (imgui.button(_(L("Smooth"))))
|
||||||
|
wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), HeightProfileSmoothEvent(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, m_smooth_params ));
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetCursorPosX(text_align);
|
||||||
|
imgui.text(_(L("Radius")));
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::PushItemWidth(120.0f);
|
||||||
|
ImGui::SetCursorPosX(widget_align);
|
||||||
|
int radius = (int)m_smooth_params.radius;
|
||||||
|
if (ImGui::SliderInt("##1", &radius, 1, 10))
|
||||||
|
m_smooth_params.radius = (unsigned int)radius;
|
||||||
|
|
||||||
|
ImGui::SetCursorPosX(text_align);
|
||||||
|
imgui.text(_(L("Keep min")));
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::PushItemWidth(120.0f);
|
||||||
|
ImGui::SetCursorPosX(widget_align);
|
||||||
|
imgui.checkbox("##2", m_smooth_params.keep_min);
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
if (imgui.button(_(L("Reset"))))
|
||||||
|
wxPostEvent((wxEvtHandler*)canvas.get_wxglcanvas(), SimpleEvent(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE));
|
||||||
|
|
||||||
|
imgui.end();
|
||||||
|
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
|
||||||
|
const Rect& bar_rect = get_bar_rect_viewport(canvas);
|
||||||
|
#else
|
||||||
const Rect& bar_rect = get_bar_rect_viewport(canvas);
|
const Rect& bar_rect = get_bar_rect_viewport(canvas);
|
||||||
const Rect& reset_rect = get_reset_rect_viewport(canvas);
|
const Rect& reset_rect = get_reset_rect_viewport(canvas);
|
||||||
|
|
||||||
_render_tooltip_texture(canvas, bar_rect, reset_rect);
|
_render_tooltip_texture(canvas, bar_rect, reset_rect);
|
||||||
_render_reset_texture(reset_rect);
|
_render_reset_texture(reset_rect);
|
||||||
_render_active_object_annotations(canvas, bar_rect);
|
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
_render_profile(bar_rect);
|
render_active_object_annotations(canvas, bar_rect);
|
||||||
|
render_profile(bar_rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
float GLCanvas3D::LayersEditing::get_cursor_z_relative(const GLCanvas3D& canvas)
|
float GLCanvas3D::LayersEditing::get_cursor_z_relative(const GLCanvas3D& canvas)
|
||||||
|
@ -248,11 +344,13 @@ bool GLCanvas3D::LayersEditing::bar_rect_contains(const GLCanvas3D& canvas, floa
|
||||||
return (rect.get_left() <= x) && (x <= rect.get_right()) && (rect.get_top() <= y) && (y <= rect.get_bottom());
|
return (rect.get_left() <= x) && (x <= rect.get_right()) && (rect.get_top() <= y) && (y <= rect.get_bottom());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
bool GLCanvas3D::LayersEditing::reset_rect_contains(const GLCanvas3D& canvas, float x, float y)
|
bool GLCanvas3D::LayersEditing::reset_rect_contains(const GLCanvas3D& canvas, float x, float y)
|
||||||
{
|
{
|
||||||
const Rect& rect = get_reset_rect_screen(canvas);
|
const Rect& rect = get_reset_rect_screen(canvas);
|
||||||
return (rect.get_left() <= x) && (x <= rect.get_right()) && (rect.get_top() <= y) && (y <= rect.get_bottom());
|
return (rect.get_left() <= x) && (x <= rect.get_right()) && (rect.get_top() <= y) && (y <= rect.get_bottom());
|
||||||
}
|
}
|
||||||
|
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
|
||||||
Rect GLCanvas3D::LayersEditing::get_bar_rect_screen(const GLCanvas3D& canvas)
|
Rect GLCanvas3D::LayersEditing::get_bar_rect_screen(const GLCanvas3D& canvas)
|
||||||
{
|
{
|
||||||
|
@ -260,9 +358,14 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_screen(const GLCanvas3D& canvas)
|
||||||
float w = (float)cnv_size.get_width();
|
float w = (float)cnv_size.get_width();
|
||||||
float h = (float)cnv_size.get_height();
|
float h = (float)cnv_size.get_height();
|
||||||
|
|
||||||
|
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
return Rect(w - thickness_bar_width(canvas), 0.0f, w, h);
|
||||||
|
#else
|
||||||
return Rect(w - thickness_bar_width(canvas), 0.0f, w, h - reset_button_height(canvas));
|
return Rect(w - thickness_bar_width(canvas), 0.0f, w, h - reset_button_height(canvas));
|
||||||
|
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
Rect GLCanvas3D::LayersEditing::get_reset_rect_screen(const GLCanvas3D& canvas)
|
Rect GLCanvas3D::LayersEditing::get_reset_rect_screen(const GLCanvas3D& canvas)
|
||||||
{
|
{
|
||||||
const Size& cnv_size = canvas.get_canvas_size();
|
const Size& cnv_size = canvas.get_canvas_size();
|
||||||
|
@ -271,6 +374,7 @@ Rect GLCanvas3D::LayersEditing::get_reset_rect_screen(const GLCanvas3D& canvas)
|
||||||
|
|
||||||
return Rect(w - thickness_bar_width(canvas), h - reset_button_height(canvas), w, h);
|
return Rect(w - thickness_bar_width(canvas), h - reset_button_height(canvas), w, h);
|
||||||
}
|
}
|
||||||
|
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
|
||||||
Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas)
|
Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas)
|
||||||
{
|
{
|
||||||
|
@ -281,9 +385,14 @@ Rect GLCanvas3D::LayersEditing::get_bar_rect_viewport(const GLCanvas3D& canvas)
|
||||||
float zoom = (float)canvas.get_camera().get_zoom();
|
float zoom = (float)canvas.get_camera().get_zoom();
|
||||||
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
|
float inv_zoom = (zoom != 0.0f) ? 1.0f / zoom : 0.0f;
|
||||||
|
|
||||||
|
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom);
|
||||||
|
#else
|
||||||
return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, (-half_h + reset_button_height(canvas)) * inv_zoom);
|
return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, half_h * inv_zoom, half_w * inv_zoom, (-half_h + reset_button_height(canvas)) * inv_zoom);
|
||||||
|
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
Rect GLCanvas3D::LayersEditing::get_reset_rect_viewport(const GLCanvas3D& canvas)
|
Rect GLCanvas3D::LayersEditing::get_reset_rect_viewport(const GLCanvas3D& canvas)
|
||||||
{
|
{
|
||||||
const Size& cnv_size = canvas.get_canvas_size();
|
const Size& cnv_size = canvas.get_canvas_size();
|
||||||
|
@ -295,13 +404,14 @@ Rect GLCanvas3D::LayersEditing::get_reset_rect_viewport(const GLCanvas3D& canvas
|
||||||
|
|
||||||
return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, (-half_h + reset_button_height(canvas)) * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom);
|
return Rect((half_w - thickness_bar_width(canvas)) * inv_zoom, (-half_h + reset_button_height(canvas)) * inv_zoom, half_w * inv_zoom, -half_h * inv_zoom);
|
||||||
}
|
}
|
||||||
|
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
|
||||||
|
bool GLCanvas3D::LayersEditing::is_initialized() const
|
||||||
bool GLCanvas3D::LayersEditing::_is_initialized() const
|
|
||||||
{
|
{
|
||||||
return m_shader.is_initialized();
|
return m_shader.is_initialized();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
void GLCanvas3D::LayersEditing::_render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const
|
void GLCanvas3D::LayersEditing::_render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const
|
||||||
{
|
{
|
||||||
// TODO: do this with ImGui
|
// TODO: do this with ImGui
|
||||||
|
@ -347,8 +457,9 @@ void GLCanvas3D::LayersEditing::_render_reset_texture(const Rect& reset_rect) co
|
||||||
|
|
||||||
GLTexture::render_texture(m_reset_texture.get_id(), reset_rect.get_left(), reset_rect.get_right(), reset_rect.get_bottom(), reset_rect.get_top());
|
GLTexture::render_texture(m_reset_texture.get_id(), reset_rect.get_left(), reset_rect.get_right(), reset_rect.get_bottom(), reset_rect.get_top());
|
||||||
}
|
}
|
||||||
|
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
|
||||||
void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const
|
void GLCanvas3D::LayersEditing::render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const
|
||||||
{
|
{
|
||||||
m_shader.start_using();
|
m_shader.start_using();
|
||||||
|
|
||||||
|
@ -379,7 +490,7 @@ void GLCanvas3D::LayersEditing::_render_active_object_annotations(const GLCanvas
|
||||||
m_shader.stop_using();
|
m_shader.stop_using();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::LayersEditing::_render_profile(const Rect& bar_rect) const
|
void GLCanvas3D::LayersEditing::render_profile(const Rect& bar_rect) const
|
||||||
{
|
{
|
||||||
//FIXME show some kind of legend.
|
//FIXME show some kind of legend.
|
||||||
|
|
||||||
|
@ -496,6 +607,24 @@ void GLCanvas3D::LayersEditing::reset_layer_height_profile(GLCanvas3D& canvas)
|
||||||
canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
|
canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
void GLCanvas3D::LayersEditing::adaptive_layer_height_profile(GLCanvas3D& canvas, float cusp)
|
||||||
|
{
|
||||||
|
m_layer_height_profile = layer_height_profile_adaptive(*m_slicing_parameters, *m_model_object, cusp);
|
||||||
|
const_cast<ModelObject*>(m_model_object)->layer_height_profile = m_layer_height_profile;
|
||||||
|
m_layers_texture.valid = false;
|
||||||
|
canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLCanvas3D::LayersEditing::smooth_layer_height_profile(GLCanvas3D& canvas, const HeightProfileSmoothingParams& smoothing_params)
|
||||||
|
{
|
||||||
|
m_layer_height_profile = smooth_height_profile(m_layer_height_profile, *m_slicing_parameters, smoothing_params);
|
||||||
|
const_cast<ModelObject*>(m_model_object)->layer_height_profile = m_layer_height_profile;
|
||||||
|
m_layers_texture.valid = false;
|
||||||
|
canvas.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
|
||||||
|
}
|
||||||
|
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
|
||||||
void GLCanvas3D::LayersEditing::generate_layer_height_texture()
|
void GLCanvas3D::LayersEditing::generate_layer_height_texture()
|
||||||
{
|
{
|
||||||
this->update_slicing_parameters();
|
this->update_slicing_parameters();
|
||||||
|
@ -557,6 +686,7 @@ float GLCanvas3D::LayersEditing::thickness_bar_width(const GLCanvas3D &canvas)
|
||||||
* THICKNESS_BAR_WIDTH;
|
* THICKNESS_BAR_WIDTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
float GLCanvas3D::LayersEditing::reset_button_height(const GLCanvas3D &canvas)
|
float GLCanvas3D::LayersEditing::reset_button_height(const GLCanvas3D &canvas)
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
|
@ -567,6 +697,7 @@ float GLCanvas3D::LayersEditing::reset_button_height(const GLCanvas3D &canvas)
|
||||||
#endif
|
#endif
|
||||||
* THICKNESS_RESET_BUTTON_HEIGHT;
|
* THICKNESS_RESET_BUTTON_HEIGHT;
|
||||||
}
|
}
|
||||||
|
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
|
||||||
|
|
||||||
const Point GLCanvas3D::Mouse::Drag::Invalid_2D_Point(INT_MAX, INT_MAX);
|
const Point GLCanvas3D::Mouse::Drag::Invalid_2D_Point(INT_MAX, INT_MAX);
|
||||||
|
@ -1118,6 +1249,11 @@ wxDEFINE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent);
|
||||||
wxDEFINE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent);
|
wxDEFINE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent);
|
||||||
wxDEFINE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent);
|
wxDEFINE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent);
|
||||||
wxDEFINE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent);
|
wxDEFINE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent);
|
||||||
|
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
wxDEFINE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent);
|
||||||
|
wxDEFINE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event<float>);
|
||||||
|
wxDEFINE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, HeightProfileSmoothEvent);
|
||||||
|
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
|
||||||
#if ENABLE_THUMBNAIL_GENERATOR
|
#if ENABLE_THUMBNAIL_GENERATOR
|
||||||
const double GLCanvas3D::DefaultCameraZoomToBoxMarginFactor = 1.25;
|
const double GLCanvas3D::DefaultCameraZoomToBoxMarginFactor = 1.25;
|
||||||
|
@ -1372,7 +1508,7 @@ void GLCanvas3D::set_model(Model* model)
|
||||||
|
|
||||||
void GLCanvas3D::bed_shape_changed()
|
void GLCanvas3D::bed_shape_changed()
|
||||||
{
|
{
|
||||||
m_camera.set_scene_box(scene_bounding_box());
|
refresh_camera_scene_box();
|
||||||
m_camera.requires_zoom_to_bed = true;
|
m_camera.requires_zoom_to_bed = true;
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
if (m_bed.is_prusa())
|
if (m_bed.is_prusa())
|
||||||
|
@ -1398,7 +1534,7 @@ BoundingBoxf3 GLCanvas3D::volumes_bounding_box() const
|
||||||
BoundingBoxf3 GLCanvas3D::scene_bounding_box() const
|
BoundingBoxf3 GLCanvas3D::scene_bounding_box() const
|
||||||
{
|
{
|
||||||
BoundingBoxf3 bb = volumes_bounding_box();
|
BoundingBoxf3 bb = volumes_bounding_box();
|
||||||
bb.merge(m_bed.get_bounding_box(false));
|
bb.merge(m_bed.get_bounding_box(true));
|
||||||
|
|
||||||
if (m_config != nullptr)
|
if (m_config != nullptr)
|
||||||
{
|
{
|
||||||
|
@ -1420,6 +1556,29 @@ bool GLCanvas3D::is_layers_editing_allowed() const
|
||||||
return m_layers_editing.is_allowed();
|
return m_layers_editing.is_allowed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
void GLCanvas3D::reset_layer_height_profile()
|
||||||
|
{
|
||||||
|
m_layers_editing.reset_layer_height_profile(*this);
|
||||||
|
m_layers_editing.state = LayersEditing::Completed;
|
||||||
|
m_dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLCanvas3D::adaptive_layer_height_profile(float cusp)
|
||||||
|
{
|
||||||
|
m_layers_editing.adaptive_layer_height_profile(*this, cusp);
|
||||||
|
m_layers_editing.state = LayersEditing::Completed;
|
||||||
|
m_dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GLCanvas3D::smooth_layer_height_profile(const HeightProfileSmoothingParams& smoothing_params)
|
||||||
|
{
|
||||||
|
m_layers_editing.smooth_layer_height_profile(*this, smoothing_params);
|
||||||
|
m_layers_editing.state = LayersEditing::Completed;
|
||||||
|
m_dirty = true;
|
||||||
|
}
|
||||||
|
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
|
||||||
bool GLCanvas3D::is_reload_delayed() const
|
bool GLCanvas3D::is_reload_delayed() const
|
||||||
{
|
{
|
||||||
return m_reload_delayed;
|
return m_reload_delayed;
|
||||||
|
@ -1546,10 +1705,11 @@ void GLCanvas3D::render()
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Size& cnv_size = get_canvas_size();
|
||||||
|
|
||||||
if (m_camera.requires_zoom_to_bed)
|
if (m_camera.requires_zoom_to_bed)
|
||||||
{
|
{
|
||||||
zoom_to_bed();
|
zoom_to_bed();
|
||||||
const Size& cnv_size = get_canvas_size();
|
|
||||||
_resize((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height());
|
_resize((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height());
|
||||||
m_camera.requires_zoom_to_bed = false;
|
m_camera.requires_zoom_to_bed = false;
|
||||||
}
|
}
|
||||||
|
@ -1640,6 +1800,8 @@ void GLCanvas3D::render()
|
||||||
m_camera.debug_render();
|
m_camera.debug_render();
|
||||||
#endif // ENABLE_CAMERA_STATISTICS
|
#endif // ENABLE_CAMERA_STATISTICS
|
||||||
|
|
||||||
|
wxGetApp().plater()->get_mouse3d_controller().render_settings_dialog((unsigned int)cnv_size.get_width(), (unsigned int)cnv_size.get_height());
|
||||||
|
|
||||||
wxGetApp().imgui()->render();
|
wxGetApp().imgui()->render();
|
||||||
|
|
||||||
m_canvas->SwapBuffers();
|
m_canvas->SwapBuffers();
|
||||||
|
@ -2098,7 +2260,7 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re
|
||||||
post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, false));
|
post_event(Event<bool>(EVT_GLCANVAS_ENABLE_ACTION_BUTTONS, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
m_camera.set_scene_box(scene_bounding_box());
|
refresh_camera_scene_box();
|
||||||
|
|
||||||
if (m_selection.is_empty())
|
if (m_selection.is_empty())
|
||||||
{
|
{
|
||||||
|
@ -2338,14 +2500,21 @@ void GLCanvas3D::on_idle(wxIdleEvent& evt)
|
||||||
m_dirty |= m_main_toolbar.update_items_state();
|
m_dirty |= m_main_toolbar.update_items_state();
|
||||||
m_dirty |= m_undoredo_toolbar.update_items_state();
|
m_dirty |= m_undoredo_toolbar.update_items_state();
|
||||||
m_dirty |= m_view_toolbar.update_items_state();
|
m_dirty |= m_view_toolbar.update_items_state();
|
||||||
|
bool mouse3d_controller_applied = wxGetApp().plater()->get_mouse3d_controller().apply(m_camera);
|
||||||
|
m_dirty |= mouse3d_controller_applied;
|
||||||
|
|
||||||
if (!m_dirty)
|
if (!m_dirty)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_refresh_if_shown_on_screen();
|
_refresh_if_shown_on_screen();
|
||||||
|
|
||||||
if (m_keep_dirty)
|
if (m_keep_dirty || mouse3d_controller_applied)
|
||||||
|
{
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
|
evt.RequestMore();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::on_char(wxKeyEvent& evt)
|
void GLCanvas3D::on_char(wxKeyEvent& evt)
|
||||||
|
@ -2390,6 +2559,20 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
|
||||||
#endif /* __APPLE__ */
|
#endif /* __APPLE__ */
|
||||||
post_event(SimpleEvent(EVT_GLTOOLBAR_COPY));
|
post_event(SimpleEvent(EVT_GLTOOLBAR_COPY));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
case 'm':
|
||||||
|
case 'M':
|
||||||
|
#else /* __APPLE__ */
|
||||||
|
case WXK_CONTROL_M:
|
||||||
|
#endif /* __APPLE__ */
|
||||||
|
{
|
||||||
|
Mouse3DController& controller = wxGetApp().plater()->get_mouse3d_controller();
|
||||||
|
controller.show_settings_dialog(!controller.is_settings_dialog_shown());
|
||||||
|
m_dirty = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
case 'v':
|
case 'v':
|
||||||
case 'V':
|
case 'V':
|
||||||
|
@ -2457,11 +2640,11 @@ void GLCanvas3D::on_char(wxKeyEvent& evt)
|
||||||
case 'B':
|
case 'B':
|
||||||
case 'b': { zoom_to_bed(); break; }
|
case 'b': { zoom_to_bed(); break; }
|
||||||
case 'I':
|
case 'I':
|
||||||
case 'i': { set_camera_zoom(1.0); break; }
|
case 'i': { _update_camera_zoom(1.0); break; }
|
||||||
case 'K':
|
case 'K':
|
||||||
case 'k': { m_camera.select_next_type(); m_dirty = true; break; }
|
case 'k': { m_camera.select_next_type(); m_dirty = true; break; }
|
||||||
case 'O':
|
case 'O':
|
||||||
case 'o': { set_camera_zoom(-1.0); break; }
|
case 'o': { _update_camera_zoom(-1.0); break; }
|
||||||
#if ENABLE_RENDER_PICKING_PASS
|
#if ENABLE_RENDER_PICKING_PASS
|
||||||
case 'T':
|
case 'T':
|
||||||
case 't': {
|
case 't': {
|
||||||
|
@ -2564,6 +2747,11 @@ void GLCanvas3D::on_key(wxKeyEvent& evt)
|
||||||
|
|
||||||
void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
|
void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
|
||||||
{
|
{
|
||||||
|
// try to filter out events coming from mouse 3d
|
||||||
|
Mouse3DController& controller = wxGetApp().plater()->get_mouse3d_controller();
|
||||||
|
if (controller.process_mouse_wheel())
|
||||||
|
return;
|
||||||
|
|
||||||
if (!m_initialized)
|
if (!m_initialized)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -2608,7 +2796,7 @@ void GLCanvas3D::on_mouse_wheel(wxMouseEvent& evt)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Calculate the zoom delta and apply it to the current zoom factor
|
// Calculate the zoom delta and apply it to the current zoom factor
|
||||||
set_camera_zoom((double)evt.GetWheelRotation() / (double)evt.GetWheelDelta());
|
_update_camera_zoom((double)evt.GetWheelRotation() / (double)evt.GetWheelDelta());
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::on_timer(wxTimerEvent& evt)
|
void GLCanvas3D::on_timer(wxTimerEvent& evt)
|
||||||
|
@ -2800,6 +2988,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||||
m_layers_editing.state = LayersEditing::Editing;
|
m_layers_editing.state = LayersEditing::Editing;
|
||||||
_perform_layer_editing_action(&evt);
|
_perform_layer_editing_action(&evt);
|
||||||
}
|
}
|
||||||
|
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
else if ((layer_editing_object_idx != -1) && m_layers_editing.reset_rect_contains(*this, pos(0), pos(1)))
|
else if ((layer_editing_object_idx != -1) && m_layers_editing.reset_rect_contains(*this, pos(0), pos(1)))
|
||||||
{
|
{
|
||||||
if (evt.LeftDown())
|
if (evt.LeftDown())
|
||||||
|
@ -2812,6 +3001,7 @@ void GLCanvas3D::on_mouse(wxMouseEvent& evt)
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
else if (evt.LeftDown() && (evt.ShiftDown() || evt.AltDown()) && m_picking_enabled)
|
else if (evt.LeftDown() && (evt.ShiftDown() || evt.AltDown()) && m_picking_enabled)
|
||||||
{
|
{
|
||||||
if (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports)
|
if (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports)
|
||||||
|
@ -3391,13 +3581,6 @@ void GLCanvas3D::do_mirror(const std::string& snapshot_type)
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GLCanvas3D::set_camera_zoom(double zoom)
|
|
||||||
{
|
|
||||||
const Size& cnv_size = get_canvas_size();
|
|
||||||
m_camera.set_zoom(zoom, _max_bounding_box(false, true), cnv_size.get_width(), cnv_size.get_height());
|
|
||||||
m_dirty = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GLCanvas3D::update_gizmos_on_off_state()
|
void GLCanvas3D::update_gizmos_on_off_state()
|
||||||
{
|
{
|
||||||
set_as_dirty();
|
set_as_dirty();
|
||||||
|
@ -4183,8 +4366,6 @@ void GLCanvas3D::_resize(unsigned int w, unsigned int h)
|
||||||
|
|
||||||
// updates camera
|
// updates camera
|
||||||
m_camera.apply_viewport(0, 0, w, h);
|
m_camera.apply_viewport(0, 0, w, h);
|
||||||
|
|
||||||
m_dirty = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BoundingBoxf3 GLCanvas3D::_max_bounding_box(bool include_gizmos, bool include_bed_model) const
|
BoundingBoxf3 GLCanvas3D::_max_bounding_box(bool include_gizmos, bool include_bed_model) const
|
||||||
|
@ -4221,6 +4402,12 @@ void GLCanvas3D::_zoom_to_box(const BoundingBoxf3& box)
|
||||||
}
|
}
|
||||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||||
|
|
||||||
|
void GLCanvas3D::_update_camera_zoom(double zoom)
|
||||||
|
{
|
||||||
|
m_camera.update_zoom(zoom);
|
||||||
|
m_dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
void GLCanvas3D::_refresh_if_shown_on_screen()
|
void GLCanvas3D::_refresh_if_shown_on_screen()
|
||||||
{
|
{
|
||||||
if (_is_shown_on_screen())
|
if (_is_shown_on_screen())
|
||||||
|
|
|
@ -81,6 +81,8 @@ template <size_t N> using Vec2dsEvent = ArrayEvent<Vec2d, N>;
|
||||||
using Vec3dEvent = Event<Vec3d>;
|
using Vec3dEvent = Event<Vec3d>;
|
||||||
template <size_t N> using Vec3dsEvent = ArrayEvent<Vec3d, N>;
|
template <size_t N> using Vec3dsEvent = ArrayEvent<Vec3d, N>;
|
||||||
|
|
||||||
|
using HeightProfileSmoothEvent = Event<HeightProfileSmoothingParams>;
|
||||||
|
|
||||||
wxDECLARE_EVENT(EVT_GLCANVAS_INIT, SimpleEvent);
|
wxDECLARE_EVENT(EVT_GLCANVAS_INIT, SimpleEvent);
|
||||||
wxDECLARE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent);
|
wxDECLARE_EVENT(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS, SimpleEvent);
|
||||||
wxDECLARE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, RBtnEvent);
|
wxDECLARE_EVENT(EVT_GLCANVAS_RIGHT_CLICK, RBtnEvent);
|
||||||
|
@ -104,6 +106,11 @@ wxDECLARE_EVENT(EVT_GLCANVAS_MOVE_DOUBLE_SLIDER, wxKeyEvent);
|
||||||
wxDECLARE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent);
|
wxDECLARE_EVENT(EVT_GLCANVAS_EDIT_COLOR_CHANGE, wxKeyEvent);
|
||||||
wxDECLARE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent);
|
wxDECLARE_EVENT(EVT_GLCANVAS_UNDO, SimpleEvent);
|
||||||
wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent);
|
wxDECLARE_EVENT(EVT_GLCANVAS_REDO, SimpleEvent);
|
||||||
|
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
wxDECLARE_EVENT(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, SimpleEvent);
|
||||||
|
wxDECLARE_EVENT(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, Event<float>);
|
||||||
|
wxDECLARE_EVENT(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, HeightProfileSmoothEvent);
|
||||||
|
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
|
||||||
class GLCanvas3D
|
class GLCanvas3D
|
||||||
{
|
{
|
||||||
|
@ -153,13 +160,17 @@ private:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const float THICKNESS_BAR_WIDTH;
|
static const float THICKNESS_BAR_WIDTH;
|
||||||
|
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
static const float THICKNESS_RESET_BUTTON_HEIGHT;
|
static const float THICKNESS_RESET_BUTTON_HEIGHT;
|
||||||
|
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
|
||||||
bool m_enabled;
|
bool m_enabled;
|
||||||
Shader m_shader;
|
Shader m_shader;
|
||||||
unsigned int m_z_texture_id;
|
unsigned int m_z_texture_id;
|
||||||
|
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
mutable GLTexture m_tooltip_texture;
|
mutable GLTexture m_tooltip_texture;
|
||||||
mutable GLTexture m_reset_texture;
|
mutable GLTexture m_reset_texture;
|
||||||
|
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
// Not owned by LayersEditing.
|
// Not owned by LayersEditing.
|
||||||
const DynamicPrintConfig *m_config;
|
const DynamicPrintConfig *m_config;
|
||||||
// ModelObject for the currently selected object (Model::objects[last_object_id]).
|
// ModelObject for the currently selected object (Model::objects[last_object_id]).
|
||||||
|
@ -171,6 +182,11 @@ private:
|
||||||
std::vector<coordf_t> m_layer_height_profile;
|
std::vector<coordf_t> m_layer_height_profile;
|
||||||
bool m_layer_height_profile_modified;
|
bool m_layer_height_profile_modified;
|
||||||
|
|
||||||
|
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
mutable float m_adaptive_cusp;
|
||||||
|
mutable HeightProfileSmoothingParams m_smooth_params;
|
||||||
|
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
|
||||||
class LayersTexture
|
class LayersTexture
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -217,28 +233,42 @@ private:
|
||||||
void adjust_layer_height_profile();
|
void adjust_layer_height_profile();
|
||||||
void accept_changes(GLCanvas3D& canvas);
|
void accept_changes(GLCanvas3D& canvas);
|
||||||
void reset_layer_height_profile(GLCanvas3D& canvas);
|
void reset_layer_height_profile(GLCanvas3D& canvas);
|
||||||
|
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
void adaptive_layer_height_profile(GLCanvas3D& canvas, float cusp);
|
||||||
|
void smooth_layer_height_profile(GLCanvas3D& canvas, const HeightProfileSmoothingParams& smoothing_paramsn);
|
||||||
|
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
|
||||||
static float get_cursor_z_relative(const GLCanvas3D& canvas);
|
static float get_cursor_z_relative(const GLCanvas3D& canvas);
|
||||||
static bool bar_rect_contains(const GLCanvas3D& canvas, float x, float y);
|
static bool bar_rect_contains(const GLCanvas3D& canvas, float x, float y);
|
||||||
|
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
static bool reset_rect_contains(const GLCanvas3D& canvas, float x, float y);
|
static bool reset_rect_contains(const GLCanvas3D& canvas, float x, float y);
|
||||||
|
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
static Rect get_bar_rect_screen(const GLCanvas3D& canvas);
|
static Rect get_bar_rect_screen(const GLCanvas3D& canvas);
|
||||||
|
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
static Rect get_reset_rect_screen(const GLCanvas3D& canvas);
|
static Rect get_reset_rect_screen(const GLCanvas3D& canvas);
|
||||||
|
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
static Rect get_bar_rect_viewport(const GLCanvas3D& canvas);
|
static Rect get_bar_rect_viewport(const GLCanvas3D& canvas);
|
||||||
|
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
static Rect get_reset_rect_viewport(const GLCanvas3D& canvas);
|
static Rect get_reset_rect_viewport(const GLCanvas3D& canvas);
|
||||||
|
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
|
||||||
float object_max_z() const { return m_object_max_z; }
|
float object_max_z() const { return m_object_max_z; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool _is_initialized() const;
|
bool is_initialized() const;
|
||||||
void generate_layer_height_texture();
|
void generate_layer_height_texture();
|
||||||
|
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
void _render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const;
|
void _render_tooltip_texture(const GLCanvas3D& canvas, const Rect& bar_rect, const Rect& reset_rect) const;
|
||||||
void _render_reset_texture(const Rect& reset_rect) const;
|
void _render_reset_texture(const Rect& reset_rect) const;
|
||||||
void _render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const;
|
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
void _render_profile(const Rect& bar_rect) const;
|
void render_active_object_annotations(const GLCanvas3D& canvas, const Rect& bar_rect) const;
|
||||||
|
void render_profile(const Rect& bar_rect) const;
|
||||||
void update_slicing_parameters();
|
void update_slicing_parameters();
|
||||||
|
|
||||||
static float thickness_bar_width(const GLCanvas3D &canvas);
|
static float thickness_bar_width(const GLCanvas3D &canvas);
|
||||||
|
#if !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
static float reset_button_height(const GLCanvas3D &canvas);
|
static float reset_button_height(const GLCanvas3D &canvas);
|
||||||
|
#endif // !ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Mouse
|
struct Mouse
|
||||||
|
@ -493,6 +523,7 @@ public:
|
||||||
void set_color_by(const std::string& value);
|
void set_color_by(const std::string& value);
|
||||||
|
|
||||||
const Camera& get_camera() const { return m_camera; }
|
const Camera& get_camera() const { return m_camera; }
|
||||||
|
Camera& get_camera() { return m_camera; }
|
||||||
|
|
||||||
BoundingBoxf3 volumes_bounding_box() const;
|
BoundingBoxf3 volumes_bounding_box() const;
|
||||||
BoundingBoxf3 scene_bounding_box() const;
|
BoundingBoxf3 scene_bounding_box() const;
|
||||||
|
@ -500,6 +531,12 @@ public:
|
||||||
bool is_layers_editing_enabled() const;
|
bool is_layers_editing_enabled() const;
|
||||||
bool is_layers_editing_allowed() const;
|
bool is_layers_editing_allowed() const;
|
||||||
|
|
||||||
|
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
void reset_layer_height_profile();
|
||||||
|
void adaptive_layer_height_profile(float cusp);
|
||||||
|
void smooth_layer_height_profile(const HeightProfileSmoothingParams& smoothing_params);
|
||||||
|
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
|
||||||
bool is_reload_delayed() const;
|
bool is_reload_delayed() const;
|
||||||
|
|
||||||
void enable_layers_editing(bool enable);
|
void enable_layers_editing(bool enable);
|
||||||
|
@ -576,8 +613,6 @@ public:
|
||||||
void do_flatten(const Vec3d& normal, const std::string& snapshot_type);
|
void do_flatten(const Vec3d& normal, const std::string& snapshot_type);
|
||||||
void do_mirror(const std::string& snapshot_type);
|
void do_mirror(const std::string& snapshot_type);
|
||||||
|
|
||||||
void set_camera_zoom(double zoom);
|
|
||||||
|
|
||||||
void update_gizmos_on_off_state();
|
void update_gizmos_on_off_state();
|
||||||
void reset_all_gizmos() { m_gizmos.reset_all_states(); }
|
void reset_all_gizmos() { m_gizmos.reset_all_states(); }
|
||||||
|
|
||||||
|
@ -655,6 +690,7 @@ private:
|
||||||
#else
|
#else
|
||||||
void _zoom_to_box(const BoundingBoxf3& box);
|
void _zoom_to_box(const BoundingBoxf3& box);
|
||||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||||
|
void _update_camera_zoom(double zoom);
|
||||||
|
|
||||||
void _refresh_if_shown_on_screen();
|
void _refresh_if_shown_on_screen();
|
||||||
|
|
||||||
|
|
|
@ -528,6 +528,9 @@ void ImGuiWrapper::init_style()
|
||||||
// Slider
|
// Slider
|
||||||
set_color(ImGuiCol_SliderGrab, COL_ORANGE_DARK);
|
set_color(ImGuiCol_SliderGrab, COL_ORANGE_DARK);
|
||||||
set_color(ImGuiCol_SliderGrabActive, COL_ORANGE_LIGHT);
|
set_color(ImGuiCol_SliderGrabActive, COL_ORANGE_LIGHT);
|
||||||
|
|
||||||
|
// Separator
|
||||||
|
set_color(ImGuiCol_Separator, COL_ORANGE_LIGHT);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImGuiWrapper::render_draw_data(ImDrawData *draw_data)
|
void ImGuiWrapper::render_draw_data(ImDrawData *draw_data)
|
||||||
|
|
|
@ -157,6 +157,7 @@ void KBShortcutsDialog::fill_shortcuts()
|
||||||
plater_shortcuts.push_back(Shortcut("Z", L("Zoom to selected object")));
|
plater_shortcuts.push_back(Shortcut("Z", L("Zoom to selected object")));
|
||||||
plater_shortcuts.push_back(Shortcut("I", L("Zoom in")));
|
plater_shortcuts.push_back(Shortcut("I", L("Zoom in")));
|
||||||
plater_shortcuts.push_back(Shortcut("O", L("Zoom out")));
|
plater_shortcuts.push_back(Shortcut("O", L("Zoom out")));
|
||||||
|
plater_shortcuts.push_back(Shortcut(ctrl+"M", L("Show/Hide 3Dconnexion devices settings dialog")));
|
||||||
plater_shortcuts.push_back(Shortcut("ESC", L("Unselect gizmo / Clear selection")));
|
plater_shortcuts.push_back(Shortcut("ESC", L("Unselect gizmo / Clear selection")));
|
||||||
#if ENABLE_RENDER_PICKING_PASS
|
#if ENABLE_RENDER_PICKING_PASS
|
||||||
// Don't localize debugging texts.
|
// Don't localize debugging texts.
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "PrintHostDialogs.hpp"
|
#include "PrintHostDialogs.hpp"
|
||||||
#include "wxExtensions.hpp"
|
#include "wxExtensions.hpp"
|
||||||
#include "GUI_ObjectList.hpp"
|
#include "GUI_ObjectList.hpp"
|
||||||
|
#include "Mouse3DController.hpp"
|
||||||
#include "I18N.hpp"
|
#include "I18N.hpp"
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
812
src/slic3r/GUI/Mouse3DController.cpp
Normal file
812
src/slic3r/GUI/Mouse3DController.cpp
Normal file
|
@ -0,0 +1,812 @@
|
||||||
|
#include "libslic3r/libslic3r.h"
|
||||||
|
#include "Mouse3DController.hpp"
|
||||||
|
|
||||||
|
#include "Camera.hpp"
|
||||||
|
#include "GUI_App.hpp"
|
||||||
|
#include "PresetBundle.hpp"
|
||||||
|
#include "AppConfig.hpp"
|
||||||
|
|
||||||
|
#include <wx/glcanvas.h>
|
||||||
|
|
||||||
|
#include <boost/nowide/convert.hpp>
|
||||||
|
#include <boost/log/trivial.hpp>
|
||||||
|
#include "I18N.hpp"
|
||||||
|
|
||||||
|
#include <bitset>
|
||||||
|
|
||||||
|
// WARN: If updating these lists, please also update resources/udev/90-3dconnexion.rules
|
||||||
|
|
||||||
|
static const std::vector<int> _3DCONNEXION_VENDORS =
|
||||||
|
{
|
||||||
|
0x046d, // LOGITECH = 1133 // Logitech (3Dconnexion is made by Logitech)
|
||||||
|
0x256F // 3DCONNECTION = 9583 // 3Dconnexion
|
||||||
|
};
|
||||||
|
|
||||||
|
// See: https://github.com/FreeSpacenav/spacenavd/blob/a9eccf34e7cac969ee399f625aef827f4f4aaec6/src/dev.c#L202
|
||||||
|
static const std::vector<int> _3DCONNEXION_DEVICES =
|
||||||
|
{
|
||||||
|
0xc603, /* 50691 spacemouse plus XT */
|
||||||
|
0xc605, /* 50693 cadman */
|
||||||
|
0xc606, /* 50694 spacemouse classic */
|
||||||
|
0xc621, /* 50721 spaceball 5000 */
|
||||||
|
0xc623, /* 50723 space traveller */
|
||||||
|
0xc625, /* 50725 space pilot */
|
||||||
|
0xc626, /* 50726 space navigator *TESTED* */
|
||||||
|
0xc627, /* 50727 space explorer */
|
||||||
|
0xc628, /* 50728 space navigator for notebooks*/
|
||||||
|
0xc629, /* 50729 space pilot pro*/
|
||||||
|
0xc62b, /* 50731 space mouse pro*/
|
||||||
|
0xc62e, /* 50734 spacemouse wireless (USB cable) *TESTED* */
|
||||||
|
0xc62f, /* 50735 spacemouse wireless receiver */
|
||||||
|
0xc631, /* 50737 spacemouse pro wireless *TESTED* */
|
||||||
|
0xc632, /* 50738 spacemouse pro wireless receiver */
|
||||||
|
0xc633, /* 50739 spacemouse enterprise */
|
||||||
|
0xc635, /* 50741 spacemouse compact *TESTED* */
|
||||||
|
0xc636, /* 50742 spacemouse module */
|
||||||
|
0xc640, /* 50752 nulooq */
|
||||||
|
0xc652, /* 50770 3Dconnexion universal receiver *TESTED* */
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
namespace GUI {
|
||||||
|
|
||||||
|
const double Mouse3DController::State::DefaultTranslationScale = 2.5;
|
||||||
|
const double Mouse3DController::State::MaxTranslationDeadzone = 0.2;
|
||||||
|
const double Mouse3DController::State::DefaultTranslationDeadzone = 0.5 * Mouse3DController::State::MaxTranslationDeadzone;
|
||||||
|
const float Mouse3DController::State::DefaultRotationScale = 1.0f;
|
||||||
|
const float Mouse3DController::State::MaxRotationDeadzone = (float)Mouse3DController::State::MaxTranslationDeadzone;
|
||||||
|
const float Mouse3DController::State::DefaultRotationDeadzone = 0.5f * Mouse3DController::State::MaxRotationDeadzone;
|
||||||
|
|
||||||
|
Mouse3DController::State::State()
|
||||||
|
: m_buttons_enabled(false)
|
||||||
|
, m_translation_params(DefaultTranslationScale, DefaultTranslationDeadzone)
|
||||||
|
, m_rotation_params(DefaultRotationScale, DefaultRotationDeadzone)
|
||||||
|
, m_mouse_wheel_counter(0)
|
||||||
|
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
, m_translation_queue_max_size(0)
|
||||||
|
, m_rotation_queue_max_size(0)
|
||||||
|
, m_buttons_queue_max_size(0)
|
||||||
|
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mouse3DController::State::append_translation(const Vec3d& translation)
|
||||||
|
{
|
||||||
|
while (m_translation.queue.size() >= m_translation.max_size)
|
||||||
|
{
|
||||||
|
m_translation.queue.pop();
|
||||||
|
}
|
||||||
|
m_translation.queue.push(translation);
|
||||||
|
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
m_translation_queue_max_size = std::max(m_translation_queue_max_size, m_translation.queue.size());
|
||||||
|
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mouse3DController::State::append_rotation(const Vec3f& rotation)
|
||||||
|
{
|
||||||
|
while (m_rotation.queue.size() >= m_rotation.max_size)
|
||||||
|
{
|
||||||
|
m_rotation.queue.pop();
|
||||||
|
}
|
||||||
|
m_rotation.queue.push(rotation);
|
||||||
|
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
m_rotation_queue_max_size = std::max(m_rotation_queue_max_size, m_rotation.queue.size());
|
||||||
|
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
if (rotation(0) != 0.0f)
|
||||||
|
++m_mouse_wheel_counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mouse3DController::State::append_button(unsigned int id)
|
||||||
|
{
|
||||||
|
if (!m_buttons_enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_buttons.push(id);
|
||||||
|
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
m_buttons_queue_max_size = std::max(m_buttons_queue_max_size, m_buttons.size());
|
||||||
|
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Mouse3DController::State::process_mouse_wheel()
|
||||||
|
{
|
||||||
|
if (m_mouse_wheel_counter == 0)
|
||||||
|
return false;
|
||||||
|
else if (!m_rotation.queue.empty())
|
||||||
|
{
|
||||||
|
--m_mouse_wheel_counter;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_mouse_wheel_counter = 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mouse3DController::State::set_queues_max_size(size_t size)
|
||||||
|
{
|
||||||
|
if (size > 0)
|
||||||
|
{
|
||||||
|
m_translation.max_size = size;
|
||||||
|
m_rotation.max_size = size;
|
||||||
|
|
||||||
|
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
m_translation_queue_max_size = 0;
|
||||||
|
m_rotation_queue_max_size = 0;
|
||||||
|
m_buttons_queue_max_size = 0;
|
||||||
|
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Mouse3DController::State::apply(Camera& camera)
|
||||||
|
{
|
||||||
|
if (!wxGetApp().IsActive())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
if (has_translation())
|
||||||
|
{
|
||||||
|
const Vec3d& translation = m_translation.queue.front();
|
||||||
|
camera.set_target(camera.get_target() + m_translation_params.scale * (translation(0) * camera.get_dir_right() + translation(1) * camera.get_dir_forward() + translation(2) * camera.get_dir_up()));
|
||||||
|
m_translation.queue.pop();
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_rotation())
|
||||||
|
{
|
||||||
|
const Vec3f& rotation = m_rotation.queue.front();
|
||||||
|
float theta = m_rotation_params.scale * rotation(0);
|
||||||
|
float phi = m_rotation_params.scale * rotation(2);
|
||||||
|
float sign = camera.inverted_phi ? -1.0f : 1.0f;
|
||||||
|
camera.phi += sign * phi;
|
||||||
|
camera.set_theta(camera.get_theta() + theta, wxGetApp().preset_bundle->printers.get_edited_preset().printer_technology() != ptSLA);
|
||||||
|
m_rotation.queue.pop();
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_buttons_enabled && has_button())
|
||||||
|
{
|
||||||
|
unsigned int button = m_buttons.front();
|
||||||
|
switch (button)
|
||||||
|
{
|
||||||
|
case 0: { camera.update_zoom(1.0); break; }
|
||||||
|
case 1: { camera.update_zoom(-1.0); break; }
|
||||||
|
default: { break; }
|
||||||
|
}
|
||||||
|
m_buttons.pop();
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mouse3DController::Mouse3DController()
|
||||||
|
: m_initialized(false)
|
||||||
|
, m_device(nullptr)
|
||||||
|
, m_device_str("")
|
||||||
|
, m_running(false)
|
||||||
|
, m_settings_dialog(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mouse3DController::init()
|
||||||
|
{
|
||||||
|
if (m_initialized)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Initialize the hidapi library
|
||||||
|
int res = hid_init();
|
||||||
|
if (res != 0)
|
||||||
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Unable to initialize hidapi library";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mouse3DController::shutdown()
|
||||||
|
{
|
||||||
|
if (!m_initialized)
|
||||||
|
return;
|
||||||
|
|
||||||
|
stop();
|
||||||
|
disconnect_device();
|
||||||
|
|
||||||
|
// Finalize the hidapi library
|
||||||
|
hid_exit();
|
||||||
|
m_initialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Mouse3DController::apply(Camera& camera)
|
||||||
|
{
|
||||||
|
if (!m_initialized)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
|
||||||
|
// check if the user unplugged the device
|
||||||
|
if (!m_running && is_device_connected())
|
||||||
|
{
|
||||||
|
disconnect_device();
|
||||||
|
// hides the settings dialog if the user re-plug the device
|
||||||
|
m_settings_dialog = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if the user plugged the device
|
||||||
|
if (connect_device())
|
||||||
|
start();
|
||||||
|
|
||||||
|
return is_device_connected() ? m_state.apply(camera) : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mouse3DController::render_settings_dialog(unsigned int canvas_width, unsigned int canvas_height) const
|
||||||
|
{
|
||||||
|
if (!m_running || !m_settings_dialog)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ImGuiWrapper& imgui = *wxGetApp().imgui();
|
||||||
|
|
||||||
|
imgui.set_next_window_pos(0.5f * (float)canvas_width, 0.5f * (float)canvas_height, ImGuiCond_Always, 0.5f, 0.5f);
|
||||||
|
imgui.set_next_window_bg_alpha(0.5f);
|
||||||
|
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
|
||||||
|
|
||||||
|
imgui.begin(_(L("3Dconnexion settings")), ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse);
|
||||||
|
|
||||||
|
const ImVec4& color = ImGui::GetStyleColorVec4(ImGuiCol_Separator);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, color);
|
||||||
|
imgui.text(_(L("Device:")));
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
ImGui::SameLine();
|
||||||
|
imgui.text(m_device_str);
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, color);
|
||||||
|
imgui.text(_(L("Speed:")));
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
|
||||||
|
float translation_scale = (float)m_state.get_translation_scale() / State::DefaultTranslationScale;
|
||||||
|
if (ImGui::SliderFloat(_(L("Translation##1")), &translation_scale, 0.5f, 2.0f, "%.1f"))
|
||||||
|
m_state.set_translation_scale(State::DefaultTranslationScale * (double)translation_scale);
|
||||||
|
|
||||||
|
float rotation_scale = m_state.get_rotation_scale() / State::DefaultRotationScale;
|
||||||
|
if (ImGui::SliderFloat(_(L("Rotation##1")), &rotation_scale, 0.5f, 2.0f, "%.1f"))
|
||||||
|
m_state.set_rotation_scale(State::DefaultRotationScale * rotation_scale);
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, color);
|
||||||
|
imgui.text(_(L("Deadzone:")));
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
|
||||||
|
float translation_deadzone = (float)m_state.get_translation_deadzone();
|
||||||
|
if (ImGui::SliderFloat(_(L("Translation##2")), &translation_deadzone, 0.0f, (float)State::MaxTranslationDeadzone, "%.2f"))
|
||||||
|
m_state.set_translation_deadzone((double)translation_deadzone);
|
||||||
|
|
||||||
|
float rotation_deadzone = m_state.get_rotation_deadzone();
|
||||||
|
if (ImGui::SliderFloat(_(L("Rotation##2")), &rotation_deadzone, 0.0f, State::MaxRotationDeadzone, "%.2f"))
|
||||||
|
m_state.set_rotation_deadzone(rotation_deadzone);
|
||||||
|
|
||||||
|
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, color);
|
||||||
|
imgui.text("DEBUG:");
|
||||||
|
imgui.text("Vectors:");
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
Vec3f translation = m_state.get_translation().cast<float>();
|
||||||
|
Vec3f rotation = m_state.get_rotation();
|
||||||
|
ImGui::InputFloat3("Translation##3", translation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly);
|
||||||
|
ImGui::InputFloat3("Rotation##3", rotation.data(), "%.3f", ImGuiInputTextFlags_ReadOnly);
|
||||||
|
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, color);
|
||||||
|
imgui.text("Queue size:");
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
|
||||||
|
int translation_size[2] = { (int)m_state.get_translation_queue_size(), (int)m_state.get_translation_queue_max_size() };
|
||||||
|
int rotation_size[2] = { (int)m_state.get_rotation_queue_size(), (int)m_state.get_rotation_queue_max_size() };
|
||||||
|
int buttons_size[2] = { (int)m_state.get_buttons_queue_size(), (int)m_state.get_buttons_queue_max_size() };
|
||||||
|
|
||||||
|
ImGui::InputInt2("Translation##4", translation_size, ImGuiInputTextFlags_ReadOnly);
|
||||||
|
ImGui::InputInt2("Rotation##4", rotation_size, ImGuiInputTextFlags_ReadOnly);
|
||||||
|
ImGui::InputInt2("Buttons", buttons_size, ImGuiInputTextFlags_ReadOnly);
|
||||||
|
|
||||||
|
int queue_size = (int)m_state.get_queues_max_size();
|
||||||
|
if (ImGui::InputInt("Max size", &queue_size, 1, 1, ImGuiInputTextFlags_ReadOnly))
|
||||||
|
{
|
||||||
|
if (queue_size > 0)
|
||||||
|
m_state.set_queues_max_size(queue_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, color);
|
||||||
|
imgui.text("Camera:");
|
||||||
|
ImGui::PopStyleColor();
|
||||||
|
Vec3f target = wxGetApp().plater()->get_camera().get_target().cast<float>();
|
||||||
|
ImGui::InputFloat3("Target", target.data(), "%.3f", ImGuiInputTextFlags_ReadOnly);
|
||||||
|
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
|
||||||
|
imgui.end();
|
||||||
|
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Mouse3DController::connect_device()
|
||||||
|
{
|
||||||
|
if (is_device_connected())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Enumerates devices
|
||||||
|
hid_device_info* devices = hid_enumerate(0, 0);
|
||||||
|
if (devices == nullptr)
|
||||||
|
{
|
||||||
|
BOOST_LOG_TRIVIAL(error) << "Unable to enumerate HID devices";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Searches for 1st connected 3Dconnexion device
|
||||||
|
struct DeviceData
|
||||||
|
{
|
||||||
|
std::string path;
|
||||||
|
unsigned short usage_page;
|
||||||
|
unsigned short usage;
|
||||||
|
|
||||||
|
DeviceData()
|
||||||
|
: path(""), usage_page(0), usage(0)
|
||||||
|
{}
|
||||||
|
DeviceData(const std::string& path, unsigned short usage_page, unsigned short usage)
|
||||||
|
: path(path), usage_page(usage_page), usage(usage)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool has_valid_usage() const { return (usage_page == 1) && (usage == 8); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
hid_device_info* cur = devices;
|
||||||
|
std::cout << std::endl << "======================================================================================================================================" << std::endl;
|
||||||
|
std::cout << "Detected devices:" << std::endl;
|
||||||
|
while (cur != nullptr)
|
||||||
|
{
|
||||||
|
std::cout << "\"";
|
||||||
|
std::wcout << ((cur->manufacturer_string != nullptr) ? cur->manufacturer_string : L"Unknown");
|
||||||
|
std::cout << "/";
|
||||||
|
std::wcout << ((cur->product_string != nullptr) ? cur->product_string : L"Unknown");
|
||||||
|
std::cout << "\" code: " << cur->vendor_id << "/" << cur->product_id << " (" << std::hex << cur->vendor_id << "/" << cur->product_id << std::dec << ")";
|
||||||
|
std::cout << " serial number: '";
|
||||||
|
std::wcout << ((cur->serial_number != nullptr) ? cur->serial_number : L"Unknown");
|
||||||
|
std::cout << "' usage page: " << cur->usage_page << " usage: " << cur->usage << " interface number: " << cur->interface_number << std::endl;
|
||||||
|
|
||||||
|
cur = cur->next;
|
||||||
|
}
|
||||||
|
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
|
||||||
|
// When using 3Dconnexion universal receiver, multiple devices are detected sharing the same vendor_id and product_id.
|
||||||
|
// To choose from them the right one we use:
|
||||||
|
// On Windows and Mac: usage_page == 1 and usage == 8
|
||||||
|
// On Linux: as usage_page and usage are not defined (see hidapi.h) we try all detected devices until one is succesfully open
|
||||||
|
// When only a single device is detected, as for wired connections, vendor_id and product_id are enough
|
||||||
|
|
||||||
|
// First we count all the valid devices from the enumerated list,
|
||||||
|
|
||||||
|
hid_device_info* current = devices;
|
||||||
|
typedef std::pair<unsigned short, unsigned short> DeviceIds;
|
||||||
|
typedef std::vector<DeviceData> DeviceDataList;
|
||||||
|
typedef std::map<DeviceIds, DeviceDataList> DetectedDevices;
|
||||||
|
DetectedDevices detected_devices;
|
||||||
|
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
std::cout << std::endl << "Detected 3D connexion devices:" << std::endl;
|
||||||
|
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
while (current != nullptr)
|
||||||
|
{
|
||||||
|
unsigned short vendor_id = 0;
|
||||||
|
unsigned short product_id = 0;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < _3DCONNEXION_VENDORS.size(); ++i)
|
||||||
|
{
|
||||||
|
if (_3DCONNEXION_VENDORS[i] == current->vendor_id)
|
||||||
|
{
|
||||||
|
vendor_id = current->vendor_id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vendor_id != 0)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < _3DCONNEXION_DEVICES.size(); ++i)
|
||||||
|
{
|
||||||
|
if (_3DCONNEXION_DEVICES[i] == current->product_id)
|
||||||
|
{
|
||||||
|
product_id = current->product_id;
|
||||||
|
DeviceIds detected_device(vendor_id, product_id);
|
||||||
|
DetectedDevices::iterator it = detected_devices.find(detected_device);
|
||||||
|
if (it == detected_devices.end())
|
||||||
|
it = detected_devices.insert(DetectedDevices::value_type(detected_device, DeviceDataList())).first;
|
||||||
|
|
||||||
|
it->second.emplace_back(current->path, current->usage_page, current->usage);
|
||||||
|
|
||||||
|
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
std::wcout << "\"" << ((current->manufacturer_string != nullptr) ? current->manufacturer_string : L"Unknown");
|
||||||
|
std::cout << "/";
|
||||||
|
std::wcout << ((current->product_string != nullptr) ? current->product_string : L"Unknown");
|
||||||
|
std::cout << "\" code: " << current->vendor_id << "/" << current->product_id << " (" << std::hex << current->vendor_id << "/" << current->product_id << std::dec << ")";
|
||||||
|
std::cout << " serial number: '";
|
||||||
|
std::wcout << ((current->serial_number != nullptr) ? current->serial_number : L"Unknown");
|
||||||
|
std::cout << "' usage page: " << current->usage_page << " usage: " << current->usage << std::endl;
|
||||||
|
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
current = current->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free enumerated devices
|
||||||
|
hid_free_enumeration(devices);
|
||||||
|
|
||||||
|
if (detected_devices.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::string path = "";
|
||||||
|
unsigned short vendor_id = 0;
|
||||||
|
unsigned short product_id = 0;
|
||||||
|
|
||||||
|
// Then we'll decide the choosing logic to apply in dependence of the device count and operating system
|
||||||
|
|
||||||
|
for (const DetectedDevices::value_type& device : detected_devices)
|
||||||
|
{
|
||||||
|
if (device.second.size() == 1)
|
||||||
|
{
|
||||||
|
#ifdef __linux__
|
||||||
|
hid_device* test_device = hid_open(device.first.first, device.first.second, nullptr);
|
||||||
|
if (test_device != nullptr)
|
||||||
|
{
|
||||||
|
hid_close(test_device);
|
||||||
|
#else
|
||||||
|
if (device.second.front().has_valid_usage())
|
||||||
|
{
|
||||||
|
#endif // __linux__
|
||||||
|
vendor_id = device.first.first;
|
||||||
|
product_id = device.first.second;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
std::cout << std::endl;
|
||||||
|
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
for (const DeviceData& data : device.second)
|
||||||
|
{
|
||||||
|
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
std::cout << "Test device: " << std::hex << device.first.first << std::dec << "/" << std::hex << device.first.second << std::dec << " \"" << data.path << "\"";
|
||||||
|
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
#ifdef __linux__
|
||||||
|
hid_device* test_device = hid_open_path(data.path.c_str());
|
||||||
|
if (test_device != nullptr)
|
||||||
|
{
|
||||||
|
path = data.path;
|
||||||
|
vendor_id = device.first.first;
|
||||||
|
product_id = device.first.second;
|
||||||
|
found = true;
|
||||||
|
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
std::cout << "-> PASSED" << std::endl;
|
||||||
|
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
hid_close(test_device);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (data.has_valid_usage())
|
||||||
|
{
|
||||||
|
path = data.path;
|
||||||
|
vendor_id = device.first.first;
|
||||||
|
product_id = device.first.second;
|
||||||
|
found = true;
|
||||||
|
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
std::cout << "-> PASSED" << std::endl;
|
||||||
|
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif // __linux__
|
||||||
|
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
else
|
||||||
|
std::cout << "-> NOT PASSED" << std::endl;
|
||||||
|
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.empty())
|
||||||
|
{
|
||||||
|
if ((vendor_id != 0) && (product_id != 0))
|
||||||
|
{
|
||||||
|
// Open the 3Dconnexion device using vendor_id and product_id
|
||||||
|
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
std::cout << std::endl << "Opening device: " << std::hex << vendor_id << std::dec << "/" << std::hex << product_id << std::dec << " using hid_open()" << std::endl;
|
||||||
|
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
m_device = hid_open(vendor_id, product_id, nullptr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Open the 3Dconnexion device using the device path
|
||||||
|
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
std::cout << std::endl << "Opening device: " << std::hex << vendor_id << std::dec << "/" << std::hex << product_id << std::dec << "\"" << path << "\" using hid_open_path()" << std::endl;
|
||||||
|
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
m_device = hid_open_path(path.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_device != nullptr)
|
||||||
|
{
|
||||||
|
std::vector<wchar_t> manufacturer(1024, 0);
|
||||||
|
hid_get_manufacturer_string(m_device, manufacturer.data(), 1024);
|
||||||
|
m_device_str = boost::nowide::narrow(manufacturer.data());
|
||||||
|
|
||||||
|
std::vector<wchar_t> product(1024, 0);
|
||||||
|
hid_get_product_string(m_device, product.data(), 1024);
|
||||||
|
m_device_str += "/" + boost::nowide::narrow(product.data());
|
||||||
|
|
||||||
|
BOOST_LOG_TRIVIAL(info) << "Connected device: " << m_device_str;
|
||||||
|
|
||||||
|
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
std::cout << std::endl << "Connected device:" << std::endl;
|
||||||
|
std::cout << "Manufacturer/product: " << m_device_str << std::endl;
|
||||||
|
std::cout << "Manufacturer id.....: " << vendor_id << " (" << std::hex << vendor_id << std::dec << ")" << std::endl;
|
||||||
|
std::cout << "Product id..........: " << product_id << " (" << std::hex << product_id << std::dec << ")" << std::endl;
|
||||||
|
std::cout << "Path................: '" << path << "'" << std::endl;
|
||||||
|
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
|
||||||
|
// get device parameters from the config, if present
|
||||||
|
double translation_speed = 1.0;
|
||||||
|
float rotation_speed = 1.0;
|
||||||
|
double translation_deadzone = State::DefaultTranslationDeadzone;
|
||||||
|
float rotation_deadzone = State::DefaultRotationDeadzone;
|
||||||
|
wxGetApp().app_config->get_mouse_device_translation_speed(m_device_str, translation_speed);
|
||||||
|
wxGetApp().app_config->get_mouse_device_translation_deadzone(m_device_str, translation_deadzone);
|
||||||
|
wxGetApp().app_config->get_mouse_device_rotation_speed(m_device_str, rotation_speed);
|
||||||
|
wxGetApp().app_config->get_mouse_device_rotation_deadzone(m_device_str, rotation_deadzone);
|
||||||
|
// clamp to valid values
|
||||||
|
m_state.set_translation_scale(State::DefaultTranslationScale * std::max(0.5, std::min(2.0, translation_speed)));
|
||||||
|
m_state.set_translation_deadzone(std::max(0.0, std::min(State::MaxTranslationDeadzone, translation_deadzone)));
|
||||||
|
m_state.set_rotation_scale(State::DefaultRotationScale * std::max(0.5f, std::min(2.0f, rotation_speed)));
|
||||||
|
m_state.set_rotation_deadzone(std::max(0.0f, std::min(State::MaxRotationDeadzone, rotation_deadzone)));
|
||||||
|
}
|
||||||
|
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cout << std::endl << "Unable to connect to device:" << std::endl;
|
||||||
|
std::cout << "Manufacturer/product: " << m_device_str << std::endl;
|
||||||
|
std::cout << "Manufacturer id.....: " << vendor_id << " (" << std::hex << vendor_id << std::dec << ")" << std::endl;
|
||||||
|
std::cout << "Product id..........: " << product_id << " (" << std::hex << product_id << std::dec << ")" << std::endl;
|
||||||
|
std::cout << "Path................: '" << path << "'" << std::endl;
|
||||||
|
}
|
||||||
|
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
|
||||||
|
return (m_device != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mouse3DController::disconnect_device()
|
||||||
|
{
|
||||||
|
if (!is_device_connected())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Stop the secondary thread, if running
|
||||||
|
if (m_thread.joinable())
|
||||||
|
m_thread.join();
|
||||||
|
|
||||||
|
// Store current device parameters into the config
|
||||||
|
wxGetApp().app_config->set_mouse_device(m_device_str, m_state.get_translation_scale() / State::DefaultTranslationScale, m_state.get_translation_deadzone(),
|
||||||
|
m_state.get_rotation_scale() / State::DefaultRotationScale, m_state.get_rotation_deadzone());
|
||||||
|
wxGetApp().app_config->save();
|
||||||
|
|
||||||
|
// Close the 3Dconnexion device
|
||||||
|
hid_close(m_device);
|
||||||
|
m_device = nullptr;
|
||||||
|
|
||||||
|
BOOST_LOG_TRIVIAL(info) << "Disconnected device: " << m_device_str;
|
||||||
|
|
||||||
|
m_device_str = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mouse3DController::start()
|
||||||
|
{
|
||||||
|
if (!is_device_connected() || m_running)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_thread = std::thread(&Mouse3DController::run, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mouse3DController::run()
|
||||||
|
{
|
||||||
|
m_running = true;
|
||||||
|
while (m_running)
|
||||||
|
{
|
||||||
|
collect_input();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mouse3DController::collect_input()
|
||||||
|
{
|
||||||
|
DataPacket packet = { 0 };
|
||||||
|
int res = hid_read_timeout(m_device, packet.data(), packet.size(), 100);
|
||||||
|
if (res < 0)
|
||||||
|
{
|
||||||
|
// An error occourred (device detached from pc ?)
|
||||||
|
stop();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wxGetApp().IsActive())
|
||||||
|
return;
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock(m_mutex);
|
||||||
|
|
||||||
|
bool updated = false;
|
||||||
|
|
||||||
|
if (res == 7)
|
||||||
|
updated = handle_packet(packet);
|
||||||
|
else if (res == 13)
|
||||||
|
updated = handle_wireless_packet(packet);
|
||||||
|
else if ((res == 3) && (packet[0] == 3))
|
||||||
|
// On Mac button packets can be 3 bytes long
|
||||||
|
updated = handle_packet(packet);
|
||||||
|
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
else if (res > 0)
|
||||||
|
std::cout << "Got unknown data packet of length: " << res << ", code:" << (int)packet[0] << std::endl;
|
||||||
|
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
|
||||||
|
if (updated)
|
||||||
|
// ask for an idle event to update 3D scene
|
||||||
|
wxWakeUpIdle();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Mouse3DController::handle_packet(const DataPacket& packet)
|
||||||
|
{
|
||||||
|
switch (packet[0])
|
||||||
|
{
|
||||||
|
case 1: // Translation
|
||||||
|
{
|
||||||
|
if (handle_packet_translation(packet))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: // Rotation
|
||||||
|
{
|
||||||
|
if (handle_packet_rotation(packet, 1))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3: // Button
|
||||||
|
{
|
||||||
|
if (handle_packet_button(packet, packet.size() - 1))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 23: // Battery charge
|
||||||
|
{
|
||||||
|
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
std::cout << m_device_str << " - battery level: " << (int)packet[1] << " percent" << std::endl;
|
||||||
|
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
std::cout << "Got unknown data packet of code: " << (int)packet[0] << std::endl;
|
||||||
|
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Mouse3DController::handle_wireless_packet(const DataPacket& packet)
|
||||||
|
{
|
||||||
|
switch (packet[0])
|
||||||
|
{
|
||||||
|
case 1: // Translation + Rotation
|
||||||
|
{
|
||||||
|
bool updated = handle_packet_translation(packet);
|
||||||
|
updated |= handle_packet_rotation(packet, 7);
|
||||||
|
|
||||||
|
if (updated)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3: // Button
|
||||||
|
{
|
||||||
|
if (handle_packet_button(packet, 12))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 23: // Battery charge
|
||||||
|
{
|
||||||
|
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
std::cout << m_device_str << " - battery level: " << (int)packet[1] << " percent" << std::endl;
|
||||||
|
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
std::cout << "Got unknown data packet of code: " << (int)packet[0] << std::endl;
|
||||||
|
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
double convert_input(unsigned char first, unsigned char second, double deadzone)
|
||||||
|
{
|
||||||
|
short value = first | second << 8;
|
||||||
|
double ret = (double)value / 350.0;
|
||||||
|
return (std::abs(ret) > deadzone) ? ret : 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Mouse3DController::handle_packet_translation(const DataPacket& packet)
|
||||||
|
{
|
||||||
|
double deadzone = m_state.get_translation_deadzone();
|
||||||
|
Vec3d translation(-convert_input(packet[1], packet[2], deadzone),
|
||||||
|
convert_input(packet[3], packet[4], deadzone),
|
||||||
|
convert_input(packet[5], packet[6], deadzone));
|
||||||
|
|
||||||
|
if (!translation.isApprox(Vec3d::Zero()))
|
||||||
|
{
|
||||||
|
m_state.append_translation(translation);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Mouse3DController::handle_packet_rotation(const DataPacket& packet, unsigned int first_byte)
|
||||||
|
{
|
||||||
|
double deadzone = (double)m_state.get_rotation_deadzone();
|
||||||
|
Vec3f rotation(-(float)convert_input(packet[first_byte + 0], packet[first_byte + 1], deadzone),
|
||||||
|
(float)convert_input(packet[first_byte + 2], packet[first_byte + 3], deadzone),
|
||||||
|
-(float)convert_input(packet[first_byte + 4], packet[first_byte + 5], deadzone));
|
||||||
|
|
||||||
|
if (!rotation.isApprox(Vec3f::Zero()))
|
||||||
|
{
|
||||||
|
m_state.append_rotation(rotation);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Mouse3DController::handle_packet_button(const DataPacket& packet, unsigned int packet_size)
|
||||||
|
{
|
||||||
|
unsigned int data = 0;
|
||||||
|
for (unsigned int i = 1; i < packet_size; ++i)
|
||||||
|
{
|
||||||
|
data |= packet[i] << 8 * (i - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::bitset<32> data_bits{ data };
|
||||||
|
for (size_t i = 0; i < data_bits.size(); ++i)
|
||||||
|
{
|
||||||
|
if (data_bits.test(i))
|
||||||
|
{
|
||||||
|
m_state.append_button((unsigned int)i);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace GUI
|
||||||
|
} // namespace Slic3r
|
173
src/slic3r/GUI/Mouse3DController.hpp
Normal file
173
src/slic3r/GUI/Mouse3DController.hpp
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
#ifndef slic3r_Mouse3DController_hpp_
|
||||||
|
#define slic3r_Mouse3DController_hpp_
|
||||||
|
|
||||||
|
// Enabled debug output to console and extended imgui dialog
|
||||||
|
#define ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT 1
|
||||||
|
|
||||||
|
#include "libslic3r/Point.hpp"
|
||||||
|
|
||||||
|
#include "hidapi.h"
|
||||||
|
|
||||||
|
#include <queue>
|
||||||
|
#include <thread>
|
||||||
|
#include <mutex>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Slic3r {
|
||||||
|
namespace GUI {
|
||||||
|
|
||||||
|
struct Camera;
|
||||||
|
|
||||||
|
class Mouse3DController
|
||||||
|
{
|
||||||
|
class State
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const double DefaultTranslationScale;
|
||||||
|
static const double MaxTranslationDeadzone;
|
||||||
|
static const double DefaultTranslationDeadzone;
|
||||||
|
static const float DefaultRotationScale;
|
||||||
|
static const float MaxRotationDeadzone;
|
||||||
|
static const float DefaultRotationDeadzone;
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <typename Number>
|
||||||
|
struct CustomParameters
|
||||||
|
{
|
||||||
|
Number scale;
|
||||||
|
Number deadzone;
|
||||||
|
|
||||||
|
CustomParameters(Number scale, Number deadzone) : scale(scale), deadzone(deadzone) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
struct InputQueue
|
||||||
|
{
|
||||||
|
size_t max_size;
|
||||||
|
std::queue<T> queue;
|
||||||
|
|
||||||
|
// The default value of 5 for max_size seems to work fine on all platforms
|
||||||
|
// The effects of changing this value can be tested by setting ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT to 1
|
||||||
|
// and playing with the imgui dialog which shows by pressing CTRL+M
|
||||||
|
InputQueue() : max_size(5) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
InputQueue<Vec3d> m_translation;
|
||||||
|
InputQueue<Vec3f> m_rotation;
|
||||||
|
std::queue<unsigned int> m_buttons;
|
||||||
|
|
||||||
|
bool m_buttons_enabled;
|
||||||
|
|
||||||
|
CustomParameters<double> m_translation_params;
|
||||||
|
CustomParameters<float> m_rotation_params;
|
||||||
|
|
||||||
|
// When the 3Dconnexion driver is running the system gets, by default, mouse wheel events when rotations around the X axis are detected.
|
||||||
|
// We want to filter these out because we are getting the data directly from the device, bypassing the driver, and those mouse wheel events interfere
|
||||||
|
// by triggering unwanted zoom in/out of the scene
|
||||||
|
// The following variable is used to count the potential mouse wheel events triggered and is updated by:
|
||||||
|
// Mouse3DController::collect_input() through the call to the append_rotation() method
|
||||||
|
// GLCanvas3D::on_mouse_wheel() through the call to the process_mouse_wheel() method
|
||||||
|
// GLCanvas3D::on_idle() through the call to the apply() method
|
||||||
|
unsigned int m_mouse_wheel_counter;
|
||||||
|
|
||||||
|
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
size_t m_translation_queue_max_size;
|
||||||
|
size_t m_rotation_queue_max_size;
|
||||||
|
size_t m_buttons_queue_max_size;
|
||||||
|
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
|
||||||
|
public:
|
||||||
|
State();
|
||||||
|
|
||||||
|
void append_translation(const Vec3d& translation);
|
||||||
|
void append_rotation(const Vec3f& rotation);
|
||||||
|
void append_button(unsigned int id);
|
||||||
|
|
||||||
|
bool has_translation() const { return !m_translation.queue.empty(); }
|
||||||
|
bool has_rotation() const { return !m_rotation.queue.empty(); }
|
||||||
|
bool has_button() const { return !m_buttons.empty(); }
|
||||||
|
|
||||||
|
bool process_mouse_wheel();
|
||||||
|
|
||||||
|
double get_translation_scale() const { return m_translation_params.scale; }
|
||||||
|
void set_translation_scale(double scale) { m_translation_params.scale = scale; }
|
||||||
|
|
||||||
|
float get_rotation_scale() const { return m_rotation_params.scale; }
|
||||||
|
void set_rotation_scale(float scale) { m_rotation_params.scale = scale; }
|
||||||
|
|
||||||
|
double get_translation_deadzone() const { return m_translation_params.deadzone; }
|
||||||
|
void set_translation_deadzone(double deadzone) { m_translation_params.deadzone = deadzone; }
|
||||||
|
|
||||||
|
float get_rotation_deadzone() const { return m_rotation_params.deadzone; }
|
||||||
|
void set_rotation_deadzone(float deadzone) { m_rotation_params.deadzone = deadzone; }
|
||||||
|
|
||||||
|
#if ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
Vec3d get_translation() const { return has_translation() ? m_translation.queue.front() : Vec3d::Zero(); }
|
||||||
|
Vec3f get_rotation() const { return has_rotation() ? m_rotation.queue.front() : Vec3f::Zero(); }
|
||||||
|
unsigned int get_button() const { return has_button() ? m_buttons.front() : 0; }
|
||||||
|
|
||||||
|
unsigned int get_translation_queue_size() const { return (unsigned int)m_translation.queue.size(); }
|
||||||
|
unsigned int get_rotation_queue_size() const { return (unsigned int)m_rotation.queue.size(); }
|
||||||
|
unsigned int get_buttons_queue_size() const { return (unsigned int)m_buttons.size(); }
|
||||||
|
|
||||||
|
size_t get_translation_queue_max_size() const { return m_translation_queue_max_size; }
|
||||||
|
size_t get_rotation_queue_max_size() const { return m_rotation_queue_max_size; }
|
||||||
|
size_t get_buttons_queue_max_size() const { return m_buttons_queue_max_size; }
|
||||||
|
#endif // ENABLE_3DCONNEXION_DEVICES_DEBUG_OUTPUT
|
||||||
|
|
||||||
|
size_t get_queues_max_size() const { return m_translation.max_size; }
|
||||||
|
void set_queues_max_size(size_t size);
|
||||||
|
|
||||||
|
// return true if any change to the camera took place
|
||||||
|
bool apply(Camera& camera);
|
||||||
|
};
|
||||||
|
|
||||||
|
bool m_initialized;
|
||||||
|
mutable State m_state;
|
||||||
|
std::mutex m_mutex;
|
||||||
|
std::thread m_thread;
|
||||||
|
hid_device* m_device;
|
||||||
|
std::string m_device_str;
|
||||||
|
bool m_running;
|
||||||
|
bool m_settings_dialog;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Mouse3DController();
|
||||||
|
|
||||||
|
void init();
|
||||||
|
void shutdown();
|
||||||
|
|
||||||
|
bool is_device_connected() const { return m_device != nullptr; }
|
||||||
|
bool is_running() const { return m_running; }
|
||||||
|
|
||||||
|
bool process_mouse_wheel() { std::lock_guard<std::mutex> lock(m_mutex); return m_state.process_mouse_wheel(); }
|
||||||
|
|
||||||
|
bool apply(Camera& camera);
|
||||||
|
|
||||||
|
bool is_settings_dialog_shown() const { return m_settings_dialog; }
|
||||||
|
void show_settings_dialog(bool show) { m_settings_dialog = show && is_running(); }
|
||||||
|
void render_settings_dialog(unsigned int canvas_width, unsigned int canvas_height) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool connect_device();
|
||||||
|
void disconnect_device();
|
||||||
|
void start();
|
||||||
|
void stop() { m_running = false; }
|
||||||
|
|
||||||
|
// secondary thread methods
|
||||||
|
void run();
|
||||||
|
void collect_input();
|
||||||
|
|
||||||
|
typedef std::array<unsigned char, 13> DataPacket;
|
||||||
|
bool handle_packet(const DataPacket& packet);
|
||||||
|
bool handle_wireless_packet(const DataPacket& packet);
|
||||||
|
bool handle_packet_translation(const DataPacket& packet);
|
||||||
|
bool handle_packet_rotation(const DataPacket& packet, unsigned int first_byte);
|
||||||
|
bool handle_packet_button(const DataPacket& packet, unsigned int packet_size);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace GUI
|
||||||
|
} // namespace Slic3r
|
||||||
|
|
||||||
|
#endif // slic3r_Mouse3DController_hpp_
|
||||||
|
|
|
@ -65,6 +65,7 @@
|
||||||
#include "GUI_Preview.hpp"
|
#include "GUI_Preview.hpp"
|
||||||
#include "3DBed.hpp"
|
#include "3DBed.hpp"
|
||||||
#include "Camera.hpp"
|
#include "Camera.hpp"
|
||||||
|
#include "Mouse3DController.hpp"
|
||||||
#include "Tab.hpp"
|
#include "Tab.hpp"
|
||||||
#include "PresetBundle.hpp"
|
#include "PresetBundle.hpp"
|
||||||
#include "BackgroundSlicingProcess.hpp"
|
#include "BackgroundSlicingProcess.hpp"
|
||||||
|
@ -1387,9 +1388,6 @@ struct Plater::priv
|
||||||
Slic3r::Model model;
|
Slic3r::Model model;
|
||||||
PrinterTechnology printer_technology = ptFFF;
|
PrinterTechnology printer_technology = ptFFF;
|
||||||
Slic3r::GCodePreviewData gcode_preview_data;
|
Slic3r::GCodePreviewData gcode_preview_data;
|
||||||
#if ENABLE_THUMBNAIL_GENERATOR
|
|
||||||
std::vector<Slic3r::ThumbnailData> thumbnail_data;
|
|
||||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
|
||||||
|
|
||||||
// GUI elements
|
// GUI elements
|
||||||
wxSizer* panel_sizer{ nullptr };
|
wxSizer* panel_sizer{ nullptr };
|
||||||
|
@ -1398,6 +1396,7 @@ struct Plater::priv
|
||||||
Sidebar *sidebar;
|
Sidebar *sidebar;
|
||||||
Bed3D bed;
|
Bed3D bed;
|
||||||
Camera camera;
|
Camera camera;
|
||||||
|
Mouse3DController mouse3d_controller;
|
||||||
View3D* view3D;
|
View3D* view3D;
|
||||||
GLToolbar view_toolbar;
|
GLToolbar view_toolbar;
|
||||||
Preview *preview;
|
Preview *preview;
|
||||||
|
@ -1946,6 +1945,7 @@ struct Plater::priv
|
||||||
|
|
||||||
#if ENABLE_THUMBNAIL_GENERATOR
|
#if ENABLE_THUMBNAIL_GENERATOR
|
||||||
void generate_thumbnail(ThumbnailData& data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool transparent_background);
|
void generate_thumbnail(ThumbnailData& data, unsigned int w, unsigned int h, bool printable_only, bool parts_only, bool transparent_background);
|
||||||
|
void generate_thumbnails(ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool transparent_background);
|
||||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||||
|
|
||||||
void msw_rescale_object_menu();
|
void msw_rescale_object_menu();
|
||||||
|
@ -2016,7 +2016,15 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||||
background_process.set_sla_print(&sla_print);
|
background_process.set_sla_print(&sla_print);
|
||||||
background_process.set_gcode_preview_data(&gcode_preview_data);
|
background_process.set_gcode_preview_data(&gcode_preview_data);
|
||||||
#if ENABLE_THUMBNAIL_GENERATOR
|
#if ENABLE_THUMBNAIL_GENERATOR
|
||||||
background_process.set_thumbnail_data(&thumbnail_data);
|
background_process.set_thumbnail_cb([this](ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool transparent_background)
|
||||||
|
{
|
||||||
|
std::packaged_task<void(ThumbnailsList&, const Vec2ds&, bool, bool, bool)> task([this](ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool transparent_background) {
|
||||||
|
generate_thumbnails(thumbnails, sizes, printable_only, parts_only, transparent_background);
|
||||||
|
});
|
||||||
|
std::future<void> result = task.get_future();
|
||||||
|
wxTheApp->CallAfter([&]() { task(thumbnails, sizes, printable_only, parts_only, transparent_background); });
|
||||||
|
result.wait();
|
||||||
|
});
|
||||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||||
background_process.set_slicing_completed_event(EVT_SLICING_COMPLETED);
|
background_process.set_slicing_completed_event(EVT_SLICING_COMPLETED);
|
||||||
background_process.set_finished_event(EVT_PROCESS_COMPLETED);
|
background_process.set_finished_event(EVT_PROCESS_COMPLETED);
|
||||||
|
@ -2087,6 +2095,11 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||||
view3D_canvas->Bind(EVT_GLCANVAS_RESETGIZMOS, [this](SimpleEvent&) { reset_all_gizmos(); });
|
view3D_canvas->Bind(EVT_GLCANVAS_RESETGIZMOS, [this](SimpleEvent&) { reset_all_gizmos(); });
|
||||||
view3D_canvas->Bind(EVT_GLCANVAS_UNDO, [this](SimpleEvent&) { this->undo(); });
|
view3D_canvas->Bind(EVT_GLCANVAS_UNDO, [this](SimpleEvent&) { this->undo(); });
|
||||||
view3D_canvas->Bind(EVT_GLCANVAS_REDO, [this](SimpleEvent&) { this->redo(); });
|
view3D_canvas->Bind(EVT_GLCANVAS_REDO, [this](SimpleEvent&) { this->redo(); });
|
||||||
|
#if ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
view3D_canvas->Bind(EVT_GLCANVAS_RESET_LAYER_HEIGHT_PROFILE, [this](SimpleEvent&) { this->view3D->get_canvas3d()->reset_layer_height_profile(); });
|
||||||
|
view3D_canvas->Bind(EVT_GLCANVAS_ADAPTIVE_LAYER_HEIGHT_PROFILE, [this](Event<float>& evt) { this->view3D->get_canvas3d()->adaptive_layer_height_profile(evt.data); });
|
||||||
|
view3D_canvas->Bind(EVT_GLCANVAS_SMOOTH_LAYER_HEIGHT_PROFILE, [this](HeightProfileSmoothEvent& evt) { this->view3D->get_canvas3d()->smooth_layer_height_profile(evt.data); });
|
||||||
|
#endif // ENABLE_ADAPTIVE_LAYER_HEIGHT_PROFILE
|
||||||
|
|
||||||
// 3DScene/Toolbar:
|
// 3DScene/Toolbar:
|
||||||
view3D_canvas->Bind(EVT_GLTOOLBAR_ADD, &priv::on_action_add, this);
|
view3D_canvas->Bind(EVT_GLTOOLBAR_ADD, &priv::on_action_add, this);
|
||||||
|
@ -2136,12 +2149,16 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||||
// updates camera type from .ini file
|
// updates camera type from .ini file
|
||||||
camera.set_type(get_config("use_perspective_camera"));
|
camera.set_type(get_config("use_perspective_camera"));
|
||||||
|
|
||||||
|
mouse3d_controller.init();
|
||||||
|
|
||||||
// Initialize the Undo / Redo stack with a first snapshot.
|
// Initialize the Undo / Redo stack with a first snapshot.
|
||||||
this->take_snapshot(_(L("New Project")));
|
this->take_snapshot(_(L("New Project")));
|
||||||
}
|
}
|
||||||
|
|
||||||
Plater::priv::~priv()
|
Plater::priv::~priv()
|
||||||
{
|
{
|
||||||
|
mouse3d_controller.shutdown();
|
||||||
|
|
||||||
if (config != nullptr)
|
if (config != nullptr)
|
||||||
delete config;
|
delete config;
|
||||||
}
|
}
|
||||||
|
@ -3062,37 +3079,6 @@ bool Plater::priv::restart_background_process(unsigned int state)
|
||||||
( ((state & UPDATE_BACKGROUND_PROCESS_FORCE_RESTART) != 0 && ! this->background_process.finished()) ||
|
( ((state & UPDATE_BACKGROUND_PROCESS_FORCE_RESTART) != 0 && ! this->background_process.finished()) ||
|
||||||
(state & UPDATE_BACKGROUND_PROCESS_FORCE_EXPORT) != 0 ||
|
(state & UPDATE_BACKGROUND_PROCESS_FORCE_EXPORT) != 0 ||
|
||||||
(state & UPDATE_BACKGROUND_PROCESS_RESTART) != 0 ) ) {
|
(state & UPDATE_BACKGROUND_PROCESS_RESTART) != 0 ) ) {
|
||||||
#if ENABLE_THUMBNAIL_GENERATOR
|
|
||||||
if (((state & UPDATE_BACKGROUND_PROCESS_FORCE_EXPORT) == 0) &&
|
|
||||||
(this->background_process.state() != BackgroundSlicingProcess::STATE_RUNNING))
|
|
||||||
{
|
|
||||||
// update thumbnail data
|
|
||||||
const std::vector<Vec2d> &thumbnail_sizes = this->background_process.current_print()->full_print_config().option<ConfigOptionPoints>("thumbnails")->values;
|
|
||||||
if (this->printer_technology == ptFFF)
|
|
||||||
{
|
|
||||||
// for ptFFF we need to generate the thumbnails before the export of gcode starts
|
|
||||||
this->thumbnail_data.clear();
|
|
||||||
for (const Vec2d &sized : thumbnail_sizes)
|
|
||||||
{
|
|
||||||
this->thumbnail_data.push_back(ThumbnailData());
|
|
||||||
Point size(sized); // round to ints
|
|
||||||
generate_thumbnail(this->thumbnail_data.back(), size.x(), size.y(), true, true, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (this->printer_technology == ptSLA)
|
|
||||||
{
|
|
||||||
// for ptSLA generate thumbnails without supports and pad (not yet calculated)
|
|
||||||
// to render also supports and pad see on_slicing_update()
|
|
||||||
this->thumbnail_data.clear();
|
|
||||||
for (const Vec2d &sized : thumbnail_sizes)
|
|
||||||
{
|
|
||||||
this->thumbnail_data.push_back(ThumbnailData());
|
|
||||||
Point size(sized); // round to ints
|
|
||||||
generate_thumbnail(this->thumbnail_data.back(), size.x(), size.y(), true, true, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
|
||||||
// The print is valid and it can be started.
|
// The print is valid and it can be started.
|
||||||
if (this->background_process.start()) {
|
if (this->background_process.start()) {
|
||||||
this->statusbar()->set_cancel_callback([this]() {
|
this->statusbar()->set_cancel_callback([this]() {
|
||||||
|
@ -3339,6 +3325,7 @@ void Plater::priv::set_current_panel(wxPanel* panel)
|
||||||
} else
|
} else
|
||||||
view3D->reload_scene(true);
|
view3D->reload_scene(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably)
|
// sets the canvas as dirty to force a render at the 1st idle event (wxWidgets IsShownOnScreen() is buggy and cannot be used reliably)
|
||||||
view3D->set_as_dirty();
|
view3D->set_as_dirty();
|
||||||
view_toolbar.select_item("3D");
|
view_toolbar.select_item("3D");
|
||||||
|
@ -3353,6 +3340,7 @@ void Plater::priv::set_current_panel(wxPanel* panel)
|
||||||
this->q->reslice();
|
this->q->reslice();
|
||||||
// keeps current gcode preview, if any
|
// keeps current gcode preview, if any
|
||||||
preview->reload_print(true);
|
preview->reload_print(true);
|
||||||
|
|
||||||
preview->set_canvas_as_dirty();
|
preview->set_canvas_as_dirty();
|
||||||
view_toolbar.select_item("Preview");
|
view_toolbar.select_item("Preview");
|
||||||
}
|
}
|
||||||
|
@ -3430,23 +3418,6 @@ void Plater::priv::on_slicing_update(SlicingStatusEvent &evt)
|
||||||
} else if (evt.status.flags & PrintBase::SlicingStatus::RELOAD_SLA_PREVIEW) {
|
} else if (evt.status.flags & PrintBase::SlicingStatus::RELOAD_SLA_PREVIEW) {
|
||||||
// Update the SLA preview. Only called if not RELOAD_SLA_SUPPORT_POINTS, as the block above will refresh the preview anyways.
|
// Update the SLA preview. Only called if not RELOAD_SLA_SUPPORT_POINTS, as the block above will refresh the preview anyways.
|
||||||
this->preview->reload_print();
|
this->preview->reload_print();
|
||||||
|
|
||||||
// uncomment the following lines if you want to render into the thumbnail also supports and pad for SLA printer
|
|
||||||
/*
|
|
||||||
#if ENABLE_THUMBNAIL_GENERATOR
|
|
||||||
// update thumbnail data
|
|
||||||
// for ptSLA generate the thumbnail after supports and pad have been calculated to have them rendered
|
|
||||||
if ((this->printer_technology == ptSLA) && (evt.status.percent == -3))
|
|
||||||
{
|
|
||||||
this->thumbnail_data.clear();
|
|
||||||
for (const std::pair<unsigned int, unsigned int>& size : THUMBNAIL_SIZE_SLA)
|
|
||||||
{
|
|
||||||
this->thumbnail_data.push_back(ThumbnailData());
|
|
||||||
generate_thumbnail(this->thumbnail_data.back(), size.first, size.second, true, false, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3677,6 +3648,19 @@ void Plater::priv::generate_thumbnail(ThumbnailData& data, unsigned int w, unsig
|
||||||
{
|
{
|
||||||
view3D->get_canvas3d()->render_thumbnail(data, w, h, printable_only, parts_only, transparent_background);
|
view3D->get_canvas3d()->render_thumbnail(data, w, h, printable_only, parts_only, transparent_background);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Plater::priv::generate_thumbnails(ThumbnailsList& thumbnails, const Vec2ds& sizes, bool printable_only, bool parts_only, bool transparent_background)
|
||||||
|
{
|
||||||
|
thumbnails.clear();
|
||||||
|
for (const Vec2d& size : sizes)
|
||||||
|
{
|
||||||
|
thumbnails.push_back(ThumbnailData());
|
||||||
|
Point isize(size); // round to ints
|
||||||
|
generate_thumbnail(thumbnails.back(), isize.x(), isize.y(), printable_only, parts_only, transparent_background);
|
||||||
|
if (!thumbnails.back().is_valid())
|
||||||
|
thumbnails.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif // ENABLE_THUMBNAIL_GENERATOR
|
#endif // ENABLE_THUMBNAIL_GENERATOR
|
||||||
|
|
||||||
void Plater::priv::msw_rescale_object_menu()
|
void Plater::priv::msw_rescale_object_menu()
|
||||||
|
@ -5239,6 +5223,16 @@ const Camera& Plater::get_camera() const
|
||||||
return p->camera;
|
return p->camera;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Mouse3DController& Plater::get_mouse3d_controller() const
|
||||||
|
{
|
||||||
|
return p->mouse3d_controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
Mouse3DController& Plater::get_mouse3d_controller()
|
||||||
|
{
|
||||||
|
return p->mouse3d_controller;
|
||||||
|
}
|
||||||
|
|
||||||
bool Plater::can_delete() const { return p->can_delete(); }
|
bool Plater::can_delete() const { return p->can_delete(); }
|
||||||
bool Plater::can_delete_all() const { return p->can_delete_all(); }
|
bool Plater::can_delete_all() const { return p->can_delete_all(); }
|
||||||
bool Plater::can_increase_instances() const { return p->can_increase_instances(); }
|
bool Plater::can_increase_instances() const { return p->can_increase_instances(); }
|
||||||
|
|
|
@ -41,6 +41,7 @@ class ObjectSettings;
|
||||||
class ObjectLayers;
|
class ObjectLayers;
|
||||||
class ObjectList;
|
class ObjectList;
|
||||||
class GLCanvas3D;
|
class GLCanvas3D;
|
||||||
|
class Mouse3DController;
|
||||||
|
|
||||||
using t_optgroups = std::vector <std::shared_ptr<ConfigOptionsGroup>>;
|
using t_optgroups = std::vector <std::shared_ptr<ConfigOptionsGroup>>;
|
||||||
|
|
||||||
|
@ -260,6 +261,8 @@ public:
|
||||||
void msw_rescale();
|
void msw_rescale();
|
||||||
|
|
||||||
const Camera& get_camera() const;
|
const Camera& get_camera() const;
|
||||||
|
const Mouse3DController& get_mouse3d_controller() const;
|
||||||
|
Mouse3DController& get_mouse3d_controller();
|
||||||
|
|
||||||
// ROII wrapper for suppressing the Undo / Redo snapshot to be taken.
|
// ROII wrapper for suppressing the Undo / Redo snapshot to be taken.
|
||||||
class SuppressSnapshots
|
class SuppressSnapshots
|
||||||
|
|
Loading…
Reference in a new issue