diff --git a/CMakeLists.txt b/CMakeLists.txt index 65097e43..363b575d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,13 @@ cmake_minimum_required(VERSION 2.8.11) # but geogram.cmake needs to be included after the project() # command, since project() resets CFLAGS and CXXFLAGS. -if(IS_DIRECTORY ${CMAKE_SOURCE_DIR}/src/lib/vorpalib) +if("$ENV{GEOGRAM_WITH_VORPALINE}" STREQUAL "") + if(IS_DIRECTORY ${CMAKE_SOURCE_DIR}/src/lib/vorpalib) + project(Vorpaline) + else() + project(Geogram) + endif() +elseif ("$ENV{GEOGRAM_WITH_VORPALINE}" STREQUAL ON) project(Vorpaline) else() project(Geogram) @@ -42,7 +48,7 @@ include(cmake/geogram.cmake) set(VORPALINE_VERSION_MAJOR 1) set(VORPALINE_VERSION_MINOR 6) -set(VORPALINE_VERSION_PATCH 8) +set(VORPALINE_VERSION_PATCH 9) set(VORPALINE_VERSION ${VORPALINE_VERSION_MAJOR}.${VORPALINE_VERSION_MINOR}.${VORPALINE_VERSION_PATCH}) set(VORPALINE_INCLUDE_SUBPATH geogram${VORPALINE_VERSION_MAJOR}) diff --git a/cmake/geogram.cmake b/cmake/geogram.cmake old mode 100755 new mode 100644 index 025d91ea..756ef35d --- a/cmake/geogram.cmake +++ b/cmake/geogram.cmake @@ -26,13 +26,22 @@ if(NOT DEFINED VORPALINE_PLATFORM) endif() # Determine whether Geogram is built with Vorpaline -if(IS_DIRECTORY ${GEOGRAM_SOURCE_DIR}/src/lib/vorpalib) +if("$ENV{GEOGRAM_WITH_VORPALINE}" STREQUAL "") + if(IS_DIRECTORY ${GEOGRAM_SOURCE_DIR}/src/lib/vorpalib) + set(GEOGRAM_WITH_VORPALINE ON) + else() + set(GEOGRAM_WITH_VORPALINE OFF) + endif() +else() +# GEOGRAM_WITH_VORPALINE is defined in the environment, used its value + set(GEOGRAM_WITH_VORPALINE $ENV{GEOGRAM_WITH_VORPALINE}) +endif() + +if ("${GEOGRAM_WITH_VORPALINE}" STREQUAL ON) message(STATUS "Configuring build for Geogram + Vorpaline") - set(GEOGRAM_WITH_VORPALINE ON) add_definitions(-DGEOGRAM_WITH_VORPALINE) else() message(STATUS "Configuring build for standalone Geogram (without Vorpaline)") - set(GEOGRAM_WITH_VORPALINE OFF) endif() if(GEOGRAM_WITH_HLBFGS) diff --git a/cmake/platforms/Android-generic/config.cmake b/cmake/platforms/Android-generic/config.cmake index 938d3c94..2f0bafa0 100644 --- a/cmake/platforms/Android-generic/config.cmake +++ b/cmake/platforms/Android-generic/config.cmake @@ -1,4 +1,3 @@ - # Reset the warning level for third parties function(vor_reset_warning_level) endfunction() diff --git a/cmake/platforms/MinGW-x86_64-w64.cmake b/cmake/platforms/MinGW-x86_64-w64.cmake new file mode 100644 index 00000000..bd95c05c --- /dev/null +++ b/cmake/platforms/MinGW-x86_64-w64.cmake @@ -0,0 +1,84 @@ +#------------------------------------------------------------------- +# Flags for compiling with Mingw +#------------------------------------------------------------------- + +# Shell script extension +set(SHELL_SUFFIX "sh") + +# Define the environment for cross compiling to Windows +SET(CMAKE_SYSTEM_NAME Windows) # Target system name +SET(CMAKE_SYSTEM_VERSION 1) + +SET(CMAKE_AR "x86_64-w64-mingw32-ar") +SET(CMAKE_RC_COMPILER "x86_64-w64-mingw32-windres") +SET(CMAKE_RANLIB "x86_64-w64-mingw32-ranlib") + +# Configure the behaviour of the find commands +SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) + +set(VORPALINE_ARCH_64 true) + +# Link with the loader library +list(APPEND SYSLIBS dl) + +# No graphics (yet) for Android +set(GEOGRAM_WITH_GRAPHICS FALSE) + +# Warning flags +set(NORMAL_WARNINGS -Wall -Wextra) +set(FULL_WARNINGS + ${NORMAL_WARNINGS} + -pedantic + -Wno-long-long + # Detect conversion problems (lot of warnings) + -Wconversion + -Wsign-conversion + -Wdouble-promotion +) + +# Compile with full warnings by default +add_definitions(${FULL_WARNINGS}) + +# Warn about missing virtual destructor (C++ only) +add_flags(CMAKE_CXX_FLAGS -Wnon-virtual-dtor) + +# define the windows version +add_flags(CMAKE_CXX_FLAGS -D_WIN32_WINNT=0x601 -DMINGW_HAS_SECURE_API -Wno-unknown-pragmas) +# Activate c++ 2011 +add_flags(CMAKE_CXX_FLAGS -std=c++11) + +# Additional debug flags +# deactivated for now: I added bound checking in VOR::vector<>. +#add_flags(CMAKE_CXX_FLAGS_DEBUG -D_GLIBCXX_DEBUG) + +# Compile and link with pthreads +add_flags(CMAKE_CXX_FLAGS -pthread) +add_flags(CMAKE_C_FLAGS -pthread) + +# Reset the warning level for third parties +function(vor_reset_warning_level) + remove_definitions(${FULL_WARNINGS}) + add_definitions(${NORMAL_WARNINGS}) +endfunction() + +if ( APPLE ) + string ( REPLACE "-Wl,-search_paths_first" "" CMAKE_C_LINK_FLAGS ${CMAKE_C_LINK_FLAGS} ) + string ( REPLACE "-Wl,-search_paths_first" "" CMAKE_CXX_LINK_FLAGS ${CMAKE_CXX_LINK_FLAGS} ) +endif () + +macro(vor_add_executable) + + if(NOT VORPALINE_BUILD_DYNAMIC) + # Create a statically linked executable + # Link with static libraries + # ... does not work with NDK 10.d + # (causes errors / multiply linked symbols) +# add_flags(CMAKE_CXX_FLAGS -static-libstdc++ -static-libgcc -static) +# add_flags(CMAKE_C_FLAGS -static-libgcc -static) + endif() + + add_executable(${ARGN}) +endmacro() + diff --git a/cmake/platforms/MinGW-x86_64-w64/config.cmake b/cmake/platforms/MinGW-x86_64-w64/config.cmake new file mode 100644 index 00000000..e6a49890 --- /dev/null +++ b/cmake/platforms/MinGW-x86_64-w64/config.cmake @@ -0,0 +1,3 @@ +set(VORPALINE_WITH_GRAPHICS FALSE) + +include(${GEOGRAM_SOURCE_DIR}/cmake/platforms/MinGW-x86_64-w64.cmake) diff --git a/cmake/platforms/MinGW-x86_64-w64/setvars.sh b/cmake/platforms/MinGW-x86_64-w64/setvars.sh new file mode 100644 index 00000000..1ad563da --- /dev/null +++ b/cmake/platforms/MinGW-x86_64-w64/setvars.sh @@ -0,0 +1,3 @@ +export AR=x86_64-w64-mingw32-ar +export CC=x86_64-w64-mingw32-gcc +export CXX=x86_64-w64-mingw32-g++ diff --git a/cmake/platforms/Win64-vs2015/config.cmake b/cmake/platforms/Win64-vs2015/config.cmake new file mode 100644 index 00000000..766a7f6f --- /dev/null +++ b/cmake/platforms/Win64-vs2015/config.cmake @@ -0,0 +1,3 @@ +set(VORPALINE_ARCH_64 true) +include(${GEOGRAM_SOURCE_DIR}/cmake/platforms/Windows-vs.cmake) + diff --git a/cmake/platforms/Win64-vs2015/setvars.bat b/cmake/platforms/Win64-vs2015/setvars.bat new file mode 100644 index 00000000..61ae90a9 --- /dev/null +++ b/cmake/platforms/Win64-vs2015/setvars.bat @@ -0,0 +1,8 @@ +:: Get the Visual Studio installation dir +set MSVCDIR=%VS140COMNTOOLS%..\..\VC + +:: Configure environment for Visual Studio +call "%MSVCDIR%\VCVARSALL.BAT" x64 + +:: Set the generator to use +set CMAKE_VS_GENERATOR=Visual Studio 14 2015 Win64 diff --git a/configure.bat b/configure.bat old mode 100755 new mode 100644 index 6a31bfcb..977bef84 --- a/configure.bat +++ b/configure.bat @@ -5,6 +5,14 @@ @echo off setlocal ENABLEDELAYEDEXPANSION +:: Check for options: [ --build_name_suffix suffix ] +set buildNameSuffix="" +if "%1" == "--build_name_suffix" ( + set buildNameSuffix=%2 + SHIFT & SHIFT +) + +:: Read platform set opsys=%1 :: Checking for CMake @@ -54,8 +62,8 @@ echo. echo ============= Creating build system for %opsys% ============ echo. -if not exist build\%opsys% ( - mkdir build\%opsys% +if not exist build\%opsys%%buildNameSuffix% ( + mkdir build\%opsys%%buildNameSuffix% ) echo Using cmake generator %CMAKE_VS_GENERATOR% @@ -68,7 +76,7 @@ if "%CMAKE_VS_GENERATOR_TOOLSET%" neq "" ( ::set cmake_debug_options=--trace --debug-output -pushd build\%opsys% +pushd build\%opsys%%buildNameSuffix% cmake ..\.. %cmake_debug_options% %cmake_generator_options% -DVORPALINE_PLATFORM:STRING=%opsys% || exit /B 1 popd @@ -76,7 +84,7 @@ echo. echo ============== Vorpaline build configured ================== echo. echo To build vorpaline: -echo - go to build/%opsys% +echo - go to build/%opsys%%buildNameSuffix% echo - run 'cmake --build . --config=Release(or Debug) [--target=target_to_build]' echo. echo Note: local configuration can be specified in CMakeOptions.txt @@ -86,5 +94,6 @@ echo. :: Clear globals +set buildNameSuffix= set opsys= exit /B 0 diff --git a/configure.sh b/configure.sh index 21d0264f..8e4e889d 100755 --- a/configure.sh +++ b/configure.sh @@ -20,6 +20,7 @@ fi # Parse command line arguments cmake_options= +build_name_suffix= while [ -n "$1" ]; do case "$1" in --debug) @@ -49,6 +50,10 @@ while [ -n "$1" ]; do done exit ;; + --build_name_suffix=*) + build_name_suffix=`echo "$1" | sed 's/--build_name_suffix=\(.*\)$/\1/'` + shift + ;; --help) cat <draw_gui()) { result = true; @@ -1048,7 +1087,18 @@ namespace GEO { p[2] = 1.0-y; } } - + + inline vec3 random_color() { + vec3 result; + while(length2(result) < 0.1) { + result = vec3( + Numeric::random_float64(), + Numeric::random_float64(), + Numeric::random_float64() + ); + } + return result; + } } diff --git a/src/examples/graphics/demo_Delaunay3d/main.cpp b/src/examples/graphics/demo_Delaunay3d/main.cpp index cf104ae2..5992dc4e 100644 --- a/src/examples/graphics/demo_Delaunay3d/main.cpp +++ b/src/examples/graphics/demo_Delaunay3d/main.cpp @@ -279,11 +279,11 @@ namespace { ); glupBegin(GLUP_SPHERES); - for(index_t i=0; iset_diffuse_coefficient(random_color()); + scene_changed_ = true; + } + if(ImGui::MenuItem("Light")) { + scene_.add_object( + new Light(vec3(0.5, 0.5, 1.5),0.15,random_color()) + ); + scene_changed_ = true; + } + if(ImGui::MenuItem("Checkerboard")) { + scene_.add_object(new HorizontalCheckerboardPlane(0.0)); + scene_changed_ = true; + } + ImGui::EndMenu(); + } + } private: Camera camera_; diff --git a/src/lib/geogram/CMakeLists.txt b/src/lib/geogram/CMakeLists.txt index 37c17d00..87793f5b 100755 --- a/src/lib/geogram/CMakeLists.txt +++ b/src/lib/geogram/CMakeLists.txt @@ -14,6 +14,7 @@ aux_source_directories(SOURCES "Source Files\\voronoi" voronoi) aux_source_directories(SOURCES "Source Files\\points" points) aux_source_directories(SOURCES "Source Files\\api" api) aux_source_directories(SOURCES "Source Files\\NL" NL) +aux_source_directories(SOURCES "Source Files\\image" image) aux_source_directories(SOURCES "Source Files\\parameterization" parameterization) aux_source_directories(SOURCES "Source Files\\bibliography" bibliography) diff --git a/src/lib/geogram/NL/nl_matrix.c b/src/lib/geogram/NL/nl_matrix.c index d7589f04..b3368879 100644 --- a/src/lib/geogram/NL/nl_matrix.c +++ b/src/lib/geogram/NL/nl_matrix.c @@ -982,13 +982,29 @@ NLMatrix nlCRSMatrixNewFromSparseMatrixSymmetric(NLSparseMatrix* M) { void nlMatrixCompress(NLMatrix* M) { - NLMatrix CRS = NULL; + NLMatrix result = NULL; + + if( + (*M)->type == NL_MATRIX_CRS && + nlExtensionIsInitialized_MKL() + ) { + result = nlMKLMatrixNewFromCRSMatrix((NLCRSMatrix*)*M); + nlDeleteMatrix(*M); + *M = result; + return; + } + if((*M)->type != NL_MATRIX_SPARSE_DYNAMIC) { return; } - CRS = nlCRSMatrixNewFromSparseMatrix((NLSparseMatrix*)*M); + + if(nlExtensionIsInitialized_MKL()) { + result = nlMKLMatrixNewFromSparseMatrix((NLSparseMatrix*)*M); + } else { + result = nlCRSMatrixNewFromSparseMatrix((NLSparseMatrix*)*M); + } nlDeleteMatrix(*M); - *M = CRS; + *M = result; } NLuint nlMatrixNNZ(NLMatrix M) { diff --git a/src/lib/geogram/NL/nl_mkl.c b/src/lib/geogram/NL/nl_mkl.c index f9756fb8..da2e5205 100644 --- a/src/lib/geogram/NL/nl_mkl.c +++ b/src/lib/geogram/NL/nl_mkl.c @@ -60,7 +60,93 @@ typedef void (*FUNPTR_mkl_cspblas_dcsrgemv)( typedef void (*FUNPTR_mkl_cspblas_dcsrsymv)( const char *transa, const MKL_INT *m, const double *a, const MKL_INT *ia, const MKL_INT *ja, const double *x, double *y - ); +); + +typedef enum { + SPARSE_STATUS_SUCCESS = 0, + SPARSE_STATUS_NOT_INITIALIZED = 1, + SPARSE_STATUS_ALLOC_FAILED = 2, + SPARSE_STATUS_INVALID_VALUE = 3, + SPARSE_STATUS_EXECUTION_FAILED = 4, + SPARSE_STATUS_INTERNAL_ERROR = 5, + SPARSE_STATUS_NOT_SUPPORTED = 6 +} sparse_status_t; + +typedef enum { + SPARSE_INDEX_BASE_ZERO = 0, + SPARSE_INDEX_BASE_ONE = 1 +} sparse_index_base_t; + +typedef enum { + SPARSE_OPERATION_NON_TRANSPOSE = 10, + SPARSE_OPERATION_TRANSPOSE = 11, + SPARSE_OPERATION_CONJUGATE_TRANSPOSE = 12 +} sparse_operation_t; + +typedef enum { + SPARSE_MATRIX_TYPE_GENERAL = 20, + SPARSE_MATRIX_TYPE_SYMMETRIC = 21, + SPARSE_MATRIX_TYPE_HERMITIAN = 22, + SPARSE_MATRIX_TYPE_TRIANGULAR = 23, + SPARSE_MATRIX_TYPE_DIAGONAL = 24, + SPARSE_MATRIX_TYPE_BLOCK_TRIANGULAR = 25, + SPARSE_MATRIX_TYPE_BLOCK_DIAGONAL = 26 +} sparse_matrix_type_t; + +typedef enum { + SPARSE_FILL_MODE_LOWER = 40, + SPARSE_FILL_MODE_UPPER = 41 +} sparse_fill_mode_t; + +typedef enum { + SPARSE_DIAG_NON_UNIT = 50, + SPARSE_DIAG_UNIT = 51 +} sparse_diag_type_t; + + +struct matrix_descr { + sparse_matrix_type_t type; + sparse_fill_mode_t mode; + sparse_diag_type_t diag; +}; + +typedef enum { + SPARSE_MEMORY_NONE = 80, + SPARSE_MEMORY_AGGRESSIVE = 81 +} sparse_memory_usage_t; + +struct sparse_matrix; +typedef struct sparse_matrix *sparse_matrix_t; + + +typedef sparse_status_t (*FUNPTR_mkl_sparse_d_create_csr)( + sparse_matrix_t* A, sparse_index_base_t indexing, + MKL_INT rows, MKL_INT cols, MKL_INT* rows_start, + MKL_INT* rows_end, MKL_INT* col_indx, double* values +); + +typedef sparse_status_t (*FUNPTR_mkl_sparse_d_mv)( + sparse_operation_t operation, double alpha, + const sparse_matrix_t A, struct matrix_descr descr, + const double* x, double beta, double* y +); + +typedef sparse_status_t (*FUNPTR_mkl_sparse_destroy)( + sparse_matrix_t A +); + +typedef sparse_status_t (*FUNPTR_mkl_sparse_set_mv_hint)( + sparse_matrix_t A, sparse_operation_t operation, + struct matrix_descr descr, MKL_INT expected_calls +); + +typedef sparse_status_t (*FUNPTR_mkl_sparse_set_memory_hint)( + sparse_matrix_t A, sparse_memory_usage_t policy +); + +typedef sparse_status_t (*FUNPTR_mkl_sparse_optimize)( + sparse_matrix_t A +); /** * \brief The structure that stores the handle to @@ -74,7 +160,14 @@ typedef struct { NLdll DLL_iomp5; FUNPTR_mkl_cspblas_dcsrgemv mkl_cspblas_dcsrgemv; - FUNPTR_mkl_cspblas_dcsrsymv mkl_cspblas_dcsrsymv; + FUNPTR_mkl_cspblas_dcsrsymv mkl_cspblas_dcsrsymv; + + FUNPTR_mkl_sparse_d_create_csr mkl_sparse_d_create_csr; + FUNPTR_mkl_sparse_d_mv mkl_sparse_d_mv; + FUNPTR_mkl_sparse_destroy mkl_sparse_destroy; + FUNPTR_mkl_sparse_set_mv_hint mkl_sparse_set_mv_hint; + FUNPTR_mkl_sparse_set_memory_hint mkl_sparse_set_memory_hint; + FUNPTR_mkl_sparse_optimize mkl_sparse_optimize; } MKLContext; /** @@ -98,7 +191,13 @@ NLboolean nlExtensionIsInitialized_MKL() { MKL()->DLL_mkl_intel_thread == NULL || MKL()->DLL_mkl_intel_lp64 == NULL || MKL()->mkl_cspblas_dcsrgemv == NULL || - MKL()->mkl_cspblas_dcsrsymv == NULL + MKL()->mkl_cspblas_dcsrsymv == NULL || + MKL()->mkl_sparse_d_create_csr == NULL || + MKL()->mkl_sparse_d_mv == NULL || + MKL()->mkl_sparse_destroy == NULL || + MKL()->mkl_sparse_set_mv_hint == NULL || + MKL()->mkl_sparse_set_memory_hint == NULL || + MKL()->mkl_sparse_optimize == NULL ) { return NL_FALSE; } @@ -209,7 +308,15 @@ NLboolean nlInitExtension_MKL(void) { find_mkl_func(mkl_cspblas_dcsrgemv); find_mkl_func(mkl_cspblas_dcsrsymv); + + find_mkl_func(mkl_sparse_d_create_csr); + find_mkl_func(mkl_sparse_d_mv); + find_mkl_func(mkl_sparse_destroy); + find_mkl_func(mkl_sparse_set_mv_hint); + find_mkl_func(mkl_sparse_set_memory_hint); + find_mkl_func(mkl_sparse_optimize); + if(nlExtensionIsInitialized_MKL()) { NLMultMatrixVector_MKL = NLMultMatrixVector_MKL_impl; } @@ -220,3 +327,128 @@ NLboolean nlInitExtension_MKL(void) { /*************************************************************************/ +typedef struct { + NLuint m; + NLuint n; + NLenum type; + NLDestroyMatrixFunc destroy_func; + NLMultMatrixVectorFunc mult_func; + sparse_matrix_t A; + NLdouble* val; + NLuint* rowptr; + NLuint* colind; +} NLMKLMatrix; + +static void nlMKLMatrixDestroy(NLMKLMatrix* M) { + MKL()->mkl_sparse_destroy(M->A); + M->A = NULL; + NL_DELETE_ARRAY(M->val); + NL_DELETE_ARRAY(M->rowptr); + NL_DELETE_ARRAY(M->colind); + M->m = 0; + M->n = 0; +} + +static void nlMKLMatrixMult( + NLMKLMatrix* M, const double* x, double* y +) { + struct matrix_descr descr; + descr.type = SPARSE_MATRIX_TYPE_GENERAL; + MKL()->mkl_sparse_d_mv( + SPARSE_OPERATION_NON_TRANSPOSE, + 1.0, M->A, descr, + x, 0.0, y + ); +} + +NLMatrix nlMKLMatrixNewFromSparseMatrix(NLSparseMatrix* M) { + NLuint nnz = nlSparseMatrixNNZ(M); + NLuint i,ij,k; + NLMKLMatrix* result = NL_NEW(NLMKLMatrix); + struct matrix_descr descr; + descr.type = SPARSE_MATRIX_TYPE_GENERAL; + + nl_assert(M->storage & NL_MATRIX_STORE_ROWS); + nl_assert((M->storage & NL_MATRIX_STORE_SYMMETRIC) == 0); + + result->m = M->m; + result->n = M->n; + result->type = NL_MATRIX_OTHER; + result->destroy_func = (NLDestroyMatrixFunc)nlMKLMatrixDestroy; + result->mult_func = (NLMultMatrixVectorFunc)nlMKLMatrixMult; + result->val = NL_NEW_ARRAY(double, nnz); + result->rowptr = NL_NEW_ARRAY(NLuint, M->m+1); + result->colind = NL_NEW_ARRAY(NLuint, nnz); + + /* Convert matrix to CRS format */ + k=0; + for(i=0; im; ++i) { + NLRowColumn* Ri = &(M->row[i]); + result->rowptr[i] = k; + for(ij=0; ijsize; ij++) { + NLCoeff* c = &(Ri->coeff[ij]); + result->val[k] = c->value; + result->colind[k] = c->index; + ++k; + } + } + result->rowptr[M->m] = k; + + MKL()->mkl_sparse_d_create_csr( + &(result->A), SPARSE_INDEX_BASE_ZERO, M->m, M->n, + result->rowptr, result->rowptr+1, + result->colind, result->val + ); + + MKL()->mkl_sparse_set_mv_hint( + result->A, SPARSE_OPERATION_NON_TRANSPOSE, descr, 1000 + ); + + MKL()->mkl_sparse_set_memory_hint( + result->A, SPARSE_MEMORY_AGGRESSIVE + ); + + MKL()->mkl_sparse_optimize(result->A); + + return (NLMatrix)result; +} + +NLMatrix nlMKLMatrixNewFromCRSMatrix(NLCRSMatrix* M) { + NLuint nnz = nlCRSMatrixNNZ(M); + NLMKLMatrix* result = NL_NEW(NLMKLMatrix); + struct matrix_descr descr; + descr.type = SPARSE_MATRIX_TYPE_GENERAL; + + nl_assert(!M->symmetric_storage); + + result->m = M->m; + result->n = M->n; + result->type = NL_MATRIX_OTHER; + result->destroy_func = (NLDestroyMatrixFunc)nlMKLMatrixDestroy; + result->mult_func = (NLMultMatrixVectorFunc)nlMKLMatrixMult; + result->val = NL_NEW_ARRAY(double, nnz); + result->rowptr = NL_NEW_ARRAY(NLuint, M->m+1); + result->colind = NL_NEW_ARRAY(NLuint, nnz); + + memcpy(result->val, M->val, nnz*sizeof(double)); + memcpy(result->rowptr, M->rowptr, (M->m+1)*sizeof(NLuint)); + memcpy(result->colind, M->colind, nnz*sizeof(NLuint)); + + MKL()->mkl_sparse_d_create_csr( + &(result->A), SPARSE_INDEX_BASE_ZERO, M->m, M->n, + result->rowptr, result->rowptr+1, + result->colind, result->val + ); + + MKL()->mkl_sparse_set_mv_hint( + result->A, SPARSE_OPERATION_NON_TRANSPOSE, descr, 1000 + ); + + MKL()->mkl_sparse_set_memory_hint( + result->A, SPARSE_MEMORY_AGGRESSIVE + ); + + MKL()->mkl_sparse_optimize(result->A); + + return (NLMatrix)result; +} diff --git a/src/lib/geogram/NL/nl_mkl.h b/src/lib/geogram/NL/nl_mkl.h old mode 100755 new mode 100644 index 601ead7e..c1217733 --- a/src/lib/geogram/NL/nl_mkl.h +++ b/src/lib/geogram/NL/nl_mkl.h @@ -83,4 +83,22 @@ NLboolean nlExtensionIsInitialized_MKL(void); */ extern NLMultMatrixVectorFunc NLMultMatrixVector_MKL; +/** + * \brief Creates an optimized MKL matrix from a dynamic sparse matrix. + * \details The matrix \p M should have stored rows. Symmetric storage + * is not supported yet. + * \param[in] M the dynamic sparse matrix. + * \return a pointer to the created matrix + */ +NLMatrix nlMKLMatrixNewFromSparseMatrix(NLSparseMatrix* M); + +/** + * \brief Creates an optimized MKL matrix from a CRS sparse matrix. + * \details The matrix \p M should have stored rows. Symmetric storage + * is not supported yet. + * \param[in] M the dynamic sparse matrix. + * \return a pointer to the created matrix + */ +NLMatrix nlMKLMatrixNewFromCRSMatrix(NLCRSMatrix* M); + #endif diff --git a/src/lib/geogram/basic/atomics.h b/src/lib/geogram/basic/atomics.h index 4cc5071f..1220e399 100644 --- a/src/lib/geogram/basic/atomics.h +++ b/src/lib/geogram/basic/atomics.h @@ -342,6 +342,11 @@ inline char atomic_bittestandreset_x86(volatile unsigned int* ptr, unsigned int #pragma intrinsic(_WriteBarrier) #pragma intrinsic(_ReadWriteBarrier) +# ifdef GEO_COMPILER_MINGW +inline void geo_pause() { +} +# endif + #endif // GEO_OS_WINDOWS #endif diff --git a/src/lib/geogram/basic/common.cpp b/src/lib/geogram/basic/common.cpp index 97cf4ee1..5c757ba5 100644 --- a/src/lib/geogram/basic/common.cpp +++ b/src/lib/geogram/basic/common.cpp @@ -55,6 +55,13 @@ #include #include #include + +#include +#include +#include +#include +#include + #include #include @@ -138,6 +145,20 @@ namespace GEO { } ); #endif + +#ifndef GEOGRAM_PSM + ImageLibrary::initialize() ; + + geo_declare_image_serializer("png"); + geo_declare_image_serializer("jpg"); + geo_declare_image_serializer("jpeg"); + geo_declare_image_serializer("tga"); + geo_declare_image_serializer("bmp"); + + geo_declare_image_serializer("xpm") ; + geo_declare_image_serializer("pgm") ; +#endif + initialized = true; } @@ -152,8 +173,9 @@ namespace GEO { } PCK::terminate(); - -#ifndef GEOGRAM_PSM + +#ifndef GEOGRAM_PSM + ImageLibrary::terminate() ; Biblio::terminate(); #endif diff --git a/src/lib/geogram/basic/common.h b/src/lib/geogram/basic/common.h index 17fc6e46..2137e646 100644 --- a/src/lib/geogram/basic/common.h +++ b/src/lib/geogram/basic/common.h @@ -283,7 +283,10 @@ namespace GEO { #error "Unsupported operating system" #endif -#if defined(GEO_COMPILER_GCC) || defined(GEO_COMPILER_CLANG) || defined(GEO_COMPILER_MINGW) +#if defined(GEO_COMPILER_GCC) || \ + defined(GEO_COMPILER_CLANG) || \ + defined(GEO_COMPILER_MINGW) || \ + defined(GEO_COMPILER_EMSCRIPTEN) #define GEO_COMPILER_GCC_FAMILY #endif @@ -311,10 +314,8 @@ namespace GEO { #if defined(GOMGEN) #define GEO_NORETURN -#elif defined(GEO_COMPILER_CLANG) || \ - defined(GEO_COMPILER_GCC) || \ - defined(GEO_COMPILER_EMSCRIPTEN) || \ - defined(GEO_COMPILER_INTEL) +#elif defined(GEO_COMPILER_GCC_FAMILY) || \ + defined(GEO_COMPILER_INTEL) #define GEO_NORETURN __attribute__((noreturn)) #else #define GEO_NORETURN diff --git a/src/lib/geogram/basic/file_system.cpp b/src/lib/geogram/basic/file_system.cpp index a2fd1c0d..b447140d 100755 --- a/src/lib/geogram/basic/file_system.cpp +++ b/src/lib/geogram/basic/file_system.cpp @@ -105,7 +105,7 @@ namespace GEO { else if(path_in.at(0) != '/' && path_in.at(0) != '\\') { current += get_current_working_directory(); } - for(size_t i = start_at; i < path.size(); i++) { + for(size_t i = size_t(start_at); i < path.size(); i++) { current += "/"; current += path[i]; if(path[i].at(0) == '.' && diff --git a/src/lib/geogram/basic/geometry.h b/src/lib/geogram/basic/geometry.h index 1df98f32..dab8f3d9 100644 --- a/src/lib/geogram/basic/geometry.h +++ b/src/lib/geogram/basic/geometry.h @@ -686,6 +686,146 @@ namespace GEO { target.xyz_max[c] = std::max(B1.xyz_max[c], B2.xyz_max[c]); } } + + /*******************************************************************/ + + /** + * \brief Applies a 3d transform to a 3d vector. + * \details Convention is the same as in OpenGL, i.e. + * vector is a row vector, multiplied on the left + * of the transform. + * Internally, the vector is converted into + * a 4d vector, with w coordinate set to zero. + * \param[in] v the input 3d vector to be transformed + * \param[in] m the transform, as a 4x4 matrix, using + * homogeneous coordinates + * \tparam FT type of the coordinates + * \return the transformed 3d vector + */ + template vecng<3,FT> transform_vector( + const vecng<3,FT>& v, + const Matrix<4,FT>& m + ){ + index_t i,j ; + FT result[4] ; + + for(i=0; i<4; i++) { + result[i] = 0 ; + } + for(i=0; i<4; i++) { + for(j=0; j<3; j++) { + result[i] += v[j] * m(j,i) ; + } + } + + return vecng<3,FT>( + result[0], result[1], result[2] + ) ; + } + + /** + * \brief Applies a 3d transform to a 3d point. + * \details Convention is the same as in OpenGL, i.e. + * vector is a row vector, multiplied on the left + * of the transform. + * Internally, the point is converted into + * a 4d vector, with w coordinate set to one. Transformed + * coordinates are divided by the transformed w to form + * a 3d point. + * \param[in] v the input 3d point to be transformed + * \param[in] m the transform, as a 4x4 matrix, using + * homogeneous coordinates + * \tparam FT type of the coordinates + * \return the transformed 3d point + */ + template vecng<3,FT> transform_point( + const vecng<3,FT>& v, + const Matrix<4,FT>& m + ){ + index_t i,j ; + FT result[4] ; + + for(i=0; i<4; i++) { + result[i] = 0 ; + } + for(i=0; i<4; i++) { + for(j=0; j<3; j++) { + result[i] += v[j] * m(j,i) ; + } + result[i] += m(3,i); + } + + return vecng<3,FT>( + result[0] / result[3], + result[1] / result[3], + result[2] / result[3] + ) ; + } + + /** + * \brief Applies a 4d transform to a 4d point. + * \details Convention is the same as in OpenGL, i.e. + * vector is a row vector, multiplied on the left + * of the transform. + * \param[in] v the input 4d point to be transformed + * \param[in] m the transform, as a 4x4 matrix + * \tparam FT type of the coordinates + * \return the transformed 4d vector + */ + template vecng<4,FT> transform_vector( + const vecng<4,FT>& v, + const Matrix<4,FT>& m + ) { + index_t i,j ; + FT res[4] = {FT(0), FT(0), FT(0), FT(0)}; + + for(i=0; i<4; i++) { + for(j=0; j<4; j++) { + res[i] += v[j] * m(j,i) ; + } + } + + return vecng<4,FT>(res[0], res[1], res[2], res[3]) ; + } + + /******************************************************************/ + + /** + * \brief Creates a translation matrix from a vector. + * \details The translation matrix is in homogeneous coordinates, + * with the same convention as OpenGL (transforms row vectors + * mutliplied on the left). + * \param[in] T a const reference to the translation vector + * \return the translation matrix + */ + inline mat4 create_translation_matrix(const vec3& T) { + mat4 result; + result.load_identity(); + result(3,0) = T.x; + result(3,1) = T.y; + result(3,2) = T.z; + return result; + } + + /** + * \brief Creates a scaling matrix. + * \details The scaling matrix is in homogeneous coordinates, + * with the same convention as OpenGL (transforms row vectors + * mutliplied on the left). + * \param[in] s the scaling coefficient + * \return the scaling matrix + */ + inline mat4 create_scaling_matrix(double s) { + mat4 result; + result.load_identity(); + result(0,0) = s; + result(1,1) = s; + result(2,2) = s; + return result; + } + + /******************************************************************/ + } #endif diff --git a/src/lib/geogram/basic/process.cpp b/src/lib/geogram/basic/process.cpp index c2073dc1..21fadd36 100644 --- a/src/lib/geogram/basic/process.cpp +++ b/src/lib/geogram/basic/process.cpp @@ -413,7 +413,13 @@ namespace GEO { index_t number_of_cores() { static index_t result = 0; if(result == 0) { +#ifdef GEO_NO_THREAD_LOCAL + // Deactivate multithreading if thread_local is + // not supported (e.g. with old OS-X). + result = 1; +#else result = os_number_of_cores(); +#endif } return result; } @@ -483,6 +489,7 @@ namespace GEO { if(number_of_cores() == 1) { Logger::warn("Process") << "Processor is not a multicore" + << "(or multithread is not supported)" << std::endl; } if(thread_manager_ == nullptr) { @@ -578,3 +585,270 @@ namespace GEO { } } + +namespace { + using namespace GEO; + + /** + * \brief Used by the implementation of GEO::parallel() + * \see GEO::parallel() + */ + class ParallelThread : public Thread { + public: + /** + * \brief ParallelThread constructor. + * \param[in] func a void function with no parameter. + */ + ParallelThread( + std::function func + ) : func_(func) { + } + + /** + * \copydoc Thread::run() + */ + void run() override { + func_(); + } + private: + std::function func_; + }; + + + /** + * \brief Used by the implementation of GEO::parallel_for() + * \see GEO::parallel_for() + */ + class ParallelForThread : public Thread { + public: + + /** + * \param[in] func a void function that takes an index_t + * \param[in] from the first iteration index + * \param[in] to one position past the last interation index + * \param[in] step iteration step + */ + ParallelForThread( + std::function func, + index_t from, index_t to, index_t step=1 + ) : func_(func), from_(from), to_(to), step_(step) { + } + + /** + * \copydoc Thread::run() + */ + void run() override { + for(index_t i = from_; i < to_; i += step_) { + func_(i); + } + } + private: + std::function func_; + index_t from_; + index_t to_; + index_t step_; + }; + + /** + * \brief Used by the implementation of GEO::parallel_for_slice() + * \see GEO::parallel_for_slice() + */ + class ParallelForSliceThread : public Thread { + public: + + /** + * \param[in] func a void function that takes two index_t arguments + * \param[in] from the first iteration index + * \param[in] to one position past the last interation index + */ + ParallelForSliceThread( + std::function func, + index_t from, index_t to + ) : func_(func), from_(from), to_(to) { + } + + /** + * \copydoc Thread::run() + */ + void run() override { + func_(from_, to_); + } + private: + std::function func_; + index_t from_; + index_t to_; + }; + +} + +namespace GEO { + + void parallel_for( + index_t from, index_t to, std::function func, + index_t threads_per_core, bool interleaved + ) { +#ifdef GEO_OS_WINDOWS + // TODO: This is a limitation of WindowsThreadManager, to be fixed. + threads_per_core = 1; +#endif + + index_t nb_threads = std::min( + to - from, + Process::maximum_concurrent_threads() * threads_per_core + ); + + nb_threads = std::max(index_t(1), nb_threads); + + index_t batch_size = (to - from) / nb_threads; + if(Process::is_running_threads() || nb_threads == 1) { + for(index_t i = from; i < to; i++) { + func(i); + } + } else { + ThreadGroup threads; + if(interleaved) { + for(index_t i = 0; i < nb_threads; i++) { + threads.push_back( + new ParallelForThread( + func, from + i, to, nb_threads + ) + ); + } + } else { + index_t cur = from; + for(index_t i = 0; i < nb_threads; i++) { + if(i == nb_threads - 1) { + threads.push_back( + new ParallelForThread( + func, cur, to + ) + ); + } else { + threads.push_back( + new ParallelForThread( + func, cur, cur + batch_size + ) + ); + } + cur += batch_size; + } + } + Process::run_threads(threads); + } + } + + + void parallel_for_slice( + index_t from, index_t to, std::function func, + index_t threads_per_core + ) { +#ifdef GEO_OS_WINDOWS + // TODO: This is a limitation of WindowsThreadManager, to be fixed. + threads_per_core = 1; +#endif + + index_t nb_threads = std::min( + to - from, + Process::maximum_concurrent_threads() * threads_per_core + ); + + nb_threads = std::max(index_t(1), nb_threads); + + index_t batch_size = (to - from) / nb_threads; + if(Process::is_running_threads() || nb_threads == 1) { + func(from, to); + } else { + ThreadGroup threads; + index_t cur = from; + for(index_t i = 0; i < nb_threads; i++) { + if(i == nb_threads - 1) { + threads.push_back( + new ParallelForSliceThread( + func, cur, to + ) + ); + } else { + threads.push_back( + new ParallelForSliceThread( + func, cur, cur + batch_size + ) + ); + } + cur += batch_size; + } + Process::run_threads(threads); + } + } + + void parallel( + std::function f1, + std::function f2 + ) { + if(Process::is_running_threads()) { + f1(); + f2(); + } else { + ThreadGroup threads; + threads.push_back(new ParallelThread(f1)); + threads.push_back(new ParallelThread(f2)); + Process::run_threads(threads); + } + } + + + void parallel( + std::function f1, + std::function f2, + std::function f3, + std::function f4 + ) { + if(Process::is_running_threads()) { + f1(); + f2(); + f3(); + f4(); + } else { + ThreadGroup threads; + threads.push_back(new ParallelThread(f1)); + threads.push_back(new ParallelThread(f2)); + threads.push_back(new ParallelThread(f3)); + threads.push_back(new ParallelThread(f4)); + Process::run_threads(threads); + } + } + + + void parallel( + std::function f1, + std::function f2, + std::function f3, + std::function f4, + std::function f5, + std::function f6, + std::function f7, + std::function f8 + ) { + if(Process::is_running_threads()) { + f1(); + f2(); + f3(); + f4(); + f5(); + f6(); + f7(); + f8(); + } else { + ThreadGroup threads; + threads.push_back(new ParallelThread(f1)); + threads.push_back(new ParallelThread(f2)); + threads.push_back(new ParallelThread(f3)); + threads.push_back(new ParallelThread(f4)); + threads.push_back(new ParallelThread(f5)); + threads.push_back(new ParallelThread(f6)); + threads.push_back(new ParallelThread(f7)); + threads.push_back(new ParallelThread(f8)); + Process::run_threads(threads); + } + } +} + diff --git a/src/lib/geogram/basic/process.h b/src/lib/geogram/basic/process.h index 4e1db481..80a0a9a8 100644 --- a/src/lib/geogram/basic/process.h +++ b/src/lib/geogram/basic/process.h @@ -65,7 +65,14 @@ namespace GEO { # define thread_local __declspec(thread) #endif - +// Older MAC OS X do not have thread_local +#ifdef GEO_OS_APPLE +# if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_9 +# define thread_local +# define GEO_NO_THREAD_LOCAL +# endif +#endif + /** * \brief Platform-independent base class for running threads. * \details @@ -531,64 +538,11 @@ namespace GEO { std::string GEOGRAM_API executable_filename(); } - /** - * \brief Thread class used internally by parallel_for() - * \details ParallelForThread is a helper Thread class used internally by - * function parallel_for(). It executes a portion of the global loop - * controlled by parallel_for(). At each iteration, ParallelForThread - * executes an action specified by the template parameter Func - * which must be a functional object. - * \tparam Func functional object called at each iteration. It must accept a - * single argument of type index_t. - */ - template - class ParallelForThread : public Thread { - public: - /** - * \brief Creates a thread for the execution of a parallel_for() - * \param[in] func the functional object called at each iteration - * \param[in] from start index of the loop - * \param[in] to stop index of the loop - * \param[in] step loop step - */ - ParallelForThread( - const Func& func, index_t from, index_t to, index_t step = 1 - ) : - func_(func), - from_(from), - to_(to), - step_(step) { - } - - /** - * \brief Starts the thread execution - * \details It executes the portion of loop configured in the - * constructor, calling the functional object at each iteration with - * the current iteration index. - */ - virtual void run() { - for(index_t i = from_; i < to_; i += step_) { - const_cast (func_)(i); - } - } - - protected: - /** ParallelForThread destructor */ - virtual ~ParallelForThread() { - } - - private: - const Func& func_; - index_t from_; - index_t to_; - index_t step_; - }; - /** * \brief Executes a loop with concurrent threads. - * - * Executes a parallel for loop from index \p to index \p to, calling - * functional object \p func at each iteration. + * \details + * Executes a parallel for loop from index \p to index \p to, calling + * functional object \p func at each iteration. * * Calling parallel_for(func, from, to) is equivalent * to the following loop, computed in parallel: @@ -598,147 +552,101 @@ namespace GEO { * } * \endcode * - * When applicable, iterations are executed by concurrent ParallelForThread - * threads: the range of the loop is split in to several contiguous - * sub-ranges, each of them being executed by a separate - * ParallelForThread. + * When applicable, iterations are executed by concurrent threads: + * the range of the loop is split in to several contiguous + * sub-ranges, each of them being executed by a separate thread. * * If parameter \p interleaved is set to true, the loop range is - * decomposed in interleaved index sets that are executed by the - * ParallelForThread. Interleaved execution may improve cache coherency. + * decomposed in interleaved index sets. Interleaved execution may + * improve cache coherency. * - * \param[in] func functional object that accepts a single argument of - * type index_t. - * \param[in] from start index of the loop - * \param[in] to stop index of the loop + * \param[in] func function that takes an index_t. + * \param[in] from the first iteration index + * \param[in] to one position past the last iteration index * \param[in] threads_per_core number of threads to allocate per physical * core (default is 1). * \param[in] interleaved if set to \c true, indices are allocated to * threads with an interleaved pattern. - * - * \tparam Func functional object called at each loop iteration. It - * must accept a single argument of type index_t. - * - * \see ParallelForThread */ - template - inline void parallel_for( - const Func& func, index_t from, index_t to, + void GEOGRAM_API parallel_for( + index_t from, index_t to, std::function func, index_t threads_per_core = 1, bool interleaved = false - ) { -#ifdef GEO_OS_WINDOWS - // TODO: This is a limitation of WindowsThreadManager, to be fixed. - threads_per_core = 1; -#endif - - index_t nb_threads = std::min( - to - from, - Process::maximum_concurrent_threads() * threads_per_core - ); - - nb_threads = std::max(index_t(1), nb_threads); - - index_t batch_size = (to - from) / nb_threads; - if(Process::is_running_threads() || nb_threads == 1) { - for(index_t i = from; i < to; i++) { - const_cast (func)(i); - } - } else { - ThreadGroup threads; - if(interleaved) { - for(index_t i = 0; i < nb_threads; i++) { - threads.push_back( - new ParallelForThread( - func, from + i, to, nb_threads - ) - ); - } - } else { - index_t cur = from; - for(index_t i = 0; i < nb_threads; i++) { - if(i == nb_threads - 1) { - threads.push_back( - new ParallelForThread( - func, cur, to - ) - ); - } else { - threads.push_back( - new ParallelForThread( - func, cur, cur + batch_size - ) - ); - } - cur += batch_size; - } - } - Process::run_threads(threads); - } - } - - /** - * \brief Used to implement parallel_for_member_callback() - * \details Stores a pointer to an object and to a pointer - * function for invoking it later. - * \tparam T object class - */ - template class ParallelForMemberCallback { - public: - /** - * \brief Function pointer type. - */ - typedef void (T::*fptr)(index_t); + ); - /** - * \brief ParallelForMemberCallback constructor. - * \param[in] object a pointer to an object - * \param[in] f a pointer to a member function of the object, - * that takes an index_t as an argument - */ - ParallelForMemberCallback(T* object, fptr f) : - object_(object), f_(f) { - } - - /** - * \brief Invokes the memorized function on the memorized object - * with the argument \p i - * \param[in] i the argument to be passed to the function. - */ - void operator()(index_t i) { - (*object_.*f_)(i); - } - private: - T* object_; - fptr f_; - }; - /** - * \brief Creates a member callback for parallel_for() - * \details This allows to run parallel_for() with a functional object - * that calls the member function \p fun on object \p obj. - * \par Example: + * \brief Executes a loop with concurrent threads. + * + * \details + * When applicable, iterations are executed by concurrent + * threads: the range of the loop is split in to several contiguous + * sub-ranges, each of them being executed by a separate thread. + * + * Calling parallel_for(func, from, to) is equivalent + * to the following loop, computed in parallel: * \code - * struct MyClass { - * void iteration(index_t i) { ... } - * }; - * MyClass obj; - * parallel_for( - * parallel_for_member_callback(&obj, &MyClass::iteration), - * from, to - * ); + * func(from, i1); + * func(i1, i2); + * ... + * func(in, to); * \endcode - * \param[in] obj pointer to an object of type T - * \param[in] fun pointer to a void member function of object \p obj that - * accepts a single argument of type index_t. - * \tparam T type of the target object + * where i1,i2,...in are automatically generated. Typically one interval + * per physical core is generated. + * + * \param[in] func functional object that accepts two arguments of + * type index_t. + * \param[in] from first iteration index of the loop + * \param[in] to one position past the last iteration index + * \param[in] threads_per_core number of threads to allocate per physical + * core (default is 1). */ - - template ParallelForMemberCallback - parallel_for_member_callback(T* obj, void (T::* fun)(index_t)) { - return ParallelForMemberCallback(obj, fun); - } - + void GEOGRAM_API parallel_for_slice( + index_t from, index_t to, std::function func, + index_t threads_per_core = 1 + ); + + /** + * \brief Calls functions in parallel. + * \details Can be typically used with lambdas that capture this. See + * mesh/mesh_reorder.cpp and points/kd_tree.cpp for examples. + * \param[in] f1 , f2 functions to be called in parallel. + */ + void GEOGRAM_API parallel( + std::function f1, + std::function f2 + ); + + /** + * \brief Calls functions in parallel. + * \details Can be typically used with lambdas that capture this. See + * mesh/mesh_reorder.cpp and points/kd_tree.cpp for examples. + * \param[in] f1 , f2 , f3 , f4 functions to be called in parallel. + */ + void GEOGRAM_API parallel( + std::function f1, + std::function f2, + std::function f3, + std::function f4 + ); + + /** + * \brief Calls functions in parallel. + * \details Can be typically used with lambdas that capture this. See + * mesh/mesh_reorder.cpp and points/kd_tree.cpp for examples. + * \param[in] f1 , f2 , f3 , f4 , f5 , f6 , f7 , f8 functions + * to be called in parallel. + */ + void GEOGRAM_API parallel( + std::function f1, + std::function f2, + std::function f3, + std::function f4, + std::function f5, + std::function f6, + std::function f7, + std::function f8 + ); + } #endif diff --git a/src/lib/geogram/basic/process.h.new b/src/lib/geogram/basic/process.h.new new file mode 100644 index 00000000..c3b181dd --- /dev/null +++ b/src/lib/geogram/basic/process.h.new @@ -0,0 +1,803 @@ +/* + * Copyright (c) 2012-2014, Bruno Levy + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the ALICE Project-Team nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * If you modify this software, you should include a notice giving the + * name of the person performing the modification, the date of modification, + * and the reason for such modification. + * + * Contact: Bruno Levy + * + * Bruno.Levy@inria.fr + * http://www.loria.fr/~levy + * + * ALICE Project + * LORIA, INRIA Lorraine, + * Campus Scientifique, BP 239 + * 54506 VANDOEUVRE LES NANCY CEDEX + * FRANCE + * + */ + +#ifndef GEOGRAM_BASIC_PROCESS +#define GEOGRAM_BASIC_PROCESS + +#include +#include +#include +#include +#include + +/** + * \file geogram/basic/process.h + * \brief Function and classes for process manipulation + */ + +namespace GEO { + +// thread_local is supposed to be supported by c++0x, +// but some old MSVC compilers do not have it. +#if defined(GEO_COMPILER_MSVC) && !defined(thread_local) +# define thread_local __declspec(thread) +#endif + +// Older MAC OS X do not have thread_local +#ifdef GEO_OS_APPLE +# if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_9 +# define thread_local +# define GEO_NO_THREAD_LOCAL +# endif +#endif + + /** + * \brief Platform-independent base class for running threads. + * \details + * A Thread object manages one thread of control within the program. + * Thread%s begin executing with run(). Operational threads can be created + * by creating a derived class and reimplement function run(). + * + * Thread%s are reference-counted objects. Their allocation and + * destruction can be automatically managed with Thread_var. + */ + class GEOGRAM_API Thread : public Counted { + public: + + /** + * \brief Thread constructor. + */ + Thread() : id_(0) { + } + + /** + * \brief Starts the thread execution. + */ + virtual void run() = 0; + + /** + * \brief Gets the identifier of this thread. + * \return the identifier of the thread, i.e. + * an unsigned integer in the range [0, N-1] + * where N denotes the number of currently + * running threads. + */ + index_t id() const { + return id_; + } + + /** + * \brief Gets the current thread. + * \return A pointer to the instance of the + * currently running thread. + */ + static Thread* current(); + + protected: + /** Thread destructor */ + virtual ~Thread(); + + + private: + /** + * \brief Sets the identifier of this thread. + * \details This function is meant to be called + * by the thread manager for each created thread. + * \param[in] id_in the identifier of this thread. + */ + void set_id(index_t id_in) { + id_ = id_in; + } + + /** + * \brief Specifies the current instance, used by current(). + * \details Stores the specified thread in the thread-local-storage + * static variable so that current() can retrieve it. + * Should be called by the ThreadManager right before launching + * the threads. + * \param[in] thread a pointer to the thread currently executed + */ + static void set_current(Thread* thread); + + index_t id_; + + // ThreadManager needs to access set_current() and + // set_id(). + friend class ThreadManager; + }; + + /** Smart pointer that contains a Thread object */ + typedef SmartPointer Thread_var; + + /** + * \brief Collection of Thread%s + * \details ThreadGroup is a std::vector of Thread_var it provides the + * same operations for adding, removing or accessing thread elements. + * ThreadGroup takes ownership of Thread elements when they are added to + * the group, so there's is no need to delete Threads when the group is + * deleted. + */ + typedef std::vector ThreadGroup; + + /** + * \brief Typed collection of Thread%s. + * \details + * TypedThreadGroup is a ThreadGroup that provides a typed accessor with + * operator[](). + * \tparam THREAD the type of Thread%s in the collection + */ + template + class TypedThreadGroup : public ThreadGroup { + public: + /** + * \brief Creates an empty group of Thread%s + * \details Thread elements can be added with the std::vector + * operation push_back() + */ + TypedThreadGroup() { + } + + /** + * \brief Gets a thread element by index + * \param[in] i index of the element + * \return a pointer to the \p THREAD at position \p i in the + * thread group + */ + THREAD* operator[] (index_t i) { + geo_debug_assert(i < size()); + Thread* result = ThreadGroup::operator[] (i); + return static_cast(result); + } + }; + + /** + * \brief Platform-independent base class for running concurrent threads. + * \details + * The ThreadManager manager provides a platform-independent abstract + * interface for running concurrent Threads and managing critical + * sections. + * + * The ThreadManager is derived in multiple platform-specific or + * technology-specific implementations. + * + * Platform-specific implementations: + * - POSIX Thread manager (Unix) + * - Windows Threads manager (Windows) + * - Windows ThreadPool manager (Windows) + * + * Technology-specific implementations: + * - OpenMP-based manager + * + * Which ThreadManager to use is determined at runtime by + * Process::initialize() according to the current platform or the current + * available technology. + * + * \note For internal use only. + * \see Process::set_thread_manager() + */ + class GEOGRAM_API ThreadManager : public Counted { + public: + /** + * \brief Runs a group of Thread%s. + * \details + * This start the execution of the threads + * contained in vector \p threads. + * + * If the threads cannot be executed in a concurrent environment + * (multi-threading is disabled or the number of maximum threads is 1), + * then the threads are executed sequentially. Otherwise the function + * run_concurrent_threads() is called to execute the threads + * concurrently. The execution terminates when the last thread + * terminates. + * + * \param[in] threads the vector of threads to be executed. + * \see maximum_concurrent_threads() + * \see run_concurrent_threads() + * \see Process::max_threads() + */ + virtual void run_threads(ThreadGroup& threads); + + /** + * \brief Gets the maximum number of possible concurrent threads + * \return The maximum number of possible concurrent threads allowed + * by this manager. It depends on the physical number of cores + * (including hyper-threading or not) and the technology implemented + * by this manager. + * \see Process::number_of_cores() + */ + virtual index_t maximum_concurrent_threads() = 0; + + /** + * \brief Enters a critical section + * \details + * One thread at a time can enter the critical section, all the other + * threads that call this function are blocked until the blocking + * thread leaves the critical section. + * \see leave_critical_section() + */ + virtual void enter_critical_section() = 0; + + /** + * \brief Leaves a critical section + * \details When a blocking thread leaves a critical section, this + * makes the critical section available for a waiting thread. + * \see enter_critical_section() + */ + virtual void leave_critical_section() = 0; + + protected: + /** + * \brief Runs a group of Thread%s concurrently. + * \details This start the concurrent execution of the threads + * contained in vector \p threads, using the given number of threads + * \p max_threads. The execution terminates when the last thread + * terminates. + * \param[in] threads the vector of threads to be executed. + * \param[in] max_threads maximum number of threads allowed for this + * execution. It is always greater than one + */ + virtual void run_concurrent_threads( + ThreadGroup& threads, index_t max_threads + ) = 0; + + + /** + * \brief Sets the id of a thread. + * \details This function is called right before starting + * the threads. Each thread will have an id in [0, N-1] + * where N denotes the number of running threads. + * \param[in] thread the thread + * \param[in] id the id + */ + static void set_thread_id(Thread* thread, index_t id) { + thread->set_id(id); + } + + /** + * \brief Specifies the current instance, used by current(). + * \details Stores the specified thread in the thread-local-storage + * static variable so that current() can retrieve it. + * Should be called by the ThreadManager right before launching + * the threads. + * \param[in] thread a pointer to the thread currently executed + */ + static void set_current_thread(Thread* thread) { + Thread::set_current(thread); + } + + /** ThreadManager destructor */ + virtual ~ThreadManager(); + }; + + /** Smart pointer that contains a ThreadManager object */ + typedef SmartPointer ThreadManager_var; + + /** + * \brief Single thread ThreadManager + * \details MonoThreadingThreadManager implements a ThreadManager for + * single thread environments. + */ + class GEOGRAM_API MonoThreadingThreadManager : public ThreadManager { + public: + /** + * \copydoc ThreadManager::maximum_concurrent_threads() + * \note This implementation always returns 1. + */ + virtual index_t maximum_concurrent_threads(); + + /** + * \copydoc ThreadManager::enter_critical_section() + * \note This implementation does actually nothing + */ + virtual void enter_critical_section(); + + /** + * \copydoc ThreadManager::leave_critical_section() + * \note This implementation does actually nothing + */ + virtual void leave_critical_section(); + + protected: + /** MonoThreadingThreadManager destructor */ + virtual ~MonoThreadingThreadManager(); + + /** + * \copydoc ThreadManager::run_concurrent_threads() + * \note This implementation always executes threads sequentially. + */ + virtual void run_concurrent_threads( + ThreadGroup& threads, index_t max_threads + ); + }; + + /** + * \brief Abstraction layer for process management and multi-threading. + */ + namespace Process { + + /** + * \brief Initializes GeogramLib + * \param[in] flags the flags passed to GEO::initialize() + * \details This function must be called once before using + * any functionality of GeogramLib. + */ + void GEOGRAM_API initialize(int flags); + + /** + * \brief Terminates GeogramLib + * \details This function is called automatically when the program + * exits, so it should never be called directly. + */ + void GEOGRAM_API terminate(); + + /** + * \brief Displays statistics about the current process + * \details Displays the maximum used amount of memory. + */ + void GEOGRAM_API show_stats(); + + /** + * \brief Terminates the current process. + */ + void GEOGRAM_API brute_force_kill(); + + /** + * \brief Returns the maximum number of threads that can be running + * simultaneously. + * \retval The number of cores if multi-threading is supported + * \retval 1 otherwise. + */ + index_t GEOGRAM_API maximum_concurrent_threads(); + + /** + * \brief Runs a set of threads simultaneously + * \details Launches the execution of the threads contained in the + * vector \p threads and waits for the completion of all of them. + */ + void GEOGRAM_API run_threads(ThreadGroup& threads); + + /** + * \brief Enters a critical section + * \details One thread at a time can enter the critical section, + * all the other threads that call this function are blocked until the + * blocking thread leaves the critical section + * \see ThreadManager::enter_critical_section() + * \see leave_critical_section() + */ + void GEOGRAM_API enter_critical_section(); + + /** + * \brief Leaves a critical section + * \details When a blocking thread leaves a critical section, this + * makes the critical section available for a waiting thread. + * \see ThreadManager::leave_critical_section() + * \see enter_critical_section() + */ + void GEOGRAM_API leave_critical_section(); + + /** + * \brief Gets the number of available cores + * \return The number of available cores including the "virtual ones" if + * hyper-threading is activated. + */ + index_t GEOGRAM_API number_of_cores(); + + /** + * \brief Sets the thread manager (internal use). + * \details This sets the ThreadManager to use for concurrent thread + * execution. This function is called internally by + * Process::initialize() and should not be called explicitly. + * \note For internal use only + */ + void GEOGRAM_API set_thread_manager(ThreadManager* thread_manager); + + /** + * \brief Checks whether threads are running. + * \retval true if concurrent threads are currently running as an + * effect to Process::run_threads(). + * \retval false otherwise. + * \see Process::run_threads() + */ + bool GEOGRAM_API is_running_threads(); + + /** + * \brief Enables/disables floating point exceptions + * \details If FPEs are enabled, then floating point exceptions + * raise a SIGFPE signal, otherwise they generate NaNs. FPEs can also + * be configured by setting the value of the property "sys:FPE" with + * Environment::set_value(). + * \param[in] flag set to \c true to enable FPEs, \c false to disable. + * \see FPE_enabled() + */ + void GEOGRAM_API enable_FPE(bool flag); + + /** + * \brief Gets the status of floating point exceptions + * \retval true if FPE are enabled + * \retval false otherwise + * \see enable_FPE() + */ + bool GEOGRAM_API FPE_enabled(); + + /** + * \brief Enables/disables multi-threaded computations + * Multi-threading can also be configured by setting the value of the + * property "sys:multithread" with Environment::set_value(). + * \param[in] flag set to \c true to enable multi-threading, \c false + * to disable. + * \see multithreading_enabled() + */ + void GEOGRAM_API enable_multithreading(bool flag); + + /** + * \brief Gets the status of multi-threading + * \retval true if multi-threading is enabled + * \retval false otherwise + * \see enable_multithreading() + */ + bool GEOGRAM_API multithreading_enabled(); + + /** + * \brief Limits the number of concurrent threads to use + * \details The number of threads can also be configured by setting + * the value of the property "sys:max_threads" with + * Environment::set_value(). + * \param[in] num_threads maximum number of threads to use. + * \see max_threads() + */ + void GEOGRAM_API set_max_threads(index_t num_threads); + + /** + * \brief Gets the number of allowed concurrent threads + * \see set_max_threads() + */ + index_t GEOGRAM_API max_threads(); + + /** + * \brief Enables interruption of cancelable tasks + * \details This allows to interrupt cancelable tasks by typing + * CTRL-C in the terminal. This sets a specific handler on the + * interrupt signal that calls Progress::cancel() is there is a + * running cancelable task. If no task is running, the program is + * interrupted. The cancel mode can also be configured by setting the + * value of the property "sys:cancel" with + * Environment::set_value(). + * \param[in] flag set to \c true to enable cancel mode, \c false + * to disable. + * \see cancel_enabled() + */ + void GEOGRAM_API enable_cancel(bool flag); + + /** + * \brief Gets the status of the cancel mode + * \retval true if the cancel mode is enabled + * \retval false otherwise + * \see enable_cancel() + */ + bool GEOGRAM_API cancel_enabled(); + + /** + * \brief Gets the currently used memory. + * \return the used memory in bytes + */ + size_t GEOGRAM_API used_memory(); + + /** + * \brief Gets the maximum used memory. + * \return the maximum used memory in bytes + */ + size_t GEOGRAM_API max_used_memory(); + + + /** + * \brief Gets the full path to the currently + * running program. + */ + std::string GEOGRAM_API executable_filename(); + } + + /** + * \brief Thread class used internally by parallel_for() + * \details ParallelForThread is a helper Thread class used internally by + * function parallel_for(). It executes a portion of the global loop + * controlled by parallel_for(). At each iteration, ParallelForThread + * executes an action specified by the template parameter Func + * which must be a functional object. + * \tparam Func functional object called at each iteration. It must accept a + * single argument of type index_t. + */ + template + class ParallelForThread : public Thread { + public: + /** + * \brief Creates a thread for the execution of a parallel_for() + * \param[in] func the functional object called at each iteration + * \param[in] from start index of the loop + * \param[in] to stop index of the loop + * \param[in] step loop step + */ + ParallelForThread( + const Func& func, index_t from, index_t to, index_t step = 1 + ) : + func_(func), + from_(from), + to_(to), + step_(step) { + } + + /** + * \brief Starts the thread execution + * \details It executes the portion of loop configured in the + * constructor, calling the functional object at each iteration with + * the current iteration index. + */ + virtual void run() { + for(index_t i = from_; i < to_; i += step_) { + const_cast (func_)(i); + } + } + + protected: + /** ParallelForThread destructor */ + virtual ~ParallelForThread() { + } + + private: + const Func& func_; + index_t from_; + index_t to_; + index_t step_; + }; + + + /** + * \brief Thread class used internally by parallel_for_slice() + * \details ParallelForThread is a helper Thread class used internally by + * function parallel_for_slice(). It executes a portion of the global loop + * controlled by parallel_for_slice(). At each iteration, ParallelForThread + * executes an action specified by the template parameter Func + * which must be a functional object. + * \tparam Func functional object called at each iteration. + * It must accept two arguments of type index_t. + */ + template + class ParallelForSliceThread : public Thread { + public: + /** + * \brief Creates a thread for the execution of a parallel_for() + * \param[in] func the functional object called at each iteration + * \param[in] from start index of the loop + * \param[in] to stop index of the loop + */ + ParallelForSliceThread( + const Func& func, index_t from, index_t to + ) : + func_(func), + from_(from), + to_(to) { + } + + /** + * \brief Starts the thread execution + * \details It executes the portion of loop configured in the + * constructor, calling the functional object at each iteration with + * the current iteration index. + */ + virtual void run() { + const_cast (func_)(from_, to_); + } + + protected: + /** ParallelForSliceThread destructor */ + virtual ~ParallelForSliceThread() { + } + + private: + const Func& func_; + index_t from_; + index_t to_; + }; + + + /** + * \brief Executes a loop with concurrent threads. + * + * Executes a parallel for loop from index \p to index \p to, calling + * functional object \p func at each iteration. + * + * Calling parallel_for(func, from, to) is equivalent + * to the following loop, computed in parallel: + * \code + * for(index_t i = from; i < to; i++) { + * func(i) + * } + * \endcode + * + * When applicable, iterations are executed by concurrent ParallelForThread + * threads: the range of the loop is split in to several contiguous + * sub-ranges, each of them being executed by a separate + * ParallelForThread. + * + * If parameter \p interleaved is set to true, the loop range is + * decomposed in interleaved index sets that are executed by the + * ParallelForThread. Interleaved execution may improve cache coherency. + * + * \param[in] func functional object that accepts a single argument of + * type index_t. + * \param[in] from start index of the loop + * \param[in] to stop index of the loop + * \param[in] threads_per_core number of threads to allocate per physical + * core (default is 1). + * \param[in] interleaved if set to \c true, indices are allocated to + * threads with an interleaved pattern. + * + * \tparam Func functional object called at each loop iteration. It + * must accept a single argument of type index_t. + * + * \see ParallelForThread + */ + template + inline void parallel_for( + index_t from, index_t to, const Func& func, + index_t threads_per_core = 1, + bool interleaved = false + ) { +#ifdef GEO_OS_WINDOWS + // TODO: This is a limitation of WindowsThreadManager, to be fixed. + threads_per_core = 1; +#endif + + index_t nb_threads = std::min( + to - from, + Process::maximum_concurrent_threads() * threads_per_core + ); + + nb_threads = std::max(index_t(1), nb_threads); + + index_t batch_size = (to - from) / nb_threads; + if(Process::is_running_threads() || nb_threads == 1) { + for(index_t i = from; i < to; i++) { + const_cast (func)(i); + } + } else { + ThreadGroup threads; + if(interleaved) { + for(index_t i = 0; i < nb_threads; i++) { + threads.push_back( + new ParallelForThread( + func, from + i, to, nb_threads + ) + ); + } + } else { + index_t cur = from; + for(index_t i = 0; i < nb_threads; i++) { + if(i == nb_threads - 1) { + threads.push_back( + new ParallelForThread( + func, cur, to + ) + ); + } else { + threads.push_back( + new ParallelForThread( + func, cur, cur + batch_size + ) + ); + } + cur += batch_size; + } + } + Process::run_threads(threads); + } + } + + + /** + * \brief Executes a loop with concurrent threads. + * + * When applicable, iterations are executed by concurrent ParallelForThread + * threads: the range of the loop is split in to several contiguous + * sub-ranges, each of them being executed by a separate + * ParallelForThread. + * + * \param[in] func functional object that accepts two arguments of + * type index_t. + * \param[in] from start index of the loop + * \param[in] to stop index of the loop + * \param[in] threads_per_core number of threads to allocate per physical + * core (default is 1). + * + * \tparam Func functional object called at each loop iteration. It + * must accept two arguments of type index_t. + * + * \see ParallelForSliceThread + */ + template + inline void parallel_for_slice( + index_t from, index_t to, const Func& func, + index_t threads_per_core = 1 + ) { +#ifdef GEO_OS_WINDOWS + // TODO: This is a limitation of WindowsThreadManager, to be fixed. + threads_per_core = 1; +#endif + + index_t nb_threads = std::min( + to - from, + Process::maximum_concurrent_threads() * threads_per_core + ); + + nb_threads = std::max(index_t(1), nb_threads); + + index_t batch_size = (to - from) / nb_threads; + if(Process::is_running_threads() || nb_threads == 1) { + const_cast (func)(from, to); + } else { + ThreadGroup threads; + index_t cur = from; + for(index_t i = 0; i < nb_threads; i++) { + if(i == nb_threads - 1) { + threads.push_back( + new ParallelForSliceThread( + func, cur, to + ) + ); + } else { + threads.push_back( + new ParallelForSliceThread( + func, cur, cur + batch_size + ) + ); + } + cur += batch_size; + } + Process::run_threads(threads); + } + } +} + +#endif + diff --git a/src/lib/geogram/basic/process.h.old b/src/lib/geogram/basic/process.h.old new file mode 100644 index 00000000..2473e451 --- /dev/null +++ b/src/lib/geogram/basic/process.h.old @@ -0,0 +1,866 @@ +/* + * Copyright (c) 2012-2014, Bruno Levy + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the ALICE Project-Team nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * If you modify this software, you should include a notice giving the + * name of the person performing the modification, the date of modification, + * and the reason for such modification. + * + * Contact: Bruno Levy + * + * Bruno.Levy@inria.fr + * http://www.loria.fr/~levy + * + * ALICE Project + * LORIA, INRIA Lorraine, + * Campus Scientifique, BP 239 + * 54506 VANDOEUVRE LES NANCY CEDEX + * FRANCE + * + */ + +#ifndef GEOGRAM_BASIC_PROCESS +#define GEOGRAM_BASIC_PROCESS + +#include +#include +#include +#include +#include + +/** + * \file geogram/basic/process.h + * \brief Function and classes for process manipulation + */ + +namespace GEO { + +// thread_local is supposed to be supported by c++0x, +// but some old MSVC compilers do not have it. +#if defined(GEO_COMPILER_MSVC) && !defined(thread_local) +# define thread_local __declspec(thread) +#endif + +// Older MAC OS X do not have thread_local +#ifdef GEO_OS_APPLE +# if MAC_OS_X_VERSION_MIN_REQUIRED <= MAC_OS_X_VERSION_10_9 +# define thread_local +# define GEO_NO_THREAD_LOCAL +# endif +#endif + + /** + * \brief Platform-independent base class for running threads. + * \details + * A Thread object manages one thread of control within the program. + * Thread%s begin executing with run(). Operational threads can be created + * by creating a derived class and reimplement function run(). + * + * Thread%s are reference-counted objects. Their allocation and + * destruction can be automatically managed with Thread_var. + */ + class GEOGRAM_API Thread : public Counted { + public: + + /** + * \brief Thread constructor. + */ + Thread() : id_(0) { + } + + /** + * \brief Starts the thread execution. + */ + virtual void run() = 0; + + /** + * \brief Gets the identifier of this thread. + * \return the identifier of the thread, i.e. + * an unsigned integer in the range [0, N-1] + * where N denotes the number of currently + * running threads. + */ + index_t id() const { + return id_; + } + + /** + * \brief Gets the current thread. + * \return A pointer to the instance of the + * currently running thread. + */ + static Thread* current(); + + protected: + /** Thread destructor */ + virtual ~Thread(); + + + private: + /** + * \brief Sets the identifier of this thread. + * \details This function is meant to be called + * by the thread manager for each created thread. + * \param[in] id_in the identifier of this thread. + */ + void set_id(index_t id_in) { + id_ = id_in; + } + + /** + * \brief Specifies the current instance, used by current(). + * \details Stores the specified thread in the thread-local-storage + * static variable so that current() can retrieve it. + * Should be called by the ThreadManager right before launching + * the threads. + * \param[in] thread a pointer to the thread currently executed + */ + static void set_current(Thread* thread); + + index_t id_; + + // ThreadManager needs to access set_current() and + // set_id(). + friend class ThreadManager; + }; + + /** Smart pointer that contains a Thread object */ + typedef SmartPointer Thread_var; + + /** + * \brief Collection of Thread%s + * \details ThreadGroup is a std::vector of Thread_var it provides the + * same operations for adding, removing or accessing thread elements. + * ThreadGroup takes ownership of Thread elements when they are added to + * the group, so there's is no need to delete Threads when the group is + * deleted. + */ + typedef std::vector ThreadGroup; + + /** + * \brief Typed collection of Thread%s. + * \details + * TypedThreadGroup is a ThreadGroup that provides a typed accessor with + * operator[](). + * \tparam THREAD the type of Thread%s in the collection + */ + template + class TypedThreadGroup : public ThreadGroup { + public: + /** + * \brief Creates an empty group of Thread%s + * \details Thread elements can be added with the std::vector + * operation push_back() + */ + TypedThreadGroup() { + } + + /** + * \brief Gets a thread element by index + * \param[in] i index of the element + * \return a pointer to the \p THREAD at position \p i in the + * thread group + */ + THREAD* operator[] (index_t i) { + geo_debug_assert(i < size()); + Thread* result = ThreadGroup::operator[] (i); + return static_cast(result); + } + }; + + /** + * \brief Platform-independent base class for running concurrent threads. + * \details + * The ThreadManager manager provides a platform-independent abstract + * interface for running concurrent Threads and managing critical + * sections. + * + * The ThreadManager is derived in multiple platform-specific or + * technology-specific implementations. + * + * Platform-specific implementations: + * - POSIX Thread manager (Unix) + * - Windows Threads manager (Windows) + * - Windows ThreadPool manager (Windows) + * + * Technology-specific implementations: + * - OpenMP-based manager + * + * Which ThreadManager to use is determined at runtime by + * Process::initialize() according to the current platform or the current + * available technology. + * + * \note For internal use only. + * \see Process::set_thread_manager() + */ + class GEOGRAM_API ThreadManager : public Counted { + public: + /** + * \brief Runs a group of Thread%s. + * \details + * This start the execution of the threads + * contained in vector \p threads. + * + * If the threads cannot be executed in a concurrent environment + * (multi-threading is disabled or the number of maximum threads is 1), + * then the threads are executed sequentially. Otherwise the function + * run_concurrent_threads() is called to execute the threads + * concurrently. The execution terminates when the last thread + * terminates. + * + * \param[in] threads the vector of threads to be executed. + * \see maximum_concurrent_threads() + * \see run_concurrent_threads() + * \see Process::max_threads() + */ + virtual void run_threads(ThreadGroup& threads); + + /** + * \brief Gets the maximum number of possible concurrent threads + * \return The maximum number of possible concurrent threads allowed + * by this manager. It depends on the physical number of cores + * (including hyper-threading or not) and the technology implemented + * by this manager. + * \see Process::number_of_cores() + */ + virtual index_t maximum_concurrent_threads() = 0; + + /** + * \brief Enters a critical section + * \details + * One thread at a time can enter the critical section, all the other + * threads that call this function are blocked until the blocking + * thread leaves the critical section. + * \see leave_critical_section() + */ + virtual void enter_critical_section() = 0; + + /** + * \brief Leaves a critical section + * \details When a blocking thread leaves a critical section, this + * makes the critical section available for a waiting thread. + * \see enter_critical_section() + */ + virtual void leave_critical_section() = 0; + + protected: + /** + * \brief Runs a group of Thread%s concurrently. + * \details This start the concurrent execution of the threads + * contained in vector \p threads, using the given number of threads + * \p max_threads. The execution terminates when the last thread + * terminates. + * \param[in] threads the vector of threads to be executed. + * \param[in] max_threads maximum number of threads allowed for this + * execution. It is always greater than one + */ + virtual void run_concurrent_threads( + ThreadGroup& threads, index_t max_threads + ) = 0; + + + /** + * \brief Sets the id of a thread. + * \details This function is called right before starting + * the threads. Each thread will have an id in [0, N-1] + * where N denotes the number of running threads. + * \param[in] thread the thread + * \param[in] id the id + */ + static void set_thread_id(Thread* thread, index_t id) { + thread->set_id(id); + } + + /** + * \brief Specifies the current instance, used by current(). + * \details Stores the specified thread in the thread-local-storage + * static variable so that current() can retrieve it. + * Should be called by the ThreadManager right before launching + * the threads. + * \param[in] thread a pointer to the thread currently executed + */ + static void set_current_thread(Thread* thread) { + Thread::set_current(thread); + } + + /** ThreadManager destructor */ + virtual ~ThreadManager(); + }; + + /** Smart pointer that contains a ThreadManager object */ + typedef SmartPointer ThreadManager_var; + + /** + * \brief Single thread ThreadManager + * \details MonoThreadingThreadManager implements a ThreadManager for + * single thread environments. + */ + class GEOGRAM_API MonoThreadingThreadManager : public ThreadManager { + public: + /** + * \copydoc ThreadManager::maximum_concurrent_threads() + * \note This implementation always returns 1. + */ + virtual index_t maximum_concurrent_threads(); + + /** + * \copydoc ThreadManager::enter_critical_section() + * \note This implementation does actually nothing + */ + virtual void enter_critical_section(); + + /** + * \copydoc ThreadManager::leave_critical_section() + * \note This implementation does actually nothing + */ + virtual void leave_critical_section(); + + protected: + /** MonoThreadingThreadManager destructor */ + virtual ~MonoThreadingThreadManager(); + + /** + * \copydoc ThreadManager::run_concurrent_threads() + * \note This implementation always executes threads sequentially. + */ + virtual void run_concurrent_threads( + ThreadGroup& threads, index_t max_threads + ); + }; + + /** + * \brief Abstraction layer for process management and multi-threading. + */ + namespace Process { + + /** + * \brief Initializes GeogramLib + * \param[in] flags the flags passed to GEO::initialize() + * \details This function must be called once before using + * any functionality of GeogramLib. + */ + void GEOGRAM_API initialize(int flags); + + /** + * \brief Terminates GeogramLib + * \details This function is called automatically when the program + * exits, so it should never be called directly. + */ + void GEOGRAM_API terminate(); + + /** + * \brief Displays statistics about the current process + * \details Displays the maximum used amount of memory. + */ + void GEOGRAM_API show_stats(); + + /** + * \brief Terminates the current process. + */ + void GEOGRAM_API brute_force_kill(); + + /** + * \brief Returns the maximum number of threads that can be running + * simultaneously. + * \retval The number of cores if multi-threading is supported + * \retval 1 otherwise. + */ + index_t GEOGRAM_API maximum_concurrent_threads(); + + /** + * \brief Runs a set of threads simultaneously + * \details Launches the execution of the threads contained in the + * vector \p threads and waits for the completion of all of them. + */ + void GEOGRAM_API run_threads(ThreadGroup& threads); + + /** + * \brief Enters a critical section + * \details One thread at a time can enter the critical section, + * all the other threads that call this function are blocked until the + * blocking thread leaves the critical section + * \see ThreadManager::enter_critical_section() + * \see leave_critical_section() + */ + void GEOGRAM_API enter_critical_section(); + + /** + * \brief Leaves a critical section + * \details When a blocking thread leaves a critical section, this + * makes the critical section available for a waiting thread. + * \see ThreadManager::leave_critical_section() + * \see enter_critical_section() + */ + void GEOGRAM_API leave_critical_section(); + + /** + * \brief Gets the number of available cores + * \return The number of available cores including the "virtual ones" if + * hyper-threading is activated. + */ + index_t GEOGRAM_API number_of_cores(); + + /** + * \brief Sets the thread manager (internal use). + * \details This sets the ThreadManager to use for concurrent thread + * execution. This function is called internally by + * Process::initialize() and should not be called explicitly. + * \note For internal use only + */ + void GEOGRAM_API set_thread_manager(ThreadManager* thread_manager); + + /** + * \brief Checks whether threads are running. + * \retval true if concurrent threads are currently running as an + * effect to Process::run_threads(). + * \retval false otherwise. + * \see Process::run_threads() + */ + bool GEOGRAM_API is_running_threads(); + + /** + * \brief Enables/disables floating point exceptions + * \details If FPEs are enabled, then floating point exceptions + * raise a SIGFPE signal, otherwise they generate NaNs. FPEs can also + * be configured by setting the value of the property "sys:FPE" with + * Environment::set_value(). + * \param[in] flag set to \c true to enable FPEs, \c false to disable. + * \see FPE_enabled() + */ + void GEOGRAM_API enable_FPE(bool flag); + + /** + * \brief Gets the status of floating point exceptions + * \retval true if FPE are enabled + * \retval false otherwise + * \see enable_FPE() + */ + bool GEOGRAM_API FPE_enabled(); + + /** + * \brief Enables/disables multi-threaded computations + * Multi-threading can also be configured by setting the value of the + * property "sys:multithread" with Environment::set_value(). + * \param[in] flag set to \c true to enable multi-threading, \c false + * to disable. + * \see multithreading_enabled() + */ + void GEOGRAM_API enable_multithreading(bool flag); + + /** + * \brief Gets the status of multi-threading + * \retval true if multi-threading is enabled + * \retval false otherwise + * \see enable_multithreading() + */ + bool GEOGRAM_API multithreading_enabled(); + + /** + * \brief Limits the number of concurrent threads to use + * \details The number of threads can also be configured by setting + * the value of the property "sys:max_threads" with + * Environment::set_value(). + * \param[in] num_threads maximum number of threads to use. + * \see max_threads() + */ + void GEOGRAM_API set_max_threads(index_t num_threads); + + /** + * \brief Gets the number of allowed concurrent threads + * \see set_max_threads() + */ + index_t GEOGRAM_API max_threads(); + + /** + * \brief Enables interruption of cancelable tasks + * \details This allows to interrupt cancelable tasks by typing + * CTRL-C in the terminal. This sets a specific handler on the + * interrupt signal that calls Progress::cancel() is there is a + * running cancelable task. If no task is running, the program is + * interrupted. The cancel mode can also be configured by setting the + * value of the property "sys:cancel" with + * Environment::set_value(). + * \param[in] flag set to \c true to enable cancel mode, \c false + * to disable. + * \see cancel_enabled() + */ + void GEOGRAM_API enable_cancel(bool flag); + + /** + * \brief Gets the status of the cancel mode + * \retval true if the cancel mode is enabled + * \retval false otherwise + * \see enable_cancel() + */ + bool GEOGRAM_API cancel_enabled(); + + /** + * \brief Gets the currently used memory. + * \return the used memory in bytes + */ + size_t GEOGRAM_API used_memory(); + + /** + * \brief Gets the maximum used memory. + * \return the maximum used memory in bytes + */ + size_t GEOGRAM_API max_used_memory(); + + + /** + * \brief Gets the full path to the currently + * running program. + */ + std::string GEOGRAM_API executable_filename(); + } + + /** + * \brief Thread class used internally by parallel_for() + * \details ParallelForThread is a helper Thread class used internally by + * function parallel_for(). It executes a portion of the global loop + * controlled by parallel_for(). At each iteration, ParallelForThread + * executes an action specified by the template parameter Func + * which must be a functional object. + * \tparam Func functional object called at each iteration. It must accept a + * single argument of type index_t. + */ + template + class ParallelForThread : public Thread { + public: + /** + * \brief Creates a thread for the execution of a parallel_for() + * \param[in] func the functional object called at each iteration + * \param[in] from start index of the loop + * \param[in] to stop index of the loop + * \param[in] step loop step + */ + ParallelForThread( + const Func& func, index_t from, index_t to, index_t step = 1 + ) : + func_(func), + from_(from), + to_(to), + step_(step) { + } + + /** + * \brief Starts the thread execution + * \details It executes the portion of loop configured in the + * constructor, calling the functional object at each iteration with + * the current iteration index. + */ + virtual void run() { + for(index_t i = from_; i < to_; i += step_) { + const_cast (func_)(i); + } + } + + protected: + /** ParallelForThread destructor */ + virtual ~ParallelForThread() { + } + + private: + const Func& func_; + index_t from_; + index_t to_; + index_t step_; + }; + + + /** + * \brief Thread class used internally by parallel_for_slice() + * \details ParallelForThread is a helper Thread class used internally by + * function parallel_for_slice(). It executes a portion of the global loop + * controlled by parallel_for_slice(). At each iteration, ParallelForThread + * executes an action specified by the template parameter Func + * which must be a functional object. + * \tparam Func functional object called at each iteration. + * It must accept two arguments of type index_t. + */ + template + class ParallelForSliceThread : public Thread { + public: + /** + * \brief Creates a thread for the execution of a parallel_for() + * \param[in] func the functional object called at each iteration + * \param[in] from start index of the loop + * \param[in] to stop index of the loop + */ + ParallelForSliceThread( + const Func& func, index_t from, index_t to + ) : + func_(func), + from_(from), + to_(to) { + } + + /** + * \brief Starts the thread execution + * \details It executes the portion of loop configured in the + * constructor, calling the functional object at each iteration with + * the current iteration index. + */ + virtual void run() { + const_cast (func_)(from_, to_); + } + + protected: + /** ParallelForSliceThread destructor */ + virtual ~ParallelForSliceThread() { + } + + private: + const Func& func_; + index_t from_; + index_t to_; + }; + + + /** + * \brief Executes a loop with concurrent threads. + * + * Executes a parallel for loop from index \p to index \p to, calling + * functional object \p func at each iteration. + * + * Calling parallel_for(func, from, to) is equivalent + * to the following loop, computed in parallel: + * \code + * for(index_t i = from; i < to; i++) { + * func(i) + * } + * \endcode + * + * When applicable, iterations are executed by concurrent ParallelForThread + * threads: the range of the loop is split in to several contiguous + * sub-ranges, each of them being executed by a separate + * ParallelForThread. + * + * If parameter \p interleaved is set to true, the loop range is + * decomposed in interleaved index sets that are executed by the + * ParallelForThread. Interleaved execution may improve cache coherency. + * + * \param[in] func functional object that accepts a single argument of + * type index_t. + * \param[in] from start index of the loop + * \param[in] to stop index of the loop + * \param[in] threads_per_core number of threads to allocate per physical + * core (default is 1). + * \param[in] interleaved if set to \c true, indices are allocated to + * threads with an interleaved pattern. + * + * \tparam Func functional object called at each loop iteration. It + * must accept a single argument of type index_t. + * + * \see ParallelForThread + */ + template + inline void parallel_for( + index_t from, index_t to, const Func& func, + index_t threads_per_core = 1, + bool interleaved = false + ) { +#ifdef GEO_OS_WINDOWS + // TODO: This is a limitation of WindowsThreadManager, to be fixed. + threads_per_core = 1; +#endif + + index_t nb_threads = std::min( + to - from, + Process::maximum_concurrent_threads() * threads_per_core + ); + + nb_threads = std::max(index_t(1), nb_threads); + + index_t batch_size = (to - from) / nb_threads; + if(Process::is_running_threads() || nb_threads == 1) { + for(index_t i = from; i < to; i++) { + const_cast (func)(i); + } + } else { + ThreadGroup threads; + if(interleaved) { + for(index_t i = 0; i < nb_threads; i++) { + threads.push_back( + new ParallelForThread( + func, from + i, to, nb_threads + ) + ); + } + } else { + index_t cur = from; + for(index_t i = 0; i < nb_threads; i++) { + if(i == nb_threads - 1) { + threads.push_back( + new ParallelForThread( + func, cur, to + ) + ); + } else { + threads.push_back( + new ParallelForThread( + func, cur, cur + batch_size + ) + ); + } + cur += batch_size; + } + } + Process::run_threads(threads); + } + } + + + /** + * \brief Executes a loop with concurrent threads. + * + * When applicable, iterations are executed by concurrent ParallelForThread + * threads: the range of the loop is split in to several contiguous + * sub-ranges, each of them being executed by a separate + * ParallelForThread. + * + * \param[in] func functional object that accepts two arguments of + * type index_t. + * \param[in] from start index of the loop + * \param[in] to stop index of the loop + * \param[in] threads_per_core number of threads to allocate per physical + * core (default is 1). + * + * \tparam Func functional object called at each loop iteration. It + * must accept two arguments of type index_t. + * + * \see ParallelForSliceThread + */ + template + inline void parallel_for_slice( + index_t from, index_t to, const Func& func, + index_t threads_per_core = 1 + ) { +#ifdef GEO_OS_WINDOWS + // TODO: This is a limitation of WindowsThreadManager, to be fixed. + threads_per_core = 1; +#endif + + index_t nb_threads = std::min( + to - from, + Process::maximum_concurrent_threads() * threads_per_core + ); + + nb_threads = std::max(index_t(1), nb_threads); + + index_t batch_size = (to - from) / nb_threads; + if(Process::is_running_threads() || nb_threads == 1) { + const_cast (func)(from, to); + } else { + ThreadGroup threads; + index_t cur = from; + for(index_t i = 0; i < nb_threads; i++) { + if(i == nb_threads - 1) { + threads.push_back( + new ParallelForSliceThread( + func, cur, to + ) + ); + } else { + threads.push_back( + new ParallelForSliceThread( + func, cur, cur + batch_size + ) + ); + } + cur += batch_size; + } + Process::run_threads(threads); + } + } + + /** + * \brief Used to implement parallel_for_member_callback() + * \details Stores a pointer to an object and to a pointer + * function for invoking it later. + * \tparam T object class + */ + template class ParallelForMemberCallback { + public: + /** + * \brief Function pointer type. + */ + typedef void (T::*fptr)(index_t); + + /** + * \brief ParallelForMemberCallback constructor. + * \param[in] object a pointer to an object + * \param[in] f a pointer to a member function of the object, + * that takes an index_t as an argument + */ + ParallelForMemberCallback(T* object, fptr f) : + object_(object), f_(f) { + } + + /** + * \brief Invokes the memorized function on the memorized object + * with the argument \p i + * \param[in] i the argument to be passed to the function. + */ + void operator()(index_t i) { + (*object_.*f_)(i); + } + private: + T* object_; + fptr f_; + }; + + /** + * \brief Creates a member callback for parallel_for() + * \details This allows to run parallel_for() with a functional object + * that calls the member function \p fun on object \p obj. + * \par Example: + * \code + * struct MyClass { + * void iteration(index_t i) { ... } + * }; + * MyClass obj; + * parallel_for( + * parallel_for_member_callback(&obj, &MyClass::iteration), + * from, to + * ); + * \endcode + * \param[in] obj pointer to an object of type T + * \param[in] fun pointer to a void member function of object \p obj that + * accepts a single argument of type index_t. + * \tparam T type of the target object + */ + + template ParallelForMemberCallback + parallel_for_member_callback(T* obj, void (T::* fun)(index_t)) { + return ParallelForMemberCallback(obj, fun); + } + +} + +#endif + diff --git a/src/lib/geogram/basic/process_unix.cpp b/src/lib/geogram/basic/process_unix.cpp index c59d3468..da564a66 100644 --- a/src/lib/geogram/basic/process_unix.cpp +++ b/src/lib/geogram/basic/process_unix.cpp @@ -163,10 +163,10 @@ namespace { /** \copydoc GEO::ThreadManager::enter_critical_section() */ void enter_critical_section() override { -#if defined(GEO_OS_ANDROID) - lock_mutex_android(&mutex_); -#elif defined(GEO_OS_ANDROID) +#if defined(GEO_OS_RASPBERRY) lock_mutex_arm32(&mutex_); +#elif defined(GEO_OS_ANDROID) + lock_mutex_android(&mutex_); #else pthread_mutex_lock(&mutex_); #endif diff --git a/src/lib/geogram/basic/stopwatch.cpp b/src/lib/geogram/basic/stopwatch.cpp old mode 100755 new mode 100644 index ca3a5382..1112ac8d --- a/src/lib/geogram/basic/stopwatch.cpp +++ b/src/lib/geogram/basic/stopwatch.cpp @@ -56,7 +56,7 @@ namespace GEO { SystemStopwatch::SystemStopwatch() { #if defined(GEO_OS_WINDOWS) - start_ = GetTickCount(); + start_ = long(GetTickCount()); #elif defined(GEO_OS_EMSCRIPTEN) startf_ = now(); #else @@ -68,7 +68,7 @@ namespace GEO { double SystemStopwatch::elapsed_user_time() const { #if defined(GEO_OS_WINDOWS) - return double(GetTickCount() - start_) / 1000.0; + return double(long(GetTickCount()) - start_) / 1000.0; #elif defined(GEO_OS_EMSCRIPTEN) return now() - startf_; #else diff --git a/src/lib/geogram/basic/thread_sync.h b/src/lib/geogram/basic/thread_sync.h index 65b99f1b..fb9c4787 100644 --- a/src/lib/geogram/basic/thread_sync.h +++ b/src/lib/geogram/basic/thread_sync.h @@ -122,7 +122,7 @@ namespace GEO { unlock_mutex_android(&x); } -#elif defined(GEO_OS_LINUX) +#elif defined(GEO_OS_LINUX) || defined(GEO_COMPILER_MINGW) /** A lightweight synchronization structure. */ typedef unsigned char spinlock; @@ -183,7 +183,7 @@ namespace GEO { } #endif // __MAC_10_12 -#elif defined(GEO_OS_WINDOWS) +#elif defined(GEO_OS_WINDOWS) && !defined(GEO_COMPILER_MINGW) /** A lightweight synchronization structure. */ typedef short spinlock; @@ -669,7 +669,7 @@ namespace GEO { geo_thread_sync_assert(i < size()); index_t w = i >> 5; index_t b = i & 31; - while(_interlockedbittestandset(&spinlocks_[w], b)) { + while(_interlockedbittestandset((long *)(&spinlocks_[w]), long(b))) { // Intel recommends to have a PAUSE asm instruction // in the spinlock loop. Under MSVC/Windows, // YieldProcessor() is a macro that calls the @@ -696,7 +696,7 @@ namespace GEO { // Note2: We do not need here _WriteBarrier() since // _interlockedbittestandreset // "acts as a full barrier in VC2005" according to the doc - _interlockedbittestandreset(&spinlocks_[w], b); + _interlockedbittestandreset((long*)(&spinlocks_[w]), long(b)); } private: diff --git a/src/lib/geogram/delaunay/delaunay.cpp b/src/lib/geogram/delaunay/delaunay.cpp index 7fa1d6f9..e0591e14 100755 --- a/src/lib/geogram/delaunay/delaunay.cpp +++ b/src/lib/geogram/delaunay/delaunay.cpp @@ -283,8 +283,9 @@ namespace GEO { } } parallel_for( - parallel_for_member_callback(this, &Delaunay::store_neighbors_CB), - 0, nb_vertices(), 1, true + 0, nb_vertices(), + [this](index_t i) { store_neighbors_CB(i); }, + 1, true ); } diff --git a/src/lib/geogram/delaunay/delaunay.cpp.new b/src/lib/geogram/delaunay/delaunay.cpp.new new file mode 100755 index 00000000..9b3c7236 --- /dev/null +++ b/src/lib/geogram/delaunay/delaunay.cpp.new @@ -0,0 +1,441 @@ +/* + * Copyright (c) 2012-2014, Bruno Levy + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the ALICE Project-Team nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * If you modify this software, you should include a notice giving the + * name of the person performing the modification, the date of modification, + * and the reason for such modification. + * + * Contact: Bruno Levy + * + * Bruno.Levy@inria.fr + * http://www.loria.fr/~levy + * + * ALICE Project + * LORIA, INRIA Lorraine, + * Campus Scientifique, BP 239 + * 54506 VANDOEUVRE LES NANCY CEDEX + * FRANCE + * + */ + +#include +#include +#include +#include + +#ifdef GEOGRAM_WITH_PDEL +#include +#endif + +#ifdef GEOGRAM_WITH_TETGEN +#include +#endif + +#ifdef GEOGRAM_WITH_TRIANGLE +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include + +namespace { + + using namespace GEO; + + /** + * \brief Builds the invalid dimension error message + * \param[in] dimension the specified dimension + * \param[in] name the name of the Delaunay implementation + * \param[in] expected the expected dimension + * \return a string that contains the error message + */ + std::string invalid_dimension_error( + coord_index_t dimension, + const char* name, + const char* expected + ) { + std::ostringstream out; + out << "Invalid dimension: dimension " << index_t(dimension) + << " is not supported by the " << name + << " algorithm. Supported dimension(s): " << expected; + return out.str(); + } +} + +namespace GEO { + + Delaunay::InvalidDimension::InvalidDimension( + coord_index_t dimension, + const char* name, + const char* expected + ) : + std::logic_error(invalid_dimension_error(dimension, name, expected)) { + } + + const char* Delaunay::InvalidDimension::what() const GEO_NOEXCEPT { + return std::logic_error::what(); + } + + + Delaunay::InvalidInput::InvalidInput(int code) : + logic_error("Invalid input for Delaunay"), + error_code(code) { + } + + Delaunay::InvalidInput::InvalidInput( + const InvalidInput& rhs + ) : + std::logic_error(rhs), + error_code(rhs.error_code), + invalid_facets(rhs.invalid_facets) { + } + + Delaunay::InvalidInput::~InvalidInput() GEO_NOEXCEPT { + } + + const char* Delaunay::InvalidInput::what() const GEO_NOEXCEPT { + return std::logic_error::what(); + } + + /************************************************************************/ + + void Delaunay::initialize() { + +#ifdef GEOGRAM_WITH_TETGEN + geo_register_Delaunay_creator(DelaunayTetgen, "tetgen"); +#endif + +#ifdef GEOGRAM_WITH_TRIANGLE + geo_register_Delaunay_creator(DelaunayTriangle, "triangle"); +#endif + + geo_register_Delaunay_creator(Delaunay3d, "BDEL"); + +#ifdef GEOGRAM_WITH_PDEL + geo_register_Delaunay_creator(ParallelDelaunay3d, "PDEL"); +#endif + geo_register_Delaunay_creator(RegularWeightedDelaunay3d, "BPOW"); + + geo_register_Delaunay_creator(Delaunay2d, "BDEL2d"); + geo_register_Delaunay_creator(RegularWeightedDelaunay2d, "BPOW2d"); + +#ifndef GEOGRAM_PSM + geo_register_Delaunay_creator(Delaunay_NearestNeighbors, "NN"); +#endif + } + + Delaunay* Delaunay::create( + coord_index_t dim, const std::string& name_in + ) { + + std::string name = name_in; + if(name == "default") { + name = CmdLine::get_arg("algo:delaunay"); + } + + try { + Delaunay* d = DelaunayFactory::create_object(name, dim); + if(d != nullptr) { + return d; + } + + Logger::warn("Delaunay") + << "Could not create Delaunay triangulation: " << name + << std::endl; + } + catch(InvalidDimension& ex) { + Logger::warn("Delaunay") << ex.what() << std::endl; + } + +#ifdef GEOGRAM_PSM + Logger::err("Delaunay") + << "Could not create Delaunay triangulation" + << std::endl; + return nullptr; +#else + Logger::warn("Delaunay") + << "Falling back to NN mode" + << std::endl; + + return new Delaunay_NearestNeighbors(dim); +#endif + } + + Delaunay::Delaunay(coord_index_t dimension) { + set_dimension(dimension); + vertices_ = nullptr; + nb_vertices_ = 0; + nb_cells_ = 0; + cell_to_v_ = nullptr; + cell_to_cell_ = nullptr; + is_locked_ = false; + store_neighbors_ = false; + default_nb_neighbors_ = 30; + constraints_ = nullptr; + do_reorder_ = true; + refine_ = false; + quality_ = 2.0; + store_cicl_ = false; + keep_infinite_ = false; + nb_finite_cells_ = 0; + keep_regions_ = false; + } + + Delaunay::~Delaunay() { + } + + void Delaunay::set_vertices( + index_t nb_vertices, const double* vertices + ) { + nb_vertices_ = nb_vertices; + vertices_ = vertices; + if(nb_vertices_ < index_t(dimension()) + 1) { + Logger::warn("Delaunay") << "Only " + << nb_vertices + << " vertices, may be not enough !" + << std::endl; + } + } + + void Delaunay::set_BRIO_levels(const vector& levels) { + geo_argused(levels); + // Default implementation does nothing + } + + void Delaunay::set_arrays( + index_t nb_cells, + const signed_index_t* cell_to_v, const signed_index_t* cell_to_cell + ) { + nb_cells_ = nb_cells; + cell_to_v_ = cell_to_v; + cell_to_cell_ = cell_to_cell; + + if(cell_to_cell != nullptr) { + if(store_cicl_) { + update_v_to_cell(); + update_cicl(); + } + if(store_neighbors_) { + update_neighbors(); + } + } + } + + bool Delaunay::supports_constraints() const { + return false; + } + + index_t Delaunay::nearest_vertex(const double* p) const { + // Unefficient implementation (but at least it works). + // Derived classes are supposed to overload. + geo_assert(nb_vertices() > 0); + index_t result = 0; + double d = Geom::distance2(vertex_ptr(0), p, dimension()); + for(index_t i = 1; i < nb_vertices(); i++) { + double cur_d = Geom::distance2(vertex_ptr(i), p, dimension()); + if(cur_d < d) { + d = cur_d; + result = i; + } + } + return result; + } + + void Delaunay::update_neighbors() { + if(nb_vertices() != neighbors_.nb_arrays()) { + neighbors_.init( + nb_vertices(), + default_nb_neighbors_ + ); + for(index_t i = 0; i < nb_vertices(); i++) { + neighbors_.resize_array(i, default_nb_neighbors_, false); + } + } + parallel_for_slice( + 0, nb_vertices(), + [this](index_t from, index_t to) { + vector neighbors; + for(index_t i=from; i& neighbors + ) const { + // Step 1: traverse the incident cells list, and insert + // all neighbors (may be duplicated) + neighbors.resize(0); + signed_index_t vt = v_to_cell_[v]; + if(vt != -1) { // Happens when there are duplicated vertices. + index_t t = index_t(vt); + do { + index_t lvit = index(t, signed_index_t(v)); + // In the current cell, test all edges incident + // to current vertex 'it' + for(index_t lv = 0; lv < cell_size(); lv++) { + if(lvit != lv) { + signed_index_t neigh = cell_vertex(t, lv); + geo_debug_assert(neigh != -1); + neighbors.push_back(index_t(neigh)); + } + } + t = index_t(next_around_vertex(t, index(t, signed_index_t(v)))); + } while(t != index_t(vt)); + } + + // Step 2: Sort the neighbors and remove all duplicates + sort_unique(neighbors); + } + + void Delaunay::update_v_to_cell() { + geo_assert(!is_locked_); // Not thread-safe + is_locked_ = true; + + // Note: if keeps_infinite is set, then infinite vertex + // tet chaining is at t2v_[nb_vertices]. + + if(keeps_infinite()) { + v_to_cell_.assign(nb_vertices()+1, -1); + for(index_t c = 0; c < nb_cells(); c++) { + for(index_t lv = 0; lv < cell_size(); lv++) { + signed_index_t v = cell_vertex(c, lv); + if(v == -1) { + v = signed_index_t(nb_vertices()); + } + v_to_cell_[v] = signed_index_t(c); + } + } + } else { + v_to_cell_.assign(nb_vertices(), -1); + for(index_t c = 0; c < nb_cells(); c++) { + for(index_t lv = 0; lv < cell_size(); lv++) { + v_to_cell_[cell_vertex(c, lv)] = signed_index_t(c); + } + } + } + is_locked_ = false; + } + + void Delaunay::update_cicl() { + geo_assert(!is_locked_); // Not thread-safe + is_locked_ = true; + cicl_.resize(cell_size() * nb_cells()); + + for(index_t v = 0; v < nb_vertices(); ++v) { + signed_index_t t = v_to_cell_[v]; + if(t != -1) { + index_t lv = index(index_t(t), signed_index_t(v)); + set_next_around_vertex(index_t(t), lv, index_t(t)); + } + } + + if(keeps_infinite()) { + + { + // Process the infinite vertex at index nb_vertices(). + signed_index_t t = v_to_cell_[nb_vertices()]; + if(t != -1) { + index_t lv = index(index_t(t), -1); + set_next_around_vertex(index_t(t), lv, index_t(t)); + } + } + + for(index_t t = 0; t < nb_cells(); ++t) { + for(index_t lv = 0; lv < cell_size(); ++lv) { + signed_index_t v = cell_vertex(t, lv); + index_t vv = (v == -1) ? nb_vertices() : index_t(v); + if(v_to_cell_[vv] != signed_index_t(t)) { + index_t t1 = index_t(v_to_cell_[vv]); + index_t lv1 = index(t1, signed_index_t(v)); + index_t t2 = index_t(next_around_vertex(t1, lv1)); + set_next_around_vertex(t1, lv1, t); + set_next_around_vertex(t, lv, t2); + } + } + } + + + } else { + for(index_t t = 0; t < nb_cells(); ++t) { + for(index_t lv = 0; lv < cell_size(); ++lv) { + index_t v = index_t(cell_vertex(t, lv)); + if(v_to_cell_[v] != signed_index_t(t)) { + index_t t1 = index_t(v_to_cell_[v]); + index_t lv1 = index(t1, signed_index_t(v)); + index_t t2 = index_t(next_around_vertex(t1, lv1)); + set_next_around_vertex(t1, lv1, t); + set_next_around_vertex(t, lv, t2); + } + } + } + } + + is_locked_ = false; + } + + void Delaunay::save_histogram(std::ostream& out) const { + vector histogram; + for(index_t v = 0; v < nb_vertices(); v++) { + index_t N = neighbors_.array_size(v); + if(histogram.size() < N) { + histogram.resize(N + 1); + } + histogram[N]++; + } + for(index_t i = 0; i < histogram.size(); i++) { + out << i << " " << histogram[i] << std::endl; + } + } + + bool Delaunay::cell_is_infinite(index_t c) const { + geo_debug_assert(c < nb_cells()); + for(index_t lv=0; lv < cell_size(); ++lv) { + if(cell_vertex(c,lv) == -1) { + return true; + } + } + return false; + } + + index_t Delaunay::region(index_t t) const { + geo_argused(t); + geo_debug_assert(t < nb_cells()); + return index_t(-1); + } +} + diff --git a/src/lib/geogram/delaunay/delaunay.cpp.old b/src/lib/geogram/delaunay/delaunay.cpp.old new file mode 100755 index 00000000..a6974e96 --- /dev/null +++ b/src/lib/geogram/delaunay/delaunay.cpp.old @@ -0,0 +1,446 @@ +/* + * Copyright (c) 2012-2014, Bruno Levy + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the ALICE Project-Team nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * If you modify this software, you should include a notice giving the + * name of the person performing the modification, the date of modification, + * and the reason for such modification. + * + * Contact: Bruno Levy + * + * Bruno.Levy@inria.fr + * http://www.loria.fr/~levy + * + * ALICE Project + * LORIA, INRIA Lorraine, + * Campus Scientifique, BP 239 + * 54506 VANDOEUVRE LES NANCY CEDEX + * FRANCE + * + */ + +#include +#include +#include +#include + +#ifdef GEOGRAM_WITH_PDEL +#include +#endif + +#ifdef GEOGRAM_WITH_TETGEN +#include +#endif + +#ifdef GEOGRAM_WITH_TRIANGLE +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include + +namespace { + + using namespace GEO; + + /** + * \brief Builds the invalid dimension error message + * \param[in] dimension the specified dimension + * \param[in] name the name of the Delaunay implementation + * \param[in] expected the expected dimension + * \return a string that contains the error message + */ + std::string invalid_dimension_error( + coord_index_t dimension, + const char* name, + const char* expected + ) { + std::ostringstream out; + out << "Invalid dimension: dimension " << index_t(dimension) + << " is not supported by the " << name + << " algorithm. Supported dimension(s): " << expected; + return out.str(); + } +} + +namespace GEO { + + Delaunay::InvalidDimension::InvalidDimension( + coord_index_t dimension, + const char* name, + const char* expected + ) : + std::logic_error(invalid_dimension_error(dimension, name, expected)) { + } + + const char* Delaunay::InvalidDimension::what() const GEO_NOEXCEPT { + return std::logic_error::what(); + } + + + Delaunay::InvalidInput::InvalidInput(int code) : + logic_error("Invalid input for Delaunay"), + error_code(code) { + } + + Delaunay::InvalidInput::InvalidInput( + const InvalidInput& rhs + ) : + std::logic_error(rhs), + error_code(rhs.error_code), + invalid_facets(rhs.invalid_facets) { + } + + Delaunay::InvalidInput::~InvalidInput() GEO_NOEXCEPT { + } + + const char* Delaunay::InvalidInput::what() const GEO_NOEXCEPT { + return std::logic_error::what(); + } + + /************************************************************************/ + + void Delaunay::initialize() { + +#ifdef GEOGRAM_WITH_TETGEN + geo_register_Delaunay_creator(DelaunayTetgen, "tetgen"); +#endif + +#ifdef GEOGRAM_WITH_TRIANGLE + geo_register_Delaunay_creator(DelaunayTriangle, "triangle"); +#endif + + geo_register_Delaunay_creator(Delaunay3d, "BDEL"); + +#ifdef GEOGRAM_WITH_PDEL + geo_register_Delaunay_creator(ParallelDelaunay3d, "PDEL"); +#endif + geo_register_Delaunay_creator(RegularWeightedDelaunay3d, "BPOW"); + + geo_register_Delaunay_creator(Delaunay2d, "BDEL2d"); + geo_register_Delaunay_creator(RegularWeightedDelaunay2d, "BPOW2d"); + +#ifndef GEOGRAM_PSM + geo_register_Delaunay_creator(Delaunay_NearestNeighbors, "NN"); +#endif + } + + Delaunay* Delaunay::create( + coord_index_t dim, const std::string& name_in + ) { + + std::string name = name_in; + if(name == "default") { + name = CmdLine::get_arg("algo:delaunay"); + } + + try { + Delaunay* d = DelaunayFactory::create_object(name, dim); + if(d != nullptr) { + return d; + } + + Logger::warn("Delaunay") + << "Could not create Delaunay triangulation: " << name + << std::endl; + } + catch(InvalidDimension& ex) { + Logger::warn("Delaunay") << ex.what() << std::endl; + } + +#ifdef GEOGRAM_PSM + Logger::err("Delaunay") + << "Could not create Delaunay triangulation" + << std::endl; + return nullptr; +#else + Logger::warn("Delaunay") + << "Falling back to NN mode" + << std::endl; + + return new Delaunay_NearestNeighbors(dim); +#endif + } + + Delaunay::Delaunay(coord_index_t dimension) { + set_dimension(dimension); + vertices_ = nullptr; + nb_vertices_ = 0; + nb_cells_ = 0; + cell_to_v_ = nullptr; + cell_to_cell_ = nullptr; + is_locked_ = false; + store_neighbors_ = false; + default_nb_neighbors_ = 30; + constraints_ = nullptr; + do_reorder_ = true; + refine_ = false; + quality_ = 2.0; + store_cicl_ = false; + keep_infinite_ = false; + nb_finite_cells_ = 0; + keep_regions_ = false; + } + + Delaunay::~Delaunay() { + } + + void Delaunay::set_vertices( + index_t nb_vertices, const double* vertices + ) { + nb_vertices_ = nb_vertices; + vertices_ = vertices; + if(nb_vertices_ < index_t(dimension()) + 1) { + Logger::warn("Delaunay") << "Only " + << nb_vertices + << " vertices, may be not enough !" + << std::endl; + } + } + + void Delaunay::set_BRIO_levels(const vector& levels) { + geo_argused(levels); + // Default implementation does nothing + } + + void Delaunay::set_arrays( + index_t nb_cells, + const signed_index_t* cell_to_v, const signed_index_t* cell_to_cell + ) { + nb_cells_ = nb_cells; + cell_to_v_ = cell_to_v; + cell_to_cell_ = cell_to_cell; + + if(cell_to_cell != nullptr) { + if(store_cicl_) { + update_v_to_cell(); + update_cicl(); + } + if(store_neighbors_) { + update_neighbors(); + } + } + } + + bool Delaunay::supports_constraints() const { + return false; + } + + index_t Delaunay::nearest_vertex(const double* p) const { + // Unefficient implementation (but at least it works). + // Derived classes are supposed to overload. + geo_assert(nb_vertices() > 0); + index_t result = 0; + double d = Geom::distance2(vertex_ptr(0), p, dimension()); + for(index_t i = 1; i < nb_vertices(); i++) { + double cur_d = Geom::distance2(vertex_ptr(i), p, dimension()); + if(cur_d < d) { + d = cur_d; + result = i; + } + } + return result; + } + + void Delaunay::update_neighbors() { + if(nb_vertices() != neighbors_.nb_arrays()) { + neighbors_.init( + nb_vertices(), + default_nb_neighbors_ + ); + for(index_t i = 0; i < nb_vertices(); i++) { + neighbors_.resize_array(i, default_nb_neighbors_, false); + } + } + parallel_for( + 0, nb_vertices(), + parallel_for_member_callback(this, &Delaunay::store_neighbors_CB), + 1, true + ); + } + + void Delaunay::get_neighbors_internal( + index_t v, vector& neighbors + ) const { + // Step 1: traverse the incident cells list, and insert + // all neighbors (may be duplicated) + neighbors.resize(0); + signed_index_t vt = v_to_cell_[v]; + if(vt != -1) { // Happens when there are duplicated vertices. + index_t t = index_t(vt); + do { + index_t lvit = index(t, signed_index_t(v)); + // In the current cell, test all edges incident + // to current vertex 'it' + for(index_t lv = 0; lv < cell_size(); lv++) { + if(lvit != lv) { + signed_index_t neigh = cell_vertex(t, lv); + geo_debug_assert(neigh != -1); + neighbors.push_back(index_t(neigh)); + } + } + t = index_t(next_around_vertex(t, index(t, signed_index_t(v)))); + } while(t != index_t(vt)); + } + + // Step 2: Sort the neighbors and remove all duplicates + sort_unique(neighbors); + } + + void Delaunay::store_neighbors_CB(index_t i) { + // TODO: this one is not multithread-friendly + // since it does dynamic memory allocation + // (but not really a problem, since the one + // that is used is in Delaunay_ANN). + vector neighbors; + get_neighbors_internal(i, neighbors); + neighbors_.set_array(i, neighbors); + } + + void Delaunay::update_v_to_cell() { + geo_assert(!is_locked_); // Not thread-safe + is_locked_ = true; + + // Note: if keeps_infinite is set, then infinite vertex + // tet chaining is at t2v_[nb_vertices]. + + if(keeps_infinite()) { + v_to_cell_.assign(nb_vertices()+1, -1); + for(index_t c = 0; c < nb_cells(); c++) { + for(index_t lv = 0; lv < cell_size(); lv++) { + signed_index_t v = cell_vertex(c, lv); + if(v == -1) { + v = signed_index_t(nb_vertices()); + } + v_to_cell_[v] = signed_index_t(c); + } + } + } else { + v_to_cell_.assign(nb_vertices(), -1); + for(index_t c = 0; c < nb_cells(); c++) { + for(index_t lv = 0; lv < cell_size(); lv++) { + v_to_cell_[cell_vertex(c, lv)] = signed_index_t(c); + } + } + } + is_locked_ = false; + } + + void Delaunay::update_cicl() { + geo_assert(!is_locked_); // Not thread-safe + is_locked_ = true; + cicl_.resize(cell_size() * nb_cells()); + + for(index_t v = 0; v < nb_vertices(); ++v) { + signed_index_t t = v_to_cell_[v]; + if(t != -1) { + index_t lv = index(index_t(t), signed_index_t(v)); + set_next_around_vertex(index_t(t), lv, index_t(t)); + } + } + + if(keeps_infinite()) { + + { + // Process the infinite vertex at index nb_vertices(). + signed_index_t t = v_to_cell_[nb_vertices()]; + if(t != -1) { + index_t lv = index(index_t(t), -1); + set_next_around_vertex(index_t(t), lv, index_t(t)); + } + } + + for(index_t t = 0; t < nb_cells(); ++t) { + for(index_t lv = 0; lv < cell_size(); ++lv) { + signed_index_t v = cell_vertex(t, lv); + index_t vv = (v == -1) ? nb_vertices() : index_t(v); + if(v_to_cell_[vv] != signed_index_t(t)) { + index_t t1 = index_t(v_to_cell_[vv]); + index_t lv1 = index(t1, signed_index_t(v)); + index_t t2 = index_t(next_around_vertex(t1, lv1)); + set_next_around_vertex(t1, lv1, t); + set_next_around_vertex(t, lv, t2); + } + } + } + + + } else { + for(index_t t = 0; t < nb_cells(); ++t) { + for(index_t lv = 0; lv < cell_size(); ++lv) { + index_t v = index_t(cell_vertex(t, lv)); + if(v_to_cell_[v] != signed_index_t(t)) { + index_t t1 = index_t(v_to_cell_[v]); + index_t lv1 = index(t1, signed_index_t(v)); + index_t t2 = index_t(next_around_vertex(t1, lv1)); + set_next_around_vertex(t1, lv1, t); + set_next_around_vertex(t, lv, t2); + } + } + } + } + + is_locked_ = false; + } + + void Delaunay::save_histogram(std::ostream& out) const { + vector histogram; + for(index_t v = 0; v < nb_vertices(); v++) { + index_t N = neighbors_.array_size(v); + if(histogram.size() < N) { + histogram.resize(N + 1); + } + histogram[N]++; + } + for(index_t i = 0; i < histogram.size(); i++) { + out << i << " " << histogram[i] << std::endl; + } + } + + bool Delaunay::cell_is_infinite(index_t c) const { + geo_debug_assert(c < nb_cells()); + for(index_t lv=0; lv < cell_size(); ++lv) { + if(cell_vertex(c,lv) == -1) { + return true; + } + } + return false; + } + + index_t Delaunay::region(index_t t) const { + geo_argused(t); + geo_debug_assert(t < nb_cells()); + return index_t(-1); + } +} + diff --git a/src/lib/geogram/delaunay/delaunay.h.new b/src/lib/geogram/delaunay/delaunay.h.new new file mode 100755 index 00000000..6dfcd018 --- /dev/null +++ b/src/lib/geogram/delaunay/delaunay.h.new @@ -0,0 +1,812 @@ +/* + * Copyright (c) 2012-2014, Bruno Levy + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the ALICE Project-Team nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * If you modify this software, you should include a notice giving the + * name of the person performing the modification, the date of modification, + * and the reason for such modification. + * + * Contact: Bruno Levy + * + * Bruno.Levy@inria.fr + * http://www.loria.fr/~levy + * + * ALICE Project + * LORIA, INRIA Lorraine, + * Campus Scientifique, BP 239 + * 54506 VANDOEUVRE LES NANCY CEDEX + * FRANCE + * + */ + +#ifndef GEOGRAM_DELAUNAY_DELAUNAY +#define GEOGRAM_DELAUNAY_DELAUNAY + +#include +#include +#include +#include +#include +#include + +/** + * \file geogram/delaunay/delaunay.h + * \brief Abstract interface for Delaunay + */ + +namespace GEO { + + class Mesh; + + /************************************************************************/ + + /** + * \brief Abstract interface for Delaunay triangulation in Nd. + * \details + * Delaunay objects are created using method create() which + * uses the Factory service. New Delaunay triangulations can be + * implemented and registered to the factory using + * geo_register_Delaunay_creator(). + * \see DelaunayFactory + * \see geo_register_Delaunay_creator + */ + class GEOGRAM_API Delaunay : public Counted { + public: + /** + * \brief Invalid dimension exception + * \details This exception is thrown by the Delaunay derived + * constructors if the dimension in the constructor is not supported + * by the implementation + */ + struct InvalidDimension : std::logic_error { + /** + * \brief Creates a invalid dimension exception + * \param[in] dimension the specified dimension + * \param[in] name the name of the Delaunay implementation + * \param[in] expected the expected dimension + */ + InvalidDimension( + coord_index_t dimension, + const char* name, + const char* expected + ); + + /** + * \brief Gets the string identifying the exception + */ + virtual const char* what() const GEO_NOEXCEPT; + }; + + + /** + * \brief Invalid input exception + * \details This exception is thrown by Delaunay implementations + * in constrained mode, when constraints self-intersect. + */ + struct InvalidInput : std::logic_error { + + /** + * \brief InvalidInput constructor. + * \param[in] error_code_in an implementation-dependent error code + */ + InvalidInput(int error_code_in); + + /** + * \brief InvalidInput copy constructor. + * \param[in] rhs a const reference to the InvalidInput to be copied + */ + InvalidInput(const InvalidInput& rhs); + + virtual ~InvalidInput() GEO_NOEXCEPT; + + /** + * \brief Gets the string identifying the exception + */ + virtual const char* what() const GEO_NOEXCEPT; + + /** + * \brief An implementation-dependent error code. + */ + int error_code; + + /** + * \brief The indices of the constrained facets that + * have an intersection (or that are duplicated). + */ + vector invalid_facets; + }; + + /** + * \brief Creates a Delaunay triangulation of the + * specified dimension. + * \param[in] dim dimension of the triangulation + * \param[in] name name of the implementation to use: + * - "tetgen" - Delaunay with the Tetgen library (dimension 3 only) + * - "BDEL" - Delaunay in 3D (dimension 3 only) + * - "BPOW" - Weighted regular 3D triangulation (dimension 4 only) + * - "NN" - Delaunay with NearestNeighborSearch (any dimension) + * - "default" - uses the command line argument "algo:delaunay" + * \retval nullptr if \p format is not a valid Delaunay algorithm name. + * \retval otherwise, a pointer to a Delaunay algorithm object. The + * returned pointer must be stored in an Delaunay_var that does + * automatic destruction: + * \code + * Delaunay_var handler = Delaunay::create(3, "default"); + * \endcode + */ + static Delaunay* create( + coord_index_t dim, const std::string& name = "default" + ); + + + /** + * \brief This function needs to be called once before + * using the Delaunay class. + * \details registers the factories. + */ + static void initialize(); + + /** + * \brief Gets the dimension of this Delaunay. + * \return the dimension of this Delauna + */ + coord_index_t dimension() const { + return dimension_; + } + + /** + * \brief Gets the number of vertices in each cell + * \details Cell_size = dimension + 1 + * \return the number of vertices in each cell + */ + index_t cell_size() const { + return cell_size_; + } + + /** + * \brief Sets the vertices of this Delaunay, and recomputes the cells. + * \param[in] nb_vertices number of vertices + * \param[in] vertices a pointer to the coordinates of the vertices, as + * a contiguous array of doubles + */ + virtual void set_vertices( + index_t nb_vertices, const double* vertices + ); + + + /** + * \brief Specifies whether vertices should be reordered. + * \details Reordering is activated by default. Some special + * usages of Delaunay3d may require to deactivate it (for + * instance if vertices are already known to be ordered). + * \param[in] x if true, then vertices are reordered using + * BRIO-Hilbert ordering. This improves speed significantly + * (enabled by default). + */ + void set_reorder(bool x) { + do_reorder_ = x; + } + + + /** + * \brief Specifies the bounds of each level to be used + * when hierarchic ordering is specified from outside. + * \details This function is used by some implementation + * when set_reorder(false) was called. + * \param[in] levels specifies the bounds of each level + * used by the hierarchical index. First level has + * indices between levels[0] ... levels[1]. + */ + virtual void set_BRIO_levels(const vector& levels); + + /** + * \brief Gets a pointer to the array of vertices. + * \return A const pointer to the array of vertices. + */ + const double* vertices_ptr() const { + return vertices_; + } + + /** + * \brief Gets a pointer to a vertex by its global index. + * \param[in] i global index of the vertex + * \return a pointer to vertex \p i + */ + const double* vertex_ptr(index_t i) const { + geo_debug_assert(i < nb_vertices()); + return vertices_ + vertex_stride_ * i; + } + + /** + * \brief Gets the number of vertices. + * \return the number of vertices in this Delaunay + */ + index_t nb_vertices() const { + return nb_vertices_; + } + + /** + * \brief Tests whether constraints are supported + * by this Delaunay. + * \retval true if constraints are supported + * \retval false otherwise + */ + virtual bool supports_constraints() const; + + /** + * \brief Defines the constraints. + * \details The triangulation will be constrained + * to pass through the vertices and triangles of + * the mesh. This function should be called + * before set_vertices(). + * \param[in] mesh the definition of the constraints + * \pre constraints_supported() + */ + virtual void set_constraints(const Mesh* mesh) { + geo_assert(supports_constraints()); + constraints_ = mesh; + } + + /** + * \brief Specifies whether the mesh should be refined. + * \details If set, then the mesh elements are improved + * by inserting additional vertices in the mesh. + * It is not taken into account by all implementations. + * This function should be called before set_vertices(). + * \param[in] x true if the mesh should be refined, false + * otherwise. + */ + void set_refine(bool x) { + refine_ = x; + } + + /** + * \brief Tests whether mesh refinement is selected. + * \retval true if mesh refinement is selected + * \retval false otherwise + * \see set_refine() + */ + bool get_refine() const { + return refine_; + } + + /** + * \brief Specifies the desired quality for mesh elements + * when refinement is enabled (\see set_refine). + * \details + * Only taken into account after set_refine(true) is called. + * It is not taken into account by all implementations. + * This function should be called before set_vertices(). + * \param[in] qual typically in [1.0, 2.0], specifies + * the desired quality of mesh elements (1.0 means maximum + * quality, and generates a higher number of elements). + */ + void set_quality(double qual) { + quality_ = qual; + } + + /** + * \brief Gets the constraints. + * \return the constraints or nullptr if no constraints + * were definied. + */ + const Mesh* constraints() const { + return constraints_; + } + + /** + * \brief Gets the number of cells. + * \return the number of cells in this Delaunay + */ + index_t nb_cells() const { + return nb_cells_; + } + + /** + * \brief Gets the number of finite cells. + * \pre this function can only be called if + * keep_finite is set + * \details Finite cells have indices 0..nb_finite_cells()-1 + * and infinite cells have indices nb_finite_cells()..nb_cells()-1 + * \see set_keeps_infinite(), keeps_infinite() + */ + index_t nb_finite_cells() const { + geo_debug_assert(keeps_infinite()); + return nb_finite_cells_; + } + + /** + * \brief Gets a pointer to the cell-to-vertex incidence array. + * \return a const pointer to the cell-to-vertex incidence array + */ + const signed_index_t* cell_to_v() const { + return cell_to_v_; + } + + /** + * \brief Gets a pointer to the cell-to-cell adjacency array. + * \return a const pointer to the cell-to-cell adjacency array + */ + const signed_index_t* cell_to_cell() const { + return cell_to_cell_; + } + + /** + * \brief Computes the nearest vertex from a query point. + * \param[in] p query point + * \return the index of the nearest vertex + */ + virtual index_t nearest_vertex(const double* p) const; + + /** + * \brief Gets a vertex index by cell index and local vertex index. + * \param[in] c cell index + * \param[in] lv local vertex index in cell \p c + * \return the index of the lv-th vertex of cell c. + */ + signed_index_t cell_vertex(index_t c, index_t lv) const { + geo_debug_assert(c < nb_cells()); + geo_debug_assert(lv < cell_size()); + return cell_to_v_[c * cell_v_stride_ + lv]; + } + + /** + * \brief Gets an adjacent cell index by cell index and + * local facet index. + * \param[in] c cell index + * \param[in] lf local facet index + * \return the index of the cell adjacent to \p c accros + * facet \p lf if it exists, or -1 if on border + */ + signed_index_t cell_adjacent(index_t c, index_t lf) const { + geo_debug_assert(c < nb_cells()); + geo_debug_assert(lf < cell_size()); + return cell_to_cell_[c * cell_neigh_stride_ + lf]; + } + + /** + * \brief Tests whether a cell is infinite. + * \retval true if cell \p c is infinite + * \retval false otherwise + * \see keeps_infinite(), set_keeps_infinite() + */ + bool cell_is_infinite(index_t c) const; + + /** + * \brief Tests whether a cell is finite. + * \retval true if cell \p c is finite + * \retval false otherwise + * \see keeps_infinite(), set_keeps_infinite() + */ + bool cell_is_finite(index_t c) const { + return !cell_is_infinite(c); + } + + /** + * \brief Retrieves a local vertex index from cell index + * and global vertex index. + * \param[in] c cell index + * \param[in] v global vertex index + * \return the local index of vertex \p v in cell \p c + * \pre cell \p c is incident to vertex \p v + */ + index_t index(index_t c, signed_index_t v) const { + geo_debug_assert(c < nb_cells()); + geo_debug_assert(v < (signed_index_t) nb_vertices()); + for(index_t iv = 0; iv < cell_size(); iv++) { + if(cell_vertex(c, iv) == v) { + return iv; + } + } + geo_assert_not_reached; + } + + /** + * \brief Retrieves a local facet index from two adacent + * cell global indices. + * \param[in] c1 global index of first cell + * \param[in] c2 global index of second cell + * \return the local index of the face accros which + * \p c2 is adjacent to \p c1 + * \pre cell \p c1 and cell \p c2 are adjacent + */ + index_t adjacent_index(index_t c1, index_t c2) const { + geo_debug_assert(c1 < nb_cells()); + geo_debug_assert(c2 < nb_cells()); + for(index_t f = 0; f < cell_size(); f++) { + if(cell_adjacent(c1, f) == signed_index_t(c2)) { + return f; + } + } + geo_assert_not_reached; + } + + /** + * \brief Gets an incident cell index by a vertex index. + * \details Can only be used if set_stores_cicl(true) was called. + * \param[in] v a vertex index + * \return the index of a cell incident to vertex \p v + * \see stores_cicl(), set_store_cicl() + */ + signed_index_t vertex_cell(index_t v) const { + geo_debug_assert(v < nb_vertices()); + geo_debug_assert(v < v_to_cell_.size()); + return v_to_cell_[v]; + } + + + /** + * \brief Traverses the list of cells incident to a vertex. + * \details Can only be used if set_stores_cicl(true) was called. + * \param[in] c cell index + * \param[in] lv local vertex index + * \return the index of the next cell around vertex \p c or -1 if + * \p c was the last one in the list + * \see stores_cicl(), set_store_cicl() + */ + signed_index_t next_around_vertex(index_t c, index_t lv) const { + geo_debug_assert(c < nb_cells()); + geo_debug_assert(lv < cell_size()); + return cicl_[cell_size() * c + lv]; + } + + /** + * \brief Gets the one-ring neighbors of vertex v. + * \details Depending on store_neighbors_ internal flag, the + * neighbors are computed or copied from the previously computed + * list. + * \param[in] v vertex index + * \param[out] neighbors indices of the one-ring neighbors of + * vertex \p v + * \see stores_neighbors(), set_stores_neighbors() + */ + void get_neighbors( + index_t v, vector& neighbors + ) const { + geo_debug_assert(v < nb_vertices()); + if(store_neighbors_) { + neighbors_.get_array(v, neighbors); + } else { + get_neighbors_internal(v, neighbors); + } + } + + /** + * \brief Saves the histogram of vertex degree (can be + * visualized with gnuplot). + * \param[out] out an ASCII stream where to output the histogram. + */ + void save_histogram(std::ostream& out) const; + + /** + * \brief Tests whether neighbors are stored. + * \details Vertices neighbors (i.e. Delaunay 1-skeleton) can be + * stored for faster access (used for instance by + * RestrictedVoronoiDiagram). + * \retval true if neighbors are stored. + * \retval false otherwise. + */ + bool stores_neighbors() const { + return store_neighbors_; + } + + /** + * \brief Specifies whether neighbors should be stored. + * \details Vertices neighbors (i.e. Delaunay 1-skeleton) can be + * stored for faster access (used for instance by + * RestrictedVoronoiDiagram). + * \param[in] x if true neighbors will be stored, else they will not + */ + void set_stores_neighbors(bool x) { + store_neighbors_ = x; + if(store_neighbors_) { + set_stores_cicl(true); + } + } + + /** + * \brief Tests whether incident tetrahedra lists + * are stored. + * \retval true if incident tetrahedra lists are stored. + * \retval false otherwise. + */ + bool stores_cicl() const { + return store_cicl_; + } + + /** + * \brief Specifies whether incident tetrahedra lists + * should be stored. + * \param[in] x if true, incident trahedra lists are stored, + * else they are not. + */ + void set_stores_cicl(bool x) { + store_cicl_ = x; + } + + + /** + * \brief Tests whether infinite elements are kept. + * \retval true if infinite elements are kept + * \retval false otherwise + */ + bool keeps_infinite() const { + return keep_infinite_; + } + + /** + * \brief Sets whether infinite elements should be kept. + * \details Internally, Delaunay implementation uses an + * infinite vertex and infinite simplices indicent to it. + * By default they are discarded at the end of set_vertices(). + * \param[in] x true if infinite elements should be kept, + * false otherwise + */ + void set_keeps_infinite(bool x) { + keep_infinite_ = x; + } + + /** + * \brief Tests whether thread-safe mode is active. + * \return true if thread-safe mode is active, false otherwise. + */ + bool thread_safe() const { + return neighbors_.thread_safe(); + } + + /** + * \brief Specifies whether thread-safe mode should be used. + * \param[in] x if true then thread-safe mode will be used, else + * it will not. + */ + void set_thread_safe(bool x) { + neighbors_.set_thread_safe(x); + } + + /** + * \brief Sets the default number of stored neighbors. + * \details Storage of neighbors is optimized for a default + * neighborhood size. + * \see store_neighbors() + * \param[in] x default number of stored neighbors + */ + void set_default_nb_neighbors(index_t x) { + default_nb_neighbors_ = x; + } + + /** + * \brief Gets the default number of stored neighbors. + * \details Storage of neighbors is optimized for a default + * neighborhood size. + * \see store_neighbors() + * \return The default number of stored neighbors. + */ + index_t default_nb_neighbors() const { + return default_nb_neighbors_; + } + + /** + * \brief Frees all memory used for neighbors storage. + */ + void clear_neighbors() { + neighbors_.clear(); + } + + /** + * \brief Specifies whether all internal regions should be kept. + * \details Only relevant in constrained mode. + * \param[in] x if true, all internal regions are kept, else only + * the outer most region is kept (default). + */ + void set_keep_regions(bool x) { + keep_regions_ = x; + } + + /** + * \brief Gets the region id associated with a tetrahedron. + * \details Only callable if set_keep_region(true) was called before + * set_vertices() in constrained mode. + * \param[in] t a tetrahedron index. + * \return the region associated with \p t. + */ + virtual index_t region(index_t t) const; + + + protected: + /** + * \brief Creates a new Delaunay triangulation + * \details This creates a new Delaunay triangulation for the + * specified \p dimension. Specific implementations of the Delaunay + * triangulation may not support the specified \p dimension and will + * throw a InvalidDimension exception. + * \param[in] dimension dimension of the triangulation + * \throw InvalidDimension This exception is thrown if the specified + * \p dimension is not supported by the Delaunay implementation. + * \note This function is never called directly, use create() + */ + Delaunay(coord_index_t dimension); + + /** + * \brief Delaunay destructor. + */ + virtual ~Delaunay(); + + /** + * \brief Internal implementation for get_neighbors (with vector). + * \param[in] v index of the Delaunay vertex + * \param[in,out] neighbors the computed neighbors of vertex \p v. + * Its size is used to determine the number of queried neighbors. + */ + virtual void get_neighbors_internal( + index_t v, vector& neighbors + ) const; + + /** + * \brief Sets the arrays that represent the combinatorics + * of this Delaunay. + * \param[in] nb_cells number of cells + * \param[in] cell_to_v the cell-to-vertex incidence array + * \param[in] cell_to_cell the cell-to-cell adjacency array + */ + virtual void set_arrays( + index_t nb_cells, + const signed_index_t* cell_to_v, const signed_index_t* cell_to_cell + ); + + /** + * \brief Stores for each vertex v a cell incident to v. + */ + virtual void update_v_to_cell(); + + /** + * \brief Updates the circular incident cell lists. + * \details Used by next_around_vertex(). + */ + virtual void update_cicl(); + + /** + * \brief Computes the stored neighbor lists. + */ + virtual void update_neighbors(); + + /** + * \brief Sets the circular incident edge list. + * \param[in] c1 index of a cell + * \param[in] lv local index of a vertex of \p c1 + * \param[in] c2 index of the next cell around \p c1%'s vertex \p lv + */ + void set_next_around_vertex( + index_t c1, index_t lv, index_t c2 + ) { + geo_debug_assert(c1 < nb_cells()); + geo_debug_assert(c2 < nb_cells()); + geo_debug_assert(lv < cell_size()); + cicl_[cell_size() * c1 + lv] = signed_index_t(c2); + } + + protected: + /** + * \brief Sets the dimension of this Delaunay. + * \details Updates all the parameters related with + * the dimension. This includes vertex_stride (number + * of doubles between two consecutive vertices), + * cell size (number of vertices in a cell), + * cell_v_stride (number of integers between two + * consecutive cell vertex arrays), + * cell_neigh_stride (number of integers + * between two consecutive cell adjacency arrays). + * \param[in] dim the dimension + */ + void set_dimension(coord_index_t dim) { + dimension_ = dim; + vertex_stride_ = dim; + cell_size_ = index_t(dim) + 1; + cell_v_stride_ = cell_size_; + cell_neigh_stride_ = cell_size_; + } + + coord_index_t dimension_; + index_t vertex_stride_; + index_t cell_size_; + index_t cell_v_stride_; + index_t cell_neigh_stride_; + + const double* vertices_; + index_t nb_vertices_; + index_t nb_cells_; + const signed_index_t* cell_to_v_; + const signed_index_t* cell_to_cell_; + vector v_to_cell_; + vector cicl_; + bool is_locked_; + PackedArrays neighbors_; + bool store_neighbors_; + index_t default_nb_neighbors_; + + /** + * \brief If true, uses BRIO reordering + * (in some implementations) + */ + bool do_reorder_; + + const Mesh* constraints_; + + bool refine_; + double quality_; + + /** + * \brief It true, circular incident tet + * lists are stored. + */ + bool store_cicl_; + + /** + * \brief If true, infinite vertex and + * infinite simplices are kept. + */ + bool keep_infinite_; + + /** + * \brief If keep_infinite_ is true, then + * finite cells are 0..nb_finite_cells_-1 + * and infinite cells are + * nb_finite_cells_ ... nb_cells_ + */ + index_t nb_finite_cells_; + + bool keep_regions_; + }; + + /** + * \brief Smart pointer that refers to a Delaunay object + * \relates Delaunay + */ + typedef SmartPointer Delaunay_var; + + /** + * \brief Delaunay Factory + * \details + * This Factory is used to create Delaunay objects. + * It can also be used to register new Delaunay + * implementations. + * \see geo_register_Delaunay_creator + * \see Factory + * \relates Delaunay + */ + typedef Factory1 DelaunayFactory; + + /** + * \brief Helper macro to register a Delaunay implementation + * \see DelaunayFactory + * \relates Delaunay + */ +#define geo_register_Delaunay_creator(type, name) \ + geo_register_creator(GEO::DelaunayFactory, type, name) +} + +#endif + diff --git a/src/lib/geogram/delaunay/parallel_delaunay_3d.cpp b/src/lib/geogram/delaunay/parallel_delaunay_3d.cpp index b5460816..23d5d6eb 100644 --- a/src/lib/geogram/delaunay/parallel_delaunay_3d.cpp +++ b/src/lib/geogram/delaunay/parallel_delaunay_3d.cpp @@ -1358,7 +1358,7 @@ namespace GEO { geo_debug_assert(t < max_t()); geo_debug_assert(!owns_tet(t)); -#ifdef GEO_OS_WINDOWS +#if defined(GEO_COMPILER_MSVC) // Note: comparand and exchange parameter are swapped in Windows API // as compared to __sync_val_compare_and_swap !! interfering_thread_ = diff --git a/src/lib/geogram/delaunay/periodic.cpp b/src/lib/geogram/delaunay/periodic.cpp index a4b241bc..3e7e39ed 100644 --- a/src/lib/geogram/delaunay/periodic.cpp +++ b/src/lib/geogram/delaunay/periodic.cpp @@ -91,6 +91,5 @@ namespace GEO { false, false, false, false, true, true, false, true, true }; - } diff --git a/src/lib/geogram/delaunay/periodic_delaunay_3d.cpp b/src/lib/geogram/delaunay/periodic_delaunay_3d.cpp index 1eb7381e..bf568190 100644 --- a/src/lib/geogram/delaunay/periodic_delaunay_3d.cpp +++ b/src/lib/geogram/delaunay/periodic_delaunay_3d.cpp @@ -119,8 +119,8 @@ namespace { * of the integer. */ inline index_t pop_count(index_t x) { - geo_debug_assert(sizeof(index_t) == 4); -#if defined(GEO_COMPILER_GCC) || defined(GEO_COMPILER_CLANG) + static_assert(sizeof(index_t) == 4, "Only supported with 32 bit indices"); +#if defined(GEO_COMPILER_GCC_FAMILY) return index_t(__builtin_popcount(x)); #elif defined(GEO_COMPILER_MSVC) return index_t(__popcnt(x)); @@ -1623,7 +1623,7 @@ namespace GEO { geo_debug_assert(t < max_t()); geo_debug_assert(!owns_tet(t)); -#ifdef GEO_OS_WINDOWS +#if defined(GEO_COMPILER_MSVC) // Note: comparand and exchange parameter are swapped in Windows API // as compared to __sync_val_compare_and_swap !! interfering_thread_ = @@ -3679,8 +3679,8 @@ namespace GEO { vertex_ptr(0), reorder_, 3, dimension(), - reorder_.begin() + b, - reorder_.begin() + e, + reorder_.begin() + long(b), + reorder_.begin() + long(e), period_ ); @@ -3923,54 +3923,56 @@ namespace GEO { index_t nb_cells_on_boundary = 0; index_t nb_cells_outside_cube = 0; - static Process::spinlock lock = GEOGRAM_SPINLOCK_INIT; + Process::spinlock lock = GEOGRAM_SPINLOCK_INIT; -#ifdef GEO_OPENMP - #pragma omp parallel for -#endif - FOR(v, nb_vertices_non_periodic_) { - -#ifdef GEO_OPENMP - static thread_local ConvexCell C; - static thread_local vector neighbors; -#else - ConvexCell C; - vector neighbors; -#endif - bool use_instance[27]; - bool cell_is_on_boundary = false; - bool cell_is_outside_cube = false; - - // Determines the periodic vertices to create, that is, - // whenever the cell of the current vertex has an intersection - // with one of the 27 cubes, an instance needs to be created there. - index_t nb_instances = get_periodic_vertex_instances_to_create( - v, C, neighbors, use_instance, - cell_is_on_boundary, cell_is_outside_cube - ); - - if(cell_is_on_boundary) { - ++nb_cells_on_boundary; - } - - if(cell_is_outside_cube) { - ++nb_cells_outside_cube; - } + parallel_for_slice( + 0, nb_vertices_non_periodic_, + [this,&lock,&nb_cells_on_boundary,&nb_cells_outside_cube]( + index_t from, index_t to + ) { + ConvexCell C; + vector neighbors; + + for(index_t v=from; v 0) { - Process::acquire_spinlock(lock); - for(index_t instance=1; instance<27; ++instance) { - if(use_instance[instance]) { - vertex_instances_[v] |= (1u << instance); - reorder_.push_back(make_periodic_vertex(v,instance)); - } - } - Process::release_spinlock(lock); - } - } + // Append the new periodic vertices in the list of vertices. + // (we put them in the reorder_ vector that is always used + // to do the insertions). + if(nb_instances > 0) { + for(index_t instance=1; instance<27; ++instance) { + if(use_instance[instance]) { + vertex_instances_[v] |= (1u << instance); + reorder_.push_back(make_periodic_vertex(v,instance)); + } + } + } + + Process::release_spinlock(lock); + } + } + ); if(benchmark_mode_) { diff --git a/src/lib/geogram/image/color.h b/src/lib/geogram/image/color.h new file mode 100755 index 00000000..81af7514 --- /dev/null +++ b/src/lib/geogram/image/color.h @@ -0,0 +1,170 @@ +/* + * OGF/Graphite: Geometry and Graphics Programming Library + Utilities + * Copyright (C) 2000 Bruno Levy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * If you modify this software, you should include a notice giving the + * name of the person performing the modification, the date of modification, + * and the reason for such modification. + * + * Contact: Bruno Levy + * + * levy@loria.fr + * + * ISA Project + * LORIA, INRIA Lorraine, + * Campus Scientifique, BP 239 + * 54506 VANDOEUVRE LES NANCY CEDEX + * FRANCE + * + * Note that the GNU General Public License does not permit incorporating + * the Software into proprietary programs. + */ + + +#ifndef H_OGF_IMAGE_TYPES_COLOR_H +#define H_OGF_IMAGE_TYPES_COLOR_H + +#include +#include + +/** + * \file geogram/image/color.h + * \brief Color types. + */ +namespace GEO { + + /** + * \brief A generic color type. + * \tparam T type of the components + */ + template class GenColor : public vecng<4,T> { + public: + /** + * \brief The superclass type. + */ + typedef vecng<4,T> superclass; + + /** + * \brief GenColor copy constructor + * \param[in] rhs the GenColor to be copied. + */ + inline GenColor(const superclass& rhs) : superclass(rhs) { + } + + /** + * \brief GenColor constructor from T array. + * \param[in] rhs a pointer to an array of 4 Ts + */ + inline GenColor(const T* rhs) : superclass(rhs) { + } + + /** + * \brief GenColor constructor from 4 parameters. + * \param[in] r , g , b , a the components of the color + */ + inline GenColor( + T r=0, T g=0, T b=0, T a=1 + ):superclass(r,g,b,a) { + } + + /** + * \brief assignment operator. + * \param[in] rhs the GenColor to be copied + * \return the new value of this GenColor after assignment + */ + inline GenColor& operator=(const superclass& rhs) { + superclass::operator=(rhs); + return *this; + } + + /** + * \brief Gets the red component. + * \return the value of the red component + */ + T r() const { + return superclass::x; + } + + /** + * \brief Gets the green component. + * \return the value of the green component + */ + T g() const { + return superclass::y; + } + + /** + * \brief Gets the blue component. + * \return the value of the blue component + */ + T b() const { + return superclass::z; + } + + /** + * \brief Gets the alpha component (transparency). + * \return the value of the alpha component + */ + T a() const { + return superclass::w; + } + + /** + * \brief Sets the red component. + * \param[in] r the value of the red component + */ + void set_r(T r) { + superclass::x = r; + } + + /** + * \brief Sets the green component. + * \param[in] g the value of the green component + */ + void set_g(T g) { + superclass::y = g; + } + + /** + * \brief Sets the blue component. + * \param[in] b the value of the blue component + */ + void set_b(T b) { + superclass::z = b; + } + + /** + * \brief Sets the alpha component (transparency). + * \param[in] a the value of the alpha component + */ + void set_a(T a) { + superclass::w = a; + } + + }; + + /** + * \brief Default representation of colors. + * \details r,g,b,a components are double-precision + * number between 0.0 and 1.0. + */ + typedef GenColor Color; +} + +#endif + + diff --git a/src/lib/geogram/image/colormap.cpp b/src/lib/geogram/image/colormap.cpp new file mode 100755 index 00000000..ce1c9684 --- /dev/null +++ b/src/lib/geogram/image/colormap.cpp @@ -0,0 +1,191 @@ +/* + * OGF/Graphite: Geometry and Graphics Programming Library + Utilities + * Copyright (C) 2000 Bruno Levy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * If you modify this software, you should include a notice giving the + * name of the person performing the modification, the date of modification, + * and the reason for such modification. + * + * Contact: Bruno Levy + * + * levy@loria.fr + * + * ISA Project + * LORIA, INRIA Lorraine, + * Campus Scientifique, BP 239 + * 54506 VANDOEUVRE LES NANCY CEDEX + * FRANCE + * + * Note that the GNU General Public License does not permit incorporating + * the Software into proprietary programs. + */ + +#include + +namespace GEO { + +//_________________________________________________________ + + Colormap::Colormap(index_t size_in) : size_(size_in) { + cells_ = new ColorCell[size_in] ; + } + + Colormap::~Colormap() { + delete[] cells_ ; + } + + + void Colormap::color_ramp_component( + index_t component, + index_t index1, Numeric::uint8 alpha1, + index_t index2, Numeric::uint8 alpha2 + ) { + if(index1 == index2) { + color_cell(index2)[component] = alpha2 ; + } else { + int n = std::abs(int(index2) - int(index1)) ; + float delta = (float(alpha2) - float(alpha1)) / float(n) ; + int sgn = geo_sgn(int(index2) - int(index1)) ; + float alpha = alpha1 ; + int index = int(index1) ; + for(int i=0 ; i<=n ; i++) { + color_cell(index_t(index))[component] = + Numeric::uint8(alpha) ; + index += sgn ; + alpha += delta ; + } + } + } + + + void Colormap::color_ramp_rgba( + index_t index1, const Color& c1, + index_t index2, const Color& c2 + ) { + if(index1 == index2) { + Colormap::ColorCell c( + Numeric::uint8(c2.r() * 255.0), + Numeric::uint8(c2.g() * 255.0), + Numeric::uint8(c2.b() * 255.0), + Numeric::uint8(c2.a() * 255.0) + ) ; + color_cell(index2) = c ; + } else { + + int n = std::abs(int(index2) - int(index1)) ; + int sgn = geo_sgn(int(index2) - int(index1)) ; + + float r = float(c1.r()) ; + float g = float(c1.g()) ; + float b = float(c1.b()) ; + float a = float(c1.a()) ; + float dr = float(c2.r() - c1.r()) / float(n) ; + float dg = float(c2.g() - c1.g()) / float(n) ; + float db = float(c2.b() - c1.b()) / float(n) ; + float da = float(c2.a() - c1.a()) / float(n) ; + int index = int(index1) ; + + for(int i=0 ; i<=n ; i++) { + set_color(index_t(index), r, g, b, a) ; + index += sgn ; + r += dr ; + g += dg ; + b += db ; + a += da ; + } + } + } + + + void Colormap::color_ramp_rgb( + index_t index1, const Color& c1, + index_t index2, const Color& c2 + ) { + if(index1 == index2) { + Colormap::ColorCell c( + Numeric::uint8(c2.r() * 255.0), + Numeric::uint8(c2.g() * 255.0), + Numeric::uint8(c2.b() * 255.0), + color_cell(index2).a() + ) ; + color_cell(index2) = c ; + } else { + + int n = std::abs(int(index2) - int(index1)) ; + int sgn = geo_sgn(int(index2) - int(index1)) ; + + float r = float(c1.r()) ; + float g = float(c1.g()) ; + float b = float(c1.b()) ; + + float dr = (float(c2.r()) - float(c1.r())) / float(n); + float dg = (float(c2.g()) - float(c1.g())) / float(n); + float db = (float(c2.b()) - float(c1.b())) / float(n); + int index = int(index1) ; + + for(int i=0 ; i<=n ; i++) { + set_color(index_t(index), r, g, b) ; + index += sgn ; + r += dr ; + g += dg ; + b += db ; + } + } + } + + + void Colormap::set_color(index_t index, float r, float g, float b) { + r *= 255.0f ; + g *= 255.0f ; + b *= 255.0f ; + geo_clamp(r, float(0), float(255)) ; + geo_clamp(g, float(0), float(255)) ; + geo_clamp(b, float(0), float(255)) ; + Colormap::ColorCell c = Colormap::ColorCell( + Numeric::uint8(r), + Numeric::uint8(g), + Numeric::uint8(b), + color_cell(index).a() + ) ; + color_cell(index) = c ; + } + + void Colormap::set_color( + index_t index, float r, float g, float b, float a + ) { + r *= 255.0f ; + g *= 255.0f ; + b *= 255.0f ; + a *= 255.0f ; + geo_clamp(r, float(0), float(255)) ; + geo_clamp(g, float(0), float(255)) ; + geo_clamp(b, float(0), float(255)) ; + geo_clamp(a, float(0), float(255)) ; + Colormap::ColorCell c = Colormap::ColorCell( + Numeric::uint8(r), + Numeric::uint8(g), + Numeric::uint8(b), + Numeric::uint8(a) + ) ; + color_cell(index) = c ; + } + + +//_________________________________________________________ + +} + diff --git a/src/lib/geogram/image/colormap.h b/src/lib/geogram/image/colormap.h new file mode 100755 index 00000000..78859db6 --- /dev/null +++ b/src/lib/geogram/image/colormap.h @@ -0,0 +1,185 @@ +/* + * OGF/Graphite: Geometry and Graphics Programming Library + Utilities + * Copyright (C) 2000 Bruno Levy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * If you modify this software, you should include a notice giving the + * name of the person performing the modification, the date of modification, + * and the reason for such modification. + * + * Contact: Bruno Levy + * + * levy@loria.fr + * + * ISA Project + * LORIA, INRIA Lorraine, + * Campus Scientifique, BP 239 + * 54506 VANDOEUVRE LES NANCY CEDEX + * FRANCE + * + * Note that the GNU General Public License does not permit incorporating + * the Software into proprietary programs. + */ + +#ifndef H_GEO_IMAGE_TYPES_COLORMAP_H +#define H_GEO_IMAGE_TYPES_COLORMAP_H + +#include +#include +#include +#include + +/** + * \file geogram/image/colormap.h + * \brief Colormap type. + */ +namespace GEO { + +/******************************************************************/ + + /** + * \brief A Colormap. + * \details A Colormap is an array of colors, + * encoded with one byte per component. + */ + class GEOGRAM_API Colormap : public Counted { + public: + + /** + * \brief Type of each cell of the Colormap. + */ + typedef GenColor ColorCell ; + + /** + * \brief Colormap constructor. + * \param[in] size_in number of cells in the Colormap + */ + Colormap(index_t size_in = 256) ; + + /** + * \brief Colormap destructor. + */ + virtual ~Colormap() ; + + /** + * \brief Gets a ColorCell by index. + * \param[in] index the index of the ColorCell + * \return a const reference to the ColorCell + * \pre index < size() + */ + const ColorCell& color_cell(index_t index) const { + geo_assert(index < size_) ; + return cells_[index] ; + } + + /** + * \brief Gets a ColorCell by index. + * \param[in] index the index of the ColorCell + * \return a modifiable reference to the ColorCell + * \pre index < size() + */ + ColorCell& color_cell(index_t index) { + geo_assert(index < size_) ; + return cells_[index] ; + } + + /** + * \brief Gets the size. + * \return the number of ColorCells in this Colormap + */ + index_t size() const { + return size_; + } + + /** + * \brief Sets a color by index and components. + * \details The transparency a is left unchanged. + * \param[in] index the index + * \param[in] r , g , b the components, as single-precision + * floating points, between 0.0f, and 1.0f + */ + void set_color(index_t index, float r, float g, float b) ; + + /** + * \brief Sets a color by index and components. + * \param[in] index the index + * \param[in] r , g , b , a the components, as single-precision + * floating points, between 0.0f, and 1.0f + */ + void set_color(index_t index, float r, float g, float b, float a) ; + + /** + * \brief Make a color component linearly interpolate two values + * between two given indices. + * \param[in] component the index of the component, one of 0,1,2,3 + * \param[in] index1 the first index + * \param[in] val1 the first value of the component, associated + * with \p index1 + * \param[in] index2 the second index + * \param[in] val2 the second value of the component, associated + * with \p index2 + */ + void color_ramp_component( + index_t component, + index_t index1, Numeric::uint8 val1, + index_t index2, Numeric::uint8 val2 + ) ; + + + /** + * \brief Make a color linearly interpolate two values + * between two given indices. + * \details All components and transparency are updated. + * \param[in] index1 the first index + * \param[in] c1 the first color, associated with \p index1 + * \param[in] index2 the second index + * \param[in] c2 the first color, associated with \p index2 + */ + void color_ramp_rgba( + index_t index1, const Color& c1, + index_t index2, const Color& c2 + ) ; + + /** + * \brief Make a color linearly interpolate two values + * between two given indices. + * \details Only r,g,b are updated. Transparency a is left + * unmodified in the concerned color cells. + * \param[in] index1 the first index + * \param[in] c1 the first color, associated with \p index1 + * \param[in] index2 the second index + * \param[in] c2 the first color, associated with \p index2 + */ + void color_ramp_rgb( + index_t index1, const Color& c1, + index_t index2, const Color& c2 + ) ; + + private: + ColorCell* cells_ ; + index_t size_ ; + } ; + + /** + * \brief An automatic reference-counted pointer to a Colormap. + */ + typedef SmartPointer Colormap_var ; + +/************************************************************************/ + +} +#endif + diff --git a/src/lib/geogram/image/image.cpp b/src/lib/geogram/image/image.cpp new file mode 100755 index 00000000..9bdeacf9 --- /dev/null +++ b/src/lib/geogram/image/image.cpp @@ -0,0 +1,178 @@ +/* + * OGF/Graphite: Geometry and Graphics Programming Library + Utilities + * Copyright (C) 2000 Bruno Levy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * If you modify this software, you should include a notice giving the + * name of the person performing the modification, the date of modification, + * and the reason for such modification. + * + * Contact: Bruno Levy + * + * levy@loria.fr + * + * ISA Project + * LORIA, INRIA Lorraine, + * Campus Scientifique, BP 239 + * 54506 VANDOEUVRE LES NANCY CEDEX + * FRANCE + * + * Note that the GNU General Public License does not permit incorporating + * the Software into proprietary programs. + */ + +#include + +namespace GEO { + +//_________________________________________________________ + + size_t Image::nb_components(ColorEncoding color_rep) { + size_t result = 0; + switch(color_rep) { + case GRAY: + case INDEXED: + result = 1; + break; + case RGB: + case BGR: + case YUV: + result = 3; + break; + case RGBA: + result = 4; + break; + } + return result; + } + + size_t Image::bytes_per_component(ComponentEncoding component_rep) { + size_t result = 0; + switch(component_rep) { + case BYTE: + result = 1; + break; + case INT16: + result = 2; + break; + case INT32: + case FLOAT32: + result = 4; + break; + case FLOAT64: + result = 8; + break; + } + return result; + } + + Image::Image() { + bytes_per_pixel_ = 0 ; + dimension_ = 0 ; + size_[0] = 0 ; + size_[1] = 0 ; + size_[2] = 0 ; + factor_[0] = 0 ; + factor_[1] = 0 ; + factor_[2] = 0 ; + base_mem_ = nullptr ; + } + + Image::~Image() { + delete[] base_mem_; + bytes_per_pixel_ = 0 ; + dimension_ = 0 ; + size_[0] = 0 ; + size_[1] = 0 ; + size_[2] = 0 ; + factor_[0] = 0 ; + factor_[1] = 0 ; + factor_[2] = 0 ; + base_mem_ = nullptr ; + } + + void Image::initialize( + ColorEncoding color_rep, ComponentEncoding component_rep, + index_t size_x, index_t size_y, index_t size_z + ) { + color_encoding_ = color_rep; + component_encoding_ = component_rep; + bytes_per_pixel_ = + nb_components(color_rep) * bytes_per_component(component_rep); + size_[0] = size_x ; + size_[1] = size_y ; + size_[2] = size_z ; + dimension_ = 3; + if(size_z == 1) { + dimension_ = 2 ; + if(size_y == 1) { + dimension_ = 1 ; + } + } + factor_[0] = bytes_per_pixel_ ; + factor_[1] = factor_[0] * size_x ; + factor_[2] = factor_[1] * size_y ; + delete[] base_mem_; + base_mem_ = new Memory::byte[bytes()]; + Memory::clear(base_mem_, bytes()); + } + + void Image::acquire() { + } + + void Image::flip_vertically() { + index_t bpp=index_t(bytes_per_pixel()); + index_t h = height() ; + index_t w = width() ; + index_t row_len = w * bpp ; + for(index_t j=0; j< h/2; j++) { + // get a pointer to the two lines we will swap + Memory::pointer row1 = base_mem() + j * row_len ; + Memory::pointer row2 = base_mem() + (h - 1 - j) * row_len ; + // for each point on line, swap all the channels + for(index_t i=0; i +#include + +/** + * \file geogram/image/image.h + * \details Types for images. + */ + +namespace GEO { + +//_________________________________________________________ + + + /** + * \brief An image. + */ + class GEOGRAM_API Image : public Counted { + public: + + /** + * \brief Indicates how colors are encoded + * within the image. + */ + enum ColorEncoding { + GRAY, INDEXED, RGB, BGR, RGBA, YUV + }; + + /** + * \brief Indicates the datatype used to + * encode each component of the colors. + */ + enum ComponentEncoding { + BYTE, INT16, INT32, FLOAT32, FLOAT64 + }; + + + /** + * \brief Image constructor. + * \details Constructs an uninitialized Image. + */ + Image(); + + /** + * \brief Image constructor. + * \param[in] color_rep the ColorEncoding + * \param[in] component_rep the ComponentEncoding + * \param[in] width the width of the image + * \param[in] height the height of the image, or 1 for 1D images + * \param[in] depth the depth of the image for 3D images, or 1 + * for 1D and 2D images + */ + Image( + ColorEncoding color_rep, ComponentEncoding component_rep, + index_t width, index_t height=1, index_t depth=1 + ) { + base_mem_ = nullptr; + initialize(color_rep, component_rep, width, height, depth); + } + + /** + * \brief Image destructor. + */ + virtual ~Image(); + + /** + * \brief Some implementations get the image from some sources. + * Default implementation does nothing. + * \details There is a derived class to access the webcam for + * instance. + */ + virtual void acquire(); + + /** + * \brief Gets the dimension of the image. + * \retval 1 for 1D images + * \retval 2 for 2D images + * \retval 3 for 3D images + */ + index_t dimension() const { + return dimension_; + } + + /** + * \brief Gets the size of the image along one of the axes. + * \param[in] axis the axis, one of (0,1,2) + * \return the number of pixels along axis + */ + index_t size(index_t axis) const { + geo_assert(axis < 3); + return size_[axis]; + } + + /** + * \brief Gets the width of the image. + * \return the width of the image, in pixels + */ + index_t width() const { + return size_[0]; + } + + /** + * \brief Gets the height of the image. + * \return the height of the image, in pixels, or 1 + * for 1D images + */ + index_t height() const { + return size_[1]; + } + + /** + * \brief Gets the depth of the image. + * \return for 3D images, the depth of the image in pixels, + * or 1 for 1D and 2D images. + */ + index_t depth() const { + return size_[2]; + } + + /** + * \brief Gets the number of bytes per pixel. + * \return the number of bytes used to store the color of one pixel. + */ + size_t bytes_per_pixel() const { + return bytes_per_pixel_; + } + + /** + * \brief Gets the number of components per pixel. + * \return the number of color components in each pixel. + */ + size_t components_per_pixel() const { + return nb_components(color_encoding()); + } + + /** + * \brief Gets the number of pixels. + * \return the total number of pixels in this image + */ + size_t nb_pixels() const { + return size_t(size_[0]) * size_t(size_[1]) * size_t(size_[2]); + } + + /** + * \brief Gets the number of bytes. + * \return the total number of bytes used to store the color + * data of this image + */ + size_t bytes() const { + return nb_pixels() * bytes_per_pixel(); + } + + /** + * \brief Gets the ComponentEncoding. + * \return the ComponentEncoding + */ + ComponentEncoding component_encoding() const { + return component_encoding_; + } + + /** + * \brief Gets the ColorEncoding. + * \return the ColorEncoding + */ + ColorEncoding color_encoding() const { + return color_encoding_; + } + + /** + * \brief Gets the Colormap + * \return a const pointer to the Colormap. + */ + const Colormap* colormap() const { + return colormap_; + } + + /** + * \brief Gets the Colormap + * \return a pointer to the Colormap. + */ + Colormap* colormap() { + return colormap_; + } + + /** + * \brief Sets the Colormap + * \param[in] colormap a pointer to the Colormap, + * ownership is transfered to this Image + */ + void set_colormap(Colormap* colormap) { + colormap_ = colormap; + } + + /** + * \brief Gets the base memory. + * \return a pointer to the color data associated + * with this image. + */ + Memory::pointer base_mem() const { + return base_mem_; + } + + /** + * \brief Gets the base memory as a byte pointer. + * \return a byte pointer to the color data associated + * with this image. + * \pre ComponentEncoding == BYTE + */ + Memory::byte* base_mem_byte_ptr() const { + return byte_ptr(base_mem_); + } + + /** + * \brief Gets the base memory as a 16 bits integer pointer. + * \return a 16 bits integer pointer to the color data associated + * with this image. + * \pre ComponentEncoding == INT16 + */ + Numeric::int16* base_mem_int16_ptr() const { + return int16_ptr(base_mem_); + } + + /** + * \brief Gets the base memory as a 32 bits integer pointer. + * \return a 32 bits integer pointer to the color data associated + * with this image. + * \pre ComponentEncoding == FLOAT32 + */ + Numeric::int32* base_mem_int32_ptr() const { + return int32_ptr(base_mem_); + } + + /** + * \brief Gets the base memory as a 32 bits floating point pointer. + * \return a 32 bits floating point pointer to the color data associated + * with this image. + * \pre ComponentEncoding == FLOAT32 + */ + Numeric::float32* base_mem_float32_ptr() const { + return float32_ptr(base_mem_); + } + + /** + * \brief Gets the base memory as a 64 bits floating point pointer. + * \return a 64 bits floating point pointer to the color data associated + * with this image. + * \pre ComponentEncoding == FLOAT64 + */ + Numeric::float64* base_mem_float64_ptr() const { + return float64_ptr(base_mem_); + } + + /** + * \brief Gets the address of a pixel in a 1D image. + * \param[in] x the x coordinate of the pixel + * \return a pointer to the color data associated with the pixel + * \pre x < width() + */ + Memory::pointer pixel_base(index_t x) { + return base_mem() + x * factor_[0]; + } + + + /** + * \brief Gets the address of a pixel in a 1D image as a byte pointer. + * \param[in] x the x coordinate of the pixel + * \return a pointer to the color data associated with the pixel + * \pre x < width() && COMPONENT_ENCODING == BYTE + */ + Memory::byte* pixel_base_byte_ptr(index_t x) { + return byte_ptr(base_mem() + x * factor_[0]); + } + + /** + * \brief Gets the address of a pixel in a 1D image as a int16 pointer. + * \param[in] x the x coordinate of the pixel + * \return a pointer to the color data associated with the pixel + * \pre x < width() && COMPONENT_ENCODING == INT16 + */ + Numeric::int16* pixel_base_int16_ptr(index_t x) { + return int16_ptr(base_mem() + x * factor_[0]); + } + + /** + * \brief Gets the address of a pixel in a 1D image as a int32 pointer. + * \param[in] x the x coordinate of the pixel + * \return a pointer to the color data associated with the pixel + * \pre x < width() && COMPONENT_ENCODING == INT32 + */ + Numeric::int32* pixel_base_int32_ptr(index_t x) { + return int32_ptr(base_mem() + x * factor_[0]); + } + + /** + * \brief Gets the address of a pixel in a 1D image as a float32 pointer. + * \param[in] x the x coordinate of the pixel + * \return a pointer to the color data associated with the pixel + * \pre x < width() && COMPONENT_ENCODING == FLOAT32 + */ + Numeric::float32* pixel_base_float32_ptr(index_t x) { + return float32_ptr(base_mem() + x * factor_[0]); + } + + /** + * \brief Gets the address of a pixel in a 1D image as a float64 pointer. + * \param[in] x the x coordinate of the pixel + * \return a pointer to the color data associated with the pixel + * \pre x < width() && COMPONENT_ENCODING == FLOAT64 + */ + Numeric::float64* pixel_base_float64_ptr(index_t x) { + return float64_ptr(base_mem() + x * factor_[0]); + } + + + /** + * \brief Gets the address of a pixel in a 2D image. + * \param[in] x , y the coordinates of the pixel + * \return a pointer to the color data associated with the pixel + * \pre x < width() && y < height() + */ + Memory::pointer pixel_base(index_t x, index_t y) { + return base_mem() + x * factor_[0] + y * factor_[1]; + } + + /** + * \brief Gets the address of a pixel in a 2D image as a byte pointer. + * \param[in] x , y the coordinates of the pixel + * \return a pointer to the color data associated with the pixel + * \pre x < width() && y < height() && component_encoding() && BYTE + */ + Memory::byte* pixel_base_byte_ptr(index_t x, index_t y) { + return byte_ptr(base_mem() + x * factor_[0] + y * factor_[1]); + } + + /** + * \brief Gets the address of a pixel in a 2D image as an int16 pointer. + * \param[in] x , y the coordinates of the pixel + * \return a pointer to the color data associated with the pixel + * \pre x < width() && y < height() && component_encoding() && INT16 + */ + Numeric::int16* pixel_base_int16_ptr(index_t x, index_t y) { + return int16_ptr(base_mem() + x * factor_[0] + y * factor_[1]); + } + + /** + * \brief Gets the address of a pixel in a 2D image as an int32 pointer. + * \param[in] x , y the coordinates of the pixel + * \return a pointer to the color data associated with the pixel + * \pre x < width() && y < height() && component_encoding() && INT32 + */ + Numeric::int32* pixel_base_int32_ptr(index_t x, index_t y) { + return int32_ptr(base_mem() + x * factor_[0] + y * factor_[1]); + } + + /** + * \brief Gets the address of a pixel in a 2D image as a float32 pointer. + * \param[in] x , y the coordinates of the pixel + * \return a pointer to the color data associated with the pixel + * \pre x < width() && y < height() && component_encoding() && FLOAT32 + */ + Numeric::float32* pixel_base_float32_ptr(index_t x, index_t y) { + return float32_ptr(base_mem() + x * factor_[0] + y * factor_[1]); + } + + /** + * \brief Gets the address of a pixel in a 2D image as a float64 pointer. + * \param[in] x , y the coordinates of the pixel + * \return a pointer to the color data associated with the pixel + * \pre x < width() && y < height() && component_encoding() && FLOAT64 + */ + Numeric::float64* pixel_base_float64_ptr(index_t x, index_t y) { + return float64_ptr(base_mem() + x * factor_[0] + y * factor_[1]); + } + + /** + * \brief Gets the address of a pixel in a 3D image. + * \param[in] x , y , z the coordinates of the pixel + * \return a pointer to the color data associated with the pixel + * \pre x < width() && y < height() && z < depth() + */ + Memory::pointer pixel_base(index_t x, index_t y, index_t z) { + return base_mem() + + x * factor_[0] + y * factor_[1] + z * factor_[2]; + } + + /** + * \brief Gets the address of a pixel in a 3D image as a byte pointer. + * \param[in] x , y , z the coordinates of the pixel + * \return a pointer to the color data associated with the pixel + * \pre x < width() && y < height() && z < depth() && + * component_encoding() && BYTE + */ + Memory::byte* pixel_base_byte_ptr(index_t x, index_t y, index_t z) { + return byte_ptr(base_mem() + + x * factor_[0] + y * factor_[1] + z * factor_[2] + ); + } + + /** + * \brief Gets the address of a pixel in a 3D image as an int16 pointer. + * \param[in] x , y , z the coordinates of the pixel + * \return a pointer to the color data associated with the pixel + * \pre x < width() && y < height() && z < depth() && + * component_encoding() && INT16 + */ + Numeric::int16* pixel_base_int16_ptr(index_t x, index_t y, index_t z) { + return int16_ptr(base_mem() + + x * factor_[0] + y * factor_[1] + z * factor_[2] + ); + } + + /** + * \brief Gets the address of a pixel in a 3D image as an int32 pointer. + * \param[in] x , y , z the coordinates of the pixel + * \return a pointer to the color data associated with the pixel + * \pre x < width() && y < height() && z < depth() && + * component_encoding() && INT32 + */ + Numeric::int32* pixel_base_int32_ptr(index_t x, index_t y, index_t z) { + return int32_ptr(base_mem() + + x * factor_[0] + y * factor_[1] + z * factor_[2] + ); + } + + /** + * \brief Gets the address of a pixel in a 3D image as a float32 + * pointer. + * \param[in] x , y , z the coordinates of the pixel + * \return a pointer to the color data associated with the pixel + * \pre x < width() && y < height() && z < depth() && + * component_encoding() && FLOAT32 + */ + Numeric::float32* pixel_base_float32_ptr( + index_t x, index_t y, index_t z + ) { + return float32_ptr(base_mem() + + x * factor_[0] + y * factor_[1] + z * factor_[2] + ); + } + + /** + * \brief Gets the address of a pixel in a 3D image as a float64 + * pointer. + * \param[in] x , y , z the coordinates of the pixel + * \return a pointer to the color data associated with the pixel + * \pre x < width() && y < height() && z < depth() && + * component_encoding() && FLOAT64 + */ + Numeric::float64* pixel_base_float64_ptr( + index_t x, index_t y, index_t z + ) { + return float64_ptr(base_mem() + + x * factor_[0] + y * factor_[1] + z * factor_[2] + ); + } + + /** + * \brief Gets the number of components associated with + * a ColorEncoding. + * \param[in] color_rep the ColorEncoding + * \return the number of components used by \p color_rep + */ + static size_t nb_components(ColorEncoding color_rep); + + /** + * \brief Gets the number of bytes used by a ComponentEncoding. + * \param[in] component_rep the ComponentEncoding + * \return the number of bytes used to represent a color component + * encoded with \p component_rep + */ + static size_t bytes_per_component(ComponentEncoding component_rep); + + /** + * \brief Converts an untyped pointer into a byte pointer. + * \param[in] ptr the pointer to be converted + * \return pointer \p ptr converted to a byte pointer + * \pre component_encoding_ == BYTE + * \note This function does nothing else than casting the pointer. In + * addition, in debug mode, it tests that the color encoding is the + * right one (and throws an assertion failure if it is not the case). + */ + Memory::byte* byte_ptr(Memory::pointer ptr) const { + geo_debug_assert(component_encoding_ == BYTE); + return ptr; + } + + /** + * \brief Converts an untyped pointer into a 16 bits integer pointer. + * \param[in] ptr the pointer to be converted + * \return pointer \p ptr converted to a 16 bits integer pointer + * \pre component_encoding_ == INT16 + * \note This function does nothing else than casting the pointer. In + * addition, in debug mode, it tests that the color encoding is the + * right one (and throws an assertion failure if it is not the case). + */ + Numeric::int16* int16_ptr(Memory::pointer ptr) const { + geo_debug_assert(component_encoding_ == INT16); + return (Numeric::int16*)(void*)(ptr); + } + + /** + * \brief Converts an untyped pointer into a 32 bits integer pointer. + * \param[in] ptr the pointer to be converted + * \return pointer \p ptr converted to a 32 bits integer pointer + * \pre component_encoding_ == INT32 + * \note This function does nothing else than casting the pointer. In + * addition, in debug mode, it tests that the color encoding is the + * right one (and throws an assertion failure if it is not the case). + */ + Numeric::int32* int32_ptr(Memory::pointer ptr) const { + geo_debug_assert(component_encoding_ == INT32); + return (Numeric::int32*)(void*)(ptr); + } + + /** + * \brief Converts an untyped pointer into a 32 bits floating point + * pointer. + * \param[in] ptr the pointer to be converted + * \return pointer \p ptr converted to a 32 bits floating point pointer + * \pre component_encoding_ == FLOAT32 + * \note This function does nothing else than casting the pointer. In + * addition, in debug mode, it tests that the color encoding is the + * right one (and throws an assertion failure if it is not the case). + */ + Numeric::float32* float32_ptr(Memory::pointer ptr) const { + geo_debug_assert(component_encoding_ == FLOAT32); + return (Numeric::float32*)(void*)(ptr); + } + + /** + * \brief Converts an untyped pointer into a 64 bits floating point + * pointer. + * \param[in] ptr the pointer to be converted + * \return pointer \p ptr converted to a 64 bits floating point pointer + * \pre component_encoding_ == FLOAT64 + * \note This function does nothing else than casting the pointer. In + * addition, in debug mode, it tests that the color encoding is the + * right one (and throws an assertion failure if it is not the case). + */ + Numeric::float64* float64_ptr(Memory::pointer ptr) const { + geo_debug_assert(component_encoding_ == FLOAT64); + return (Numeric::float64*)(void*)(ptr); + } + + /** + * \brief Flips this image along the y axis. + */ + void flip_vertically(); + + /** + * \brief Swaps two color components of this image. + * \param[in] channel1 , channel2 the two channels to + * be swapped + * \pre channel1 < nb_components(color_encoding()) && + * channel2 < nb_components(color_encoding()) + */ + void swap_components(index_t channel1, index_t channel2); + + /** + * \brief Creates storage for the specified encoding + * and image dimensions. + * \param[in] color_rep the ColorEncoding + * \param[in] component_rep the ComponentEncoding + * \param[in] size_x the image width + * \param[in] size_y the image height, or 1 for 1D images + * \param[in] size_z the image depth for 3D images, or 1 for 1D and + * 2D images + */ + void initialize( + ColorEncoding color_rep, ComponentEncoding component_rep, + index_t size_x, index_t size_y=1, index_t size_z=1 + ); + + protected: + ColorEncoding color_encoding_; + ComponentEncoding component_encoding_; + Colormap_var colormap_; + size_t factor_[3]; + Memory::pointer base_mem_; + index_t dimension_; + index_t size_[3]; + size_t bytes_per_pixel_; + + private: + /** + * \brief Forbids copy-construction. + */ + Image(const Image& rhs); + + /** + * \brief Forbids assignment operator. + */ + Image& operator=(const Image& rhs); + }; + + typedef SmartPointer Image_var; + +//_________________________________________________________ + +} +#endif + diff --git a/src/lib/geogram/image/image_library.cpp b/src/lib/geogram/image/image_library.cpp new file mode 100755 index 00000000..a9ab2bdf --- /dev/null +++ b/src/lib/geogram/image/image_library.cpp @@ -0,0 +1,333 @@ +/* + * OGF/Graphite: Geometry and Graphics Programming Library + Utilities + * Copyright (C) 2000 Bruno Levy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * If you modify this software, you should include a notice giving the + * name of the person performing the modification, the date of modification, + * and the reason for such modification. + * + * Contact: Bruno Levy + * + * levy@loria.fr + * + * ISA Project + * LORIA, INRIA Lorraine, + * Campus Scientifique, BP 239 + * 54506 VANDOEUVRE LES NANCY CEDEX + * FRANCE + * + * Note that the GNU General Public License does not permit incorporating + * the Software into proprietary programs. + */ + +#include +#include +#include + +#include +#include +#include + +// For clipboard +#ifdef GEO_OS_WINDOWS +#include +#endif + +namespace GEO { + +//_________________________________________________________ + + ImageLibrary* ImageLibrary::instance_ = nullptr ; + + ImageLibrary* ImageLibrary::instance() { + return instance_; + } + + void ImageLibrary::initialize() { + geo_assert(instance_ == nullptr) ; + instance_ = new ImageLibrary() ; + Environment::instance()->add_environment(instance_) ; + } + + void ImageLibrary::terminate() { + // Note: instance is not deleted here. Since it + // is declared in the Environment, now Environment + // has the ownership and will delete it. + geo_assert(instance_ != nullptr) ; + instance_ = nullptr ; + } + + ImageLibrary::ImageLibrary() { + } + + ImageLibrary::~ImageLibrary() { + } + + + bool ImageLibrary::bind_image_serializer( + const std::string& extension, ImageSerializer* serializer + ) { + std::string upper_extension = extension ; + String::to_uppercase(upper_extension) ; + if( + resolve_image_serializer(extension) != nullptr || + resolve_image_serializer(upper_extension) != nullptr + ) { + return false ; + } + image_serializers_[extension] = serializer ; + image_serializers_[upper_extension] = serializer ; + Environment::notify_observers("image_read_extensions") ; + Environment::notify_observers("image_write_extensions") ; + return true ; + } + + ImageSerializer* ImageLibrary::resolve_image_serializer( + const std::string& extension + ) const { + auto it = image_serializers_.find(extension) ; + if(it == image_serializers_.end()) { + return nullptr ; + } + return it->second ; + } + + bool ImageLibrary::bind_image(const std::string& name, Image* image) { + if(resolve_image(name) != nullptr) { + return false ; + } + images_[name] = image ; + return true ; + } + + bool ImageLibrary::unbind_image(const std::string& name) { + auto it = images_.find(name) ; + if(it == images_.end()) { + return false ; + } + images_.erase(it) ; + return true ; + } + + Image* ImageLibrary::resolve_image(const std::string& name) const { + auto it = images_.find(name) ; + if(it == images_.end()) { + return nullptr ; + } + return it->second ; + } + + Image* ImageLibrary::load_image(const std::string& file_name) { + std::string extension = FileSystem::extension(file_name) ; + if(extension.length() == 0) { + Image* result = resolve_image(file_name) ; + if(result != nullptr) { result->acquire(); return result ; } + Logger::err("ImageLibrary") + << "no extension in file name and no such registered image" << std::endl ; + return nullptr ; + } + + ImageSerializer* serializer = resolve_image_serializer(extension) ; + if(serializer == nullptr) { + Logger::err("ImageLibrary") + << "could not find serializer for extension \'" + << extension << "\'" << std::endl ; + return nullptr ; + } + + if(!serializer->read_supported()) { + Logger::err("ImageLibrary") + << "serializer for extension \'" + << extension << "\' does not have a \'read\' function" + << std::endl ; + return nullptr ; + } + + return serializer->serialize_read(file_name) ; + } + + bool ImageLibrary::save_image( + const std::string& file_name, Image* image + ) { + + std::string extension = FileSystem::extension(file_name) ; + if(extension.length() == 0) { + Logger::err("ImageLibrary") + << "no extension in file name" << std::endl ; + return false ; + } + + ImageSerializer* serializer = resolve_image_serializer(extension) ; + if(serializer == nullptr) { + Logger::err("ImageLibrary") + << "could not find serializer for extension \'" + << extension << "\'" << std::endl ; + return false ; + } + + if(!serializer->write_supported()) { + Logger::err("ImageLibrary") + << "serializer for extension \'" + << extension << "\' does not have a \'write\' function" + << std::endl ; + return false ; + } + + return serializer->serialize_write(file_name, image) ; + } + + void ImageLibrary::copy_image_to_clipboard(Image* image) { + geo_argused(image); + +#ifdef GEO_OS_WINDOWS + + if(image->color_encoding() != Image::RGB) { + Logger::err("ImageLibrary") + << "copy_image_to_clipboard() " + << "not implemented for this color encoding" + << std::endl ; + return ; + } + + // Thanks to Pierre Alliez for his help with + // Windows clipboard programming. + + // Step 1: Try to open Window's clipboard + // nullptr -> bind to current process + if(!::OpenClipboard( nullptr )) { + return ; + } + + int h = image->height() ; + int w = image->width() ; + + // Step 2: Prepare the image for Windows: + // flip the image and flip rgb -> bgr + { + int row_len = image->width() * 3 ; + for(int j=0; j< h/2; j++) { + Memory::pointer row1 = + image->base_mem() + j * row_len ; + Memory::pointer row2 = + image->base_mem() + (h - 1 - j) * row_len ; + for(int i=0; iwidth() * image->height(); + int size = sizeof(BITMAPINFOHEADER) + image_size ; + + handle = (HANDLE)::GlobalAlloc(GHND,size); + if(handle != nullptr) { + char *pData = (char *) ::GlobalLock((HGLOBAL)handle); + BITMAPINFOHEADER header ; + header.biSize = sizeof(BITMAPINFOHEADER); + header.biWidth = image->width() ; + header.biHeight = image->height() ; + header.biPlanes = 1 ; + header.biBitCount = 24 ; + header.biCompression = BI_RGB ; + header.biSizeImage = 0 ; + header.biXPelsPerMeter = 1000000 ; + header.biYPelsPerMeter = 1000000 ; + header.biClrUsed = 0 ; + header.biClrImportant = 0 ; + ::memcpy(pData,&header,sizeof(BITMAPINFOHEADER)); + ::memcpy( + pData+sizeof(BITMAPINFOHEADER),image->base_mem(),image_size + ) ; + + // Step 4: put the data in the clipboard. + ::GlobalUnlock((HGLOBAL)handle); + ::EmptyClipboard() ; + ::SetClipboardData(CF_DIB,handle); + ::CloseClipboard(); + + // Step 5: restore the image + { + int row_len = image->width() * 3 ; + for(int j=0; j< h/2; j++) { + Memory::pointer + row1 = image->base_mem() + j * row_len ; + Memory::pointer + row2 = image->base_mem() + (h - 1 - j) * row_len ; + for(int i=0; iread_supported()) { + if(value.length() != 0) { + value += ";" ; + } + value += "*." ; + value += it.first ; + } + } + return true ; + } else if(name=="image_write_extensions") { + value = "" ; + for(auto& it : image_serializers_) { + if(it.second->write_supported()) { + if(value.length() != 0) { + value += ";" ; + } + value += "*." ; + value += it.first ; + } + } + return true ; + } else { + return false ; + } + } + + bool ImageLibrary::set_local_value(const std::string& name, const std::string& value) { + geo_argused(name); + geo_argused(value); + return false; + } + + +//_________________________________________________________ + +} + diff --git a/src/lib/geogram/image/image_library.h b/src/lib/geogram/image/image_library.h new file mode 100755 index 00000000..4763b50b --- /dev/null +++ b/src/lib/geogram/image/image_library.h @@ -0,0 +1,226 @@ +/* + * OGF/Graphite: Geometry and Graphics Programming Library + Utilities + * Copyright (C) 2000 Bruno Levy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * If you modify this software, you should include a notice giving the + * name of the person performing the modification, the date of modification, + * and the reason for such modification. + * + * Contact: Bruno Levy + * + * levy@loria.fr + * + * ISA Project + * LORIA, INRIA Lorraine, + * Campus Scientifique, BP 239 + * 54506 VANDOEUVRE LES NANCY CEDEX + * FRANCE + * + * Note that the GNU General Public License does not permit incorporating + * the Software into proprietary programs. + */ + +#ifndef H_OGF_IMAGE_TYPES_IMAGE_LIBRARY_H +#define H_OGF_IMAGE_TYPES_IMAGE_LIBRARY_H + +#include +#include +#include + +#include + +#include +#include + +/** + * \file geogram/image/image_library.h + * \brief Management of image serializers and named images. + */ + +namespace GEO { + +//_________________________________________________________ + + class ImageSerializer; + class Image; + + /** + * \brief Manages the ImageSerializer repository and + * the named images. + */ + class GEOGRAM_API ImageLibrary : public Environment { + public: + + /** + * \brief Gets the instance. + * \return a pointer to the unique instance of ImageLibrary. + */ + static ImageLibrary* instance(); + + /** + * \brief Initializes the ImageLibrary instance. + * \details This function is automatically called during + * Graphite startup. It should not be called by client + * code. + */ + static void initialize(); + + /** + * \brief Terminates the ImageLibrary instance. + * \details This function is automatically called during + * Graphite shutdown. It should not be called by client + * code. + */ + static void terminate(); + + /** + * \brief Binds an ImageSerializer. + * \param[in] extension the file extension without the "." + * \param[in] serializer a pointer to an ImageSerializer. Ownership + * is transferred to this ImageLibrary + * \retval true if the ImageSerializer could be successfully bound + * \retval false otherwise (i.e. if there was already a serializer + * with the same name). + */ + bool bind_image_serializer( + const std::string& extension, ImageSerializer* serializer + ); + + /** + * \brief Finds an ImageSerializer by extension. + * \param[in] extension the file extension without the "." + * \return a pointer to the ImageSerializer that can serialize + * images in the file format that corresponds to the extension, + * or nil if no such ImageSerializer was found + */ + ImageSerializer* resolve_image_serializer( + const std::string& extension + ) const; + + /** + * \brief Binds an image with a name. + * \param[in] name the name of the image + * \param[in] image a pointer to the image to be bound. Ownership + * is transferred to this ImageLibrary. + * \retval true if the Image could be successfully bound + * \retval false otherwise, i.e. if there was already an Image + * bound with the same name + */ + bool bind_image(const std::string& name, Image* image); + + /** + * \brief Unbinds a named image. + * \param[in] name the name of the image + * \retval true if the Image could be successfully unbound + * \retval false otherwise, i.e. if there was no Image + * bound with the specified name + */ + bool unbind_image(const std::string& name); + + /** + * \brief Finds an image by name. + * \param[in] name the name of the image + * \return a pointer to the image, or nil if no image + * is bound to the name + * \see bind_image(), unbind_image() + */ + Image* resolve_image(const std::string& name) const; + + /** + * \brief Loads an image from a file. + * \param[in] file_name the name of the file that contains the image + * \return a pointer to the loaded image, or nil if the image could + * not be loaded. + * \note The returned pointer can be stored in an Image_var + */ + Image* load_image(const std::string& file_name); + + /** + * \brief Saves an image into a file. + * \param[in] file_name the name of the file that will receive the + * image + * \param[in] image a pointer to the Image to be saved + * \retval true if the image could be successfully saved + * \retval false otherwise + */ + bool save_image(const std::string& file_name, Image* image); + + /** + * \brief Copies an image to the clipboard of the operating system. + * \param[in] image the image to be copied to the clipboard. + * \note Only implemented under Windows + */ + void copy_image_to_clipboard(Image* image); + + /** + * \copydoc Environment::get_local_value() + * \details Provides the following environment variables: + * - image_read_extensions + * - image_write_extensions + */ + bool get_local_value( + const std::string& name, std::string& value + ) const override; + + /** + * \copydoc Environment::set_local_value() + */ + bool set_local_value( + const std::string& name, const std::string& value + ) override; + + protected: + ImageLibrary(); + ~ImageLibrary() override; + friend class World; + + private: + static ImageLibrary* instance_; + std::map image_serializers_; + std::map images_; + }; + +//_________________________________________________________ + + /** + * \brief Declares an image serializer for a given extension. + * \tparam T the type of the ImageSerializer + */ + template class geo_declare_image_serializer { + public: + /** + * \brief Declares an image serializer for a given extension. + * \param[in] extension the extension of the image file names + * without the "." + * \details This function is supposed to be used in the + * initializers of the libraries. An example of usage: + * \code + * ogf_declare_image_serializer("png"); + * \endcode + */ + geo_declare_image_serializer(const std::string& extension) { + ImageLibrary::instance()->bind_image_serializer( + extension, new T + ); + } + }; + +//_________________________________________________________ + +} +#endif + diff --git a/src/lib/geogram/image/image_rasterizer.cpp b/src/lib/geogram/image/image_rasterizer.cpp new file mode 100644 index 00000000..4cc42144 --- /dev/null +++ b/src/lib/geogram/image/image_rasterizer.cpp @@ -0,0 +1,134 @@ +/* + * OGF/Graphite: Geometry and Graphics Programming Library + Utilities + * Copyright (C) 2000 Bruno Levy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * If you modify this software, you should include a notice giving the + * name of the person performing the modification, the date of modification, + * and the reason for such modification. + * + * Contact: Bruno Levy + * + * levy@loria.fr + * + * ISA Project + * LORIA, INRIA Lorraine, + * Campus Scientifique, BP 239 + * 54506 VANDOEUVRE LES NANCY CEDEX + * FRANCE + * + * Note that the GNU General Public License does not permit incorporating + * the Software into proprietary programs. + */ + +#include + +namespace GEO { + + ImageRasterizer::ImageRasterizer( + Image* image, double x1, double y1, double x2, double y2 + ) : image_(image) { + x1_ = x1; + y1_ = y1; + x2_ = x2; + y2_ = y2; + component_encoding_ = image_->component_encoding(); + nb_components_ = index_t( + Image::nb_components(image_->color_encoding()) + ); + } + + void ImageRasterizer::clear() { + Memory::clear(image_->base_mem(), image_->bytes()); + } + + void ImageRasterizer::triangle( + const vec2& p1, const Color& c1, + const vec2& p2, const Color& c2, + const vec2& p3, const Color& c3 + ) { + vec2i P1,P2,P3; + + transform(p1,P1); + transform(p2,P2); + transform(p3,P3); + + // Find triangle's bounding box. + int xmin = std::min(P1.x,std::min(P2.x,P3.x)); + int ymin = std::min(P1.y,std::min(P2.y,P3.y)); + int xmax = std::max(P1.x,std::max(P2.x,P3.x)); + int ymax = std::max(P1.y,std::max(P2.y,P3.y)); + + geo_clamp(xmin, 0, int(image_->width()-1)); + geo_clamp(xmax, 0, int(image_->width()-1)); + geo_clamp(ymin, 0, int(image_->height()-1)); + geo_clamp(ymax, 0, int(image_->height()-1)); + + int D = (P2.x - P1.x) * (P3.y - P1.y) - (P2.y - P1.y) * (P3.x - P1.x); + + if(D == 0) { + return; + } + + // Iterative computation of barycentric coordinates. + + int cxl1 = P2.y - P3.y; + int cxl2 = P3.y - P1.y; + int cxl3 = P1.y - P2.y; + + int cyl1 = P3.x - P2.x; + int cyl2 = P1.x - P3.x; + int cyl3 = P2.x - P1.x; + + int c0l1 = P2.x*P3.y-P3.x*P2.y; + int c0l2 = P3.x*P1.y-P1.x*P3.y; + int c0l3 = P1.x*P2.y-P2.x*P1.y; + + int row_l1 = xmin * cxl1 + ymin * cyl1 + c0l1; + int row_l2 = xmin * cxl2 + ymin * cyl2 + c0l2; + int row_l3 = xmin * cxl3 + ymin * cyl3 + c0l3; + + for(int y=ymin; y 0 && l1 >= 0.0 && l2 >= 0.0 && l3 >= 0.0) || + (D < 0 && l1 <= 0.0 && l2 <= 0.0 && l3 <= 0.0) + ) { + Color c; + interpolate_color( + c1,c2,c3, + double(l1)/double(D), + double(l2)/double(D), + double(l3)/double(D), + c + ); + set_pixel(x,y,c); + } + l1 += cxl1; + l2 += cxl2; + l3 += cxl3; + } + row_l1 += cyl1; + row_l2 += cyl2; + row_l3 += cyl3; + } + } + +} + diff --git a/src/lib/geogram/image/image_rasterizer.h b/src/lib/geogram/image/image_rasterizer.h new file mode 100644 index 00000000..57bf13d6 --- /dev/null +++ b/src/lib/geogram/image/image_rasterizer.h @@ -0,0 +1,175 @@ +/* + * OGF/Graphite: Geometry and Graphics Programming Library + Utilities + * Copyright (C) 2000 Bruno Levy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * If you modify this software, you should include a notice giving the + * name of the person performing the modification, the date of modification, + * and the reason for such modification. + * + * Contact: Bruno Levy + * + * levy@loria.fr + * + * ISA Project + * LORIA, INRIA Lorraine, + * Campus Scientifique, BP 239 + * 54506 VANDOEUVRE LES NANCY CEDEX + * FRANCE + * + * Note that the GNU General Public License does not permit incorporating + * the Software into proprietary programs. + */ + +#ifndef H_OGF_IMAGE_IO_IMAGE_RASTERIZER_H +#define H_OGF_IMAGE_IO_IMAGE_RASTERIZER_H + +#include +#include + +/** + * \file geogram/image/image_serializer.h + * \brief Class to draw triangles in an image. + */ + +namespace GEO { + + /** + * \brief Draws triangles in an image. + */ + class GEOGRAM_API ImageRasterizer { + public: + + /** + * \brief ImageRasterizer constructor. + * \param[in] image a pointer to the target image. + * \param x1 , y1 , x2 , y2 viewport coordinates. + */ + ImageRasterizer( + Image* image, + double x1=0.0, double y1=0.0, + double x2=1.0, double y2=1.0 + ); + + /** + * \brief Clears the target image. + */ + void clear(); + + /** + * \brief Draws a triangle in the target image. + * \details Colors are linearly interpolated (Gouraud shading). + * \param p1 , p2 , p3 the three vertices of the triangle. + * \param c1 , c2 , c3 the three colors of the vertices. + */ + void triangle( + const vec2& p1, const Color& c1, + const vec2& p2, const Color& c2, + const vec2& p3, const Color& c3 + ); + + /** + * \brief Sets a pixel of the image. + * \details Only BYTE, FLOAT32 and FLOAT64 component encoding + * are supported. If the image has less than 4 channels, the + * extra channels in \p c are ignored. + * \param[in] x , y the integer pixel coordinates + * \param[in] c the color of the pixel + */ + void set_pixel(int x, int y, const Color& c) { + geo_debug_assert(x >= 0 && x < int(image_->width())); + geo_debug_assert(y >= 0 && y < int(image_->height())); + switch(component_encoding_) { + case Image::BYTE: { + Memory::byte* pixel_ptr = + (Memory::byte*)( + image_->pixel_base(index_t(x),index_t(y)) + ); + for(index_t comp=0; comppixel_base(index_t(x),index_t(y)) + ); + for(index_t comp=0; comppixel_base(index_t(x),index_t(y)) + ); + for(index_t comp=0; compwidth())*(p.x - x1_) / (x2_ - x1_) + ); + transformed.y = Numeric::int32( + double(image_->height())*(p.y - y1_) / (y2_ - y1_) + ); + } + + /** + * \brief Computes the linear interpolation between three colors. + * \param[in] c1 , c2 , c3 the three colors to be interpolated. + * \param[in] l1 , l2 , l3 the coefficients of the interpolation. + * \param[out] c the interpolated color. + */ + void interpolate_color( + const Color& c1, const Color& c2, const Color& c3, + double l1, double l2, double l3, + Color& c + ) { + c[0] = l1*c1[0] + l2*c2[0] + l3*c3[0]; + c[1] = l1*c1[1] + l2*c2[1] + l3*c3[1]; + c[2] = l1*c1[2] + l2*c2[2] + l3*c3[2]; + c[3] = l1*c1[3] + l2*c2[3] + l3*c3[3]; + } + + private: + Image* image_; + Image::ComponentEncoding component_encoding_; + index_t nb_components_; + double x1_; + double y1_; + double x2_; + double y2_; + }; +} + +#endif diff --git a/src/lib/geogram/image/image_serializer.cpp b/src/lib/geogram/image/image_serializer.cpp new file mode 100755 index 00000000..e7d90aba --- /dev/null +++ b/src/lib/geogram/image/image_serializer.cpp @@ -0,0 +1,120 @@ +/* + * OGF/Graphite: Geometry and Graphics Programming Library + Utilities + * Copyright (C) 2000 Bruno Levy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * If you modify this software, you should include a notice giving the + * name of the person performing the modification, the date of modification, + * and the reason for such modification. + * + * Contact: Bruno Levy + * + * levy@loria.fr + * + * ISA Project + * LORIA, INRIA Lorraine, + * Campus Scientifique, BP 239 + * 54506 VANDOEUVRE LES NANCY CEDEX + * FRANCE + * + * Note that the GNU General Public License does not permit incorporating + * the Software into proprietary programs. + */ + + +#include +#include + +#include + +namespace GEO { + +//_________________________________________________________ + + + Image* ImageSerializer::serialize_read(const std::string& file_name) { + + std::fstream::openmode mode = binary() ? + (std::fstream::in | std::fstream::binary) : + std::fstream::in ; + + std::ifstream input(file_name.c_str(),mode) ; + if(!input) { + Logger::err("ImageSerializer") + << "could not open file\'" + << file_name << "\'" << std::endl ; + return nullptr ; + } + return serialize_read(input) ; + } + + bool ImageSerializer::serialize_write( + const std::string& file_name, const Image* image + ) { + std::fstream::openmode mode = binary() ? + (std::fstream::out | std::fstream::trunc | std::fstream::binary) : + (std::fstream::out | std::fstream::trunc) ; + + std::ofstream output(file_name.c_str(), mode) ; + + if(!output) { + Logger::err("ImageSerializer") + << "could not open file\'" + << file_name << "\'" << std::endl ; + return false ; + } + + return serialize_write(output, image) ; + } + + Image* ImageSerializer::serialize_read(std::istream& stream) { + geo_argused(stream); + bool implemented = false ; + geo_assert(implemented) ; + return nullptr ; + } + + bool ImageSerializer::serialize_write( + std::ostream& stream, const Image* image + ) { + geo_argused(stream); + geo_argused(image); + bool implemented = false ; + geo_assert(implemented) ; + return false ; + } + + + bool ImageSerializer::binary() const { + return true ; + } + + bool ImageSerializer::streams_supported() const { + return true ; + } + + bool ImageSerializer::read_supported() const { + return false ; + } + + bool ImageSerializer::write_supported() const { + return false ; + } + +//_________________________________________________________ + +} + diff --git a/src/lib/geogram/image/image_serializer.h b/src/lib/geogram/image/image_serializer.h new file mode 100755 index 00000000..3befff66 --- /dev/null +++ b/src/lib/geogram/image/image_serializer.h @@ -0,0 +1,154 @@ +/* + * OGF/Graphite: Geometry and Graphics Programming Library + Utilities + * Copyright (C) 2000 Bruno Levy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * If you modify this software, you should include a notice giving the + * name of the person performing the modification, the date of modification, + * and the reason for such modification. + * + * Contact: Bruno Levy + * + * levy@loria.fr + * + * ISA Project + * LORIA, INRIA Lorraine, + * Campus Scientifique, BP 239 + * 54506 VANDOEUVRE LES NANCY CEDEX + * FRANCE + * + * Note that the GNU General Public License does not permit incorporating + * the Software into proprietary programs. + */ + +#ifndef H_OGF_IMAGE_IO_IMAGE_SERIALIZER_H +#define H_OGF_IMAGE_IO_IMAGE_SERIALIZER_H + +#include +#include +#include +#include + +#include + +/** + * \file geogram/image/image_serializer.h + * \brief Baseclass for loading and saving images. + */ + +namespace GEO { + +//_________________________________________________________ + + + class Image ; + + /** + * \brief Loads and saves images. + */ + class GEOGRAM_API ImageSerializer : public Counted { + public: + + /** + * \brief reads an image from a file. + * \param[in] file_name the name of the file + * \return a pointer to the loaded image or nil if + * the image could not be loaded + * \note the loaded image can be stored in an Image_var + */ + virtual Image* serialize_read(const std::string& file_name) ; + + /** + * \brief writes an image into a file. + * \param[in] file_name the name of the file + * \param[in] image a pointer to the image to be saved + * \retval true if the image could be saved + * \retval false otherwise + */ + virtual bool serialize_write( + const std::string& file_name, const Image* image + ) ; + + /** + * \brief reads an image from a stream. + * \param[in,out] stream the stream where the image should + * be read from + * \return a pointer to the loaded image or nil if + * the image could not be loaded + * \note the loaded image can be stored in an Image_var + */ + virtual Image* serialize_read(std::istream& stream) ; + + /** + * \brief writes an image into a stream. + * \param[in,out] stream the stream where the image should be written + * \param[in] image a pointer to the image to be saved + * \retval true if the image could be saved + * \retval false otherwise + */ + virtual bool serialize_write( + std::ostream& stream, const Image* image + ) ; + + /** + * \brief Tests whether the file format is binary or ASCII. + * \details It is used to determine whether a stream should be + * opened in ASCII or binary mode for loading image files. + * Default implementation in base class returns true. + * \retval true if the file format is binary + * \retval false if the file format is ASCII + */ + virtual bool binary() const ; + + /** + * \brief Tests whether the functions that use streams + * are supported. + * \details Default implementation in base class returns true. + * \retval true if the functions that use streams are + * supported + * \retval false otherwise + */ + virtual bool streams_supported() const ; + + /** + * \brief Tests whether reading is implemented. + * \details Some serializers implement only reading or only + * writing. Default implementation returns false + * \retval true if reading is supported + * \retval false otherwise + */ + virtual bool read_supported() const ; + + /** + * \brief Tests whether writing is implemented. + * \details Some serializers implement only reading or only + * writing. Default implementation returns false + * \retval true if writing is supported + * \retval false otherwise + */ + virtual bool write_supported() const ; + } ; + + /** + * \brief An automatic reference-counted pointer to an ImageSerializer + */ + typedef SmartPointer ImageSerializer_var ; + +//_________________________________________________________ + +} +#endif + diff --git a/src/lib/geogram/image/image_serializer_pgm.cpp b/src/lib/geogram/image/image_serializer_pgm.cpp new file mode 100755 index 00000000..904928d6 --- /dev/null +++ b/src/lib/geogram/image/image_serializer_pgm.cpp @@ -0,0 +1,101 @@ +/* + * OGF/Graphite: Geometry and Graphics Programming Library + Utilities + * Copyright (C) 2000 Bruno Levy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * If you modify this software, you should include a notice giving the + * name of the person performing the modification, the date of modification, + * and the reason for such modification. + * + * Contact: Bruno Levy + * + * levy@loria.fr + * + * ISA Project + * LORIA, INRIA Lorraine, + * Campus Scientifique, BP 239 + * 54506 VANDOEUVRE LES NANCY CEDEX + * FRANCE + * + * Note that the GNU General Public License does not permit incorporating + * the Software into proprietary programs. + */ + +#include +#include +#include + +#include +#include + +namespace GEO { + +/***************************************************************/ + + Image* ImageSerializer_pgm::serialize_read(const std::string& file_name) { + FILE* f = fopen(file_name.c_str(),"rb"); + if(f == nullptr) { + Logger::err("Image")<< file_name << ": could not open file" + << std::endl; + return nullptr; + } + + char magic[255]; + int width, height, maxval; + if( + (fscanf(f, "%s", magic) != 1) || + (strcmp(magic,"P5") != 0) || + (fscanf(f, "%d", &width) != 1) || + (fscanf(f, "%d", &height) != 1) || + (fscanf(f, "%d", &maxval) != 1) + + ) { + Logger::err("PGM") << "Invalid header" << std::endl; + return nullptr; + } + + Image* result = nullptr; + if(maxval == 255) { + result = new Image(Image::GRAY, Image::BYTE, index_t(width), index_t(height)); + } else if(maxval == 65535) { + result = new Image(Image::GRAY, Image::INT16, index_t(width), index_t(height)); + } else { + Logger::err("PGM") << maxval << " unsupported maxval" + << std::endl; + return nullptr; + } + size_t nbread = fread(result->base_mem(), 1, result->bytes(), f); + if(nbread != result->bytes()) { + Logger::warn("PGM") << "file is truncated" + << std::endl; + } + fclose(f); + return result; + } + + bool ImageSerializer_pgm::binary() const { + return true; + } + + + bool ImageSerializer_pgm::read_supported() const { + return true ; + } + +/**************************************************************/ + +} + diff --git a/src/lib/geogram/image/image_serializer_pgm.h b/src/lib/geogram/image/image_serializer_pgm.h new file mode 100755 index 00000000..fc16f41a --- /dev/null +++ b/src/lib/geogram/image/image_serializer_pgm.h @@ -0,0 +1,61 @@ +/* + * OGF/Graphite: Geometry and Graphics Programming Library + Utilities + * Copyright (C) 2000 Bruno Levy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * If you modify this software, you should include a notice giving the + * name of the person performing the modification, the date of modification, + * and the reason for such modification. + * + * Contact: Bruno Levy + * + * levy@loria.fr + * + * ISA Project + * LORIA, INRIA Lorraine, + * Campus Scientifique, BP 239 + * 54506 VANDOEUVRE LES NANCY CEDEX + * FRANCE + * + * Note that the GNU General Public License does not permit incorporating + * the Software into proprietary programs. + */ + +#ifndef OGF_IMAGE_IO_IMAGE_SERIALIZER_PGM +#define OGF_IMAGE_IO_IMAGE_SERIALIZER_PGM + +#include +#include + +namespace GEO { + +//_________________________________________________________ + + class GEOGRAM_API ImageSerializer_pgm : public ImageSerializer { + public: + /** + * \copydoc ImageSerializer::read() + */ + Image* serialize_read(const std::string& file_name) override; + bool read_supported() const override; + bool binary() const override; + } ; + +//_________________________________________________________ + +} +#endif + diff --git a/src/lib/geogram/image/image_serializer_stb.cpp b/src/lib/geogram/image/image_serializer_stb.cpp new file mode 100644 index 00000000..fcab2b24 --- /dev/null +++ b/src/lib/geogram/image/image_serializer_stb.cpp @@ -0,0 +1,188 @@ +/* + * OGF/Graphite: Geometry and Graphics Programming Library + Utilities + * Copyright (C) 2000 Bruno Levy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * If you modify this software, you should include a notice giving the + * name of the person performing the modification, the date of modification, + * and the reason for such modification. + * + * Contact: Bruno Levy + * + * levy@loria.fr + * + * ISA Project + * LORIA, INRIA Lorraine, + * Campus Scientifique, BP 239 + * 54506 VANDOEUVRE LES NANCY CEDEX + * FRANCE + * + * Note that the GNU General Public License does not permit incorporating + * the Software into proprietary programs. + */ + + +#include +#include +#include +#include +#include + +#define STB_IMAGE_IMPLEMENTATION +#define STB_IMAGE_WRITE_IMPLEMENTATION + +// [Bruno] I got too many complaints in STB so I "close my eyes" :-) +#ifdef __GNUC__ +#ifndef __ICC +#pragma GCC diagnostic ignored "-Wconversion" +#pragma GCC diagnostic ignored "-Wsign-conversion" +#pragma GCC diagnostic ignored "-Wimplicit-fallthrough" +#pragma GCC diagnostic ignored "-Wdouble-promotion" +#endif + +#ifdef __clang__ +#pragma GCC diagnostic ignored "-Wdisabled-macro-expansion" +#pragma GCC diagnostic ignored "-Wcast-align" +#pragma GCC diagnostic ignored "-Wmissing-prototypes" +#pragma GCC diagnostic ignored "-Wunknown-pragmas" +#pragma GCC diagnostic ignored "-Wreserved-id-macro" +#pragma GCC diagnostic ignored "-Wcomma" +#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif +#endif + +#include +#include + +namespace GEO { + + ImageSerializerSTB::ImageSerializerSTB(bool read, bool write) : + read_(read), + write_(write) + { + } + + Image* ImageSerializerSTB::serialize_read(const std::string& file_name) { + std::string extension = String::to_lowercase( + FileSystem::extension(file_name) + ); + if( + extension != "png" && + extension != "jpg" && + extension != "jpeg" && + extension != "tga" && + extension != "bmp" + ) { + return nullptr; + } + + Image* result = nullptr; + int width, height, bpp; + int desired_bpp = 4; + unsigned char* data = stbi_load( + file_name.c_str(), &width, &height, &bpp, desired_bpp + ); + if(data == nullptr) { + Logger::err("Image") + << file_name << " : could not load file." << std::endl; + } else { + result = new Image( + Image::RGBA, + Image::BYTE, index_t(width), index_t(height) + ); + // bpp: how many bytes per pixel there was in the file + // desired_bpp set to 4, thus stbi expands it to r,g,b,a always. + Memory::copy(result->base_mem(), data, size_t(width*height*4)); + stbi_image_free(data); + // Seems that STB has inverse convention as mine for image Y axis, + result->flip_vertically(); + } + + return result; + } + + bool ImageSerializerSTB::serialize_write( + const std::string& file_name, const Image* image_in + ) { + bool result = true; + std::string extension = String::to_lowercase( + FileSystem::extension(file_name) + ); + + // Seems that STB has inverse convention as mine for image Y axis, + // so we flip before saving and flip back after. + + Image* image = const_cast(image_in); + image->flip_vertically(); + + int w = int(image->width()); + int h = int(image->height()); + int comp = int(image->components_per_pixel()); + const char* data = (const char*)(image->base_mem()); + + if(extension == "png") { + result = (stbi_write_png(file_name.c_str(), w, h, comp, data, w*comp) != 0); + } else if(extension == "bmp") { + result = (stbi_write_bmp(file_name.c_str(), w, h, comp, data) != 0); + } else if(extension == "jpeg" || extension == "jpg") { + result = (stbi_write_jpg(file_name.c_str(), w, h, comp, data, 80) != 0); + } else if(extension == "tga") { + result = (stbi_write_tga(file_name.c_str(), w, h, comp, data) != 0); + } + + image->flip_vertically(); + return result; + } + + bool ImageSerializerSTB::binary() const { + return true; + } + + bool ImageSerializerSTB::streams_supported() const { + return false; + } + + bool ImageSerializerSTB::read_supported() const { + return read_; + } + + bool ImageSerializerSTB::write_supported() const { + return write_; + } + + /*****************************************************/ + + ImageSerializerSTBRead::ImageSerializerSTBRead() : + ImageSerializerSTB(true, false) { + } + + ImageSerializerSTBRead::~ImageSerializerSTBRead() { + } + + /*****************************************************/ + + ImageSerializerSTBReadWrite::ImageSerializerSTBReadWrite() : + ImageSerializerSTB(true, true) { + } + + ImageSerializerSTBReadWrite::~ImageSerializerSTBReadWrite() { + } + + /*****************************************************/ + +} + diff --git a/src/lib/geogram/image/image_serializer_stb.h b/src/lib/geogram/image/image_serializer_stb.h new file mode 100755 index 00000000..701df16f --- /dev/null +++ b/src/lib/geogram/image/image_serializer_stb.h @@ -0,0 +1,143 @@ +/* + * OGF/Graphite: Geometry and Graphics Programming Library + Utilities + * Copyright (C) 2000 Bruno Levy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * If you modify this software, you should include a notice giving the + * name of the person performing the modification, the date of modification, + * and the reason for such modification. + * + * Contact: Bruno Levy + * + * levy@loria.fr + * + * ISA Project + * LORIA, INRIA Lorraine, + * Campus Scientifique, BP 239 + * 54506 VANDOEUVRE LES NANCY CEDEX + * FRANCE + * + * Note that the GNU General Public License does not permit incorporating + * the Software into proprietary programs. + */ + +#ifndef H_OGF_IMAGE_IO_IMAGE_SERIALIZER_STB_H +#define H_OGF_IMAGE_IO_IMAGE_SERIALIZER_STB_H + +#include +#include + +/** + * \file geogram/image/image_serializer_stb.h + * \brief Implementation of image serializer using + * stb_image.h and stb_image_write.h + */ + +namespace GEO { + +//_________________________________________________________ + + + class Image; + + /** + * \brief Loads and saves images. + */ + class GEOGRAM_API ImageSerializerSTB : public ImageSerializer { + public: + + /** + * \brief ImageSerializerSTB constructor. + * \param[in] read true if reading is supported + * \param[in] write true if writing is supported + */ + ImageSerializerSTB(bool read, bool write); + + /** + * \copydoc ImageSerializer::read() + */ + Image* serialize_read(const std::string& file_name) override; + + /** + * \copydoc ImageSerializer::write() + */ + bool serialize_write( + const std::string& file_name, const Image* image + ) override; + + /** + * \copydoc ImageSerializer::binary() + */ + bool binary() const override; + + /** + * \copydoc ImageSerializer::streams_supported() + * \details This version returns false + */ + bool streams_supported() const override; + + /** + * \copydoc ImageSerializer::read_supported() + */ + bool read_supported() const override; + + /** + * \copydoc ImageSerializer::write_supported() + */ + bool write_supported() const override; + + private: + bool read_; + bool write_; + }; + + /** + * \brief An image serializer that can read images. + */ + class GEOGRAM_API ImageSerializerSTBRead : public ImageSerializerSTB { + public: + /** + * \brief ImageSerializerSTBRead constructor. + */ + ImageSerializerSTBRead(); + + /** + * \brief ImageSerializerSTBRead destructor. + */ + ~ImageSerializerSTBRead() override; + }; + + /** + * \brief An image serializer that can read and write images. + */ + class GEOGRAM_API ImageSerializerSTBReadWrite : public ImageSerializerSTB { + public: + /** + * \brief ImageSerializerSTBReadWrite constructor. + */ + ImageSerializerSTBReadWrite(); + + /** + * \brief ImageSerializerSTBReadWrite destructor. + */ + ~ImageSerializerSTBReadWrite() override; + }; + +//*************************************************************************** + +} +#endif + diff --git a/src/lib/geogram/image/image_serializer_xpm.cpp b/src/lib/geogram/image/image_serializer_xpm.cpp new file mode 100755 index 00000000..0f3ad0d1 --- /dev/null +++ b/src/lib/geogram/image/image_serializer_xpm.cpp @@ -0,0 +1,361 @@ +/* + * OGF/Graphite: Geometry and Graphics Programming Library + Utilities + * Copyright (C) 2000 Bruno Levy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * If you modify this software, you should include a notice giving the + * name of the person performing the modification, the date of modification, + * and the reason for such modification. + * + * Contact: Bruno Levy + * + * levy@loria.fr + * + * ISA Project + * LORIA, INRIA Lorraine, + * Campus Scientifique, BP 239 + * 54506 VANDOEUVRE LES NANCY CEDEX + * FRANCE + * + * Note that the GNU General Public License does not permit incorporating + * the Software into proprietary programs. + */ + +#include +#include +#include +#include + +#include +#include + +namespace { + using namespace GEO; + + inline int htoi(char digit) { + if(digit >= '0' && digit <= '9') { + return digit - '0' ; + } + if(digit >= 'a' && digit <= 'f') { + return digit - 'a' + 10 ; + } + if(digit >= 'A' && digit <= 'F') { + return digit - 'A' + 10 ; + } + Logger::err("Image") + << "XPM Image reader: hex digit to integer: invalid digit: \'" + << digit << "\'" << std::endl ; + abort() ; + } + + bool decode_colormap_entry( + const char* colormap_entry, Colormap::ColorCell& colorcell + ) { + const char* colorcode = strstr(colormap_entry, "c #"); + if(colorcode == nullptr) { + if(strstr(colormap_entry, "None") != nullptr) { + colorcell = Colormap::ColorCell(0,0,0,0); + return true; + } else { + Logger::err("Image") + << "XPM Image reader: Colormap entry without any color" + << std::endl ; + Logger::err("Image") + << " entry = \'" << colormap_entry << "\'" << std::endl ; + return false; + } + } + colorcode += 3 ; + + Memory::byte r,g,b,a; + + if(strlen(colorcode) == 12) { + r = Memory::byte(16 * htoi(colorcode[0]) + htoi(colorcode[1])) ; + g = Memory::byte(16 * htoi(colorcode[4]) + htoi(colorcode[5])) ; + b = Memory::byte(16 * htoi(colorcode[8]) + htoi(colorcode[9])) ; + a = 255; + } else { + r = Memory::byte(16 * htoi(colorcode[0]) + htoi(colorcode[1])) ; + g = Memory::byte(16 * htoi(colorcode[2]) + htoi(colorcode[3])) ; + b = Memory::byte(16 * htoi(colorcode[4]) + htoi(colorcode[5])) ; + a = 255; + } + colorcell = Colormap::ColorCell(r,g,b,a); + return true; + } + +} + +namespace GEO { + +//_________________________________________________________ + + + + + Image* ImageSerializer_xpm::serialize_read(std::istream& stream) { + + Image* result = nullptr; + + // Well, ugly code ahead, + // sorry about that, but I really needed + // an XPM image reader ... + + int num_colors ; + int chars_per_pixels ; + int width ; + int height ; + + // _______________________ header + { + char* header = next_xpm_data(stream) ; + if(header == nullptr) { + Logger::err("Image") + << "XPM image input: unexpected end of file" << std::endl ; + return nullptr ; + } + std::istringstream in(header) ; + in >> width >> height >> num_colors >> chars_per_pixels ; + if(num_colors > 256) { + Logger::err("Image") + << "XPM image input: too many colors (" + << num_colors + << ")" << std::endl ; + Logger::err("Image") + << " should not be greater than 256" << std::endl ; + return nullptr ; + } + + switch(chars_per_pixels) { + case 1: + result=read_xpm_1_byte_per_pixel( + width, height, num_colors, stream + ) ; + break ; + case 2: + result=read_xpm_2_bytes_per_pixel( + width, height, num_colors, stream + ) ; + break ; + default: + Logger::err("Image") + << "XPM image input: invalid chars per pixels (" + << chars_per_pixels << ")" << std::endl ; + Logger::err("Image") << " should be 2" << std::endl ; + return nullptr ; + } + + // Convert colormapped to RGBA. + // We do that because texturing functions are not implemented for colormapped. + // TODO: move function to Image library. + Image* result_rgba = new Image(Image::RGBA, Image::BYTE, result->width(), result->height()); + for(index_t y=0; yheight(); ++y) { + for(index_t x=0; xwidth(); ++x) { + index_t c = index_t(*result->pixel_base(x,y)); + result_rgba->pixel_base(x,y)[0] = result->colormap()->color_cell(c).r(); + result_rgba->pixel_base(x,y)[1] = result->colormap()->color_cell(c).g(); + result_rgba->pixel_base(x,y)[2] = result->colormap()->color_cell(c).b(); + result_rgba->pixel_base(x,y)[3] = result->colormap()->color_cell(c).a(); + } + } + delete result; + + return result_rgba; + } + } + + Image* ImageSerializer_xpm::read_xpm_2_bytes_per_pixel( + int width, int height, int num_colors, std::istream& stream + ) { + + // Converts a two-digit XPM color code into + // a color index. + static int conv_table[256][256] ; + + // For checking, put a negative value to + // detect invalid color codes. + for(int k1=0; k1 < 256; k1++) { + for(int k2=0; k2 < 256; k2++) { + conv_table[k1][k2] = -1 ; + } + } + + // _______________________ colormap + + typedef Numeric::uint8 byte ; + + Colormap* colormap = new Colormap(index_t(num_colors)) ; + + for(int entry_num=0; entry_num color_cell(index_t(entry_num)) = cell; + conv_table[key1][key2] = (unsigned char)entry_num ; + } + + // _______________________ image + + Image* result = new Image( + Image::INDEXED, Image::BYTE, index_t(width), index_t(height) + ) ; + result-> set_colormap(colormap) ; + + for(int y=0; y num_colors) { + Logger::err("Image") + << "XPM Image reader: Invalid color index in image" + << std::endl ; + delete result ; + return nullptr ; + } + result-> base_mem()[y * width + x] = byte(color_index) ; + } + } + + result->flip_vertically(); + return result ; + } + + + Image* ImageSerializer_xpm::read_xpm_1_byte_per_pixel( + int width, int height, int num_colors, std::istream& stream + ) { + + // Converts a two-digit XPM color code into + // a color index. + static int conv_table[256] ; + + // For checking, put a negative value to + // detect invalid color codes. + for(int k1=0; k1 < 256; k1++) { + conv_table[k1] = -1 ; + } + + // _______________________ colormap + + typedef Numeric::uint8 byte ; + + Colormap* colormap = new Colormap(index_t(num_colors)) ; + + for(int entry_num=0; entry_num color_cell(index_t(entry_num)) = cell; + conv_table[key1] = (unsigned char)entry_num ; + } + + // _______________________ image + + Image* result = new Image( + Image::INDEXED, Image::BYTE, index_t(width), index_t(height) + ) ; + result-> set_colormap(colormap) ; + + for(int y=0; y num_colors) { + Logger::err("Image") + << "XPM Image reader: Invalid color index in image" + << std::endl ; + delete result ; + return nullptr ; + } + result-> base_mem()[y * width + x] = byte(color_index) ; + } + } + + return result ; + } + + bool ImageSerializer_xpm::binary() const { + return false ; + } + + char* ImageSerializer_xpm::next_xpm_data(std::istream& input) { + static char line_buffer[4096] ; + char* result = nullptr ; + bool found = false ; + while(!found && !input.eof()) { + input.getline(line_buffer,4096) ; + char* p1 = strchr(line_buffer,'\"') ; + char* p2 = strchr(line_buffer + 1, '\"') ; + found = (p1 != nullptr && p2 != nullptr) ; + if(found) { + result = p1 + 1 ; + *p2 = '\0' ; + } + } + return result ; + } + + bool ImageSerializer_xpm::read_supported() const { + return true ; + } + +//_________________________________________________________ + +} + diff --git a/src/lib/geogram/image/image_serializer_xpm.h b/src/lib/geogram/image/image_serializer_xpm.h new file mode 100755 index 00000000..cdad372f --- /dev/null +++ b/src/lib/geogram/image/image_serializer_xpm.h @@ -0,0 +1,71 @@ +/* + * OGF/Graphite: Geometry and Graphics Programming Library + Utilities + * Copyright (C) 2000 Bruno Levy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * If you modify this software, you should include a notice giving the + * name of the person performing the modification, the date of modification, + * and the reason for such modification. + * + * Contact: Bruno Levy + * + * levy@loria.fr + * + * ISA Project + * LORIA, INRIA Lorraine, + * Campus Scientifique, BP 239 + * 54506 VANDOEUVRE LES NANCY CEDEX + * FRANCE + * + * Note that the GNU General Public License does not permit incorporating + * the Software into proprietary programs. + */ + +#ifndef OGF_IMAGE_IO_IMAGE_SERIALIZER_XPM +#define OGF_IMAGE_IO_IMAGE_SERIALIZER_XPM + +#include +#include + +namespace GEO { + +//_________________________________________________________ + + class GEOGRAM_API ImageSerializer_xpm : public ImageSerializer { + public: + virtual Image* serialize_read(std::istream& stream) ; + virtual bool read_supported() const ; + virtual bool binary() const ; + + protected: + Image* read_xpm_1_byte_per_pixel( + int width, int height, int num_colors, + std::istream& input + ) ; + + Image* read_xpm_2_bytes_per_pixel( + int width, int height, int num_colors, + std::istream& input + ) ; + + static char* next_xpm_data(std::istream& input) ; + } ; + +//_________________________________________________________ + +} +#endif + diff --git a/src/lib/geogram/image/morpho_math.cpp b/src/lib/geogram/image/morpho_math.cpp new file mode 100755 index 00000000..fd513534 --- /dev/null +++ b/src/lib/geogram/image/morpho_math.cpp @@ -0,0 +1,164 @@ +/* + * GXML/Graphite: Geometry and Graphics Programming Library + Utilities + * Copyright (C) 2000 Bruno Levy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * If you modify this software, you should include a notice giving the + * name of the person performing the modification, the date of modification, + * and the reason for such modification. + * + * Contact: Bruno Levy + * + * levy@loria.fr + * + * ISA Project + * LORIA, INRIA Lorraine, + * Campus Scientifique, BP 239 + * 54506 VANDOEUVRE LES NANCY CEDEX + * FRANCE + * + * Note that the GNU General Public License does not permit incorporating + * the Software into proprietary programs. + */ + +#include +#include +#include +#include + +namespace GEO { + + static inline bool has_value(Memory::byte* p, size_t bytes_per_pixel_) { + bool result = false; + switch(bytes_per_pixel_) { + case 1: + result = (*p != 0); + break; + case 3: + result = (p[0] != 0 || p[1] != 0 || p[2] != 0); + break; + case 4: + result = (p[3] != 0); + break; + default: + geo_assert_not_reached; + } + return result; + } + + void StructuringElement::convolve( + Memory::byte* from, Memory::byte* to + ) const { + if(has_value(from, bytes_per_pixel_)) { + for(size_t i=0; icomponent_encoding() == Image::BYTE); + width_ = target_->width(); + height_ = target_->height(); + bytes_per_pixel_ = target_->bytes_per_pixel(); + bytes_per_line_ = bytes_per_pixel_ * width_; + graph_mem_ = target_->base_mem(); + } + + MorphoMath::~MorphoMath() { + target_ = nullptr; + } + + void MorphoMath::dilate(index_t nb_iterations) { + StructuringElement str(target_); + str.add_neighbor(-1,-1); + str.add_neighbor(-1, 0); + str.add_neighbor(-1, 1); + str.add_neighbor( 0,-1); + str.add_neighbor( 0, 0); + str.add_neighbor( 0, 1); + str.add_neighbor( 1,-1); + str.add_neighbor( 1, 0); + str.add_neighbor( 1, 1); + dilate(str, nb_iterations); + } + + void MorphoMath::dilate( + const StructuringElement& str, index_t nb_iterations + ) { + Image_var tmp = new Image( + target_->color_encoding(), + target_->component_encoding(), + target_->width(), + target_->height() + ); + Memory::copy(tmp->base_mem(), target_->base_mem(), target_->bytes()); + + index_t R = str.radius(); + + index_t line_offset = (R * index_t(bytes_per_pixel_)); + + for(index_t iter=0; iterbase_mem() + R * bytes_per_line_; + Memory::byte* to_line = + tmp->base_mem() + R * bytes_per_line_; + for(index_t y=R; ybase_mem(), tmp->base_mem(), + target_->bytes() + ); + } + } +} + diff --git a/src/lib/geogram/image/morpho_math.h b/src/lib/geogram/image/morpho_math.h new file mode 100755 index 00000000..50bad9e4 --- /dev/null +++ b/src/lib/geogram/image/morpho_math.h @@ -0,0 +1,164 @@ +/* + * GXML/Graphite: Geometry and Graphics Programming Library + Utilities + * Copyright (C) 2000 Bruno Levy + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * If you modify this software, you should include a notice giving the + * name of the person performing the modification, the date of modification, + * and the reason for such modification. + * + * Contact: Bruno Levy + * + * levy@loria.fr + * + * ISA Project + * LORIA, INRIA Lorraine, + * Campus Scientifique, BP 239 + * 54506 VANDOEUVRE LES NANCY CEDEX + * FRANCE + * + * Note that the GNU General Public License does not permit incorporating + * the Software into proprietary programs. + */ + +#ifndef H_IMAGE_ALGOS_MORPHO_MATH_H +#define H_IMAGE_ALGOS_MORPHO_MATH_H + +#include +#include + +/** + * \file geogram/image/morpho_math.h + * \brief Classes for morphological operations on images. + */ + +namespace GEO { + + /** + * \brief A structuring element, that is the definition of + * neighborhood used by a morphological operation. + */ + class GEOGRAM_API StructuringElement { + public: + + /** + * \brief StructuringElement constructor. + * \param[in] source a pointer to the source image. + */ + StructuringElement(Image* source) : source_(source) { + base_mem_ = source_->base_mem(); + width_ = source_->width(); + radius_ = 0; + bytes_per_pixel_ = source->bytes_per_pixel(); + } + + /** + * \brief Gets the radius. + * \return the maximum difference of coordinate between the + * center and one of the neighbors. + */ + index_t radius() const { + return radius_; + } + + /** + * \brief Adds a neighbor to this structuting element. + * \param[in] xrel , yrel the coordinates of the neighbor, + * relative to the center of this structuring element. + */ + void add_neighbor(int xrel, int yrel) { + offset_.push_back( + (xrel + int(width_) * yrel) * int(bytes_per_pixel_) + ); + radius_ = std::max(radius_, index_t(std::abs(xrel))); + radius_ = std::max(radius_, index_t(std::abs(yrel))); + } + + /** + * \brief Computes the convolution at a given memory location. + * \param[in] from a pointer to the source pixel at the center + * of the structuring element. + * \param[in] to a pointer to the target pixel at the center of + * the structuring element. + */ + void convolve(Memory::byte* from, Memory::byte* to) const; + + /** + * \brief Computes the convolution at a given pixel. + * \details The source image is the one that was specified to the + * constructor of this StructuringElement. + * \param[in] x , y the coordinates of the pixel. + * \param[in] target_img a pointer to the target image. + */ + inline void convolve(int x, int y, Image* target_img) const { + int pixel_base = ((x + int(width_) * y) * int(bytes_per_pixel_)); + convolve( + base_mem_ + pixel_base, target_img->base_mem() + pixel_base + ); + } + + private: + Memory::byte* base_mem_; + index_t width_; + Image* source_; + index_t radius_; + vector offset_; + size_t bytes_per_pixel_; + }; + + + /** + * \brief Implements morphological operators for images. + */ + class GEOGRAM_API MorphoMath { + public: + /** + * \brief MorphoMath constructor. + * \details Only implemented for 2D images with byte components. + * \param[in] target a pointer to the target image. + * \pre target->component_encoding() == Image::BYTE + */ + MorphoMath(Image* target); + + /** + * \brief MorphoMath destructor; + */ + ~MorphoMath(); + + /** + * \brief Computes a dilation. + * \param[in] elt a const reference to the structuring element. + * \param[in] nb_iterations number of dilations to be applied. + */ + void dilate(const StructuringElement& elt, index_t nb_iterations = 1); + + /** + * \brief Computes a dilation with a default structuring element. + * \param[in] nb_iterations number of dilations to be applied. + */ + void dilate(index_t nb_iterations = 1); + + private: + Image* target_; + Numeric::uint8* graph_mem_; + index_t width_; + index_t height_; + size_t bytes_per_pixel_; + size_t bytes_per_line_; + }; +} + +#endif diff --git a/src/lib/geogram/mesh/mesh_baking.cpp b/src/lib/geogram/mesh/mesh_baking.cpp new file mode 100644 index 00000000..ea44672a --- /dev/null +++ b/src/lib/geogram/mesh/mesh_baking.cpp @@ -0,0 +1,381 @@ +/* + * Copyright (c) 2012-2014, Bruno Levy + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the ALICE Project-Team nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * If you modify this software, you should include a notice giving the + * name of the person performing the modification, the date of modification, + * and the reason for such modification. + * + * Contact: Bruno Levy + * + * Bruno.Levy@inria.fr + * http://www.loria.fr/~levy + * + * ALICE Project + * LORIA, INRIA Lorraine, + * Campus Scientifique, BP 239 + * 54506 VANDOEUVRE LES NANCY CEDEX + * FRANCE + * + */ + +#include +#include +#include +#include +#include +#include + +namespace { + using namespace GEO; + + /** + * \brief Reads a (facet or vertex or facet corner) vector attribute + * as a color. + * \details If the vector attribute has less components than 4, then + * the additional color components are set to (0 + \p bias)* \p scale. + * If the vector attribute has more components than 4, then the additional + * attribute components are ignored. + * \param[out] C the color + * \param[in] attribute a reference to the attribute. + * \param[in] attrib_loc one of MESH_FACETS,MESH_VERTICES,MESH_FACET_CORNERS + * \param[in] f the facet + * \param[in] c the facet corner + * \param[in] v the vertex + * \param[in] bias optional value to be added to the attribute values + * before storing them into the color components. + * \param[in] scale optional value that scales the attribute values + * after \p bias is added and before storing them into the color + * components. + */ + inline void get_attribute_as_color( + Color& C, + Attribute& attribute, + MeshElementsFlags attrib_loc, + index_t f, index_t c, index_t v, + double bias = 0.0, + double scale = 1.0 + ) { + index_t dim = attribute.dimension(); + index_t base = index_t(-1); + if(attrib_loc == MESH_FACETS) { + base = f*dim; + } else if(attrib_loc == MESH_VERTICES) { + base = v*dim; + } else if(attrib_loc == MESH_FACET_CORNERS) { + base = c*dim; + } else { + geo_assert_not_reached; + } + C.set_r(attribute[base]); + C.set_g((dim >= 2) ? attribute[base+1] : 0.0); + C.set_b((dim >= 3) ? attribute[base+2] : 0.0); + C.set_a((dim >= 4) ? attribute[base+3] : 0.0); + + C.set_r(scale*(C.r() + bias)); + C.set_g(scale*(C.g() + bias)); + C.set_b(scale*(C.b() + bias)); + C.set_a(scale*(C.a() + bias)); + } +} + +namespace GEO { + + /**************************************************************************/ + + void bake_mesh_facet_normals(Mesh* mesh, Image* target) { + Attribute tex_coord; + tex_coord.bind_if_is_defined( + mesh->facet_corners.attributes(), "tex_coord" + ); + geo_assert(tex_coord.is_bound() && tex_coord.dimension() == 2); + ImageRasterizer rasterizer(target); + for(index_t f=0; ffacets.nb(); ++f) { + vec3 N = normalize(Geom::mesh_facet_normal(*mesh, f)); + Color C = 0.5*Color(N.x+1.0, N.y+1.0, N.z+1.0, 2.0); + index_t c1 = mesh->facets.corners_begin(f); + vec2 p1(tex_coord[2*c1], tex_coord[2*c1+1]); + for(index_t c2 = c1+1; + c2+1 < mesh->facets.corners_end(f); ++c2) { + index_t c3 = c2+1; + vec2 p2(tex_coord[2*c2], tex_coord[2*c2+1]); + vec2 p3(tex_coord[2*c3], tex_coord[2*c3+1]); + rasterizer.triangle( + p1, C, + p2, C, + p3, C + ); + } + } + } + + /**************************************************************************/ + + void bake_mesh_vertex_normals(Mesh* mesh, Image* normal_map) { + + // Step 1: compute vertex normals. + Attribute N; + N.create_vector_attribute(mesh->vertices.attributes(), "N", 3); + FOR(v, mesh->vertices.nb()) { + N[3*v] = 0.0; + N[3*v+1] = 0.0; + N[3*v+2] = 0.0; + } + FOR(f, mesh->facets.nb()) { + vec3 Nf = GEO::Geom::mesh_facet_normal(*mesh, f); + for( + index_t corner = mesh->facets.corners_begin(f); + corner < mesh->facets.corners_end(f); corner++ + ) { + index_t v = mesh->facet_corners.vertex(corner); + N[3*v] += Nf.x; + N[3*v+1] += Nf.y ; + N[3*v+2] += Nf.z; + } + } + FOR(v, mesh->vertices.nb()) { + vec3 Nf(N[3*v], N[3*v+1], N[3*v+2]); + Nf = normalize(Nf); + N[3*v] = Nf.x; + N[3*v+1] = Nf.y; + N[3*v+2] = Nf.z; + } + + // Step 2: bake interpolated vertex normals. + bake_mesh_attribute(mesh, normal_map, N); + + // Step 3: normalize interpolated normals. + FOR(y, normal_map->height()) { + FOR(x, normal_map->width()) { + double* pix = normal_map->pixel_base_float64_ptr(x,y); + vec3 Npix(pix); + Npix = normalize(Npix); + pix[0] = Npix.x; + pix[1] = Npix.y; + pix[2] = Npix.z; + } + } + N.unbind(); + mesh->vertices.attributes().delete_attribute_store("N"); + } + + /**************************************************************************/ + + void bake_mesh_attribute( + Mesh* mesh, Image* target, Attribute& attribute, + double bias, double scale + ) { + Attribute tex_coord; + tex_coord.bind_if_is_defined( + mesh->facet_corners.attributes(), "tex_coord" + ); + geo_assert(tex_coord.is_bound() && tex_coord.dimension() == 2); + geo_assert(attribute.is_bound()); + + MeshElementsFlags attrib_loc = MESH_NONE; + + if(attribute.manager() == &mesh->vertices.attributes()) { + attrib_loc = MESH_VERTICES; + } else if(attribute.manager() == &mesh->facets.attributes()) { + attrib_loc = MESH_FACETS; + } else if(attribute.manager() == &mesh->facet_corners.attributes()) { + attrib_loc = MESH_FACET_CORNERS; + } else { + // Attribute is not a vertex/facet/facet_corner attribute + // or it is bound to a different mesh. + geo_assert_not_reached; + } + + ImageRasterizer rasterizer(target); + for(index_t f=0; ffacets.nb(); ++f) { + Color C1,C2,C3; + index_t c1 = mesh->facets.corners_begin(f); + vec2 p1(tex_coord[2*c1], tex_coord[2*c1+1]); + index_t v1 = mesh->facet_corners.vertex(c1); + get_attribute_as_color( + C1, attribute, attrib_loc, f, c1, v1, bias, scale + ); + for(index_t c2 = c1+1; + c2+1 < mesh->facets.corners_end(f); ++c2) { + index_t c3 = c2+1; + vec2 p2(tex_coord[2*c2], tex_coord[2*c2+1]); + vec2 p3(tex_coord[2*c3], tex_coord[2*c3+1]); + index_t v2 = mesh->facet_corners.vertex(c2); + index_t v3 = mesh->facet_corners.vertex(c3); + get_attribute_as_color( + C2, attribute, attrib_loc, f, c2, v2, bias, scale + ); + get_attribute_as_color( + C3, attribute, attrib_loc, f, c3, v3, bias, scale + ); + rasterizer.triangle( + p1, C1, + p2, C2, + p3, C3 + ); + } + } + } + + /**************************************************************************/ + + void bake_mesh_geometry(Mesh* mesh, Image* target, bool clear) { + geo_assert(target->component_encoding() == Image::FLOAT64); + Attribute point; + point.bind_if_is_defined(mesh->vertices.attributes(), "point"); + geo_assert(point.is_bound()); + + if(clear) { + double* base_mem = target->base_mem_float64_ptr(); + size_t nb = target->nb_pixels()*target->components_per_pixel(); + for(size_t i=0; idimension() == 2); + geo_assert(target->dimension() == 2); + geo_assert( + geometry->width() == target->width() && + geometry->height() == target->height() + ); + geo_assert(geometry->component_encoding() == Image::FLOAT64); + + MeshFacetsAABB AABB(*highres); + vector normal(highres->facets.nb()); + for(index_t f=0; ffacets.nb(); ++f) { + vec3 N = normalize(Geom::mesh_facet_normal(*highres,f)); + normal[f] = Color( + 0.5*(N.x+1.0), 0.5*(N.y+1.0), 0.5*(N.z+1.0) + ); + } + ImageRasterizer rasterizer(target); + +#ifdef _OPENMP + #pragma omp parallel for +#endif + for(index_t y=0; yheight(); ++y) { + index_t nearest_facet = NO_FACET; + vec3 nearest_point; + double sq_dist; + for(index_t x=0; xwidth(); ++x) { + vec3 p((double*)(void*)geometry->pixel_base(x,y)); + if( + p[0] == Numeric::max_float64() && + p[1] == Numeric::max_float64() && + p[2] == Numeric::max_float64() + ) { + continue; + } + if(nearest_facet == NO_FACET) { + nearest_facet = + AABB.nearest_facet(p, nearest_point, sq_dist + ); + } else { + sq_dist = length2(p - nearest_point); + AABB.nearest_facet_with_hint( + p,nearest_facet,nearest_point,sq_dist + ); + } + rasterizer.set_pixel( + int(x),int(y),normal[nearest_facet] + ); + } + } + } + + /**************************************************************************/ + + void bake_mesh_points_attribute_indirect( + Image* geometry, Image* target, + Mesh* highres, Attribute& attribute, + double bias, double scale + ) { + geo_assert(geometry->dimension() == 2); + geo_assert(target->dimension() == 2); + geo_assert( + geometry->width() == target->width() && + geometry->height() == target->height() + ); + geo_assert(geometry->component_encoding() == Image::FLOAT64); + geo_assert(highres->vertices.dimension() >= 3); + + NearestNeighborSearch_var kd_tree = new BalancedKdTree(3); + + kd_tree->set_points( + highres->vertices.nb(), highres->vertices.point_ptr(0), + highres->vertices.dimension() + ); + + ImageRasterizer rasterizer(target); + +#ifdef _OPENMP + #pragma omp parallel for +#endif + for(index_t y=0; yheight(); ++y) { + index_t nearest_vertex = NO_VERTEX; + double sq_dist; + Color C; + for(index_t x=0; xwidth(); ++x) { + vec3 p((double*)(void*)geometry->pixel_base(x,y)); + if( + p[0] == Numeric::max_float64() && + p[1] == Numeric::max_float64() && + p[2] == Numeric::max_float64() + ) { + continue; + } + kd_tree->get_nearest_neighbors( + 1, p.data(), &nearest_vertex, &sq_dist + ); + get_attribute_as_color( + C, attribute, MESH_VERTICES, + index_t(-1), index_t(-1), nearest_vertex, + bias, scale + ); + + rasterizer.set_pixel(int(x),int(y),C); + } + } + + } + + + + /**************************************************************************/ + +} + + + diff --git a/src/lib/geogram/mesh/mesh_baking.h b/src/lib/geogram/mesh/mesh_baking.h new file mode 100644 index 00000000..b5a26315 --- /dev/null +++ b/src/lib/geogram/mesh/mesh_baking.h @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2012-2014, Bruno Levy + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the ALICE Project-Team nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * If you modify this software, you should include a notice giving the + * name of the person performing the modification, the date of modification, + * and the reason for such modification. + * + * Contact: Bruno Levy + * + * Bruno.Levy@inria.fr + * http://www.loria.fr/~levy + * + * ALICE Project + * LORIA, INRIA Lorraine, + * Campus Scientifique, BP 239 + * 54506 VANDOEUVRE LES NANCY CEDEX + * FRANCE + * + */ + +#ifndef H_GEO_MESH_ALGO_MESH_BAKING_H +#define H_GEO_MESH_ALGO_MESH_BAKING_H + +#include +#include + +/** + * \file geogram/mesh/mesh_baking.h + * \brief Functions to bake attributes from meshes to + * images. + */ + +namespace GEO { + + class Mesh; + class Image; + + /** + * \brief Bakes the facet normals of a parameterized mesh + * into an image. + * \details The facet normals are baked over the previous content + * of the image (the image is not cleared). + * \param[in] mesh a pointer to the input esh, with a + * texture coordinates stored in the "tex_coord" facet + * corner attribute. + * \param[out] target the target image. + */ + void GEOGRAM_API bake_mesh_facet_normals(Mesh* mesh, Image* target); + + /** + * \brief Bakes the facet normals of a parameterized mesh + * into an image. + * \details The facet normals are baked over the previous content + * of the image (the image is not cleared). Normals are linearly + * interpolated in mesh facets. + * \param[in] mesh a pointer to the input esh, with a + * texture coordinates stored in the "tex_coord" facet + * corner attribute. + * \param[out] target the target image. + */ + void GEOGRAM_API bake_mesh_vertex_normals(Mesh* mesh, Image* target); + + /** + * \brief Bakes an attribute of a parameterized mesh + * \details The attribute is baked over the previous content + * of the image (the image is not cleared). + * \param[in] mesh a pointer to the input mesh, with a + * texture coordinates stored in the "tex_coord" facet + * corner attribute. + * \param[out] target the target image. + * \param[in] attribute the attribute, can be a vertex, facet corner + * or facet attribute. Vertex and facet corner attributes are + * linearly interpolated. If it is a vector attribute, its available + * components are copied to the r,g,b,a components of the interpolated color. + * \param[in] bias optional value to be added to the attribute values + * before storing them into the color components. + * \param[in] scale optional value that scales the attribute values + * after \p bias is added and before storing them into the color + * components. + */ + void GEOGRAM_API bake_mesh_attribute( + Mesh* mesh, Image* target, Attribute& attribute, + double bias=0.0, double scale=1.0 + ); + + /** + * \brief Bakes mesh geometry into an image (creates a geometry image). + * \param[in] mesh a pointer to the input mesh, with a + * texture coordinates stored in the "tex_coord" facet + * corner attribute. + * \param[out] target a pointer to the geometry image. It should use + * Image::FLOAT64 storage for the color components. + * \param[in] clear if set, then all the pixel components of the target + * geometry image are set to Numeric:max_float64() before rasterizing + * the facets. + */ + void GEOGRAM_API bake_mesh_geometry( + Mesh* mesh, Image* target, bool clear = true + ); + + /** + * \brief Bakes facet normals from a (in general) high resolution + * mesh to a (in general) parameterized lower resolution mesh + * represented as a geometry image. + * \details The facet normals are baked over the previous content + * of the image (the image is not cleared). The implementation uses + * a MeshFacetsAABB that changes the order of the elements of \p highres. + * Whenever a pixel of \p geometry has both its r,g,b components set + * to Numeric::max_float64(), the corresponding pixel in \p target is left + * unchanged. + * \param[in] geometry the geometry image + * \param[out] target the target image + * \param[in] highres the high-resolution mesh + * \pre geometry and target have the same size, and geometry uses + * FLOAT64 component encoding. + */ + void GEOGRAM_API bake_mesh_facet_normals_indirect( + Image* geometry, Image* target, Mesh* highres + ); + + + /** + * \brief Bakes a vertex attribute from a (in general) + * high resolution point set to a (in general) parameterized + * lower resolution mesh represented as a geometry image. + * \details The attribute is baked over the previous content + * of the image (the image is not cleared). + * Whenever a pixel of \p geometry has both its r,g,b components set + * to Numeric::max_float64(), the corresponding pixel in \p target is left + * unchanged. + * \param[in] geometry the geometry image + * \param[out] target the target image + * \param[in] highres the high-resolution pointset. + * \param[in] attribute the vertex attribute of \p highres to be baked. + * \pre geometry and target have the same size, and geometry uses + * FLOAT64 component encoding. + * \param[in] bias optional value to be added to the attribute values + * before storing them into the color components. + * \param[in] scale optional value that scales the attribute values + * after \p bias is added and before storing them into the color + * components. + */ + void GEOGRAM_API bake_mesh_points_attribute_indirect( + Image* geometry, Image* target, + Mesh* highres, Attribute& attribute, + double bias=0.0, double scale=1.0 + ); +} + +#endif diff --git a/src/lib/geogram/mesh/mesh_reorder.cpp b/src/lib/geogram/mesh/mesh_reorder.cpp index a1557266..a39fc251 100644 --- a/src/lib/geogram/mesh/mesh_reorder.cpp +++ b/src/lib/geogram/mesh/mesh_reorder.cpp @@ -808,73 +808,52 @@ namespace { return; } - // Parallel sorting + // Parallel sorting (2 then 4 then 8 sorts in parallel) + +// Unfortunately we cannot access consts for template arguments in lambdas in all +// compilers (gcc is OK but not MSVC) so I'm using (ugly) macros here... + +# define COORDX 0 +# define COORDY 1 +# define COORDZ 2 +# define UPX false +# define UPY false +# define UPZ false + m0_ = b; m8_ = e; - m4_ = reorder_split(m0_, m8_, CMP<0, false, MESH>(M)); - parallel_for(*this, 0, 2); // computes m2,m6 in parallel - parallel_for(*this, 10, 14); // computes m1,m3,m5,m7 in parallel - parallel_for(*this, 20, 28); // sorts the 8 subsets in parallel - } + m4_ = reorder_split(m0_, m8_, CMP(M)); - /** - * \brief The callback for the - * multithreaded implementation (used internally). - * \param[in] i the job index, that determines the job to be - * done by one of the threads. The job index \p i can be one - * of 0,1 (first level), 10,11,12,13 (second level) or - * 20,21,22,23,24,25,26,27 (third level). - */ - void operator() (index_t i) { - const int COORDX = 0, COORDY = 1, COORDZ = 2; - const bool UPX = false, UPY = false, UPZ = false; - switch(i) { - case 0: - m2_ = reorder_split(m0_, m4_, CMP(M_)); - break; - case 1: - m6_ = reorder_split(m4_, m8_, CMP(M_)); - break; - case 10: - m1_ = reorder_split(m0_, m2_, CMP(M_)); - break; - case 11: - m3_ = reorder_split(m2_, m4_, CMP(M_)); - break; - case 12: - m5_ = reorder_split(m4_, m6_, CMP(M_)); - break; - case 13: - m7_ = reorder_split(m6_, m8_, CMP(M_)); - break; - case 20: - sort(M_, m0_, m1_); - break; - case 21: - sort(M_, m1_, m2_); - break; - case 22: - sort(M_, m2_, m3_); - break; - case 23: - sort(M_, m3_, m4_); - break; - case 24: - sort(M_, m4_, m5_); - break; - case 25: - sort(M_, m5_, m6_); - break; - case 26: - sort(M_, m6_, m7_); - break; - case 27: - sort(M_, m7_, m8_); - break; - default: - geo_assert_not_reached; - break; - } + + parallel( + [this]() { m2_ = reorder_split(m0_, m4_, CMP(M_)); }, + [this]() { m6_ = reorder_split(m4_, m8_, CMP(M_)); } + ); + + parallel( + [this]() { m1_ = reorder_split(m0_, m2_, CMP(M_)); }, + [this]() { m3_ = reorder_split(m2_, m4_, CMP(M_)); }, + [this]() { m5_ = reorder_split(m4_, m6_, CMP(M_)); }, + [this]() { m7_ = reorder_split(m6_, m8_, CMP(M_)); } + ); + + parallel( + [this]() { sort(M_, m0_, m1_); }, + [this]() { sort(M_, m1_, m2_); }, + [this]() { sort(M_, m2_, m3_); }, + [this]() { sort(M_, m3_, m4_); }, + [this]() { sort(M_, m4_, m5_); }, + [this]() { sort(M_, m5_, m6_); }, + [this]() { sort(M_, m6_, m7_); }, + [this]() { sort(M_, m7_, m8_); } + ); + +# undef COORDX +# undef COORDY +# undef COORDZ +# undef UPX +# undef UPY +# undef UPZ } private: @@ -1301,6 +1280,41 @@ namespace GEO { namespace { using namespace GEO; + + // Same as in delaunay/periodic.cpp, + // copied here for now because linker does not find + // it under Android. + int Periodic_translation[27][3] = { + { 0, 0, 0}, //13 -> 0 + <-- zero displacement is first. + { -1, -1, -1}, //0 -> 1 - + { -1, -1, 0}, //1 -> 2 - + { -1, -1, 1}, //2 -> 3 - + { -1, 0, -1}, //3 -> 4 - + { -1, 0, 0}, //4 -> 5 - + { -1, 0, 1}, //5 -> 6 - + { -1, 1, -1}, //6 -> 7 - + { -1, 1, 0}, //7 -> 8 - + { -1, 1, 1}, //8 -> 9 - + { 0, -1, -1}, //9 -> 10 - + { 0, -1, 0}, //10 -> 11 - + { 0, -1, 1}, //11 -> 12 - + { 0, 0, -1}, //12 -> 13 - + // (zero displacement was there) + { 0, 0, 1}, //14 -> 14 + + { 0, 1, -1}, //15 -> 15 - + { 0, 1, 0}, //16 -> 16 + + { 0, 1, 1}, //17 -> 17 + + { 1, -1, -1}, //18 -> 18 - + { 1, -1, 0}, //19 -> 19 - + { 1, -1, 1}, //20 -> 20 - + { 1, 0, -1}, //21 -> 21 - + { 1, 0, 0}, //22 -> 22 + + { 1, 0, 1}, //23 -> 23 + + { 1, 1, -1}, //24 -> 24 - + { 1, 1, 0}, //25 -> 25 + + { 1, 1, 1} //26 -> 26 + + }; + /** * \details Exposes an interface compatible with the requirement @@ -1331,7 +1345,7 @@ namespace { for(index_t i=0; i<27; ++i) { for(index_t j=0; j<3; ++j) { - xlat_[i][j] = period * double(Periodic::translation[i][j]); + xlat_[i][j] = period * double(Periodic_translation[i][j]); } } } diff --git a/src/lib/geogram/parameterization/mesh_LSCM.cpp b/src/lib/geogram/parameterization/mesh_LSCM.cpp index a8a04f9d..01303819 100644 --- a/src/lib/geogram/parameterization/mesh_LSCM.cpp +++ b/src/lib/geogram/parameterization/mesh_LSCM.cpp @@ -74,7 +74,7 @@ namespace { * store the texture coordinates. */ LSCM(Mesh& M, Attribute& tex_coord, Attribute& angle) : - mesh_(M), tex_coord_(tex_coord), angle_(angle) { + mesh_(M), tex_coord_(tex_coord), angle_(angle), eigen_(0) { geo_assert(tex_coord.dimension() == 2); locked_1_ = index_t(-1); locked_2_ = index_t(-1); diff --git a/src/lib/geogram/points/colocate.cpp b/src/lib/geogram/points/colocate.cpp index 0d0bbf7c..9e2ff92e 100644 --- a/src/lib/geogram/points/colocate.cpp +++ b/src/lib/geogram/points/colocate.cpp @@ -120,7 +120,7 @@ namespace { * \details Called in parallel using parallel_for(). * \param[in] i index of the query point */ - void operator() (index_t i) { + void do_it(index_t i) { index_t nb = std::min(index_t(6),nb_points()); while(!find_nearest_neighbors(i, nb) && nb < nb_points()) { if(nb == nb_points()) { @@ -242,10 +242,14 @@ namespace GEO { Colocate colocate_obj(NN, old2new, tolerance); if(CmdLine::get_arg_bool("sys:multithread")) { - parallel_for(colocate_obj, 0, nb_points, 1, true); + parallel_for( + 0, nb_points, + [&colocate_obj](index_t i){ colocate_obj.do_it(i); }, + 1, true + ); } else { for(index_t i = 0; i < nb_points; i++) { - colocate_obj(i); + colocate_obj.do_it(i); } } index_t result = 0; diff --git a/src/lib/geogram/points/kd_tree.cpp b/src/lib/geogram/points/kd_tree.cpp index db4fe342..64b8c344 100644 --- a/src/lib/geogram/points/kd_tree.cpp +++ b/src/lib/geogram/points/kd_tree.cpp @@ -446,15 +446,36 @@ namespace GEO { m8_ = nb_points(); // Create the first level of the tree m4_ = split_kd_node(1, m0_, m8_); + // Create the second level of the tree // (using two threads) - parallel_for(*this, 2, 4); + parallel( + [this]() { m2_ = split_kd_node(2, m0_, m4_); }, + [this]() { m6_ = split_kd_node(3, m4_, m8_); } + ); + // Create the third level of the tree // (using four threads) - parallel_for(*this, 4, 8); + parallel( + [this]() { m1_ = split_kd_node(4, m0_, m2_); }, + [this]() { m3_ = split_kd_node(5, m2_, m4_); }, + [this]() { m5_ = split_kd_node(6, m4_, m6_); }, + [this]() { m7_ = split_kd_node(7, m6_, m8_); } + ); + // Create the fourth level of the tree // (using eight threads) - parallel_for(*this, 8, 16); + parallel( + [this]() { create_kd_tree_recursive(8 , m0_, m1_); }, + [this]() { create_kd_tree_recursive(9 , m1_, m2_); }, + [this]() { create_kd_tree_recursive(10, m2_, m3_); }, + [this]() { create_kd_tree_recursive(11, m3_, m4_); }, + [this]() { create_kd_tree_recursive(12, m4_, m5_); }, + [this]() { create_kd_tree_recursive(13, m5_, m6_); }, + [this]() { create_kd_tree_recursive(14, m6_, m7_); }, + [this]() { create_kd_tree_recursive(15, m7_, m8_); } + ); + } else { create_kd_tree_recursive(1, 0, nb_points()); } @@ -465,62 +486,6 @@ namespace GEO { return 1; } - - void BalancedKdTree::operator() (index_t i) { - switch(i) { - // Second level of the tree: create two nodes in - // parallel. - case 2: - m2_ = split_kd_node(2, m0_, m4_); - break; - case 3: - m6_ = split_kd_node(3, m4_, m8_); - break; - - // Third level of the tree: create four nodes in - // parallel. - case 4: - m1_ = split_kd_node(4, m0_, m2_); - break; - case 5: - m3_ = split_kd_node(5, m2_, m4_); - break; - case 6: - m5_ = split_kd_node(6, m4_, m6_); - break; - case 7: - m7_ = split_kd_node(7, m6_, m8_); - break; - - // Fourth level of the tree: create eight subtrees - // in parallel. - case 8: - create_kd_tree_recursive(8, m0_, m1_); - break; - case 9: - create_kd_tree_recursive(9, m1_, m2_); - break; - case 10: - create_kd_tree_recursive(10, m2_, m3_); - break; - case 11: - create_kd_tree_recursive(11, m3_, m4_); - break; - case 12: - create_kd_tree_recursive(12, m4_, m5_); - break; - case 13: - create_kd_tree_recursive(13, m5_, m6_); - break; - case 14: - create_kd_tree_recursive(14, m6_, m7_); - break; - case 15: - create_kd_tree_recursive(15, m7_, m8_); - break; - } - } - index_t BalancedKdTree::split_kd_node( index_t node_index, index_t b, index_t e ) { diff --git a/src/lib/geogram/points/kd_tree.h b/src/lib/geogram/points/kd_tree.h index d22881f4..1a34ba1f 100644 --- a/src/lib/geogram/points/kd_tree.h +++ b/src/lib/geogram/points/kd_tree.h @@ -79,7 +79,7 @@ namespace GEO { index_t nb_points, const double* points, index_t stride ); - /** \copydoc NearestNeighborSearch::get_nearest_beighbors() */ + /** \copydoc NearestNeighborSearch::get_nearest_neighbors() */ virtual void get_nearest_neighbors( index_t nb_neighbors, const double* query_point, @@ -87,7 +87,7 @@ namespace GEO { double* neighbors_sq_dist ) const; - /** \copydoc NearestNeighborSearch::get_nearest_beighbors() */ + /** \copydoc NearestNeighborSearch::get_nearest_neighbors() */ virtual void get_nearest_neighbors( index_t nb_neighbors, const double* query_point, @@ -96,7 +96,7 @@ namespace GEO { KeepInitialValues ) const; - /** \copydoc NearestNeighborSearch::get_nearest_beighbors() */ + /** \copydoc NearestNeighborSearch::get_nearest_neighbors() */ virtual void get_nearest_neighbors( index_t nb_neighbors, index_t query_point, @@ -458,13 +458,6 @@ namespace GEO { */ BalancedKdTree(coord_index_t dim); - public: - /** - * \brief Used by multithread tree construction - * in the implementation of set_points() - */ - void operator() (index_t i); - protected: /** * \brief BalancedKdTree destructor diff --git a/src/lib/geogram/third_party/pstdint.h b/src/lib/geogram/third_party/pstdint.h old mode 100755 new mode 100644 index 5a830e24..7c188260 --- a/src/lib/geogram/third_party/pstdint.h +++ b/src/lib/geogram/third_party/pstdint.h @@ -31,23 +31,23 @@ * **************************************************************************** * - * Version 0.1.15.4 + * Version 0.1.16.0 * * The ANSI C standard committee, for the C99 standard, specified the * inclusion of a new standard include file called stdint.h. This is * a very useful and long desired include file which contains several - * very precise definitions for integer scalar types that is - * critically important for making portable several classes of - * applications including cryptography, hashing, variable length - * integer libraries and so on. But for most developers its likely - * useful just for programming sanity. + * very precise definitions for integer scalar types that is critically + * important for making several classes of applications portable + * including cryptography, hashing, variable length integer libraries + * and so on. But for most developers its likely useful just for + * programming sanity. * * The problem is that some compiler vendors chose to ignore the C99 * standard and some older compilers have no opportunity to be updated. * Because of this situation, simply including stdint.h in your code * makes it unportable. * - * So that's what this file is all about. Its an attempt to build a + * So that's what this file is all about. It's an attempt to build a * single universal include file that works on as many platforms as * possible to deliver what stdint.h is supposed to. Even compilers * that already come with stdint.h can use this file instead without @@ -197,7 +197,7 @@ * do nothing else. On the Mac OS X version of gcc this is _STDINT_H_. */ -#if ((defined(__SUNPRO_C) && __SUNPRO_C >= 0x570) || (defined(_MSC_VER) && _MSC_VER >= 1600) || (defined(__STDC__) && __STDC__ && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || (defined (__WATCOMC__) && (defined (_STDINT_H_INCLUDED) || __WATCOMC__ >= 1250)) || (defined(__GNUC__) && (__GNUC__ > 3 || defined(_STDINT_H) || defined(_STDINT_H_) || defined (__UINT_FAST64_TYPE__)) )) && !defined (_PSTDINT_H_INCLUDED) +#if ((defined(__SUNPRO_C) && __SUNPRO_C >= 0x570) || (defined(_MSC_VER) && _MSC_VER >= 1600) || (defined(__STDC__) && __STDC__ && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || (defined (__WATCOMC__) && (defined (_STDINT_H_INCLUDED) || __WATCOMC__ >= 1250)) || (defined(__GNUC__) && (__GNUC__ > 3 || defined(_STDINT_H) || defined(_STDINT_H_) || defined (__UINT_FAST64_TYPE__)) )) && !defined (PSTDINT_H_INCLUDED) #include #define PSTDINT_H_INCLUDED # if defined(__GNUC__) && (defined(__x86_64__) || defined(__ppc64__)) && !(defined(__APPLE__) && defined(__MACH__)) @@ -355,7 +355,7 @@ #define PSTDINT_H_INCLUDED #ifndef SIZE_MAX -# define SIZE_MAX (~(size_t)0) +# define SIZE_MAX ((size_t)-1) #endif /* @@ -681,14 +681,12 @@ #undef stdint_least_defined /* - * The ANSI C committee pretending to know or specify anything about - * performance is the epitome of misguided arrogance. The mandate of - * this file is to *ONLY* ever support that absolute minimum - * definition of the fast integer types, for compatibility purposes. - * No extensions, and no attempt to suggest what may or may not be a - * faster integer type will ever be made in this file. Developers are - * warned to stay away from these types when using this or any other - * stdint.h. + * The ANSI C committee has defined *int*_fast*_t types as well. This, + * of course, defies rationality -- you can't know what will be fast + * just from the type itself. Even for a given architecture, compatible + * implementations might have different performance characteristics. + * Developers are warned to stay away from these types when using this + * or any other stdint.h. */ typedef int_least8_t int_fast8_t; @@ -827,86 +825,95 @@ typedef uint_least32_t uint_fast32_t; #define REPORTERROR(msg) { err_n++; if (err_first <= 0) err_first = __LINE__; printf msg; } +#define X_SIZE_MAX ((size_t)-1) + int main () { - int err_n = 0; - int err_first = 0; - DECL(I,8) - DECL(U,8) - DECL(I,16) - DECL(U,16) - DECL(I,32) - DECL(U,32) + int err_n = 0; + int err_first = 0; + DECL(I,8) + DECL(U,8) + DECL(I,16) + DECL(U,16) + DECL(I,32) + DECL(U,32) #ifdef INT64_MAX - DECL(I,64) - DECL(U,64) -#endif - intmax_t imax = INTMAX_C(0); - uintmax_t umax = UINTMAX_C(0); - char str0[256], str1[256]; - - sprintf (str0, "%" PRINTF_INT32_MODIFIER "d", INT32_C(2147483647)); - if (0 != strcmp (str0, "2147483647")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str0)); - if (atoi(PRINTF_INT32_DEC_WIDTH) != (int) strlen(str0)) REPORTERROR (("Something wrong with PRINTF_INT32_DEC_WIDTH : %s\n", PRINTF_INT32_DEC_WIDTH)); - sprintf (str0, "%" PRINTF_INT32_MODIFIER "u", UINT32_C(4294967295)); - if (0 != strcmp (str0, "4294967295")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str0)); - if (atoi(PRINTF_UINT32_DEC_WIDTH) != (int) strlen(str0)) REPORTERROR (("Something wrong with PRINTF_UINT32_DEC_WIDTH : %s\n", PRINTF_UINT32_DEC_WIDTH)); + DECL(I,64) + DECL(U,64) +#endif + intmax_t imax = INTMAX_C(0); + uintmax_t umax = UINTMAX_C(0); + char str0[256], str1[256]; + + sprintf (str0, "%" PRINTF_INT32_MODIFIER "d", INT32_C(2147483647)); + if (0 != strcmp (str0, "2147483647")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str0)); + if (atoi(PRINTF_INT32_DEC_WIDTH) != (int) strlen(str0)) REPORTERROR (("Something wrong with PRINTF_INT32_DEC_WIDTH : %s\n", PRINTF_INT32_DEC_WIDTH)); + sprintf (str0, "%" PRINTF_INT32_MODIFIER "u", UINT32_C(4294967295)); + if (0 != strcmp (str0, "4294967295")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str0)); + if (atoi(PRINTF_UINT32_DEC_WIDTH) != (int) strlen(str0)) REPORTERROR (("Something wrong with PRINTF_UINT32_DEC_WIDTH : %s\n", PRINTF_UINT32_DEC_WIDTH)); #ifdef INT64_MAX - sprintf (str1, "%" PRINTF_INT64_MODIFIER "d", INT64_C(9223372036854775807)); - if (0 != strcmp (str1, "9223372036854775807")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str1)); - if (atoi(PRINTF_INT64_DEC_WIDTH) != (int) strlen(str1)) REPORTERROR (("Something wrong with PRINTF_INT64_DEC_WIDTH : %s, %d\n", PRINTF_INT64_DEC_WIDTH, (int) strlen(str1))); - sprintf (str1, "%" PRINTF_INT64_MODIFIER "u", UINT64_C(18446744073709550591)); - if (0 != strcmp (str1, "18446744073709550591")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str1)); - if (atoi(PRINTF_UINT64_DEC_WIDTH) != (int) strlen(str1)) REPORTERROR (("Something wrong with PRINTF_UINT64_DEC_WIDTH : %s, %d\n", PRINTF_UINT64_DEC_WIDTH, (int) strlen(str1))); -#endif - - sprintf (str0, "%d %x\n", 0, ~0); - - sprintf (str1, "%d %x\n", i8, ~0); - if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i8 : %s\n", str1)); - sprintf (str1, "%u %x\n", u8, ~0); - if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with u8 : %s\n", str1)); - sprintf (str1, "%d %x\n", i16, ~0); - if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i16 : %s\n", str1)); - sprintf (str1, "%u %x\n", u16, ~0); - if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with u16 : %s\n", str1)); - sprintf (str1, "%" PRINTF_INT32_MODIFIER "d %x\n", i32, ~0); - if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i32 : %s\n", str1)); - sprintf (str1, "%" PRINTF_INT32_MODIFIER "u %x\n", u32, ~0); - if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with u32 : %s\n", str1)); + sprintf (str1, "%" PRINTF_INT64_MODIFIER "d", INT64_C(9223372036854775807)); + if (0 != strcmp (str1, "9223372036854775807")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str1)); + if (atoi(PRINTF_INT64_DEC_WIDTH) != (int) strlen(str1)) REPORTERROR (("Something wrong with PRINTF_INT64_DEC_WIDTH : %s, %d\n", PRINTF_INT64_DEC_WIDTH, (int) strlen(str1))); + sprintf (str1, "%" PRINTF_INT64_MODIFIER "u", UINT64_C(18446744073709550591)); + if (0 != strcmp (str1, "18446744073709550591")) REPORTERROR (("Something wrong with PRINTF_INT32_MODIFIER : %s\n", str1)); + if (atoi(PRINTF_UINT64_DEC_WIDTH) != (int) strlen(str1)) REPORTERROR (("Something wrong with PRINTF_UINT64_DEC_WIDTH : %s, %d\n", PRINTF_UINT64_DEC_WIDTH, (int) strlen(str1))); +#endif + + sprintf (str0, "%d %x\n", 0, ~0); + + sprintf (str1, "%d %x\n", i8, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i8 : %s\n", str1)); + sprintf (str1, "%u %x\n", u8, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with u8 : %s\n", str1)); + sprintf (str1, "%d %x\n", i16, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i16 : %s\n", str1)); + sprintf (str1, "%u %x\n", u16, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with u16 : %s\n", str1)); + sprintf (str1, "%" PRINTF_INT32_MODIFIER "d %x\n", i32, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i32 : %s\n", str1)); + sprintf (str1, "%" PRINTF_INT32_MODIFIER "u %x\n", u32, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with u32 : %s\n", str1)); #ifdef INT64_MAX - sprintf (str1, "%" PRINTF_INT64_MODIFIER "d %x\n", i64, ~0); - if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i64 : %s\n", str1)); + sprintf (str1, "%" PRINTF_INT64_MODIFIER "d %x\n", i64, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with i64 : %s\n", str1)); #endif - sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "d %x\n", imax, ~0); - if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with imax : %s\n", str1)); - sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "u %x\n", umax, ~0); - if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with umax : %s\n", str1)); + sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "d %x\n", imax, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with imax : %s\n", str1)); + sprintf (str1, "%" PRINTF_INTMAX_MODIFIER "u %x\n", umax, ~0); + if (0 != strcmp (str0, str1)) REPORTERROR (("Something wrong with umax : %s\n", str1)); - TESTUMAX(8); - TESTUMAX(16); - TESTUMAX(32); + TESTUMAX(8); + TESTUMAX(16); + TESTUMAX(32); #ifdef INT64_MAX - TESTUMAX(64); + TESTUMAX(64); #endif #define STR(v) #v #define Q(v) printf ("sizeof " STR(v) " = %u\n", (unsigned) sizeof (v)); - if (err_n) { - printf ("pstdint.h is not correct. Please use sizes below to correct it:\n"); - } - - Q(int) - Q(unsigned) - Q(long int) - Q(short int) - Q(int8_t) - Q(int16_t) - Q(int32_t) + if (err_n) { + printf ("pstdint.h is not correct. Please use sizes below to correct it:\n"); + } + + Q(int) + Q(unsigned) + Q(long int) + Q(short int) + Q(int8_t) + Q(int16_t) + Q(int32_t) #ifdef INT64_MAX - Q(int64_t) + Q(int64_t) +#endif + +#if UINT_MAX < X_SIZE_MAX + printf ("UINT_MAX < X_SIZE_MAX\n"); +#else + printf ("UINT_MAX >= X_SIZE_MAX\n"); #endif + printf ("%" PRINTF_INT64_MODIFIER "u vs %" PRINTF_INT64_MODIFIER "u\n", UINT_MAX, X_SIZE_MAX); - return EXIT_SUCCESS; + return EXIT_SUCCESS; } #endif diff --git a/src/lib/geogram/third_party/stb_image/stb_image.h b/src/lib/geogram/third_party/stb_image/stb_image.h new file mode 100644 index 00000000..339453c7 --- /dev/null +++ b/src/lib/geogram/third_party/stb_image/stb_image.h @@ -0,0 +1,7187 @@ +/* stb_image - v2.16 - public domain image loader - http://nothings.org/stb_image.h + no warranty implied; use at your own risk + + Do this: + #define STB_IMAGE_IMPLEMENTATION + before you include this file in *one* C or C++ file to create the implementation. + + // i.e. it should look like this: + #include ... + #include ... + #include ... + #define STB_IMAGE_IMPLEMENTATION + #include "stb_image.h" + + You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. + And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free + + + QUICK NOTES: + Primarily of interest to game developers and other people who can + avoid problematic images and only need the trivial interface + + JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) + PNG 1/2/4/8/16-bit-per-channel + + TGA (not sure what subset, if a subset) + BMP non-1bpp, non-RLE + PSD (composited view only, no extra channels, 8/16 bit-per-channel) + + GIF (*comp always reports as 4-channel) + HDR (radiance rgbE format) + PIC (Softimage PIC) + PNM (PPM and PGM binary only) + + Animated GIF still needs a proper API, but here's one way to do it: + http://gist.github.com/urraka/685d9a6340b26b830d49 + + - decode from memory or through FILE (define STBI_NO_STDIO to remove code) + - decode from arbitrary I/O callbacks + - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) + + Full documentation under "DOCUMENTATION" below. + + +LICENSE + + See end of file for license information. + +RECENT REVISION HISTORY: + + 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes + 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 + RGB-format JPEG; remove white matting in PSD; + allocate large structures on the stack; + correct channel count for PNG & BMP + 2.10 (2016-01-22) avoid warning introduced in 2.09 + 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED + + See end of file for full revision history. + + + ============================ Contributors ========================= + + Image formats Extensions, features + Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) + Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) + Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) + Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) + Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) + Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) + Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) + github:urraka (animated gif) Junggon Kim (PNM comments) + Daniel Gibson (16-bit TGA) + socks-the-fox (16-bit PNG) + Jeremy Sawicki (handle all ImageNet JPGs) + Optimizations & bugfixes + Fabian "ryg" Giesen + Arseny Kapoulkine + John-Mark Allen + + Bug & warning fixes + Marc LeBlanc David Woo Guillaume George Martins Mozeiko + Christpher Lloyd Jerry Jansson Joseph Thomson Phil Jordan + Dave Moore Roy Eltham Hayaki Saito Nathan Reed + Won Chun Luke Graham Johan Duparc Nick Verigakis + the Horde3D community Thomas Ruf Ronny Chevalier Baldur Karlsson + Janez Zemva John Bartholomew Michal Cichon github:rlyeh + Jonathan Blow Ken Hamada Tero Hanninen github:romigrou + Laurent Gomila Cort Stratton Sergio Gonzalez github:svdijk + Aruelien Pocheville Thibault Reuille Cass Everitt github:snagar + Ryamond Barbiero Paul Du Bois Engin Manap github:Zelex + Michaelangel007@github Philipp Wiesemann Dale Weiler github:grim210 + Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:sammyhw + Blazej Dariusz Roszkowski Gregory Mullen github:phprus + Christian Floisand Kevin Schmidt github:poppolopoppo +*/ + +#ifndef STBI_INCLUDE_STB_IMAGE_H +#define STBI_INCLUDE_STB_IMAGE_H + +// DOCUMENTATION +// +// Limitations: +// - no 16-bit-per-channel PNG +// - no 12-bit-per-channel JPEG +// - no JPEGs with arithmetic coding +// - no 1-bit BMP +// - GIF always returns *comp=4 +// +// Basic usage (see HDR discussion below for HDR usage): +// int x,y,n; +// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); +// // ... process data if not NULL ... +// // ... x = width, y = height, n = # 8-bit components per pixel ... +// // ... replace '0' with '1'..'4' to force that many components per pixel +// // ... but 'n' will always be the number that it would have been if you said 0 +// stbi_image_free(data) +// +// Standard parameters: +// int *x -- outputs image width in pixels +// int *y -- outputs image height in pixels +// int *channels_in_file -- outputs # of image components in image file +// int desired_channels -- if non-zero, # of image components requested in result +// +// The return value from an image loader is an 'unsigned char *' which points +// to the pixel data, or NULL on an allocation failure or if the image is +// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, +// with each pixel consisting of N interleaved 8-bit components; the first +// pixel pointed to is top-left-most in the image. There is no padding between +// image scanlines or between pixels, regardless of format. The number of +// components N is 'desired_channels' if desired_channels is non-zero, or +// *channels_in_file otherwise. If desired_channels is non-zero, +// *channels_in_file has the number of components that _would_ have been +// output otherwise. E.g. if you set desired_channels to 4, you will always +// get RGBA output, but you can check *channels_in_file to see if it's trivially +// opaque because e.g. there were only 3 channels in the source image. +// +// An output image with N components has the following components interleaved +// in this order in each pixel: +// +// N=#comp components +// 1 grey +// 2 grey, alpha +// 3 red, green, blue +// 4 red, green, blue, alpha +// +// If image loading fails for any reason, the return value will be NULL, +// and *x, *y, *channels_in_file will be unchanged. The function +// stbi_failure_reason() can be queried for an extremely brief, end-user +// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS +// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly +// more user-friendly ones. +// +// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. +// +// =========================================================================== +// +// Philosophy +// +// stb libraries are designed with the following priorities: +// +// 1. easy to use +// 2. easy to maintain +// 3. good performance +// +// Sometimes I let "good performance" creep up in priority over "easy to maintain", +// and for best performance I may provide less-easy-to-use APIs that give higher +// performance, in addition to the easy to use ones. Nevertheless, it's important +// to keep in mind that from the standpoint of you, a client of this library, +// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. +// +// Some secondary priorities arise directly from the first two, some of which +// make more explicit reasons why performance can't be emphasized. +// +// - Portable ("ease of use") +// - Small source code footprint ("easy to maintain") +// - No dependencies ("ease of use") +// +// =========================================================================== +// +// I/O callbacks +// +// I/O callbacks allow you to read from arbitrary sources, like packaged +// files or some other source. Data read from callbacks are processed +// through a small internal buffer (currently 128 bytes) to try to reduce +// overhead. +// +// The three functions you must define are "read" (reads some bytes of data), +// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). +// +// =========================================================================== +// +// SIMD support +// +// The JPEG decoder will try to automatically use SIMD kernels on x86 when +// supported by the compiler. For ARM Neon support, you must explicitly +// request it. +// +// (The old do-it-yourself SIMD API is no longer supported in the current +// code.) +// +// On x86, SSE2 will automatically be used when available based on a run-time +// test; if not, the generic C versions are used as a fall-back. On ARM targets, +// the typical path is to have separate builds for NEON and non-NEON devices +// (at least this is true for iOS and Android). Therefore, the NEON support is +// toggled by a build flag: define STBI_NEON to get NEON loops. +// +// If for some reason you do not want to use any of SIMD code, or if +// you have issues compiling it, you can disable it entirely by +// defining STBI_NO_SIMD. +// +// =========================================================================== +// +// HDR image support (disable by defining STBI_NO_HDR) +// +// stb_image now supports loading HDR images in general, and currently +// the Radiance .HDR file format, although the support is provided +// generically. You can still load any file through the existing interface; +// if you attempt to load an HDR file, it will be automatically remapped to +// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// both of these constants can be reconfigured through this interface: +// +// stbi_hdr_to_ldr_gamma(2.2f); +// stbi_hdr_to_ldr_scale(1.0f); +// +// (note, do not use _inverse_ constants; stbi_image will invert them +// appropriately). +// +// Additionally, there is a new, parallel interface for loading files as +// (linear) floats to preserve the full dynamic range: +// +// float *data = stbi_loadf(filename, &x, &y, &n, 0); +// +// If you load LDR images through this interface, those images will +// be promoted to floating point values, run through the inverse of +// constants corresponding to the above: +// +// stbi_ldr_to_hdr_scale(1.0f); +// stbi_ldr_to_hdr_gamma(2.2f); +// +// Finally, given a filename (or an open file or memory block--see header +// file for details) containing image data, you can query for the "most +// appropriate" interface to use (that is, whether the image is HDR or +// not), using: +// +// stbi_is_hdr(char *filename); +// +// =========================================================================== +// +// iPhone PNG support: +// +// By default we convert iphone-formatted PNGs back to RGB, even though +// they are internally encoded differently. You can disable this conversion +// by by calling stbi_convert_iphone_png_to_rgb(0), in which case +// you will always just get the native iphone "format" through (which +// is BGR stored in RGB). +// +// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per +// pixel to remove any premultiplied alpha *only* if the image file explicitly +// says there's premultiplied data (currently only happens in iPhone images, +// and only if iPhone convert-to-rgb processing is on). +// +// =========================================================================== +// +// ADDITIONAL CONFIGURATION +// +// - You can suppress implementation of any of the decoders to reduce +// your code footprint by #defining one or more of the following +// symbols before creating the implementation. +// +// STBI_NO_JPEG +// STBI_NO_PNG +// STBI_NO_BMP +// STBI_NO_PSD +// STBI_NO_TGA +// STBI_NO_GIF +// STBI_NO_HDR +// STBI_NO_PIC +// STBI_NO_PNM (.ppm and .pgm) +// +// - You can request *only* certain decoders and suppress all other ones +// (this will be more forward-compatible, as addition of new decoders +// doesn't require you to disable them explicitly): +// +// STBI_ONLY_JPEG +// STBI_ONLY_PNG +// STBI_ONLY_BMP +// STBI_ONLY_PSD +// STBI_ONLY_TGA +// STBI_ONLY_GIF +// STBI_ONLY_HDR +// STBI_ONLY_PIC +// STBI_ONLY_PNM (.ppm and .pgm) +// +// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still +// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB +// + + +#ifndef STBI_NO_STDIO +#include +#endif // STBI_NO_STDIO + +#define STBI_VERSION 1 + +enum +{ + STBI_default = 0, // only used for desired_channels + + STBI_grey = 1, + STBI_grey_alpha = 2, + STBI_rgb = 3, + STBI_rgb_alpha = 4 +}; + +typedef unsigned char stbi_uc; +typedef unsigned short stbi_us; + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef STB_IMAGE_STATIC +#define STBIDEF static +#else +#define STBIDEF extern +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// PRIMARY API - works on images of any type +// + +// +// load image by filename, open file, or memory buffer +// + +typedef struct +{ + int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read + void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative + int (*eof) (void *user); // returns nonzero if we are at end of file/data +} stbi_io_callbacks; + +//////////////////////////////////// +// +// 8-bits-per-channel interface +// + +STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +// for stbi_load_from_file, file pointer is left pointing immediately after image +#endif + +//////////////////////////////////// +// +// 16-bits-per-channel interface +// + +STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + +#ifndef STBI_NO_STDIO +STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); +STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); +#endif + +//////////////////////////////////// +// +// float-per-channel interface +// +#ifndef STBI_NO_LINEAR + STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); + + #ifndef STBI_NO_STDIO + STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); + STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); + #endif +#endif + +#ifndef STBI_NO_HDR + STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); + STBIDEF void stbi_hdr_to_ldr_scale(float scale); +#endif // STBI_NO_HDR + +#ifndef STBI_NO_LINEAR + STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); + STBIDEF void stbi_ldr_to_hdr_scale(float scale); +#endif // STBI_NO_LINEAR + +// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename); +STBIDEF int stbi_is_hdr_from_file(FILE *f); +#endif // STBI_NO_STDIO + + +// get a VERY brief reason for failure +// NOT THREADSAFE +STBIDEF const char *stbi_failure_reason (void); + +// free the loaded image -- this is just free() +STBIDEF void stbi_image_free (void *retval_from_stbi_load); + +// get image dimensions & components without fully decoding +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); +STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); + +#endif + + + +// for image formats that explicitly notate that they have premultiplied alpha, +// we just return the colors as stored in the file. set this flag to force +// unpremultiplication. results are undefined if the unpremultiply overflow. +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); + +// indicate whether we should process iphone images back to canonical format, +// or just pass them through "as-is" +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); + +// flip the image vertically, so the first pixel in the output array is the bottom left +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); + +// ZLIB client - used by PNG, available for other purposes + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); +STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + +STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); + + +#ifdef __cplusplus +} +#endif + +// +// +//// end header file ///////////////////////////////////////////////////// +#endif // STBI_INCLUDE_STB_IMAGE_H + +#ifdef STB_IMAGE_IMPLEMENTATION + +#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ + || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ + || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ + || defined(STBI_ONLY_ZLIB) + #ifndef STBI_ONLY_JPEG + #define STBI_NO_JPEG + #endif + #ifndef STBI_ONLY_PNG + #define STBI_NO_PNG + #endif + #ifndef STBI_ONLY_BMP + #define STBI_NO_BMP + #endif + #ifndef STBI_ONLY_PSD + #define STBI_NO_PSD + #endif + #ifndef STBI_ONLY_TGA + #define STBI_NO_TGA + #endif + #ifndef STBI_ONLY_GIF + #define STBI_NO_GIF + #endif + #ifndef STBI_ONLY_HDR + #define STBI_NO_HDR + #endif + #ifndef STBI_ONLY_PIC + #define STBI_NO_PIC + #endif + #ifndef STBI_ONLY_PNM + #define STBI_NO_PNM + #endif +#endif + +#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) +#define STBI_NO_ZLIB +#endif + + +#include +#include // ptrdiff_t on osx +#include +#include +#include + +#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) +#include // ldexp +#endif + +#ifndef STBI_NO_STDIO +#include +#endif + +#ifndef STBI_ASSERT +#include +#define STBI_ASSERT(x) assert(x) +#endif + + +#ifndef _MSC_VER + #ifdef __cplusplus + #define stbi_inline inline + #else + #define stbi_inline + #endif +#else + #define stbi_inline __forceinline +#endif + + +#ifdef _MSC_VER +typedef unsigned short stbi__uint16; +typedef signed short stbi__int16; +typedef unsigned int stbi__uint32; +typedef signed int stbi__int32; +#else +#include +typedef uint16_t stbi__uint16; +typedef int16_t stbi__int16; +typedef uint32_t stbi__uint32; +typedef int32_t stbi__int32; +#endif + +// should produce compiler error if size is wrong +typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; + +#ifdef _MSC_VER +#define STBI_NOTUSED(v) (void)(v) +#else +#define STBI_NOTUSED(v) (void)sizeof(v) +#endif + +#ifdef _MSC_VER +#define STBI_HAS_LROTL +#endif + +#ifdef STBI_HAS_LROTL + #define stbi_lrot(x,y) _lrotl(x,y) +#else + #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) +#endif + +#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) +// ok +#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." +#endif + +#ifndef STBI_MALLOC +#define STBI_MALLOC(sz) malloc(sz) +#define STBI_REALLOC(p,newsz) realloc(p,newsz) +#define STBI_FREE(p) free(p) +#endif + +#ifndef STBI_REALLOC_SIZED +#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) +#endif + +// x86/x64 detection +#if defined(__x86_64__) || defined(_M_X64) +#define STBI__X64_TARGET +#elif defined(__i386) || defined(_M_IX86) +#define STBI__X86_TARGET +#endif + +#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) +// gcc doesn't support sse2 intrinsics unless you compile with -msse2, +// which in turn means it gets to use SSE2 everywhere. This is unfortunate, +// but previous attempts to provide the SSE2 functions with runtime +// detection caused numerous issues. The way architecture extensions are +// exposed in GCC/Clang is, sadly, not really suited for one-file libs. +// New behavior: if compiled with -msse2, we use SSE2 without any +// detection; if not, we don't use it at all. +#define STBI_NO_SIMD +#endif + +#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) +// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET +// +// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the +// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. +// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not +// simultaneously enabling "-mstackrealign". +// +// See https://github.com/nothings/stb/issues/81 for more information. +// +// So default to no SSE2 on 32-bit MinGW. If you've read this far and added +// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. +#define STBI_NO_SIMD +#endif + +#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) +#define STBI_SSE2 +#include + +#ifdef _MSC_VER + +#if _MSC_VER >= 1400 // not VC6 +#include // __cpuid +static int stbi__cpuid3(void) +{ + int info[4]; + __cpuid(info,1); + return info[3]; +} +#else +static int stbi__cpuid3(void) +{ + int res; + __asm { + mov eax,1 + cpuid + mov res,edx + } + return res; +} +#endif + +#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name + +static int stbi__sse2_available(void) +{ + int info3 = stbi__cpuid3(); + return ((info3 >> 26) & 1) != 0; +} +#else // assume GCC-style if not VC++ +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) + +static int stbi__sse2_available(void) +{ + // If we're even attempting to compile this on GCC/Clang, that means + // -msse2 is on, which means the compiler is allowed to use SSE2 + // instructions at will, and so are we. + return 1; +} +#endif +#endif + +// ARM NEON +#if defined(STBI_NO_SIMD) && defined(STBI_NEON) +#undef STBI_NEON +#endif + +#ifdef STBI_NEON +#include +// assume GCC or Clang on ARM targets +#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#endif + +#ifndef STBI_SIMD_ALIGN +#define STBI_SIMD_ALIGN(type, name) type name +#endif + +/////////////////////////////////////////////// +// +// stbi__context struct and start_xxx functions + +// stbi__context structure is our basic context used by all images, so it +// contains all the IO context, plus some basic image information +typedef struct +{ + stbi__uint32 img_x, img_y; + int img_n, img_out_n; + + stbi_io_callbacks io; + void *io_user_data; + + int read_from_callbacks; + int buflen; + stbi_uc buffer_start[128]; + + stbi_uc *img_buffer, *img_buffer_end; + stbi_uc *img_buffer_original, *img_buffer_original_end; +} stbi__context; + + +static void stbi__refill_buffer(stbi__context *s); + +// initialize a memory-decode context +static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) +{ + s->io.read = NULL; + s->read_from_callbacks = 0; + s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; + s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; +} + +// initialize a callback-based context +static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) +{ + s->io = *c; + s->io_user_data = user; + s->buflen = sizeof(s->buffer_start); + s->read_from_callbacks = 1; + s->img_buffer_original = s->buffer_start; + stbi__refill_buffer(s); + s->img_buffer_original_end = s->img_buffer_end; +} + +#ifndef STBI_NO_STDIO + +static int stbi__stdio_read(void *user, char *data, int size) +{ + return (int) fread(data,1,size,(FILE*) user); +} + +static void stbi__stdio_skip(void *user, int n) +{ + fseek((FILE*) user, n, SEEK_CUR); +} + +static int stbi__stdio_eof(void *user) +{ + return feof((FILE*) user); +} + +static stbi_io_callbacks stbi__stdio_callbacks = +{ + stbi__stdio_read, + stbi__stdio_skip, + stbi__stdio_eof, +}; + +static void stbi__start_file(stbi__context *s, FILE *f) +{ + stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); +} + +//static void stop_file(stbi__context *s) { } + +#endif // !STBI_NO_STDIO + +static void stbi__rewind(stbi__context *s) +{ + // conceptually rewind SHOULD rewind to the beginning of the stream, + // but we just rewind to the beginning of the initial buffer, because + // we only use it after doing 'test', which only ever looks at at most 92 bytes + s->img_buffer = s->img_buffer_original; + s->img_buffer_end = s->img_buffer_original_end; +} + +enum +{ + STBI_ORDER_RGB, + STBI_ORDER_BGR +}; + +typedef struct +{ + int bits_per_channel; + int num_channels; + int channel_order; +} stbi__result_info; + +#ifndef STBI_NO_JPEG +static int stbi__jpeg_test(stbi__context *s); +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNG +static int stbi__png_test(stbi__context *s); +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_BMP +static int stbi__bmp_test(stbi__context *s); +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_TGA +static int stbi__tga_test(stbi__context *s); +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s); +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_HDR +static int stbi__hdr_test(stbi__context *s); +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_test(stbi__context *s); +static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_GIF +static int stbi__gif_test(stbi__context *s); +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +#ifndef STBI_NO_PNM +static int stbi__pnm_test(stbi__context *s); +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); +#endif + +// this is not threadsafe +static const char *stbi__g_failure_reason; + +STBIDEF const char *stbi_failure_reason(void) +{ + return stbi__g_failure_reason; +} + +static int stbi__err(const char *str) +{ + stbi__g_failure_reason = str; + return 0; +} + +static void *stbi__malloc(size_t size) +{ + return STBI_MALLOC(size); +} + +// stb_image uses ints pervasively, including for offset calculations. +// therefore the largest decoded image size we can support with the +// current code, even on 64-bit targets, is INT_MAX. this is not a +// significant limitation for the intended use case. +// +// we do, however, need to make sure our size calculations don't +// overflow. hence a few helper functions for size calculations that +// multiply integers together, making sure that they're non-negative +// and no overflow occurs. + +// return 1 if the sum is valid, 0 on overflow. +// negative terms are considered invalid. +static int stbi__addsizes_valid(int a, int b) +{ + if (b < 0) return 0; + // now 0 <= b <= INT_MAX, hence also + // 0 <= INT_MAX - b <= INTMAX. + // And "a + b <= INT_MAX" (which might overflow) is the + // same as a <= INT_MAX - b (no overflow) + return a <= INT_MAX - b; +} + +// returns 1 if the product is valid, 0 on overflow. +// negative factors are considered invalid. +static int stbi__mul2sizes_valid(int a, int b) +{ + if (a < 0 || b < 0) return 0; + if (b == 0) return 1; // mul-by-0 is always safe + // portable way to check for no overflows in a*b + return a <= INT_MAX/b; +} + +// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow +static int stbi__mad2sizes_valid(int a, int b, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); +} + +// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow +static int stbi__mad3sizes_valid(int a, int b, int c, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__addsizes_valid(a*b*c, add); +} + +// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow +static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) +{ + return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && + stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); +} + +// mallocs with size overflow checking +static void *stbi__malloc_mad2(int a, int b, int add) +{ + if (!stbi__mad2sizes_valid(a, b, add)) return NULL; + return stbi__malloc(a*b + add); +} + +static void *stbi__malloc_mad3(int a, int b, int c, int add) +{ + if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; + return stbi__malloc(a*b*c + add); +} + +static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) +{ + if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; + return stbi__malloc(a*b*c*d + add); +} + +// stbi__err - error +// stbi__errpf - error returning pointer to float +// stbi__errpuc - error returning pointer to unsigned char + +#ifdef STBI_NO_FAILURE_STRINGS + #define stbi__err(x,y) 0 +#elif defined(STBI_FAILURE_USERMSG) + #define stbi__err(x,y) stbi__err(y) +#else + #define stbi__err(x,y) stbi__err(x) +#endif + +#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) +#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) + +STBIDEF void stbi_image_free(void *retval_from_stbi_load) +{ + STBI_FREE(retval_from_stbi_load); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); +#endif + +#ifndef STBI_NO_HDR +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); +#endif + +static int stbi__vertically_flip_on_load = 0; + +STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load = flag_true_if_should_flip; +} + +static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields + ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed + ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order + ri->num_channels = 0; + + #ifndef STBI_NO_JPEG + if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PNG + if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_BMP + if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_GIF + if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PSD + if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); + #endif + #ifndef STBI_NO_PIC + if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); + #endif + #ifndef STBI_NO_PNM + if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); + return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); + } + #endif + + #ifndef STBI_NO_TGA + // test tga last because it's a crappy test! + if (stbi__tga_test(s)) + return stbi__tga_load(s,x,y,comp,req_comp, ri); + #endif + + return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); +} + +static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi_uc *reduced; + + reduced = (stbi_uc *) stbi__malloc(img_len); + if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling + + STBI_FREE(orig); + return reduced; +} + +static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) +{ + int i; + int img_len = w * h * channels; + stbi__uint16 *enlarged; + + enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); + if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + + for (i = 0; i < img_len; ++i) + enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff + + STBI_FREE(orig); + return enlarged; +} + +static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) +{ + int row; + size_t bytes_per_row = (size_t)w * bytes_per_pixel; + stbi_uc temp[2048]; + stbi_uc *bytes = (stbi_uc *)image; + + for (row = 0; row < (h>>1); row++) { + stbi_uc *row0 = bytes + row*bytes_per_row; + stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; + // swap row0 with row1 + size_t bytes_left = bytes_per_row; + while (bytes_left) { + size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); + memcpy(temp, row0, bytes_copy); + memcpy(row0, row1, bytes_copy); + memcpy(row1, temp, bytes_copy); + row0 += bytes_copy; + row1 += bytes_copy; + bytes_left -= bytes_copy; + } + } +} + +static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); + + if (result == NULL) + return NULL; + + if (ri.bits_per_channel != 8) { + STBI_ASSERT(ri.bits_per_channel == 16); + result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 8; + } + + // @TODO: move stbi__convert_format to here + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); + } + + return (unsigned char *) result; +} + +static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + stbi__result_info ri; + void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); + + if (result == NULL) + return NULL; + + if (ri.bits_per_channel != 16) { + STBI_ASSERT(ri.bits_per_channel == 8); + result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); + ri.bits_per_channel = 16; + } + + // @TODO: move stbi__convert_format16 to here + // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision + + if (stbi__vertically_flip_on_load) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); + } + + return (stbi__uint16 *) result; +} + +#ifndef STBI_NO_HDR +static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) +{ + if (stbi__vertically_flip_on_load && result != NULL) { + int channels = req_comp ? req_comp : *comp; + stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); + } +} +#endif + +#ifndef STBI_NO_STDIO + +static FILE *stbi__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + + +STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + unsigned char *result; + if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__uint16 *result; + stbi__context s; + stbi__start_file(&s,f); + result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); + if (result) { + // need to 'unget' all the characters in the IO buffer + fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); + } + return result; +} + +STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + stbi__uint16 *result; + if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); + result = stbi_load_from_file_16(f,x,y,comp,req_comp); + fclose(f); + return result; +} + + +#endif //!STBI_NO_STDIO + +STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); + return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); +} + +STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_LINEAR +static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) +{ + unsigned char *data; + #ifndef STBI_NO_HDR + if (stbi__hdr_test(s)) { + stbi__result_info ri; + float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); + if (hdr_data) + stbi__float_postprocess(hdr_data,x,y,comp,req_comp); + return hdr_data; + } + #endif + data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); + if (data) + return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); + return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); +} + +STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} + +#ifndef STBI_NO_STDIO +STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) +{ + float *result; + FILE *f = stbi__fopen(filename, "rb"); + if (!f) return stbi__errpf("can't fopen", "Unable to open file"); + result = stbi_loadf_from_file(f,x,y,comp,req_comp); + fclose(f); + return result; +} + +STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) +{ + stbi__context s; + stbi__start_file(&s,f); + return stbi__loadf_main(&s,x,y,comp,req_comp); +} +#endif // !STBI_NO_STDIO + +#endif // !STBI_NO_LINEAR + +// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is +// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always +// reports false! + +STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(buffer); + STBI_NOTUSED(len); + return 0; + #endif +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_is_hdr (char const *filename) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result=0; + if (f) { + result = stbi_is_hdr_from_file(f); + fclose(f); + } + return result; +} + +STBIDEF int stbi_is_hdr_from_file(FILE *f) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_file(&s,f); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(f); + return 0; + #endif +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) +{ + #ifndef STBI_NO_HDR + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); + return stbi__hdr_test(&s); + #else + STBI_NOTUSED(clbk); + STBI_NOTUSED(user); + return 0; + #endif +} + +#ifndef STBI_NO_LINEAR +static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; + +STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } +STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } +#endif + +static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; + +STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } +STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } + + +////////////////////////////////////////////////////////////////////////////// +// +// Common code used by all image loaders +// + +enum +{ + STBI__SCAN_load=0, + STBI__SCAN_type, + STBI__SCAN_header +}; + +static void stbi__refill_buffer(stbi__context *s) +{ + int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + if (n == 0) { + // at end of file, treat same as if from memory, but need to handle case + // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file + s->read_from_callbacks = 0; + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start+1; + *s->img_buffer = 0; + } else { + s->img_buffer = s->buffer_start; + s->img_buffer_end = s->buffer_start + n; + } +} + +stbi_inline static stbi_uc stbi__get8(stbi__context *s) +{ + if (s->img_buffer < s->img_buffer_end) + return *s->img_buffer++; + if (s->read_from_callbacks) { + stbi__refill_buffer(s); + return *s->img_buffer++; + } + return 0; +} + +stbi_inline static int stbi__at_eof(stbi__context *s) +{ + if (s->io.read) { + if (!(s->io.eof)(s->io_user_data)) return 0; + // if feof() is true, check if buffer = end + // special case: we've only got the special 0 character at the end + if (s->read_from_callbacks == 0) return 1; + } + + return s->img_buffer >= s->img_buffer_end; +} + +static void stbi__skip(stbi__context *s, int n) +{ + if (n < 0) { + s->img_buffer = s->img_buffer_end; + return; + } + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + s->img_buffer = s->img_buffer_end; + (s->io.skip)(s->io_user_data, n - blen); + return; + } + } + s->img_buffer += n; +} + +static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) +{ + if (s->io.read) { + int blen = (int) (s->img_buffer_end - s->img_buffer); + if (blen < n) { + int res, count; + + memcpy(buffer, s->img_buffer, blen); + + count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); + res = (count == (n-blen)); + s->img_buffer = s->img_buffer_end; + return res; + } + } + + if (s->img_buffer+n <= s->img_buffer_end) { + memcpy(buffer, s->img_buffer, n); + s->img_buffer += n; + return 1; + } else + return 0; +} + +static int stbi__get16be(stbi__context *s) +{ + int z = stbi__get8(s); + return (z << 8) + stbi__get8(s); +} + +static stbi__uint32 stbi__get32be(stbi__context *s) +{ + stbi__uint32 z = stbi__get16be(s); + return (z << 16) + stbi__get16be(s); +} + +#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) +// nothing +#else +static int stbi__get16le(stbi__context *s) +{ + int z = stbi__get8(s); + return z + (stbi__get8(s) << 8); +} +#endif + +#ifndef STBI_NO_BMP +static stbi__uint32 stbi__get32le(stbi__context *s) +{ + stbi__uint32 z = stbi__get16le(s); + return z + (stbi__get16le(s) << 16); +} +#endif + +#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings + + +////////////////////////////////////////////////////////////////////////////// +// +// generic converter from built-in img_n to req_comp +// individual types do this automatically as much as possible (e.g. jpeg +// does all cases internally since it needs to colorspace convert anyway, +// and it never has alpha, so very few cases ). png can automatically +// interleave an alpha=255 channel, but falls back to this for other cases +// +// assume data buffer is malloced, so malloc a new one and free that one +// only failure mode is malloc failing + +static stbi_uc stbi__compute_y(int r, int g, int b) +{ + return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + unsigned char *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); + if (good == NULL) { + STBI_FREE(data); + return stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + unsigned char *src = data + j * x * img_n ; + unsigned char *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0], dest[1]=255; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; + default: STBI_ASSERT(0); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} + +static stbi__uint16 stbi__compute_y_16(int r, int g, int b) +{ + return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); +} + +static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) +{ + int i,j; + stbi__uint16 *good; + + if (req_comp == img_n) return data; + STBI_ASSERT(req_comp >= 1 && req_comp <= 4); + + good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); + if (good == NULL) { + STBI_FREE(data); + return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); + } + + for (j=0; j < (int) y; ++j) { + stbi__uint16 *src = data + j * x * img_n ; + stbi__uint16 *dest = good + j * x * req_comp; + + #define STBI__COMBO(a,b) ((a)*8+(b)) + #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) + // convert source image with img_n components to one with req_comp components; + // avoid switch per pixel, so use switch per scanline and massive macros + switch (STBI__COMBO(img_n, req_comp)) { + STBI__CASE(1,2) { dest[0]=src[0], dest[1]=0xffff; } break; + STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=0xffff; } break; + STBI__CASE(2,1) { dest[0]=src[0]; } break; + STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=0xffff; } break; + STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = 0xffff; } break; + STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; + default: STBI_ASSERT(0); + } + #undef STBI__CASE + } + + STBI_FREE(data); + return good; +} + +#ifndef STBI_NO_LINEAR +static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) +{ + int i,k,n; + float *output; + if (!data) return NULL; + output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); + } + if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; + } + STBI_FREE(data); + return output; +} +#endif + +#ifndef STBI_NO_HDR +#define stbi__float2int(x) ((int) (x)) +static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) +{ + int i,k,n; + stbi_uc *output; + if (!data) return NULL; + output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); + if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } + // compute number of non-alpha components + if (comp & 1) n = comp; else n = comp-1; + for (i=0; i < x*y; ++i) { + for (k=0; k < n; ++k) { + float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + if (k < comp) { + float z = data[i*comp+k] * 255 + 0.5f; + if (z < 0) z = 0; + if (z > 255) z = 255; + output[i*comp + k] = (stbi_uc) stbi__float2int(z); + } + } + STBI_FREE(data); + return output; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// "baseline" JPEG/JFIF decoder +// +// simple implementation +// - doesn't support delayed output of y-dimension +// - simple interface (only one output format: 8-bit interleaved RGB) +// - doesn't try to recover corrupt jpegs +// - doesn't allow partial loading, loading multiple at once +// - still fast on x86 (copying globals into locals doesn't help x86) +// - allocates lots of intermediate memory (full size of all components) +// - non-interleaved case requires this anyway +// - allows good upsampling (see next) +// high-quality +// - upsampled channels are bilinearly interpolated, even across blocks +// - quality integer IDCT derived from IJG's 'slow' +// performance +// - fast huffman; reasonable integer IDCT +// - some SIMD kernels for common paths on targets with SSE2/NEON +// - uses a lot of intermediate memory, could cache poorly + +#ifndef STBI_NO_JPEG + +// huffman decoding acceleration +#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache + +typedef struct +{ + stbi_uc fast[1 << FAST_BITS]; + // weirdly, repacking this into AoS is a 10% speed loss, instead of a win + stbi__uint16 code[256]; + stbi_uc values[256]; + stbi_uc size[257]; + unsigned int maxcode[18]; + int delta[17]; // old 'firstsymbol' - old 'firstcode' +} stbi__huffman; + +typedef struct +{ + stbi__context *s; + stbi__huffman huff_dc[4]; + stbi__huffman huff_ac[4]; + stbi__uint16 dequant[4][64]; + stbi__int16 fast_ac[4][1 << FAST_BITS]; + +// sizes for components, interleaved MCUs + int img_h_max, img_v_max; + int img_mcu_x, img_mcu_y; + int img_mcu_w, img_mcu_h; + +// definition of jpeg image component + struct + { + int id; + int h,v; + int tq; + int hd,ha; + int dc_pred; + + int x,y,w2,h2; + stbi_uc *data; + void *raw_data, *raw_coeff; + stbi_uc *linebuf; + short *coeff; // progressive only + int coeff_w, coeff_h; // number of 8x8 coefficient blocks + } img_comp[4]; + + stbi__uint32 code_buffer; // jpeg entropy-coded buffer + int code_bits; // number of valid bits + unsigned char marker; // marker seen while filling entropy buffer + int nomore; // flag if we saw a marker so must stop + + int progressive; + int spec_start; + int spec_end; + int succ_high; + int succ_low; + int eob_run; + int jfif; + int app14_color_transform; // Adobe APP14 tag + int rgb; + + int scan_n, order[4]; + int restart_interval, todo; + +// kernels + void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); + void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); + stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); +} stbi__jpeg; + +static int stbi__build_huffman(stbi__huffman *h, int *count) +{ + int i,j,k=0,code; + // build size list for each symbol (from JPEG spec) + for (i=0; i < 16; ++i) + for (j=0; j < count[i]; ++j) + h->size[k++] = (stbi_uc) (i+1); + h->size[k] = 0; + + // compute actual symbols (from jpeg spec) + code = 0; + k = 0; + for(j=1; j <= 16; ++j) { + // compute delta to add to code to compute symbol id + h->delta[j] = k - code; + if (h->size[k] == j) { + while (h->size[k] == j) + h->code[k++] = (stbi__uint16) (code++); + if (code-1 >= (1 << j)) return stbi__err("bad code lengths","Corrupt JPEG"); + } + // compute largest code + 1 for this size, preshifted as needed later + h->maxcode[j] = code << (16-j); + code <<= 1; + } + h->maxcode[j] = 0xffffffff; + + // build non-spec acceleration table; 255 is flag for not-accelerated + memset(h->fast, 255, 1 << FAST_BITS); + for (i=0; i < k; ++i) { + int s = h->size[i]; + if (s <= FAST_BITS) { + int c = h->code[i] << (FAST_BITS-s); + int m = 1 << (FAST_BITS-s); + for (j=0; j < m; ++j) { + h->fast[c+j] = (stbi_uc) i; + } + } + } + return 1; +} + +// build a table that decodes both magnitude and value of small ACs in +// one go. +static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) +{ + int i; + for (i=0; i < (1 << FAST_BITS); ++i) { + stbi_uc fast = h->fast[i]; + fast_ac[i] = 0; + if (fast < 255) { + int rs = h->values[fast]; + int run = (rs >> 4) & 15; + int magbits = rs & 15; + int len = h->size[fast]; + + if (magbits && len + magbits <= FAST_BITS) { + // magnitude code followed by receive_extend code + int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); + int m = 1 << (magbits - 1); + if (k < m) k += (~0U << magbits) + 1; + // if the result is small enough, we can fit it in fast_ac table + if (k >= -128 && k <= 127) + fast_ac[i] = (stbi__int16) ((k << 8) + (run << 4) + (len + magbits)); + } + } + } +} + +static void stbi__grow_buffer_unsafe(stbi__jpeg *j) +{ + do { + int b = j->nomore ? 0 : stbi__get8(j->s); + if (b == 0xff) { + int c = stbi__get8(j->s); + while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes + if (c != 0) { + j->marker = (unsigned char) c; + j->nomore = 1; + return; + } + } + j->code_buffer |= b << (24 - j->code_bits); + j->code_bits += 8; + } while (j->code_bits <= 24); +} + +// (1 << n) - 1 +static stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; + +// decode a jpeg huffman value from the bitstream +stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) +{ + unsigned int temp; + int c,k; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + // look at the top FAST_BITS and determine what symbol ID it is, + // if the code is <= FAST_BITS + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + k = h->fast[c]; + if (k < 255) { + int s = h->size[k]; + if (s > j->code_bits) + return -1; + j->code_buffer <<= s; + j->code_bits -= s; + return h->values[k]; + } + + // naive test is to shift the code_buffer down so k bits are + // valid, then test against maxcode. To speed this up, we've + // preshifted maxcode left so that it has (16-k) 0s at the + // end; in other words, regardless of the number of bits, it + // wants to be compared against something shifted to have 16; + // that way we don't need to shift inside the loop. + temp = j->code_buffer >> 16; + for (k=FAST_BITS+1 ; ; ++k) + if (temp < h->maxcode[k]) + break; + if (k == 17) { + // error! code not found + j->code_bits -= 16; + return -1; + } + + if (k > j->code_bits) + return -1; + + // convert the huffman code to the symbol id + c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; + STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); + + // convert the id to a symbol + j->code_bits -= k; + j->code_buffer <<= k; + return h->values[c]; +} + +// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); + + sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB + k = stbi_lrot(j->code_buffer, n); + STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k + (stbi__jbias[n] & ~sgn); +} + +// get some unsigned bits +stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) +{ + unsigned int k; + if (j->code_bits < n) stbi__grow_buffer_unsafe(j); + k = stbi_lrot(j->code_buffer, n); + j->code_buffer = k & ~stbi__bmask[n]; + k &= stbi__bmask[n]; + j->code_bits -= n; + return k; +} + +stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) +{ + unsigned int k; + if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); + k = j->code_buffer; + j->code_buffer <<= 1; + --j->code_bits; + return k & 0x80000000; +} + +// given a value that's at position X in the zigzag stream, +// where does it appear in the 8x8 matrix coded as row-major? +static stbi_uc stbi__jpeg_dezigzag[64+15] = +{ + 0, 1, 8, 16, 9, 2, 3, 10, + 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, + 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, + 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, + 53, 60, 61, 54, 47, 55, 62, 63, + // let corrupt input sample past end + 63, 63, 63, 63, 63, 63, 63, 63, + 63, 63, 63, 63, 63, 63, 63 +}; + +// decode one 64-entry block-- +static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) +{ + int diff,dc,k; + int t; + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + t = stbi__jpeg_huff_decode(j, hdc); + if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + + // 0 all the ac values now so we can do it 32-bits at a time + memset(data,0,64*sizeof(data[0])); + + diff = t ? stbi__extend_receive(j, t) : 0; + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc * dequant[0]); + + // decode AC components, see JPEG spec + k = 1; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) * dequant[zig]); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (rs != 0xf0) break; // end block + k += 16; + } else { + k += r; + // decode into unzigzag'd location + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); + } + } + } while (k < 64); + return 1; +} + +static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) +{ + int diff,dc; + int t; + if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + + if (j->succ_high == 0) { + // first scan for DC coefficient, must be first + memset(data,0,64*sizeof(data[0])); // 0 all the ac values now + t = stbi__jpeg_huff_decode(j, hdc); + diff = t ? stbi__extend_receive(j, t) : 0; + + dc = j->img_comp[b].dc_pred + diff; + j->img_comp[b].dc_pred = dc; + data[0] = (short) (dc << j->succ_low); + } else { + // refinement scan for DC coefficient + if (stbi__jpeg_get_bit(j)) + data[0] += (short) (1 << j->succ_low); + } + return 1; +} + +// @OPTIMIZE: store non-zigzagged during the decode passes, +// and only de-zigzag when dequantizing +static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) +{ + int k; + if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); + + if (j->succ_high == 0) { + int shift = j->succ_low; + + if (j->eob_run) { + --j->eob_run; + return 1; + } + + k = j->spec_start; + do { + unsigned int zig; + int c,r,s; + if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); + c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); + r = fac[c]; + if (r) { // fast-AC path + k += (r >> 4) & 15; // run + s = r & 15; // combined length + j->code_buffer <<= s; + j->code_bits -= s; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) ((r >> 8) << shift); + } else { + int rs = stbi__jpeg_huff_decode(j, hac); + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r); + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + --j->eob_run; + break; + } + k += 16; + } else { + k += r; + zig = stbi__jpeg_dezigzag[k++]; + data[zig] = (short) (stbi__extend_receive(j,s) << shift); + } + } + } while (k <= j->spec_end); + } else { + // refinement scan for these AC coefficients + + short bit = (short) (1 << j->succ_low); + + if (j->eob_run) { + --j->eob_run; + for (k = j->spec_start; k <= j->spec_end; ++k) { + short *p = &data[stbi__jpeg_dezigzag[k]]; + if (*p != 0) + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } + } else { + k = j->spec_start; + do { + int r,s; + int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh + if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); + s = rs & 15; + r = rs >> 4; + if (s == 0) { + if (r < 15) { + j->eob_run = (1 << r) - 1; + if (r) + j->eob_run += stbi__jpeg_get_bits(j, r); + r = 64; // force end of block + } else { + // r=15 s=0 should write 16 0s, so we just do + // a run of 15 0s and then write s (which is 0), + // so we don't have to do anything special here + } + } else { + if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); + // sign bit + if (stbi__jpeg_get_bit(j)) + s = bit; + else + s = -bit; + } + + // advance by r + while (k <= j->spec_end) { + short *p = &data[stbi__jpeg_dezigzag[k++]]; + if (*p != 0) { + if (stbi__jpeg_get_bit(j)) + if ((*p & bit)==0) { + if (*p > 0) + *p += bit; + else + *p -= bit; + } + } else { + if (r == 0) { + *p = (short) s; + break; + } + --r; + } + } + } while (k <= j->spec_end); + } + } + return 1; +} + +// take a -128..127 value and stbi__clamp it and convert to 0..255 +stbi_inline static stbi_uc stbi__clamp(int x) +{ + // trick to use a single test to catch both cases + if ((unsigned int) x > 255) { + if (x < 0) return 0; + if (x > 255) return 255; + } + return (stbi_uc) x; +} + +#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) +#define stbi__fsh(x) ((x) << 12) + +// derived from jidctint -- DCT_ISLOW +#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ + int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ + p2 = s2; \ + p3 = s6; \ + p1 = (p2+p3) * stbi__f2f(0.5411961f); \ + t2 = p1 + p3*stbi__f2f(-1.847759065f); \ + t3 = p1 + p2*stbi__f2f( 0.765366865f); \ + p2 = s0; \ + p3 = s4; \ + t0 = stbi__fsh(p2+p3); \ + t1 = stbi__fsh(p2-p3); \ + x0 = t0+t3; \ + x3 = t0-t3; \ + x1 = t1+t2; \ + x2 = t1-t2; \ + t0 = s7; \ + t1 = s5; \ + t2 = s3; \ + t3 = s1; \ + p3 = t0+t2; \ + p4 = t1+t3; \ + p1 = t0+t3; \ + p2 = t1+t2; \ + p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ + t0 = t0*stbi__f2f( 0.298631336f); \ + t1 = t1*stbi__f2f( 2.053119869f); \ + t2 = t2*stbi__f2f( 3.072711026f); \ + t3 = t3*stbi__f2f( 1.501321110f); \ + p1 = p5 + p1*stbi__f2f(-0.899976223f); \ + p2 = p5 + p2*stbi__f2f(-2.562915447f); \ + p3 = p3*stbi__f2f(-1.961570560f); \ + p4 = p4*stbi__f2f(-0.390180644f); \ + t3 += p1+p4; \ + t2 += p2+p3; \ + t1 += p2+p4; \ + t0 += p1+p3; + +static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) +{ + int i,val[64],*v=val; + stbi_uc *o; + short *d = data; + + // columns + for (i=0; i < 8; ++i,++d, ++v) { + // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing + if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 + && d[40]==0 && d[48]==0 && d[56]==0) { + // no shortcut 0 seconds + // (1|2|3|4|5|6|7)==0 0 seconds + // all separate -0.047 seconds + // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds + int dcterm = d[0] << 2; + v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; + } else { + STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) + // constants scaled things up by 1<<12; let's bring them back + // down, but keep 2 extra bits of precision + x0 += 512; x1 += 512; x2 += 512; x3 += 512; + v[ 0] = (x0+t3) >> 10; + v[56] = (x0-t3) >> 10; + v[ 8] = (x1+t2) >> 10; + v[48] = (x1-t2) >> 10; + v[16] = (x2+t1) >> 10; + v[40] = (x2-t1) >> 10; + v[24] = (x3+t0) >> 10; + v[32] = (x3-t0) >> 10; + } + } + + for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { + // no fast case since the first 1D IDCT spread components out + STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) + // constants scaled things up by 1<<12, plus we had 1<<2 from first + // loop, plus horizontal and vertical each scale by sqrt(8) so together + // we've got an extra 1<<3, so 1<<17 total we need to remove. + // so we want to round that, which means adding 0.5 * 1<<17, + // aka 65536. Also, we'll end up with -128 to 127 that we want + // to encode as 0..255 by adding 128, so we'll add that before the shift + x0 += 65536 + (128<<17); + x1 += 65536 + (128<<17); + x2 += 65536 + (128<<17); + x3 += 65536 + (128<<17); + // tried computing the shifts into temps, or'ing the temps to see + // if any were out of range, but that was slower + o[0] = stbi__clamp((x0+t3) >> 17); + o[7] = stbi__clamp((x0-t3) >> 17); + o[1] = stbi__clamp((x1+t2) >> 17); + o[6] = stbi__clamp((x1-t2) >> 17); + o[2] = stbi__clamp((x2+t1) >> 17); + o[5] = stbi__clamp((x2-t1) >> 17); + o[3] = stbi__clamp((x3+t0) >> 17); + o[4] = stbi__clamp((x3-t0) >> 17); + } +} + +#ifdef STBI_SSE2 +// sse2 integer IDCT. not the fastest possible implementation but it +// produces bit-identical results to the generic C version so it's +// fully "transparent". +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + // This is constructed to match our regular (generic) integer IDCT exactly. + __m128i row0, row1, row2, row3, row4, row5, row6, row7; + __m128i tmp; + + // dot product constant: even elems=x, odd elems=y + #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) + + // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) + // out(1) = c1[even]*x + c1[odd]*y + #define dct_rot(out0,out1, x,y,c0,c1) \ + __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ + __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ + __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ + __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ + __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ + __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) + + // out = in << 12 (in 16-bit, out 32-bit) + #define dct_widen(out, in) \ + __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ + __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) + + // wide add + #define dct_wadd(out, a, b) \ + __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_add_epi32(a##_h, b##_h) + + // wide sub + #define dct_wsub(out, a, b) \ + __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ + __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) + + // butterfly a/b, add bias, then shift by "s" and pack + #define dct_bfly32o(out0, out1, a,b,bias,s) \ + { \ + __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ + __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ + dct_wadd(sum, abiased, b); \ + dct_wsub(dif, abiased, b); \ + out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ + out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ + } + + // 8-bit interleave step (for transposes) + #define dct_interleave8(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi8(a, b); \ + b = _mm_unpackhi_epi8(tmp, b) + + // 16-bit interleave step (for transposes) + #define dct_interleave16(a, b) \ + tmp = a; \ + a = _mm_unpacklo_epi16(a, b); \ + b = _mm_unpackhi_epi16(tmp, b) + + #define dct_pass(bias,shift) \ + { \ + /* even part */ \ + dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ + __m128i sum04 = _mm_add_epi16(row0, row4); \ + __m128i dif04 = _mm_sub_epi16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ + dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ + __m128i sum17 = _mm_add_epi16(row1, row7); \ + __m128i sum35 = _mm_add_epi16(row3, row5); \ + dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ + dct_wadd(x4, y0o, y4o); \ + dct_wadd(x5, y1o, y5o); \ + dct_wadd(x6, y2o, y5o); \ + dct_wadd(x7, y3o, y4o); \ + dct_bfly32o(row0,row7, x0,x7,bias,shift); \ + dct_bfly32o(row1,row6, x1,x6,bias,shift); \ + dct_bfly32o(row2,row5, x2,x5,bias,shift); \ + dct_bfly32o(row3,row4, x3,x4,bias,shift); \ + } + + __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); + __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); + __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); + __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); + __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); + __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); + __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); + __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); + + // rounding biases in column/row passes, see stbi__idct_block for explanation. + __m128i bias_0 = _mm_set1_epi32(512); + __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); + + // load + row0 = _mm_load_si128((const __m128i *) (data + 0*8)); + row1 = _mm_load_si128((const __m128i *) (data + 1*8)); + row2 = _mm_load_si128((const __m128i *) (data + 2*8)); + row3 = _mm_load_si128((const __m128i *) (data + 3*8)); + row4 = _mm_load_si128((const __m128i *) (data + 4*8)); + row5 = _mm_load_si128((const __m128i *) (data + 5*8)); + row6 = _mm_load_si128((const __m128i *) (data + 6*8)); + row7 = _mm_load_si128((const __m128i *) (data + 7*8)); + + // column pass + dct_pass(bias_0, 10); + + { + // 16bit 8x8 transpose pass 1 + dct_interleave16(row0, row4); + dct_interleave16(row1, row5); + dct_interleave16(row2, row6); + dct_interleave16(row3, row7); + + // transpose pass 2 + dct_interleave16(row0, row2); + dct_interleave16(row1, row3); + dct_interleave16(row4, row6); + dct_interleave16(row5, row7); + + // transpose pass 3 + dct_interleave16(row0, row1); + dct_interleave16(row2, row3); + dct_interleave16(row4, row5); + dct_interleave16(row6, row7); + } + + // row pass + dct_pass(bias_1, 17); + + { + // pack + __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 + __m128i p1 = _mm_packus_epi16(row2, row3); + __m128i p2 = _mm_packus_epi16(row4, row5); + __m128i p3 = _mm_packus_epi16(row6, row7); + + // 8bit 8x8 transpose pass 1 + dct_interleave8(p0, p2); // a0e0a1e1... + dct_interleave8(p1, p3); // c0g0c1g1... + + // transpose pass 2 + dct_interleave8(p0, p1); // a0c0e0g0... + dct_interleave8(p2, p3); // b0d0f0h0... + + // transpose pass 3 + dct_interleave8(p0, p2); // a0b0c0d0... + dct_interleave8(p1, p3); // a4b4c4d4... + + // store + _mm_storel_epi64((__m128i *) out, p0); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p2); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p1); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; + _mm_storel_epi64((__m128i *) out, p3); out += out_stride; + _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); + } + +#undef dct_const +#undef dct_rot +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_interleave8 +#undef dct_interleave16 +#undef dct_pass +} + +#endif // STBI_SSE2 + +#ifdef STBI_NEON + +// NEON integer IDCT. should produce bit-identical +// results to the generic C version. +static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) +{ + int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; + + int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); + int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); + int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); + int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); + int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); + int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); + int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); + int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); + int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); + int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); + int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); + int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); + +#define dct_long_mul(out, inq, coeff) \ + int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) + +#define dct_long_mac(out, acc, inq, coeff) \ + int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ + int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) + +#define dct_widen(out, inq) \ + int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ + int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) + +// wide add +#define dct_wadd(out, a, b) \ + int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vaddq_s32(a##_h, b##_h) + +// wide sub +#define dct_wsub(out, a, b) \ + int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ + int32x4_t out##_h = vsubq_s32(a##_h, b##_h) + +// butterfly a/b, then shift using "shiftop" by "s" and pack +#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ + { \ + dct_wadd(sum, a, b); \ + dct_wsub(dif, a, b); \ + out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ + out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ + } + +#define dct_pass(shiftop, shift) \ + { \ + /* even part */ \ + int16x8_t sum26 = vaddq_s16(row2, row6); \ + dct_long_mul(p1e, sum26, rot0_0); \ + dct_long_mac(t2e, p1e, row6, rot0_1); \ + dct_long_mac(t3e, p1e, row2, rot0_2); \ + int16x8_t sum04 = vaddq_s16(row0, row4); \ + int16x8_t dif04 = vsubq_s16(row0, row4); \ + dct_widen(t0e, sum04); \ + dct_widen(t1e, dif04); \ + dct_wadd(x0, t0e, t3e); \ + dct_wsub(x3, t0e, t3e); \ + dct_wadd(x1, t1e, t2e); \ + dct_wsub(x2, t1e, t2e); \ + /* odd part */ \ + int16x8_t sum15 = vaddq_s16(row1, row5); \ + int16x8_t sum17 = vaddq_s16(row1, row7); \ + int16x8_t sum35 = vaddq_s16(row3, row5); \ + int16x8_t sum37 = vaddq_s16(row3, row7); \ + int16x8_t sumodd = vaddq_s16(sum17, sum35); \ + dct_long_mul(p5o, sumodd, rot1_0); \ + dct_long_mac(p1o, p5o, sum17, rot1_1); \ + dct_long_mac(p2o, p5o, sum35, rot1_2); \ + dct_long_mul(p3o, sum37, rot2_0); \ + dct_long_mul(p4o, sum15, rot2_1); \ + dct_wadd(sump13o, p1o, p3o); \ + dct_wadd(sump24o, p2o, p4o); \ + dct_wadd(sump23o, p2o, p3o); \ + dct_wadd(sump14o, p1o, p4o); \ + dct_long_mac(x4, sump13o, row7, rot3_0); \ + dct_long_mac(x5, sump24o, row5, rot3_1); \ + dct_long_mac(x6, sump23o, row3, rot3_2); \ + dct_long_mac(x7, sump14o, row1, rot3_3); \ + dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ + dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ + dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ + dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ + } + + // load + row0 = vld1q_s16(data + 0*8); + row1 = vld1q_s16(data + 1*8); + row2 = vld1q_s16(data + 2*8); + row3 = vld1q_s16(data + 3*8); + row4 = vld1q_s16(data + 4*8); + row5 = vld1q_s16(data + 5*8); + row6 = vld1q_s16(data + 6*8); + row7 = vld1q_s16(data + 7*8); + + // add DC bias + row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); + + // column pass + dct_pass(vrshrn_n_s32, 10); + + // 16bit 8x8 transpose + { +// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. +// whether compilers actually get this is another story, sadly. +#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } +#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } + + // pass 1 + dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 + dct_trn16(row2, row3); + dct_trn16(row4, row5); + dct_trn16(row6, row7); + + // pass 2 + dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 + dct_trn32(row1, row3); + dct_trn32(row4, row6); + dct_trn32(row5, row7); + + // pass 3 + dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 + dct_trn64(row1, row5); + dct_trn64(row2, row6); + dct_trn64(row3, row7); + +#undef dct_trn16 +#undef dct_trn32 +#undef dct_trn64 + } + + // row pass + // vrshrn_n_s32 only supports shifts up to 16, we need + // 17. so do a non-rounding shift of 16 first then follow + // up with a rounding shift by 1. + dct_pass(vshrn_n_s32, 16); + + { + // pack and round + uint8x8_t p0 = vqrshrun_n_s16(row0, 1); + uint8x8_t p1 = vqrshrun_n_s16(row1, 1); + uint8x8_t p2 = vqrshrun_n_s16(row2, 1); + uint8x8_t p3 = vqrshrun_n_s16(row3, 1); + uint8x8_t p4 = vqrshrun_n_s16(row4, 1); + uint8x8_t p5 = vqrshrun_n_s16(row5, 1); + uint8x8_t p6 = vqrshrun_n_s16(row6, 1); + uint8x8_t p7 = vqrshrun_n_s16(row7, 1); + + // again, these can translate into one instruction, but often don't. +#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } +#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } +#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } + + // sadly can't use interleaved stores here since we only write + // 8 bytes to each scan line! + + // 8x8 8-bit transpose pass 1 + dct_trn8_8(p0, p1); + dct_trn8_8(p2, p3); + dct_trn8_8(p4, p5); + dct_trn8_8(p6, p7); + + // pass 2 + dct_trn8_16(p0, p2); + dct_trn8_16(p1, p3); + dct_trn8_16(p4, p6); + dct_trn8_16(p5, p7); + + // pass 3 + dct_trn8_32(p0, p4); + dct_trn8_32(p1, p5); + dct_trn8_32(p2, p6); + dct_trn8_32(p3, p7); + + // store + vst1_u8(out, p0); out += out_stride; + vst1_u8(out, p1); out += out_stride; + vst1_u8(out, p2); out += out_stride; + vst1_u8(out, p3); out += out_stride; + vst1_u8(out, p4); out += out_stride; + vst1_u8(out, p5); out += out_stride; + vst1_u8(out, p6); out += out_stride; + vst1_u8(out, p7); + +#undef dct_trn8_8 +#undef dct_trn8_16 +#undef dct_trn8_32 + } + +#undef dct_long_mul +#undef dct_long_mac +#undef dct_widen +#undef dct_wadd +#undef dct_wsub +#undef dct_bfly32o +#undef dct_pass +} + +#endif // STBI_NEON + +#define STBI__MARKER_none 0xff +// if there's a pending marker from the entropy stream, return that +// otherwise, fetch from the stream and get a marker. if there's no +// marker, return 0xff, which is never a valid marker value +static stbi_uc stbi__get_marker(stbi__jpeg *j) +{ + stbi_uc x; + if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } + x = stbi__get8(j->s); + if (x != 0xff) return STBI__MARKER_none; + while (x == 0xff) + x = stbi__get8(j->s); // consume repeated 0xff fill bytes + return x; +} + +// in each scan, we'll have scan_n components, and the order +// of the components is specified by order[] +#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) + +// after a restart interval, stbi__jpeg_reset the entropy decoder and +// the dc prediction +static void stbi__jpeg_reset(stbi__jpeg *j) +{ + j->code_bits = 0; + j->code_buffer = 0; + j->nomore = 0; + j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; + j->marker = STBI__MARKER_none; + j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; + j->eob_run = 0; + // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, + // since we don't even allow 1<<30 pixels +} + +static int stbi__parse_entropy_coded_data(stbi__jpeg *z) +{ + stbi__jpeg_reset(z); + if (!z->progressive) { + if (z->scan_n == 1) { + int i,j; + STBI_SIMD_ALIGN(short, data[64]); + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + // if it's NOT a restart, then just bail, so we get corrupt data + // rather than no data + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + STBI_SIMD_ALIGN(short, data[64]); + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x)*8; + int y2 = (j*z->img_comp[n].v + y)*8; + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } else { + if (z->scan_n == 1) { + int i,j; + int n = z->order[0]; + // non-interleaved data, we just need to process one block at a time, + // in trivial scanline order + // number of blocks to do just depends on how many actual "pixels" this + // component has, independent of interleaved MCU blocking and such + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + if (z->spec_start == 0) { + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } else { + int ha = z->img_comp[n].ha; + if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) + return 0; + } + // every data block is an MCU, so countdown the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } else { // interleaved + int i,j,k,x,y; + for (j=0; j < z->img_mcu_y; ++j) { + for (i=0; i < z->img_mcu_x; ++i) { + // scan an interleaved mcu... process scan_n components in order + for (k=0; k < z->scan_n; ++k) { + int n = z->order[k]; + // scan out an mcu's worth of this component; that's just determined + // by the basic H and V specified for the component + for (y=0; y < z->img_comp[n].v; ++y) { + for (x=0; x < z->img_comp[n].h; ++x) { + int x2 = (i*z->img_comp[n].h + x); + int y2 = (j*z->img_comp[n].v + y); + short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); + if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) + return 0; + } + } + } + // after all interleaved components, that's an interleaved MCU, + // so now count down the restart interval + if (--z->todo <= 0) { + if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); + if (!STBI__RESTART(z->marker)) return 1; + stbi__jpeg_reset(z); + } + } + } + return 1; + } + } +} + +static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) +{ + int i; + for (i=0; i < 64; ++i) + data[i] *= dequant[i]; +} + +static void stbi__jpeg_finish(stbi__jpeg *z) +{ + if (z->progressive) { + // dequantize and idct the data + int i,j,n; + for (n=0; n < z->s->img_n; ++n) { + int w = (z->img_comp[n].x+7) >> 3; + int h = (z->img_comp[n].y+7) >> 3; + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) { + short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); + stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); + z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); + } + } + } + } +} + +static int stbi__process_marker(stbi__jpeg *z, int m) +{ + int L; + switch (m) { + case STBI__MARKER_none: // no marker found + return stbi__err("expected marker","Corrupt JPEG"); + + case 0xDD: // DRI - specify restart interval + if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); + z->restart_interval = stbi__get16be(z->s); + return 1; + + case 0xDB: // DQT - define quantization table + L = stbi__get16be(z->s)-2; + while (L > 0) { + int q = stbi__get8(z->s); + int p = q >> 4, sixteen = (p != 0); + int t = q & 15,i; + if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); + if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); + + for (i=0; i < 64; ++i) + z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); + L -= (sixteen ? 129 : 65); + } + return L==0; + + case 0xC4: // DHT - define huffman table + L = stbi__get16be(z->s)-2; + while (L > 0) { + stbi_uc *v; + int sizes[16],i,n=0; + int q = stbi__get8(z->s); + int tc = q >> 4; + int th = q & 15; + if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); + for (i=0; i < 16; ++i) { + sizes[i] = stbi__get8(z->s); + n += sizes[i]; + } + L -= 17; + if (tc == 0) { + if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; + v = z->huff_dc[th].values; + } else { + if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; + v = z->huff_ac[th].values; + } + for (i=0; i < n; ++i) + v[i] = stbi__get8(z->s); + if (tc != 0) + stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); + L -= n; + } + return L==0; + } + + // check for comment block or APP blocks + if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { + L = stbi__get16be(z->s); + if (L < 2) { + if (m == 0xFE) + return stbi__err("bad COM len","Corrupt JPEG"); + else + return stbi__err("bad APP len","Corrupt JPEG"); + } + L -= 2; + + if (m == 0xE0 && L >= 5) { // JFIF APP0 segment + static const unsigned char tag[5] = {'J','F','I','F','\0'}; + int ok = 1; + int i; + for (i=0; i < 5; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 5; + if (ok) + z->jfif = 1; + } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment + static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; + int ok = 1; + int i; + for (i=0; i < 6; ++i) + if (stbi__get8(z->s) != tag[i]) + ok = 0; + L -= 6; + if (ok) { + stbi__get8(z->s); // version + stbi__get16be(z->s); // flags0 + stbi__get16be(z->s); // flags1 + z->app14_color_transform = stbi__get8(z->s); // color transform + L -= 6; + } + } + + stbi__skip(z->s, L); + return 1; + } + + return stbi__err("unknown marker","Corrupt JPEG"); +} + +// after we see SOS +static int stbi__process_scan_header(stbi__jpeg *z) +{ + int i; + int Ls = stbi__get16be(z->s); + z->scan_n = stbi__get8(z->s); + if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); + if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); + for (i=0; i < z->scan_n; ++i) { + int id = stbi__get8(z->s), which; + int q = stbi__get8(z->s); + for (which = 0; which < z->s->img_n; ++which) + if (z->img_comp[which].id == id) + break; + if (which == z->s->img_n) return 0; // no match + z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); + z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); + z->order[i] = which; + } + + { + int aa; + z->spec_start = stbi__get8(z->s); + z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 + aa = stbi__get8(z->s); + z->succ_high = (aa >> 4); + z->succ_low = (aa & 15); + if (z->progressive) { + if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) + return stbi__err("bad SOS", "Corrupt JPEG"); + } else { + if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); + if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); + z->spec_end = 63; + } + } + + return 1; +} + +static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) +{ + int i; + for (i=0; i < ncomp; ++i) { + if (z->img_comp[i].raw_data) { + STBI_FREE(z->img_comp[i].raw_data); + z->img_comp[i].raw_data = NULL; + z->img_comp[i].data = NULL; + } + if (z->img_comp[i].raw_coeff) { + STBI_FREE(z->img_comp[i].raw_coeff); + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].coeff = 0; + } + if (z->img_comp[i].linebuf) { + STBI_FREE(z->img_comp[i].linebuf); + z->img_comp[i].linebuf = NULL; + } + } + return why; +} + +static int stbi__process_frame_header(stbi__jpeg *z, int scan) +{ + stbi__context *s = z->s; + int Lf,p,i,q, h_max=1,v_max=1,c; + Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG + p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline + s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG + s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + c = stbi__get8(s); + if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); + s->img_n = c; + for (i=0; i < c; ++i) { + z->img_comp[i].data = NULL; + z->img_comp[i].linebuf = NULL; + } + + if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); + + z->rgb = 0; + for (i=0; i < s->img_n; ++i) { + static unsigned char rgb[3] = { 'R', 'G', 'B' }; + z->img_comp[i].id = stbi__get8(s); + if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) + ++z->rgb; + q = stbi__get8(s); + z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); + z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); + z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); + } + + if (scan != STBI__SCAN_load) return 1; + + if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); + + for (i=0; i < s->img_n; ++i) { + if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; + if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; + } + + // compute interleaved mcu info + z->img_h_max = h_max; + z->img_v_max = v_max; + z->img_mcu_w = h_max * 8; + z->img_mcu_h = v_max * 8; + // these sizes can't be more than 17 bits + z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; + z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; + + for (i=0; i < s->img_n; ++i) { + // number of effective pixels (e.g. for non-interleaved MCU) + z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; + z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; + // to simplify generation, we'll allocate enough memory to decode + // the bogus oversized data from using interleaved MCUs and their + // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't + // discard the extra data until colorspace conversion + // + // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) + // so these muls can't overflow with 32-bit ints (which we require) + z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; + z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; + z->img_comp[i].coeff = 0; + z->img_comp[i].raw_coeff = 0; + z->img_comp[i].linebuf = NULL; + z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); + if (z->img_comp[i].raw_data == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + // align blocks for idct using mmx/sse + z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); + if (z->progressive) { + // w2, h2 are multiples of 8 (see above) + z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; + z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; + z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); + if (z->img_comp[i].raw_coeff == NULL) + return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); + z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); + } + } + + return 1; +} + +// use comparisons since in some cases we handle more than one case (e.g. SOF) +#define stbi__DNL(x) ((x) == 0xdc) +#define stbi__SOI(x) ((x) == 0xd8) +#define stbi__EOI(x) ((x) == 0xd9) +#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) +#define stbi__SOS(x) ((x) == 0xda) + +#define stbi__SOF_progressive(x) ((x) == 0xc2) + +static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) +{ + int m; + z->jfif = 0; + z->app14_color_transform = -1; // valid values are 0,1,2 + z->marker = STBI__MARKER_none; // initialize cached marker to empty + m = stbi__get_marker(z); + if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); + if (scan == STBI__SCAN_type) return 1; + m = stbi__get_marker(z); + while (!stbi__SOF(m)) { + if (!stbi__process_marker(z,m)) return 0; + m = stbi__get_marker(z); + while (m == STBI__MARKER_none) { + // some files have extra padding after their blocks, so ok, we'll scan + if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); + m = stbi__get_marker(z); + } + } + z->progressive = stbi__SOF_progressive(m); + if (!stbi__process_frame_header(z, scan)) return 0; + return 1; +} + +// decode image to YCbCr format +static int stbi__decode_jpeg_image(stbi__jpeg *j) +{ + int m; + for (m = 0; m < 4; m++) { + j->img_comp[m].raw_data = NULL; + j->img_comp[m].raw_coeff = NULL; + } + j->restart_interval = 0; + if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; + m = stbi__get_marker(j); + while (!stbi__EOI(m)) { + if (stbi__SOS(m)) { + if (!stbi__process_scan_header(j)) return 0; + if (!stbi__parse_entropy_coded_data(j)) return 0; + if (j->marker == STBI__MARKER_none ) { + // handle 0s at the end of image data from IP Kamera 9060 + while (!stbi__at_eof(j->s)) { + int x = stbi__get8(j->s); + if (x == 255) { + j->marker = stbi__get8(j->s); + break; + } + } + // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 + } + } else if (stbi__DNL(m)) { + int Ld = stbi__get16be(j->s); + stbi__uint32 NL = stbi__get16be(j->s); + if (Ld != 4) stbi__err("bad DNL len", "Corrupt JPEG"); + if (NL != j->s->img_y) stbi__err("bad DNL height", "Corrupt JPEG"); + } else { + if (!stbi__process_marker(j, m)) return 0; + } + m = stbi__get_marker(j); + } + if (j->progressive) + stbi__jpeg_finish(j); + return 1; +} + +// static jfif-centered resampling (across block boundaries) + +typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, + int w, int hs); + +#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) + +static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + STBI_NOTUSED(out); + STBI_NOTUSED(in_far); + STBI_NOTUSED(w); + STBI_NOTUSED(hs); + return in_near; +} + +static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples vertically for every one in input + int i; + STBI_NOTUSED(hs); + for (i=0; i < w; ++i) + out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); + return out; +} + +static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate two samples horizontally for every one in input + int i; + stbi_uc *input = in_near; + + if (w == 1) { + // if only one sample, can't do any interpolation + out[0] = out[1] = input[0]; + return out; + } + + out[0] = input[0]; + out[1] = stbi__div4(input[0]*3 + input[1] + 2); + for (i=1; i < w-1; ++i) { + int n = 3*input[i]+2; + out[i*2+0] = stbi__div4(n+input[i-1]); + out[i*2+1] = stbi__div4(n+input[i+1]); + } + out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); + out[i*2+1] = input[w-1]; + + STBI_NOTUSED(in_far); + STBI_NOTUSED(hs); + + return out; +} + +#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) + +static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i,t0,t1; + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + out[0] = stbi__div4(t1+2); + for (i=1; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // need to generate 2x2 samples for every one in input + int i=0,t0,t1; + + if (w == 1) { + out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); + return out; + } + + t1 = 3*in_near[0] + in_far[0]; + // process groups of 8 pixels for as long as we can. + // note we can't handle the last pixel in a row in this loop + // because we need to handle the filter boundary conditions. + for (; i < ((w-1) & ~7); i += 8) { +#if defined(STBI_SSE2) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + __m128i zero = _mm_setzero_si128(); + __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); + __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); + __m128i farw = _mm_unpacklo_epi8(farb, zero); + __m128i nearw = _mm_unpacklo_epi8(nearb, zero); + __m128i diff = _mm_sub_epi16(farw, nearw); + __m128i nears = _mm_slli_epi16(nearw, 2); + __m128i curr = _mm_add_epi16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + __m128i prv0 = _mm_slli_si128(curr, 2); + __m128i nxt0 = _mm_srli_si128(curr, 2); + __m128i prev = _mm_insert_epi16(prv0, t1, 0); + __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + __m128i bias = _mm_set1_epi16(8); + __m128i curs = _mm_slli_epi16(curr, 2); + __m128i prvd = _mm_sub_epi16(prev, curr); + __m128i nxtd = _mm_sub_epi16(next, curr); + __m128i curb = _mm_add_epi16(curs, bias); + __m128i even = _mm_add_epi16(prvd, curb); + __m128i odd = _mm_add_epi16(nxtd, curb); + + // interleave even and odd pixels, then undo scaling. + __m128i int0 = _mm_unpacklo_epi16(even, odd); + __m128i int1 = _mm_unpackhi_epi16(even, odd); + __m128i de0 = _mm_srli_epi16(int0, 4); + __m128i de1 = _mm_srli_epi16(int1, 4); + + // pack and write output + __m128i outv = _mm_packus_epi16(de0, de1); + _mm_storeu_si128((__m128i *) (out + i*2), outv); +#elif defined(STBI_NEON) + // load and perform the vertical filtering pass + // this uses 3*x + y = 4*x + (y - x) + uint8x8_t farb = vld1_u8(in_far + i); + uint8x8_t nearb = vld1_u8(in_near + i); + int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); + int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); + int16x8_t curr = vaddq_s16(nears, diff); // current row + + // horizontal filter works the same based on shifted vers of current + // row. "prev" is current row shifted right by 1 pixel; we need to + // insert the previous pixel value (from t1). + // "next" is current row shifted left by 1 pixel, with first pixel + // of next block of 8 pixels added in. + int16x8_t prv0 = vextq_s16(curr, curr, 7); + int16x8_t nxt0 = vextq_s16(curr, curr, 1); + int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); + int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); + + // horizontal filter, polyphase implementation since it's convenient: + // even pixels = 3*cur + prev = cur*4 + (prev - cur) + // odd pixels = 3*cur + next = cur*4 + (next - cur) + // note the shared term. + int16x8_t curs = vshlq_n_s16(curr, 2); + int16x8_t prvd = vsubq_s16(prev, curr); + int16x8_t nxtd = vsubq_s16(next, curr); + int16x8_t even = vaddq_s16(curs, prvd); + int16x8_t odd = vaddq_s16(curs, nxtd); + + // undo scaling and round, then store with even/odd phases interleaved + uint8x8x2_t o; + o.val[0] = vqrshrun_n_s16(even, 4); + o.val[1] = vqrshrun_n_s16(odd, 4); + vst2_u8(out + i*2, o); +#endif + + // "previous" value for next iter + t1 = 3*in_near[i+7] + in_far[i+7]; + } + + t0 = t1; + t1 = 3*in_near[i] + in_far[i]; + out[i*2] = stbi__div16(3*t1 + t0 + 8); + + for (++i; i < w; ++i) { + t0 = t1; + t1 = 3*in_near[i]+in_far[i]; + out[i*2-1] = stbi__div16(3*t0 + t1 + 8); + out[i*2 ] = stbi__div16(3*t1 + t0 + 8); + } + out[w*2-1] = stbi__div4(t1+2); + + STBI_NOTUSED(hs); + + return out; +} +#endif + +static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) +{ + // resample with nearest-neighbor + int i,j; + STBI_NOTUSED(in_far); + for (i=0; i < w; ++i) + for (j=0; j < hs; ++j) + out[i*hs+j] = in_near[i]; + return out; +} + +// this is a reduced-precision calculation of YCbCr-to-RGB introduced +// to make sure the code produces the same results in both SIMD and scalar +#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) +static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) +{ + int i; + for (i=0; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} + +#if defined(STBI_SSE2) || defined(STBI_NEON) +static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) +{ + int i = 0; + +#ifdef STBI_SSE2 + // step == 3 is pretty ugly on the final interleave, and i'm not convinced + // it's useful in practice (you wouldn't use it for textures, for example). + // so just accelerate step == 4 case. + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + __m128i signflip = _mm_set1_epi8(-0x80); + __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); + __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); + __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); + __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); + __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); + __m128i xw = _mm_set1_epi16(255); // alpha channel + + for (; i+7 < count; i += 8) { + // load + __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); + __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); + __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); + __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 + __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 + + // unpack to short (and left-shift cr, cb by 8) + __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); + __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); + __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); + + // color transform + __m128i yws = _mm_srli_epi16(yw, 4); + __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); + __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); + __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); + __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); + __m128i rws = _mm_add_epi16(cr0, yws); + __m128i gwt = _mm_add_epi16(cb0, yws); + __m128i bws = _mm_add_epi16(yws, cb1); + __m128i gws = _mm_add_epi16(gwt, cr1); + + // descale + __m128i rw = _mm_srai_epi16(rws, 4); + __m128i bw = _mm_srai_epi16(bws, 4); + __m128i gw = _mm_srai_epi16(gws, 4); + + // back to byte, set up for transpose + __m128i brb = _mm_packus_epi16(rw, bw); + __m128i gxb = _mm_packus_epi16(gw, xw); + + // transpose to interleave channels + __m128i t0 = _mm_unpacklo_epi8(brb, gxb); + __m128i t1 = _mm_unpackhi_epi8(brb, gxb); + __m128i o0 = _mm_unpacklo_epi16(t0, t1); + __m128i o1 = _mm_unpackhi_epi16(t0, t1); + + // store + _mm_storeu_si128((__m128i *) (out + 0), o0); + _mm_storeu_si128((__m128i *) (out + 16), o1); + out += 32; + } + } +#endif + +#ifdef STBI_NEON + // in this version, step=3 support would be easy to add. but is there demand? + if (step == 4) { + // this is a fairly straightforward implementation and not super-optimized. + uint8x8_t signflip = vdup_n_u8(0x80); + int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); + int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); + int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); + int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); + + for (; i+7 < count; i += 8) { + // load + uint8x8_t y_bytes = vld1_u8(y + i); + uint8x8_t cr_bytes = vld1_u8(pcr + i); + uint8x8_t cb_bytes = vld1_u8(pcb + i); + int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); + int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); + + // expand to s16 + int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); + int16x8_t crw = vshll_n_s8(cr_biased, 7); + int16x8_t cbw = vshll_n_s8(cb_biased, 7); + + // color transform + int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); + int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); + int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); + int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); + int16x8_t rws = vaddq_s16(yws, cr0); + int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); + int16x8_t bws = vaddq_s16(yws, cb1); + + // undo scaling, round, convert to byte + uint8x8x4_t o; + o.val[0] = vqrshrun_n_s16(rws, 4); + o.val[1] = vqrshrun_n_s16(gws, 4); + o.val[2] = vqrshrun_n_s16(bws, 4); + o.val[3] = vdup_n_u8(255); + + // store, interleaving r/g/b/a + vst4_u8(out, o); + out += 8*4; + } + } +#endif + + for (; i < count; ++i) { + int y_fixed = (y[i] << 20) + (1<<19); // rounding + int r,g,b; + int cr = pcr[i] - 128; + int cb = pcb[i] - 128; + r = y_fixed + cr* stbi__float2fixed(1.40200f); + g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); + b = y_fixed + cb* stbi__float2fixed(1.77200f); + r >>= 20; + g >>= 20; + b >>= 20; + if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } + if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } + if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } + out[0] = (stbi_uc)r; + out[1] = (stbi_uc)g; + out[2] = (stbi_uc)b; + out[3] = 255; + out += step; + } +} +#endif + +// set up the kernels +static void stbi__setup_jpeg(stbi__jpeg *j) +{ + j->idct_block_kernel = stbi__idct_block; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; + +#ifdef STBI_SSE2 + if (stbi__sse2_available()) { + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; + } +#endif + +#ifdef STBI_NEON + j->idct_block_kernel = stbi__idct_simd; + j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; + j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; +#endif +} + +// clean up the temporary component buffers +static void stbi__cleanup_jpeg(stbi__jpeg *j) +{ + stbi__free_jpeg_components(j, j->s->img_n, 0); +} + +typedef struct +{ + resample_row_func resample; + stbi_uc *line0,*line1; + int hs,vs; // expansion factor in each axis + int w_lores; // horizontal pixels pre-expansion + int ystep; // how far through vertical expansion we are + int ypos; // which pre-expansion row we're on +} stbi__resample; + +// fast 0..255 * 0..255 => 0..255 rounded multiplication +static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) +{ + unsigned int t = x*y + 128; + return (stbi_uc) ((t + (t >>8)) >> 8); +} + +static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) +{ + int n, decode_n, is_rgb; + z->s->img_n = 0; // make stbi__cleanup_jpeg safe + + // validate req_comp + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + + // load a jpeg image from whichever source, but leave in YCbCr format + if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } + + // determine actual number of components to generate + n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; + + is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); + + if (z->s->img_n == 3 && n < 3 && !is_rgb) + decode_n = 1; + else + decode_n = z->s->img_n; + + // resample and color-convert + { + int k; + unsigned int i,j; + stbi_uc *output; + stbi_uc *coutput[4]; + + stbi__resample res_comp[4]; + + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + + // allocate line buffer big enough for upsampling off the edges + // with upsample factor of 4 + z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); + if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + r->hs = z->img_h_max / z->img_comp[k].h; + r->vs = z->img_v_max / z->img_comp[k].v; + r->ystep = r->vs >> 1; + r->w_lores = (z->s->img_x + r->hs-1) / r->hs; + r->ypos = 0; + r->line0 = r->line1 = z->img_comp[k].data; + + if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; + else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; + else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; + else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; + else r->resample = stbi__resample_row_generic; + } + + // can't error after this so, this is safe + output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); + if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } + + // now go ahead and resample + for (j=0; j < z->s->img_y; ++j) { + stbi_uc *out = output + n * z->s->img_x * j; + for (k=0; k < decode_n; ++k) { + stbi__resample *r = &res_comp[k]; + int y_bot = r->ystep >= (r->vs >> 1); + coutput[k] = r->resample(z->img_comp[k].linebuf, + y_bot ? r->line1 : r->line0, + y_bot ? r->line0 : r->line1, + r->w_lores, r->hs); + if (++r->ystep >= r->vs) { + r->ystep = 0; + r->line0 = r->line1; + if (++r->ypos < z->img_comp[k].y) + r->line1 += z->img_comp[k].w2; + } + } + if (n >= 3) { + stbi_uc *y = coutput[0]; + if (z->s->img_n == 3) { + if (is_rgb) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = y[i]; + out[1] = coutput[1][i]; + out[2] = coutput[2][i]; + out[3] = 255; + out += n; + } + } else { + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else if (z->s->img_n == 4) { + if (z->app14_color_transform == 0) { // CMYK + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(coutput[0][i], m); + out[1] = stbi__blinn_8x8(coutput[1][i], m); + out[2] = stbi__blinn_8x8(coutput[2][i], m); + out[3] = 255; + out += n; + } + } else if (z->app14_color_transform == 2) { // YCCK + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + out[0] = stbi__blinn_8x8(255 - out[0], m); + out[1] = stbi__blinn_8x8(255 - out[1], m); + out[2] = stbi__blinn_8x8(255 - out[2], m); + out += n; + } + } else { // YCbCr + alpha? Ignore the fourth channel for now + z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); + } + } else + for (i=0; i < z->s->img_x; ++i) { + out[0] = out[1] = out[2] = y[i]; + out[3] = 255; // not used if n==3 + out += n; + } + } else { + if (is_rgb) { + if (n == 1) + for (i=0; i < z->s->img_x; ++i) + *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + else { + for (i=0; i < z->s->img_x; ++i, out += 2) { + out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); + out[1] = 255; + } + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { + for (i=0; i < z->s->img_x; ++i) { + stbi_uc m = coutput[3][i]; + stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); + stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); + stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); + out[0] = stbi__compute_y(r, g, b); + out[1] = 255; + out += n; + } + } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { + for (i=0; i < z->s->img_x; ++i) { + out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); + out[1] = 255; + out += n; + } + } else { + stbi_uc *y = coutput[0]; + if (n == 1) + for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; + else + for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; + } + } + } + stbi__cleanup_jpeg(z); + *out_x = z->s->img_x; + *out_y = z->s->img_y; + if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output + return output; + } +} + +static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + unsigned char* result; + stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); + STBI_NOTUSED(ri); + j->s = s; + stbi__setup_jpeg(j); + result = load_jpeg_image(j, x,y,comp,req_comp); + STBI_FREE(j); + return result; +} + +static int stbi__jpeg_test(stbi__context *s) +{ + int r; + stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); + j->s = s; + stbi__setup_jpeg(j); + r = stbi__decode_jpeg_header(j, STBI__SCAN_type); + stbi__rewind(s); + STBI_FREE(j); + return r; +} + +static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) +{ + if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { + stbi__rewind( j->s ); + return 0; + } + if (x) *x = j->s->img_x; + if (y) *y = j->s->img_y; + if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; + return 1; +} + +static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) +{ + int result; + stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); + j->s = s; + result = stbi__jpeg_info_raw(j, x, y, comp); + STBI_FREE(j); + return result; +} +#endif + +// public domain zlib decode v0.2 Sean Barrett 2006-11-18 +// simple implementation +// - all input must be provided in an upfront buffer +// - all output is written to a single output buffer (can malloc/realloc) +// performance +// - fast huffman + +#ifndef STBI_NO_ZLIB + +// fast-way is faster to check than jpeg huffman, but slow way is slower +#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables +#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) + +// zlib-style huffman encoding +// (jpegs packs from left, zlib from right, so can't share code) +typedef struct +{ + stbi__uint16 fast[1 << STBI__ZFAST_BITS]; + stbi__uint16 firstcode[16]; + int maxcode[17]; + stbi__uint16 firstsymbol[16]; + stbi_uc size[288]; + stbi__uint16 value[288]; +} stbi__zhuffman; + +stbi_inline static int stbi__bitreverse16(int n) +{ + n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); + n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); + n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); + n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); + return n; +} + +stbi_inline static int stbi__bit_reverse(int v, int bits) +{ + STBI_ASSERT(bits <= 16); + // to bit reverse n bits, reverse 16 and shift + // e.g. 11 bits, bit reverse and shift away 5 + return stbi__bitreverse16(v) >> (16-bits); +} + +static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) +{ + int i,k=0; + int code, next_code[16], sizes[17]; + + // DEFLATE spec for generating codes + memset(sizes, 0, sizeof(sizes)); + memset(z->fast, 0, sizeof(z->fast)); + for (i=0; i < num; ++i) + ++sizes[sizelist[i]]; + sizes[0] = 0; + for (i=1; i < 16; ++i) + if (sizes[i] > (1 << i)) + return stbi__err("bad sizes", "Corrupt PNG"); + code = 0; + for (i=1; i < 16; ++i) { + next_code[i] = code; + z->firstcode[i] = (stbi__uint16) code; + z->firstsymbol[i] = (stbi__uint16) k; + code = (code + sizes[i]); + if (sizes[i]) + if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); + z->maxcode[i] = code << (16-i); // preshift for inner loop + code <<= 1; + k += sizes[i]; + } + z->maxcode[16] = 0x10000; // sentinel + for (i=0; i < num; ++i) { + int s = sizelist[i]; + if (s) { + int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; + stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); + z->size [c] = (stbi_uc ) s; + z->value[c] = (stbi__uint16) i; + if (s <= STBI__ZFAST_BITS) { + int j = stbi__bit_reverse(next_code[s],s); + while (j < (1 << STBI__ZFAST_BITS)) { + z->fast[j] = fastv; + j += (1 << s); + } + } + ++next_code[s]; + } + } + return 1; +} + +// zlib-from-memory implementation for PNG reading +// because PNG allows splitting the zlib stream arbitrarily, +// and it's annoying structurally to have PNG call ZLIB call PNG, +// we require PNG read all the IDATs and combine them into a single +// memory buffer + +typedef struct +{ + stbi_uc *zbuffer, *zbuffer_end; + int num_bits; + stbi__uint32 code_buffer; + + char *zout; + char *zout_start; + char *zout_end; + int z_expandable; + + stbi__zhuffman z_length, z_distance; +} stbi__zbuf; + +stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) +{ + if (z->zbuffer >= z->zbuffer_end) return 0; + return *z->zbuffer++; +} + +static void stbi__fill_bits(stbi__zbuf *z) +{ + do { + STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); + z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; + z->num_bits += 8; + } while (z->num_bits <= 24); +} + +stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) +{ + unsigned int k; + if (z->num_bits < n) stbi__fill_bits(z); + k = z->code_buffer & ((1 << n) - 1); + z->code_buffer >>= n; + z->num_bits -= n; + return k; +} + +static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s,k; + // not resolved by fast table, so compute it the slow way + // use jpeg approach, which requires MSbits at top + k = stbi__bit_reverse(a->code_buffer, 16); + for (s=STBI__ZFAST_BITS+1; ; ++s) + if (k < z->maxcode[s]) + break; + if (s == 16) return -1; // invalid code! + // code size is s, so: + b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; + STBI_ASSERT(z->size[b] == s); + a->code_buffer >>= s; + a->num_bits -= s; + return z->value[b]; +} + +stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) +{ + int b,s; + if (a->num_bits < 16) stbi__fill_bits(a); + b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; + if (b) { + s = b >> 9; + a->code_buffer >>= s; + a->num_bits -= s; + return b & 511; + } + return stbi__zhuffman_decode_slowpath(a, z); +} + +static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes +{ + char *q; + int cur, limit, old_limit; + z->zout = zout; + if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); + cur = (int) (z->zout - z->zout_start); + limit = old_limit = (int) (z->zout_end - z->zout_start); + while (cur + n > limit) + limit *= 2; + q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); + STBI_NOTUSED(old_limit); + if (q == NULL) return stbi__err("outofmem", "Out of memory"); + z->zout_start = q; + z->zout = q + cur; + z->zout_end = q + limit; + return 1; +} + +static int stbi__zlength_base[31] = { + 3,4,5,6,7,8,9,10,11,13, + 15,17,19,23,27,31,35,43,51,59, + 67,83,99,115,131,163,195,227,258,0,0 }; + +static int stbi__zlength_extra[31]= +{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; + +static int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, +257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; + +static int stbi__zdist_extra[32] = +{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +static int stbi__parse_huffman_block(stbi__zbuf *a) +{ + char *zout = a->zout; + for(;;) { + int z = stbi__zhuffman_decode(a, &a->z_length); + if (z < 256) { + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes + if (zout >= a->zout_end) { + if (!stbi__zexpand(a, zout, 1)) return 0; + zout = a->zout; + } + *zout++ = (char) z; + } else { + stbi_uc *p; + int len,dist; + if (z == 256) { + a->zout = zout; + return 1; + } + z -= 257; + len = stbi__zlength_base[z]; + if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); + z = stbi__zhuffman_decode(a, &a->z_distance); + if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); + dist = stbi__zdist_base[z]; + if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); + if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); + if (zout + len > a->zout_end) { + if (!stbi__zexpand(a, zout, len)) return 0; + zout = a->zout; + } + p = (stbi_uc *) (zout - dist); + if (dist == 1) { // run of one byte; common in images. + stbi_uc v = *p; + if (len) { do *zout++ = v; while (--len); } + } else { + if (len) { do *zout++ = *p++; while (--len); } + } + } + } +} + +static int stbi__compute_huffman_codes(stbi__zbuf *a) +{ + static stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; + stbi__zhuffman z_codelength; + stbi_uc lencodes[286+32+137];//padding for maximum single op + stbi_uc codelength_sizes[19]; + int i,n; + + int hlit = stbi__zreceive(a,5) + 257; + int hdist = stbi__zreceive(a,5) + 1; + int hclen = stbi__zreceive(a,4) + 4; + int ntot = hlit + hdist; + + memset(codelength_sizes, 0, sizeof(codelength_sizes)); + for (i=0; i < hclen; ++i) { + int s = stbi__zreceive(a,3); + codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; + } + if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; + + n = 0; + while (n < ntot) { + int c = stbi__zhuffman_decode(a, &z_codelength); + if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); + if (c < 16) + lencodes[n++] = (stbi_uc) c; + else { + stbi_uc fill = 0; + if (c == 16) { + c = stbi__zreceive(a,2)+3; + if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); + fill = lencodes[n-1]; + } else if (c == 17) + c = stbi__zreceive(a,3)+3; + else { + STBI_ASSERT(c == 18); + c = stbi__zreceive(a,7)+11; + } + if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); + memset(lencodes+n, fill, c); + n += c; + } + } + if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); + if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; + return 1; +} + +static int stbi__parse_uncompressed_block(stbi__zbuf *a) +{ + stbi_uc header[4]; + int len,nlen,k; + if (a->num_bits & 7) + stbi__zreceive(a, a->num_bits & 7); // discard + // drain the bit-packed data into header + k = 0; + while (a->num_bits > 0) { + header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check + a->code_buffer >>= 8; + a->num_bits -= 8; + } + STBI_ASSERT(a->num_bits == 0); + // now fill header the normal way + while (k < 4) + header[k++] = stbi__zget8(a); + len = header[1] * 256 + header[0]; + nlen = header[3] * 256 + header[2]; + if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); + if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); + if (a->zout + len > a->zout_end) + if (!stbi__zexpand(a, a->zout, len)) return 0; + memcpy(a->zout, a->zbuffer, len); + a->zbuffer += len; + a->zout += len; + return 1; +} + +static int stbi__parse_zlib_header(stbi__zbuf *a) +{ + int cmf = stbi__zget8(a); + int cm = cmf & 15; + /* int cinfo = cmf >> 4; */ + int flg = stbi__zget8(a); + if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec + if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png + if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png + // window = 1 << (8 + cinfo)... but who cares, we fully buffer output + return 1; +} + +static const stbi_uc stbi__zdefault_length[288] = +{ + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 +}; +static const stbi_uc stbi__zdefault_distance[32] = +{ + 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 +}; +/* +Init algorithm: +{ + int i; // use <= to match clearly with spec + for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; + for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; + for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; + for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; + + for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; +} +*/ + +static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) +{ + int final, type; + if (parse_header) + if (!stbi__parse_zlib_header(a)) return 0; + a->num_bits = 0; + a->code_buffer = 0; + do { + final = stbi__zreceive(a,1); + type = stbi__zreceive(a,2); + if (type == 0) { + if (!stbi__parse_uncompressed_block(a)) return 0; + } else if (type == 3) { + return 0; + } else { + if (type == 1) { + // use fixed code lengths + if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; + if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; + } else { + if (!stbi__compute_huffman_codes(a)) return 0; + } + if (!stbi__parse_huffman_block(a)) return 0; + } + } while (!final); + return 1; +} + +static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) +{ + a->zout_start = obuf; + a->zout = obuf; + a->zout_end = obuf + olen; + a->z_expandable = exp; + + return stbi__parse_zlib(a, parse_header); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) +{ + return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); +} + +STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(initial_size); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer + len; + if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) + return (int) (a.zout - a.zout_start); + else + return -1; +} + +STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) +{ + stbi__zbuf a; + char *p = (char *) stbi__malloc(16384); + if (p == NULL) return NULL; + a.zbuffer = (stbi_uc *) buffer; + a.zbuffer_end = (stbi_uc *) buffer+len; + if (stbi__do_zlib(&a, p, 16384, 1, 0)) { + if (outlen) *outlen = (int) (a.zout - a.zout_start); + return a.zout_start; + } else { + STBI_FREE(a.zout_start); + return NULL; + } +} + +STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) +{ + stbi__zbuf a; + a.zbuffer = (stbi_uc *) ibuffer; + a.zbuffer_end = (stbi_uc *) ibuffer + ilen; + if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) + return (int) (a.zout - a.zout_start); + else + return -1; +} +#endif + +// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 +// simple implementation +// - only 8-bit samples +// - no CRC checking +// - allocates lots of intermediate memory +// - avoids problem of streaming data between subsystems +// - avoids explicit window management +// performance +// - uses stb_zlib, a PD zlib implementation with fast huffman decoding + +#ifndef STBI_NO_PNG +typedef struct +{ + stbi__uint32 length; + stbi__uint32 type; +} stbi__pngchunk; + +static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) +{ + stbi__pngchunk c; + c.length = stbi__get32be(s); + c.type = stbi__get32be(s); + return c; +} + +static int stbi__check_png_header(stbi__context *s) +{ + static stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; + int i; + for (i=0; i < 8; ++i) + if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); + return 1; +} + +typedef struct +{ + stbi__context *s; + stbi_uc *idata, *expanded, *out; + int depth; +} stbi__png; + + +enum { + STBI__F_none=0, + STBI__F_sub=1, + STBI__F_up=2, + STBI__F_avg=3, + STBI__F_paeth=4, + // synthetic filters used for first scanline to avoid needing a dummy row of 0s + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static stbi_uc first_row_filter[5] = +{ + STBI__F_none, + STBI__F_sub, + STBI__F_none, + STBI__F_avg_first, + STBI__F_paeth_first +}; + +static int stbi__paeth(int a, int b, int c) +{ + int p = a + b - c; + int pa = abs(p-a); + int pb = abs(p-b); + int pc = abs(p-c); + if (pa <= pb && pa <= pc) return a; + if (pb <= pc) return b; + return c; +} + +static stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; + +// create the png data from post-deflated data +static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) +{ + int bytes = (depth == 16? 2 : 1); + stbi__context *s = a->s; + stbi__uint32 i,j,stride = x*out_n*bytes; + stbi__uint32 img_len, img_width_bytes; + int k; + int img_n = s->img_n; // copy it into a local for later + + int output_bytes = out_n*bytes; + int filter_bytes = img_n*bytes; + int width = x; + + STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); + a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into + if (!a->out) return stbi__err("outofmem", "Out of memory"); + + img_width_bytes = (((img_n * x * depth) + 7) >> 3); + img_len = (img_width_bytes + 1) * y; + // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, + // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), + // so just check for raw_len < img_len always. + if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); + + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *prior; + int filter = *raw++; + + if (filter > 4) + return stbi__err("invalid filter","Corrupt PNG"); + + if (depth < 8) { + STBI_ASSERT(img_width_bytes <= x); + cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place + filter_bytes = 1; + width = img_width_bytes; + } + prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above + + // if first row, use special filter that doesn't sample previous row + if (j == 0) filter = first_row_filter[filter]; + + // handle first byte explicitly + for (k=0; k < filter_bytes; ++k) { + switch (filter) { + case STBI__F_none : cur[k] = raw[k]; break; + case STBI__F_sub : cur[k] = raw[k]; break; + case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; + case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; + case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; + case STBI__F_avg_first : cur[k] = raw[k]; break; + case STBI__F_paeth_first: cur[k] = raw[k]; break; + } + } + + if (depth == 8) { + if (img_n != out_n) + cur[img_n] = 255; // first pixel + raw += img_n; + cur += out_n; + prior += out_n; + } else if (depth == 16) { + if (img_n != out_n) { + cur[filter_bytes] = 255; // first pixel top byte + cur[filter_bytes+1] = 255; // first pixel bottom byte + } + raw += filter_bytes; + cur += output_bytes; + prior += output_bytes; + } else { + raw += 1; + cur += 1; + prior += 1; + } + + // this is a little gross, so that we don't switch per-pixel or per-component + if (depth < 8 || img_n == out_n) { + int nk = (width - 1)*filter_bytes; + #define STBI__CASE(f) \ + case f: \ + for (k=0; k < nk; ++k) + switch (filter) { + // "none" filter turns into a memcpy here; make that explicit. + case STBI__F_none: memcpy(cur, raw, nk); break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; + } + #undef STBI__CASE + raw += nk; + } else { + STBI_ASSERT(img_n+1 == out_n); + #define STBI__CASE(f) \ + case f: \ + for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ + for (k=0; k < filter_bytes; ++k) + switch (filter) { + STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; + STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; + STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; + STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; + STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; + STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; + STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; + } + #undef STBI__CASE + + // the loop above sets the high byte of the pixels' alpha, but for + // 16 bit png files we also need the low byte set. we'll do that here. + if (depth == 16) { + cur = a->out + stride*j; // start at the beginning of the row again + for (i=0; i < x; ++i,cur+=output_bytes) { + cur[filter_bytes+1] = 255; + } + } + } + } + + // we make a separate pass to expand bits to pixels; for performance, + // this could run two scanlines behind the above code, so it won't + // intefere with filtering but will still be in the cache. + if (depth < 8) { + for (j=0; j < y; ++j) { + stbi_uc *cur = a->out + stride*j; + stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; + // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit + // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop + stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range + + // note that the final byte might overshoot and write more data than desired. + // we can allocate enough data that this never writes out of memory, but it + // could also overwrite the next scanline. can it overwrite non-empty data + // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. + // so we need to explicitly clamp the final ones + + if (depth == 4) { + for (k=x*img_n; k >= 2; k-=2, ++in) { + *cur++ = scale * ((*in >> 4) ); + *cur++ = scale * ((*in ) & 0x0f); + } + if (k > 0) *cur++ = scale * ((*in >> 4) ); + } else if (depth == 2) { + for (k=x*img_n; k >= 4; k-=4, ++in) { + *cur++ = scale * ((*in >> 6) ); + *cur++ = scale * ((*in >> 4) & 0x03); + *cur++ = scale * ((*in >> 2) & 0x03); + *cur++ = scale * ((*in ) & 0x03); + } + if (k > 0) *cur++ = scale * ((*in >> 6) ); + if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); + if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); + } else if (depth == 1) { + for (k=x*img_n; k >= 8; k-=8, ++in) { + *cur++ = scale * ((*in >> 7) ); + *cur++ = scale * ((*in >> 6) & 0x01); + *cur++ = scale * ((*in >> 5) & 0x01); + *cur++ = scale * ((*in >> 4) & 0x01); + *cur++ = scale * ((*in >> 3) & 0x01); + *cur++ = scale * ((*in >> 2) & 0x01); + *cur++ = scale * ((*in >> 1) & 0x01); + *cur++ = scale * ((*in ) & 0x01); + } + if (k > 0) *cur++ = scale * ((*in >> 7) ); + if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); + if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); + if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); + if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); + if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); + if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); + } + if (img_n != out_n) { + int q; + // insert alpha = 255 + cur = a->out + stride*j; + if (img_n == 1) { + for (q=x-1; q >= 0; --q) { + cur[q*2+1] = 255; + cur[q*2+0] = cur[q]; + } + } else { + STBI_ASSERT(img_n == 3); + for (q=x-1; q >= 0; --q) { + cur[q*4+3] = 255; + cur[q*4+2] = cur[q*3+2]; + cur[q*4+1] = cur[q*3+1]; + cur[q*4+0] = cur[q*3+0]; + } + } + } + } + } else if (depth == 16) { + // force the image data from big-endian to platform-native. + // this is done in a separate pass due to the decoding relying + // on the data being untouched, but could probably be done + // per-line during decode if care is taken. + stbi_uc *cur = a->out; + stbi__uint16 *cur16 = (stbi__uint16*)cur; + + for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { + *cur16 = (cur[0] << 8) | cur[1]; + } + } + + return 1; +} + +static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) +{ + int bytes = (depth == 16 ? 2 : 1); + int out_bytes = out_n * bytes; + stbi_uc *final; + int p; + if (!interlaced) + return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); + + // de-interlacing + final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); + for (p=0; p < 7; ++p) { + int xorig[] = { 0,4,0,2,0,1,0 }; + int yorig[] = { 0,0,4,0,2,0,1 }; + int xspc[] = { 8,8,4,4,2,2,1 }; + int yspc[] = { 8,8,8,4,4,2,2 }; + int i,j,x,y; + // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 + x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; + y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; + if (x && y) { + stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; + if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { + STBI_FREE(final); + return 0; + } + for (j=0; j < y; ++j) { + for (i=0; i < x; ++i) { + int out_y = j*yspc[p]+yorig[p]; + int out_x = i*xspc[p]+xorig[p]; + memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, + a->out + (j*x+i)*out_bytes, out_bytes); + } + } + STBI_FREE(a->out); + image_data += img_len; + image_data_len -= img_len; + } + } + a->out = final; + + return 1; +} + +static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + // compute color-based transparency, assuming we've + // already got 255 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i=0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 255); + p += 2; + } + } else { + for (i=0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi__uint16 *p = (stbi__uint16*) z->out; + + // compute color-based transparency, assuming we've + // already got 65535 as the alpha value in the output + STBI_ASSERT(out_n == 2 || out_n == 4); + + if (out_n == 2) { + for (i = 0; i < pixel_count; ++i) { + p[1] = (p[0] == tc[0] ? 0 : 65535); + p += 2; + } + } else { + for (i = 0; i < pixel_count; ++i) { + if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) + p[3] = 0; + p += 4; + } + } + return 1; +} + +static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) +{ + stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; + stbi_uc *p, *temp_out, *orig = a->out; + + p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); + if (p == NULL) return stbi__err("outofmem", "Out of memory"); + + // between here and free(out) below, exitting would leak + temp_out = p; + + if (pal_img_n == 3) { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p += 3; + } + } else { + for (i=0; i < pixel_count; ++i) { + int n = orig[i]*4; + p[0] = palette[n ]; + p[1] = palette[n+1]; + p[2] = palette[n+2]; + p[3] = palette[n+3]; + p += 4; + } + } + STBI_FREE(a->out); + a->out = temp_out; + + STBI_NOTUSED(len); + + return 1; +} + +static int stbi__unpremultiply_on_load = 0; +static int stbi__de_iphone_flag = 0; + +STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) +{ + stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; +} + +STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) +{ + stbi__de_iphone_flag = flag_true_if_should_convert; +} + +static void stbi__de_iphone(stbi__png *z) +{ + stbi__context *s = z->s; + stbi__uint32 i, pixel_count = s->img_x * s->img_y; + stbi_uc *p = z->out; + + if (s->img_out_n == 3) { // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 3; + } + } else { + STBI_ASSERT(s->img_out_n == 4); + if (stbi__unpremultiply_on_load) { + // convert bgr to rgb and unpremultiply + for (i=0; i < pixel_count; ++i) { + stbi_uc a = p[3]; + stbi_uc t = p[0]; + if (a) { + stbi_uc half = a / 2; + p[0] = (p[2] * 255 + half) / a; + p[1] = (p[1] * 255 + half) / a; + p[2] = ( t * 255 + half) / a; + } else { + p[0] = p[2]; + p[2] = t; + } + p += 4; + } + } else { + // convert bgr to rgb + for (i=0; i < pixel_count; ++i) { + stbi_uc t = p[0]; + p[0] = p[2]; + p[2] = t; + p += 4; + } + } + } +} + +#define STBI__PNG_TYPE(a,b,c,d) (((a) << 24) + ((b) << 16) + ((c) << 8) + (d)) + +static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) +{ + stbi_uc palette[1024], pal_img_n=0; + stbi_uc has_trans=0, tc[3]; + stbi__uint16 tc16[3]; + stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; + int first=1,k,interlace=0, color=0, is_iphone=0; + stbi__context *s = z->s; + + z->expanded = NULL; + z->idata = NULL; + z->out = NULL; + + if (!stbi__check_png_header(s)) return 0; + + if (scan == STBI__SCAN_type) return 1; + + for (;;) { + stbi__pngchunk c = stbi__get_chunk_header(s); + switch (c.type) { + case STBI__PNG_TYPE('C','g','B','I'): + is_iphone = 1; + stbi__skip(s, c.length); + break; + case STBI__PNG_TYPE('I','H','D','R'): { + int comp,filter; + if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); + first = 0; + if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); + s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); + color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); + if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); + comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); + filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); + interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); + if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); + if (!pal_img_n) { + s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); + if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); + if (scan == STBI__SCAN_header) return 1; + } else { + // if paletted, then pal_n is our final components, and + // img_n is # components to decompress/filter. + s->img_n = 1; + if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); + // if SCAN_header, have to scan to see if we have a tRNS + } + break; + } + + case STBI__PNG_TYPE('P','L','T','E'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); + pal_len = c.length / 3; + if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); + for (i=0; i < pal_len; ++i) { + palette[i*4+0] = stbi__get8(s); + palette[i*4+1] = stbi__get8(s); + palette[i*4+2] = stbi__get8(s); + palette[i*4+3] = 255; + } + break; + } + + case STBI__PNG_TYPE('t','R','N','S'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); + if (pal_img_n) { + if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } + if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); + if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); + pal_img_n = 4; + for (i=0; i < c.length; ++i) + palette[i*4+3] = stbi__get8(s); + } else { + if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); + if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); + has_trans = 1; + if (z->depth == 16) { + for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is + } else { + for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger + } + } + break; + } + + case STBI__PNG_TYPE('I','D','A','T'): { + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); + if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } + if ((int)(ioff + c.length) < (int)ioff) return 0; + if (ioff + c.length > idata_limit) { + stbi__uint32 idata_limit_old = idata_limit; + stbi_uc *p; + if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; + while (ioff + c.length > idata_limit) + idata_limit *= 2; + STBI_NOTUSED(idata_limit_old); + p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); + z->idata = p; + } + if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); + ioff += c.length; + break; + } + + case STBI__PNG_TYPE('I','E','N','D'): { + stbi__uint32 raw_len, bpl; + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if (scan != STBI__SCAN_load) return 1; + if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); + // initial guess for decoded data size to avoid unnecessary reallocs + bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component + raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; + z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); + if (z->expanded == NULL) return 0; // zlib should set error + STBI_FREE(z->idata); z->idata = NULL; + if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) + s->img_out_n = s->img_n+1; + else + s->img_out_n = s->img_n; + if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; + if (has_trans) { + if (z->depth == 16) { + if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; + } else { + if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; + } + } + if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) + stbi__de_iphone(z); + if (pal_img_n) { + // pal_img_n == 3 or 4 + s->img_n = pal_img_n; // record the actual colors we had + s->img_out_n = pal_img_n; + if (req_comp >= 3) s->img_out_n = req_comp; + if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) + return 0; + } else if (has_trans) { + // non-paletted image with tRNS -> source image has (constant) alpha + ++s->img_n; + } + STBI_FREE(z->expanded); z->expanded = NULL; + return 1; + } + + default: + // if critical, fail + if (first) return stbi__err("first not IHDR", "Corrupt PNG"); + if ((c.type & (1 << 29)) == 0) { + #ifndef STBI_NO_FAILURE_STRINGS + // not threadsafe + static char invalid_chunk[] = "XXXX PNG chunk not known"; + invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); + invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); + invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); + invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); + #endif + return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); + } + stbi__skip(s, c.length); + break; + } + // end of PNG chunk, read and skip CRC + stbi__get32be(s); + } +} + +static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) +{ + void *result=NULL; + if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); + if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { + if (p->depth < 8) + ri->bits_per_channel = 8; + else + ri->bits_per_channel = p->depth; + result = p->out; + p->out = NULL; + if (req_comp && req_comp != p->s->img_out_n) { + if (ri->bits_per_channel == 8) + result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + else + result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); + p->s->img_out_n = req_comp; + if (result == NULL) return result; + } + *x = p->s->img_x; + *y = p->s->img_y; + if (n) *n = p->s->img_n; + } + STBI_FREE(p->out); p->out = NULL; + STBI_FREE(p->expanded); p->expanded = NULL; + STBI_FREE(p->idata); p->idata = NULL; + + return result; +} + +static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi__png p; + p.s = s; + return stbi__do_png(&p, x,y,comp,req_comp, ri); +} + +static int stbi__png_test(stbi__context *s) +{ + int r; + r = stbi__check_png_header(s); + stbi__rewind(s); + return r; +} + +static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) +{ + if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { + stbi__rewind( p->s ); + return 0; + } + if (x) *x = p->s->img_x; + if (y) *y = p->s->img_y; + if (comp) *comp = p->s->img_n; + return 1; +} + +static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__png p; + p.s = s; + return stbi__png_info_raw(&p, x, y, comp); +} +#endif + +// Microsoft/Windows BMP image + +#ifndef STBI_NO_BMP +static int stbi__bmp_test_raw(stbi__context *s) +{ + int r; + int sz; + if (stbi__get8(s) != 'B') return 0; + if (stbi__get8(s) != 'M') return 0; + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + stbi__get32le(s); // discard data offset + sz = stbi__get32le(s); + r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); + return r; +} + +static int stbi__bmp_test(stbi__context *s) +{ + int r = stbi__bmp_test_raw(s); + stbi__rewind(s); + return r; +} + + +// returns 0..31 for the highest set bit +static int stbi__high_bit(unsigned int z) +{ + int n=0; + if (z == 0) return -1; + if (z >= 0x10000) n += 16, z >>= 16; + if (z >= 0x00100) n += 8, z >>= 8; + if (z >= 0x00010) n += 4, z >>= 4; + if (z >= 0x00004) n += 2, z >>= 2; + if (z >= 0x00002) n += 1, z >>= 1; + return n; +} + +static int stbi__bitcount(unsigned int a) +{ + a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 + a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 + a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits + a = (a + (a >> 8)); // max 16 per 8 bits + a = (a + (a >> 16)); // max 32 per 8 bits + return a & 0xff; +} + +static int stbi__shiftsigned(int v, int shift, int bits) +{ + int result; + int z=0; + + if (shift < 0) v <<= -shift; + else v >>= shift; + result = v; + + z = bits; + while (z < 8) { + result += v >> z; + z += bits; + } + return result; +} + +typedef struct +{ + int bpp, offset, hsz; + unsigned int mr,mg,mb,ma, all_a; +} stbi__bmp_data; + +static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) +{ + int hsz; + if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); + stbi__get32le(s); // discard filesize + stbi__get16le(s); // discard reserved + stbi__get16le(s); // discard reserved + info->offset = stbi__get32le(s); + info->hsz = hsz = stbi__get32le(s); + info->mr = info->mg = info->mb = info->ma = 0; + + if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); + if (hsz == 12) { + s->img_x = stbi__get16le(s); + s->img_y = stbi__get16le(s); + } else { + s->img_x = stbi__get32le(s); + s->img_y = stbi__get32le(s); + } + if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); + info->bpp = stbi__get16le(s); + if (info->bpp == 1) return stbi__errpuc("monochrome", "BMP type not supported: 1-bit"); + if (hsz != 12) { + int compress = stbi__get32le(s); + if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); + stbi__get32le(s); // discard sizeof + stbi__get32le(s); // discard hres + stbi__get32le(s); // discard vres + stbi__get32le(s); // discard colorsused + stbi__get32le(s); // discard max important + if (hsz == 40 || hsz == 56) { + if (hsz == 56) { + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + stbi__get32le(s); + } + if (info->bpp == 16 || info->bpp == 32) { + if (compress == 0) { + if (info->bpp == 32) { + info->mr = 0xffu << 16; + info->mg = 0xffu << 8; + info->mb = 0xffu << 0; + info->ma = 0xffu << 24; + info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 + } else { + info->mr = 31u << 10; + info->mg = 31u << 5; + info->mb = 31u << 0; + } + } else if (compress == 3) { + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + // not documented, but generated by photoshop and handled by mspaint + if (info->mr == info->mg && info->mg == info->mb) { + // ?!?!? + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else + return stbi__errpuc("bad BMP", "bad BMP"); + } + } else { + int i; + if (hsz != 108 && hsz != 124) + return stbi__errpuc("bad BMP", "bad BMP"); + info->mr = stbi__get32le(s); + info->mg = stbi__get32le(s); + info->mb = stbi__get32le(s); + info->ma = stbi__get32le(s); + stbi__get32le(s); // discard color space + for (i=0; i < 12; ++i) + stbi__get32le(s); // discard color space parameters + if (hsz == 124) { + stbi__get32le(s); // discard rendering intent + stbi__get32le(s); // discard offset of profile data + stbi__get32le(s); // discard size of profile data + stbi__get32le(s); // discard reserved + } + } + } + return (void *) 1; +} + + +static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + unsigned int mr=0,mg=0,mb=0,ma=0, all_a; + stbi_uc pal[256][4]; + int psize=0,i,j,width; + int flip_vertically, pad, target; + stbi__bmp_data info; + STBI_NOTUSED(ri); + + info.all_a = 255; + if (stbi__bmp_parse_header(s, &info) == NULL) + return NULL; // error code already set + + flip_vertically = ((int) s->img_y) > 0; + s->img_y = abs((int) s->img_y); + + mr = info.mr; + mg = info.mg; + mb = info.mb; + ma = info.ma; + all_a = info.all_a; + + if (info.hsz == 12) { + if (info.bpp < 24) + psize = (info.offset - 14 - 24) / 3; + } else { + if (info.bpp < 16) + psize = (info.offset - 14 - info.hsz) >> 2; + } + + s->img_n = ma ? 4 : 3; + if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 + target = req_comp; + else + target = s->img_n; // if they want monochrome, we'll post-convert + + // sanity-check size + if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "Corrupt BMP"); + + out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + if (info.bpp < 16) { + int z=0; + if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } + for (i=0; i < psize; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + if (info.hsz != 12) stbi__get8(s); + pal[i][3] = 255; + } + stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + if (info.bpp == 4) width = (s->img_x + 1) >> 1; + else if (info.bpp == 8) width = s->img_x; + else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } + pad = (-width)&3; + for (j=0; j < (int) s->img_y; ++j) { + for (i=0; i < (int) s->img_x; i += 2) { + int v=stbi__get8(s),v2=0; + if (info.bpp == 4) { + v2 = v & 15; + v >>= 4; + } + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; + v = (info.bpp == 8) ? stbi__get8(s) : v2; + out[z++] = pal[v][0]; + out[z++] = pal[v][1]; + out[z++] = pal[v][2]; + if (target == 4) out[z++] = 255; + } + stbi__skip(s, pad); + } + } else { + int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; + int z = 0; + int easy=0; + stbi__skip(s, info.offset - 14 - info.hsz); + if (info.bpp == 24) width = 3 * s->img_x; + else if (info.bpp == 16) width = 2*s->img_x; + else /* bpp = 32 and pad = 0 */ width=0; + pad = (-width) & 3; + if (info.bpp == 24) { + easy = 1; + } else if (info.bpp == 32) { + if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) + easy = 2; + } + if (!easy) { + if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } + // right shift amt to put high bit in position #7 + rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); + gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); + bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); + ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + } + for (j=0; j < (int) s->img_y; ++j) { + if (easy) { + for (i=0; i < (int) s->img_x; ++i) { + unsigned char a; + out[z+2] = stbi__get8(s); + out[z+1] = stbi__get8(s); + out[z+0] = stbi__get8(s); + z += 3; + a = (easy == 2 ? stbi__get8(s) : 255); + all_a |= a; + if (target == 4) out[z++] = a; + } + } else { + int bpp = info.bpp; + for (i=0; i < (int) s->img_x; ++i) { + stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); + int a; + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); + out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); + a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); + all_a |= a; + if (target == 4) out[z++] = STBI__BYTECAST(a); + } + } + stbi__skip(s, pad); + } + } + + // if alpha channel is all 0s, replace with all 255s + if (target == 4 && all_a == 0) + for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) + out[i] = 255; + + if (flip_vertically) { + stbi_uc t; + for (j=0; j < (int) s->img_y>>1; ++j) { + stbi_uc *p1 = out + j *s->img_x*target; + stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; + for (i=0; i < (int) s->img_x*target; ++i) { + t = p1[i], p1[i] = p2[i], p2[i] = t; + } + } + } + + if (req_comp && req_comp != target) { + out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + return out; +} +#endif + +// Targa Truevision - TGA +// by Jonathan Dummer +#ifndef STBI_NO_TGA +// returns STBI_rgb or whatever, 0 on error +static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) +{ + // only RGB or RGBA (incl. 16bit) or grey allowed + if(is_rgb16) *is_rgb16 = 0; + switch(bits_per_pixel) { + case 8: return STBI_grey; + case 16: if(is_grey) return STBI_grey_alpha; + // else: fall-through + case 15: if(is_rgb16) *is_rgb16 = 1; + return STBI_rgb; + case 24: // fall-through + case 32: return bits_per_pixel/8; + default: return 0; + } +} + +static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) +{ + int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; + int sz, tga_colormap_type; + stbi__get8(s); // discard Offset + tga_colormap_type = stbi__get8(s); // colormap type + if( tga_colormap_type > 1 ) { + stbi__rewind(s); + return 0; // only RGB or indexed allowed + } + tga_image_type = stbi__get8(s); // image type + if ( tga_colormap_type == 1 ) { // colormapped (paletted) image + if (tga_image_type != 1 && tga_image_type != 9) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { + stbi__rewind(s); + return 0; + } + stbi__skip(s,4); // skip image x and y origin + tga_colormap_bpp = sz; + } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE + if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { + stbi__rewind(s); + return 0; // only RGB or grey allowed, +/- RLE + } + stbi__skip(s,9); // skip colormap specification and image x/y origin + tga_colormap_bpp = 0; + } + tga_w = stbi__get16le(s); + if( tga_w < 1 ) { + stbi__rewind(s); + return 0; // test width + } + tga_h = stbi__get16le(s); + if( tga_h < 1 ) { + stbi__rewind(s); + return 0; // test height + } + tga_bits_per_pixel = stbi__get8(s); // bits per pixel + stbi__get8(s); // ignore alpha bits + if (tga_colormap_bpp != 0) { + if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { + // when using a colormap, tga_bits_per_pixel is the size of the indexes + // I don't think anything but 8 or 16bit indexes makes sense + stbi__rewind(s); + return 0; + } + tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); + } else { + tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); + } + if(!tga_comp) { + stbi__rewind(s); + return 0; + } + if (x) *x = tga_w; + if (y) *y = tga_h; + if (comp) *comp = tga_comp; + return 1; // seems to have passed everything +} + +static int stbi__tga_test(stbi__context *s) +{ + int res = 0; + int sz, tga_color_type; + stbi__get8(s); // discard Offset + tga_color_type = stbi__get8(s); // color type + if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed + sz = stbi__get8(s); // image type + if ( tga_color_type == 1 ) { // colormapped (paletted) image + if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 + stbi__skip(s,4); // skip index of first colormap entry and number of entries + sz = stbi__get8(s); // check bits per palette color entry + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + stbi__skip(s,4); // skip image x and y origin + } else { // "normal" image w/o colormap + if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE + stbi__skip(s,9); // skip colormap specification and image x/y origin + } + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width + if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height + sz = stbi__get8(s); // bits per pixel + if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index + if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; + + res = 1; // if we got this far, everything's good and we can return 1 instead of 0 + +errorEnd: + stbi__rewind(s); + return res; +} + +// read 16bit value and convert to 24bit RGB +static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) +{ + stbi__uint16 px = (stbi__uint16)stbi__get16le(s); + stbi__uint16 fiveBitMask = 31; + // we have 3 channels with 5bits each + int r = (px >> 10) & fiveBitMask; + int g = (px >> 5) & fiveBitMask; + int b = px & fiveBitMask; + // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later + out[0] = (stbi_uc)((r * 255)/31); + out[1] = (stbi_uc)((g * 255)/31); + out[2] = (stbi_uc)((b * 255)/31); + + // some people claim that the most significant bit might be used for alpha + // (possibly if an alpha-bit is set in the "image descriptor byte") + // but that only made 16bit test images completely translucent.. + // so let's treat all 15 and 16bit TGAs as RGB with no alpha. +} + +static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + // read in the TGA header stuff + int tga_offset = stbi__get8(s); + int tga_indexed = stbi__get8(s); + int tga_image_type = stbi__get8(s); + int tga_is_RLE = 0; + int tga_palette_start = stbi__get16le(s); + int tga_palette_len = stbi__get16le(s); + int tga_palette_bits = stbi__get8(s); + int tga_x_origin = stbi__get16le(s); + int tga_y_origin = stbi__get16le(s); + int tga_width = stbi__get16le(s); + int tga_height = stbi__get16le(s); + int tga_bits_per_pixel = stbi__get8(s); + int tga_comp, tga_rgb16=0; + int tga_inverted = stbi__get8(s); + // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) + // image data + unsigned char *tga_data; + unsigned char *tga_palette = NULL; + int i, j; + unsigned char raw_data[4] = {0}; + int RLE_count = 0; + int RLE_repeating = 0; + int read_next_pixel = 1; + STBI_NOTUSED(ri); + + // do a tiny bit of precessing + if ( tga_image_type >= 8 ) + { + tga_image_type -= 8; + tga_is_RLE = 1; + } + tga_inverted = 1 - ((tga_inverted >> 5) & 1); + + // If I'm paletted, then I'll use the number of bits from the palette + if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); + else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); + + if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency + return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); + + // tga info + *x = tga_width; + *y = tga_height; + if (comp) *comp = tga_comp; + + if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) + return stbi__errpuc("too large", "Corrupt TGA"); + + tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); + if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); + + // skip to the data's starting position (offset usually = 0) + stbi__skip(s, tga_offset ); + + if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { + for (i=0; i < tga_height; ++i) { + int row = tga_inverted ? tga_height -i - 1 : i; + stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; + stbi__getn(s, tga_row, tga_width * tga_comp); + } + } else { + // do I need to load a palette? + if ( tga_indexed) + { + // any data to skip? (offset usually = 0) + stbi__skip(s, tga_palette_start ); + // load the palette + tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); + if (!tga_palette) { + STBI_FREE(tga_data); + return stbi__errpuc("outofmem", "Out of memory"); + } + if (tga_rgb16) { + stbi_uc *pal_entry = tga_palette; + STBI_ASSERT(tga_comp == STBI_rgb); + for (i=0; i < tga_palette_len; ++i) { + stbi__tga_read_rgb16(s, pal_entry); + pal_entry += tga_comp; + } + } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { + STBI_FREE(tga_data); + STBI_FREE(tga_palette); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + } + // load the data + for (i=0; i < tga_width * tga_height; ++i) + { + // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? + if ( tga_is_RLE ) + { + if ( RLE_count == 0 ) + { + // yep, get the next byte as a RLE command + int RLE_cmd = stbi__get8(s); + RLE_count = 1 + (RLE_cmd & 127); + RLE_repeating = RLE_cmd >> 7; + read_next_pixel = 1; + } else if ( !RLE_repeating ) + { + read_next_pixel = 1; + } + } else + { + read_next_pixel = 1; + } + // OK, if I need to read a pixel, do it now + if ( read_next_pixel ) + { + // load however much data we did have + if ( tga_indexed ) + { + // read in index, then perform the lookup + int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); + if ( pal_idx >= tga_palette_len ) { + // invalid index + pal_idx = 0; + } + pal_idx *= tga_comp; + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = tga_palette[pal_idx+j]; + } + } else if(tga_rgb16) { + STBI_ASSERT(tga_comp == STBI_rgb); + stbi__tga_read_rgb16(s, raw_data); + } else { + // read in the data raw + for (j = 0; j < tga_comp; ++j) { + raw_data[j] = stbi__get8(s); + } + } + // clear the reading flag for the next pixel + read_next_pixel = 0; + } // end of reading a pixel + + // copy data + for (j = 0; j < tga_comp; ++j) + tga_data[i*tga_comp+j] = raw_data[j]; + + // in case we're in RLE mode, keep counting down + --RLE_count; + } + // do I need to invert the image? + if ( tga_inverted ) + { + for (j = 0; j*2 < tga_height; ++j) + { + int index1 = j * tga_width * tga_comp; + int index2 = (tga_height - 1 - j) * tga_width * tga_comp; + for (i = tga_width * tga_comp; i > 0; --i) + { + unsigned char temp = tga_data[index1]; + tga_data[index1] = tga_data[index2]; + tga_data[index2] = temp; + ++index1; + ++index2; + } + } + } + // clear my palette, if I had one + if ( tga_palette != NULL ) + { + STBI_FREE( tga_palette ); + } + } + + // swap RGB - if the source data was RGB16, it already is in the right order + if (tga_comp >= 3 && !tga_rgb16) + { + unsigned char* tga_pixel = tga_data; + for (i=0; i < tga_width * tga_height; ++i) + { + unsigned char temp = tga_pixel[0]; + tga_pixel[0] = tga_pixel[2]; + tga_pixel[2] = temp; + tga_pixel += tga_comp; + } + } + + // convert to target component count + if (req_comp && req_comp != tga_comp) + tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); + + // the things I do to get rid of an error message, and yet keep + // Microsoft's C compilers happy... [8^( + tga_palette_start = tga_palette_len = tga_palette_bits = + tga_x_origin = tga_y_origin = 0; + // OK, done + return tga_data; +} +#endif + +// ************************************************************************************************* +// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB + +#ifndef STBI_NO_PSD +static int stbi__psd_test(stbi__context *s) +{ + int r = (stbi__get32be(s) == 0x38425053); + stbi__rewind(s); + return r; +} + +static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) +{ + int count, nleft, len; + + count = 0; + while ((nleft = pixelCount - count) > 0) { + len = stbi__get8(s); + if (len == 128) { + // No-op. + } else if (len < 128) { + // Copy next len+1 bytes literally. + len++; + if (len > nleft) return 0; // corrupt data + count += len; + while (len) { + *p = stbi__get8(s); + p += 4; + len--; + } + } else if (len > 128) { + stbi_uc val; + // Next -len+1 bytes in the dest are replicated from next source byte. + // (Interpret len as a negative 8-bit int.) + len = 257 - len; + if (len > nleft) return 0; // corrupt data + val = stbi__get8(s); + count += len; + while (len) { + *p = val; + p += 4; + len--; + } + } + } + + return 1; +} + +static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) +{ + int pixelCount; + int channelCount, compression; + int channel, i; + int bitdepth; + int w,h; + stbi_uc *out; + STBI_NOTUSED(ri); + + // Check identifier + if (stbi__get32be(s) != 0x38425053) // "8BPS" + return stbi__errpuc("not PSD", "Corrupt PSD image"); + + // Check file type version. + if (stbi__get16be(s) != 1) + return stbi__errpuc("wrong version", "Unsupported version of PSD image"); + + // Skip 6 reserved bytes. + stbi__skip(s, 6 ); + + // Read the number of channels (R, G, B, A, etc). + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) + return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); + + // Read the rows and columns of the image. + h = stbi__get32be(s); + w = stbi__get32be(s); + + // Make sure the depth is 8 bits. + bitdepth = stbi__get16be(s); + if (bitdepth != 8 && bitdepth != 16) + return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); + + // Make sure the color mode is RGB. + // Valid options are: + // 0: Bitmap + // 1: Grayscale + // 2: Indexed color + // 3: RGB color + // 4: CMYK color + // 7: Multichannel + // 8: Duotone + // 9: Lab color + if (stbi__get16be(s) != 3) + return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); + + // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) + stbi__skip(s,stbi__get32be(s) ); + + // Skip the image resources. (resolution, pen tool paths, etc) + stbi__skip(s, stbi__get32be(s) ); + + // Skip the reserved data. + stbi__skip(s, stbi__get32be(s) ); + + // Find out if the data is compressed. + // Known values: + // 0: no compression + // 1: RLE compressed + compression = stbi__get16be(s); + if (compression > 1) + return stbi__errpuc("bad compression", "PSD has an unknown compression format"); + + // Check size + if (!stbi__mad3sizes_valid(4, w, h, 0)) + return stbi__errpuc("too large", "Corrupt PSD"); + + // Create the destination image. + + if (!compression && bitdepth == 16 && bpc == 16) { + out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); + ri->bits_per_channel = 16; + } else + out = (stbi_uc *) stbi__malloc(4 * w*h); + + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + pixelCount = w*h; + + // Initialize the data to zero. + //memset( out, 0, pixelCount * 4 ); + + // Finally, the image data. + if (compression) { + // RLE as used by .PSD and .TIFF + // Loop until you get the number of unpacked bytes you are expecting: + // Read the next source byte into n. + // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. + // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. + // Else if n is 128, noop. + // Endloop + + // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, + // which we're going to just skip. + stbi__skip(s, h * channelCount * 2 ); + + // Read the RLE data by channel. + for (channel = 0; channel < 4; channel++) { + stbi_uc *p; + + p = out+channel; + if (channel >= channelCount) { + // Fill this channel with default data. + for (i = 0; i < pixelCount; i++, p += 4) + *p = (channel == 3 ? 255 : 0); + } else { + // Read the RLE data. + if (!stbi__psd_decode_rle(s, p, pixelCount)) { + STBI_FREE(out); + return stbi__errpuc("corrupt", "bad RLE data"); + } + } + } + + } else { + // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) + // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. + + // Read the data by channel. + for (channel = 0; channel < 4; channel++) { + if (channel >= channelCount) { + // Fill this channel with default data. + if (bitdepth == 16 && bpc == 16) { + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + stbi__uint16 val = channel == 3 ? 65535 : 0; + for (i = 0; i < pixelCount; i++, q += 4) + *q = val; + } else { + stbi_uc *p = out+channel; + stbi_uc val = channel == 3 ? 255 : 0; + for (i = 0; i < pixelCount; i++, p += 4) + *p = val; + } + } else { + if (ri->bits_per_channel == 16) { // output bpc + stbi__uint16 *q = ((stbi__uint16 *) out) + channel; + for (i = 0; i < pixelCount; i++, q += 4) + *q = (stbi__uint16) stbi__get16be(s); + } else { + stbi_uc *p = out+channel; + if (bitdepth == 16) { // input bpc + for (i = 0; i < pixelCount; i++, p += 4) + *p = (stbi_uc) (stbi__get16be(s) >> 8); + } else { + for (i = 0; i < pixelCount; i++, p += 4) + *p = stbi__get8(s); + } + } + } + } + } + + // remove weird white matte from PSD + if (channelCount >= 4) { + if (ri->bits_per_channel == 16) { + for (i=0; i < w*h; ++i) { + stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; + if (pixel[3] != 0 && pixel[3] != 65535) { + float a = pixel[3] / 65535.0f; + float ra = 1.0f / a; + float inv_a = 65535.0f * (1 - ra); + pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); + pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); + pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); + } + } + } else { + for (i=0; i < w*h; ++i) { + unsigned char *pixel = out + 4*i; + if (pixel[3] != 0 && pixel[3] != 255) { + float a = pixel[3] / 255.0f; + float ra = 1.0f / a; + float inv_a = 255.0f * (1 - ra); + pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); + pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); + pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); + } + } + } + } + + // convert to desired output format + if (req_comp && req_comp != 4) { + if (ri->bits_per_channel == 16) + out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); + else + out = stbi__convert_format(out, 4, req_comp, w, h); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + + if (comp) *comp = 4; + *y = h; + *x = w; + + return out; +} +#endif + +// ************************************************************************************************* +// Softimage PIC loader +// by Tom Seddon +// +// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format +// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ + +#ifndef STBI_NO_PIC +static int stbi__pic_is4(stbi__context *s,const char *str) +{ + int i; + for (i=0; i<4; ++i) + if (stbi__get8(s) != (stbi_uc)str[i]) + return 0; + + return 1; +} + +static int stbi__pic_test_core(stbi__context *s) +{ + int i; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) + return 0; + + for(i=0;i<84;++i) + stbi__get8(s); + + if (!stbi__pic_is4(s,"PICT")) + return 0; + + return 1; +} + +typedef struct +{ + stbi_uc size,type,channel; +} stbi__pic_packet; + +static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) +{ + int mask=0x80, i; + + for (i=0; i<4; ++i, mask>>=1) { + if (channel & mask) { + if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); + dest[i]=stbi__get8(s); + } + } + + return dest; +} + +static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) +{ + int mask=0x80,i; + + for (i=0;i<4; ++i, mask>>=1) + if (channel&mask) + dest[i]=src[i]; +} + +static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) +{ + int act_comp=0,num_packets=0,y,chained; + stbi__pic_packet packets[10]; + + // this will (should...) cater for even some bizarre stuff like having data + // for the same channel in multiple packets. + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return stbi__errpuc("bad format","too many packets"); + + packet = &packets[num_packets++]; + + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + + act_comp |= packet->channel; + + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); + if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? + + for(y=0; ytype) { + default: + return stbi__errpuc("bad format","packet has bad compression type"); + + case 0: {//uncompressed + int x; + + for(x=0;xchannel,dest)) + return 0; + break; + } + + case 1://Pure RLE + { + int left=width, i; + + while (left>0) { + stbi_uc count,value[4]; + + count=stbi__get8(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); + + if (count > left) + count = (stbi_uc) left; + + if (!stbi__readval(s,packet->channel,value)) return 0; + + for(i=0; ichannel,dest,value); + left -= count; + } + } + break; + + case 2: {//Mixed RLE + int left=width; + while (left>0) { + int count = stbi__get8(s), i; + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); + + if (count >= 128) { // Repeated + stbi_uc value[4]; + + if (count==128) + count = stbi__get16be(s); + else + count -= 127; + if (count > left) + return stbi__errpuc("bad file","scanline overrun"); + + if (!stbi__readval(s,packet->channel,value)) + return 0; + + for(i=0;ichannel,dest,value); + } else { // Raw + ++count; + if (count>left) return stbi__errpuc("bad file","scanline overrun"); + + for(i=0;ichannel,dest)) + return 0; + } + left-=count; + } + break; + } + } + } + } + + return result; +} + +static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) +{ + stbi_uc *result; + int i, x,y, internal_comp; + STBI_NOTUSED(ri); + + if (!comp) comp = &internal_comp; + + for (i=0; i<92; ++i) + stbi__get8(s); + + x = stbi__get16be(s); + y = stbi__get16be(s); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); + if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); + + stbi__get32be(s); //skip `ratio' + stbi__get16be(s); //skip `fields' + stbi__get16be(s); //skip `pad' + + // intermediate buffer is RGBA + result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); + memset(result, 0xff, x*y*4); + + if (!stbi__pic_load_core(s,x,y,comp, result)) { + STBI_FREE(result); + result=0; + } + *px = x; + *py = y; + if (req_comp == 0) req_comp = *comp; + result=stbi__convert_format(result,4,req_comp,x,y); + + return result; +} + +static int stbi__pic_test(stbi__context *s) +{ + int r = stbi__pic_test_core(s); + stbi__rewind(s); + return r; +} +#endif + +// ************************************************************************************************* +// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb + +#ifndef STBI_NO_GIF +typedef struct +{ + stbi__int16 prefix; + stbi_uc first; + stbi_uc suffix; +} stbi__gif_lzw; + +typedef struct +{ + int w,h; + stbi_uc *out, *old_out; // output buffer (always 4 components) + int flags, bgindex, ratio, transparent, eflags, delay; + stbi_uc pal[256][4]; + stbi_uc lpal[256][4]; + stbi__gif_lzw codes[4096]; + stbi_uc *color_table; + int parse, step; + int lflags; + int start_x, start_y; + int max_x, max_y; + int cur_x, cur_y; + int line_size; +} stbi__gif; + +static int stbi__gif_test_raw(stbi__context *s) +{ + int sz; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; + sz = stbi__get8(s); + if (sz != '9' && sz != '7') return 0; + if (stbi__get8(s) != 'a') return 0; + return 1; +} + +static int stbi__gif_test(stbi__context *s) +{ + int r = stbi__gif_test_raw(s); + stbi__rewind(s); + return r; +} + +static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) +{ + int i; + for (i=0; i < num_entries; ++i) { + pal[i][2] = stbi__get8(s); + pal[i][1] = stbi__get8(s); + pal[i][0] = stbi__get8(s); + pal[i][3] = transp == i ? 0 : 255; + } +} + +static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) +{ + stbi_uc version; + if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') + return stbi__err("not GIF", "Corrupt GIF"); + + version = stbi__get8(s); + if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); + if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); + + stbi__g_failure_reason = ""; + g->w = stbi__get16le(s); + g->h = stbi__get16le(s); + g->flags = stbi__get8(s); + g->bgindex = stbi__get8(s); + g->ratio = stbi__get8(s); + g->transparent = -1; + + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments + + if (is_info) return 1; + + if (g->flags & 0x80) + stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); + + return 1; +} + +static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) +{ + stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + if (!stbi__gif_header(s, g, comp, 1)) { + STBI_FREE(g); + stbi__rewind( s ); + return 0; + } + if (x) *x = g->w; + if (y) *y = g->h; + STBI_FREE(g); + return 1; +} + +static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) +{ + stbi_uc *p, *c; + + // recurse to decode the prefixes, since the linked-list is backwards, + // and working backwards through an interleaved image would be nasty + if (g->codes[code].prefix >= 0) + stbi__out_gif_code(g, g->codes[code].prefix); + + if (g->cur_y >= g->max_y) return; + + p = &g->out[g->cur_x + g->cur_y]; + c = &g->color_table[g->codes[code].suffix * 4]; + + if (c[3] >= 128) { + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = c[3]; + } + g->cur_x += 4; + + if (g->cur_x >= g->max_x) { + g->cur_x = g->start_x; + g->cur_y += g->step; + + while (g->cur_y >= g->max_y && g->parse > 0) { + g->step = (1 << g->parse) * g->line_size; + g->cur_y = g->start_y + (g->step >> 1); + --g->parse; + } + } +} + +static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) +{ + stbi_uc lzw_cs; + stbi__int32 len, init_code; + stbi__uint32 first; + stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; + stbi__gif_lzw *p; + + lzw_cs = stbi__get8(s); + if (lzw_cs > 12) return NULL; + clear = 1 << lzw_cs; + first = 1; + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + bits = 0; + valid_bits = 0; + for (init_code = 0; init_code < clear; init_code++) { + g->codes[init_code].prefix = -1; + g->codes[init_code].first = (stbi_uc) init_code; + g->codes[init_code].suffix = (stbi_uc) init_code; + } + + // support no starting clear code + avail = clear+2; + oldcode = -1; + + len = 0; + for(;;) { + if (valid_bits < codesize) { + if (len == 0) { + len = stbi__get8(s); // start new block + if (len == 0) + return g->out; + } + --len; + bits |= (stbi__int32) stbi__get8(s) << valid_bits; + valid_bits += 8; + } else { + stbi__int32 code = bits & codemask; + bits >>= codesize; + valid_bits -= codesize; + // @OPTIMIZE: is there some way we can accelerate the non-clear path? + if (code == clear) { // clear code + codesize = lzw_cs + 1; + codemask = (1 << codesize) - 1; + avail = clear + 2; + oldcode = -1; + first = 0; + } else if (code == clear + 1) { // end of stream code + stbi__skip(s, len); + while ((len = stbi__get8(s)) > 0) + stbi__skip(s,len); + return g->out; + } else if (code <= avail) { + if (first) return stbi__errpuc("no clear code", "Corrupt GIF"); + + if (oldcode >= 0) { + p = &g->codes[avail++]; + if (avail > 4096) return stbi__errpuc("too many codes", "Corrupt GIF"); + p->prefix = (stbi__int16) oldcode; + p->first = g->codes[oldcode].first; + p->suffix = (code == avail) ? p->first : g->codes[code].first; + } else if (code == avail) + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + + stbi__out_gif_code(g, (stbi__uint16) code); + + if ((avail & codemask) == 0 && avail <= 0x0FFF) { + codesize++; + codemask = (1 << codesize) - 1; + } + + oldcode = code; + } else { + return stbi__errpuc("illegal code in raster", "Corrupt GIF"); + } + } + } +} + +static void stbi__fill_gif_background(stbi__gif *g, int x0, int y0, int x1, int y1) +{ + int x, y; + stbi_uc *c = g->pal[g->bgindex]; + for (y = y0; y < y1; y += 4 * g->w) { + for (x = x0; x < x1; x += 4) { + stbi_uc *p = &g->out[y + x]; + p[0] = c[2]; + p[1] = c[1]; + p[2] = c[0]; + p[3] = 0; + } + } +} + +// this function is designed to support animated gifs, although stb_image doesn't support it +static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp) +{ + int i; + stbi_uc *prev_out = 0; + + if (g->out == 0 && !stbi__gif_header(s, g, comp,0)) + return 0; // stbi__g_failure_reason set by stbi__gif_header + + if (!stbi__mad3sizes_valid(g->w, g->h, 4, 0)) + return stbi__errpuc("too large", "GIF too large"); + + prev_out = g->out; + g->out = (stbi_uc *) stbi__malloc_mad3(4, g->w, g->h, 0); + if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); + + switch ((g->eflags & 0x1C) >> 2) { + case 0: // unspecified (also always used on 1st frame) + stbi__fill_gif_background(g, 0, 0, 4 * g->w, 4 * g->w * g->h); + break; + case 1: // do not dispose + if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); + g->old_out = prev_out; + break; + case 2: // dispose to background + if (prev_out) memcpy(g->out, prev_out, 4 * g->w * g->h); + stbi__fill_gif_background(g, g->start_x, g->start_y, g->max_x, g->max_y); + break; + case 3: // dispose to previous + if (g->old_out) { + for (i = g->start_y; i < g->max_y; i += 4 * g->w) + memcpy(&g->out[i + g->start_x], &g->old_out[i + g->start_x], g->max_x - g->start_x); + } + break; + } + + for (;;) { + switch (stbi__get8(s)) { + case 0x2C: /* Image Descriptor */ + { + int prev_trans = -1; + stbi__int32 x, y, w, h; + stbi_uc *o; + + x = stbi__get16le(s); + y = stbi__get16le(s); + w = stbi__get16le(s); + h = stbi__get16le(s); + if (((x + w) > (g->w)) || ((y + h) > (g->h))) + return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); + + g->line_size = g->w * 4; + g->start_x = x * 4; + g->start_y = y * g->line_size; + g->max_x = g->start_x + w * 4; + g->max_y = g->start_y + h * g->line_size; + g->cur_x = g->start_x; + g->cur_y = g->start_y; + + g->lflags = stbi__get8(s); + + if (g->lflags & 0x40) { + g->step = 8 * g->line_size; // first interlaced spacing + g->parse = 3; + } else { + g->step = g->line_size; + g->parse = 0; + } + + if (g->lflags & 0x80) { + stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); + g->color_table = (stbi_uc *) g->lpal; + } else if (g->flags & 0x80) { + if (g->transparent >= 0 && (g->eflags & 0x01)) { + prev_trans = g->pal[g->transparent][3]; + g->pal[g->transparent][3] = 0; + } + g->color_table = (stbi_uc *) g->pal; + } else + return stbi__errpuc("missing color table", "Corrupt GIF"); + + o = stbi__process_gif_raster(s, g); + if (o == NULL) return NULL; + + if (prev_trans != -1) + g->pal[g->transparent][3] = (stbi_uc) prev_trans; + + return o; + } + + case 0x21: // Comment Extension. + { + int len; + if (stbi__get8(s) == 0xF9) { // Graphic Control Extension. + len = stbi__get8(s); + if (len == 4) { + g->eflags = stbi__get8(s); + g->delay = stbi__get16le(s); + g->transparent = stbi__get8(s); + } else { + stbi__skip(s, len); + break; + } + } + while ((len = stbi__get8(s)) != 0) + stbi__skip(s, len); + break; + } + + case 0x3B: // gif stream termination code + return (stbi_uc *) s; // using '1' causes warning on some compilers + + default: + return stbi__errpuc("unknown code", "Corrupt GIF"); + } + } + + STBI_NOTUSED(req_comp); +} + +static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *u = 0; + stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); + memset(g, 0, sizeof(*g)); + STBI_NOTUSED(ri); + + u = stbi__gif_load_next(s, g, comp, req_comp); + if (u == (stbi_uc *) s) u = 0; // end of animated gif marker + if (u) { + *x = g->w; + *y = g->h; + if (req_comp && req_comp != 4) + u = stbi__convert_format(u, 4, req_comp, g->w, g->h); + } + else if (g->out) + STBI_FREE(g->out); + STBI_FREE(g); + return u; +} + +static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) +{ + return stbi__gif_info_raw(s,x,y,comp); +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR loader +// originally by Nicolas Schulz +#ifndef STBI_NO_HDR +static int stbi__hdr_test_core(stbi__context *s, const char *signature) +{ + int i; + for (i=0; signature[i]; ++i) + if (stbi__get8(s) != signature[i]) + return 0; + stbi__rewind(s); + return 1; +} + +static int stbi__hdr_test(stbi__context* s) +{ + int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); + stbi__rewind(s); + if(!r) { + r = stbi__hdr_test_core(s, "#?RGBE\n"); + stbi__rewind(s); + } + return r; +} + +#define STBI__HDR_BUFLEN 1024 +static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) +{ + int len=0; + char c = '\0'; + + c = (char) stbi__get8(z); + + while (!stbi__at_eof(z) && c != '\n') { + buffer[len++] = c; + if (len == STBI__HDR_BUFLEN-1) { + // flush to end of line + while (!stbi__at_eof(z) && stbi__get8(z) != '\n') + ; + break; + } + c = (char) stbi__get8(z); + } + + buffer[len] = 0; + return buffer; +} + +static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) +{ + if ( input[3] != 0 ) { + float f1; + // Exponent + f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); + if (req_comp <= 2) + output[0] = (input[0] + input[1] + input[2]) * f1 / 3; + else { + output[0] = input[0] * f1; + output[1] = input[1] * f1; + output[2] = input[2] * f1; + } + if (req_comp == 2) output[1] = 1; + if (req_comp == 4) output[3] = 1; + } else { + switch (req_comp) { + case 4: output[3] = 1; /* fallthrough */ + case 3: output[0] = output[1] = output[2] = 0; + break; + case 2: output[1] = 1; /* fallthrough */ + case 1: output[0] = 0; + break; + } + } +} + +static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int width, height; + stbi_uc *scanline; + float *hdr_data; + int len; + unsigned char count, value; + int i, j, k, c1,c2, z; + const char *headerToken; + STBI_NOTUSED(ri); + + // Check identifier + headerToken = stbi__hdr_gettoken(s,buffer); + if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) + return stbi__errpf("not HDR", "Corrupt HDR image"); + + // Parse header + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); + + // Parse width and height + // can't use sscanf() if we're not using stdio! + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + height = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); + token += 3; + width = (int) strtol(token, NULL, 10); + + *x = width; + *y = height; + + if (comp) *comp = 3; + if (req_comp == 0) req_comp = 3; + + if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) + return stbi__errpf("too large", "HDR image is too large"); + + // Read data + hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); + if (!hdr_data) + return stbi__errpf("outofmem", "Out of memory"); + + // Load image data + // image data is stored as some number of sca + if ( width < 8 || width >= 32768) { + // Read flat data + for (j=0; j < height; ++j) { + for (i=0; i < width; ++i) { + stbi_uc rgbe[4]; + main_decode_loop: + stbi__getn(s, rgbe, 4); + stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); + } + } + } else { + // Read RLE-encoded data + scanline = NULL; + + for (j = 0; j < height; ++j) { + c1 = stbi__get8(s); + c2 = stbi__get8(s); + len = stbi__get8(s); + if (c1 != 2 || c2 != 2 || (len & 0x80)) { + // not run-length encoded, so we have to actually use THIS data as a decoded + // pixel (note this can't be a valid pixel--one of RGB must be >= 128) + stbi_uc rgbe[4]; + rgbe[0] = (stbi_uc) c1; + rgbe[1] = (stbi_uc) c2; + rgbe[2] = (stbi_uc) len; + rgbe[3] = (stbi_uc) stbi__get8(s); + stbi__hdr_convert(hdr_data, rgbe, req_comp); + i = 1; + j = 0; + STBI_FREE(scanline); + goto main_decode_loop; // yes, this makes no sense + } + len <<= 8; + len |= stbi__get8(s); + if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } + if (scanline == NULL) { + scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); + if (!scanline) { + STBI_FREE(hdr_data); + return stbi__errpf("outofmem", "Out of memory"); + } + } + + for (k = 0; k < 4; ++k) { + int nleft; + i = 0; + while ((nleft = width - i) > 0) { + count = stbi__get8(s); + if (count > 128) { + // Run + value = stbi__get8(s); + count -= 128; + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = value; + } else { + // Dump + if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } + for (z = 0; z < count; ++z) + scanline[i++ * 4 + k] = stbi__get8(s); + } + } + } + for (i=0; i < width; ++i) + stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); + } + if (scanline) + STBI_FREE(scanline); + } + + return hdr_data; +} + +static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) +{ + char buffer[STBI__HDR_BUFLEN]; + char *token; + int valid = 0; + int dummy; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (stbi__hdr_test(s) == 0) { + stbi__rewind( s ); + return 0; + } + + for(;;) { + token = stbi__hdr_gettoken(s,buffer); + if (token[0] == 0) break; + if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; + } + + if (!valid) { + stbi__rewind( s ); + return 0; + } + token = stbi__hdr_gettoken(s,buffer); + if (strncmp(token, "-Y ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *y = (int) strtol(token, &token, 10); + while (*token == ' ') ++token; + if (strncmp(token, "+X ", 3)) { + stbi__rewind( s ); + return 0; + } + token += 3; + *x = (int) strtol(token, NULL, 10); + *comp = 3; + return 1; +} +#endif // STBI_NO_HDR + +#ifndef STBI_NO_BMP +static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) +{ + void *p; + stbi__bmp_data info; + + info.all_a = 255; + p = stbi__bmp_parse_header(s, &info); + stbi__rewind( s ); + if (p == NULL) + return 0; + if (x) *x = s->img_x; + if (y) *y = s->img_y; + if (comp) *comp = info.ma ? 4 : 3; + return 1; +} +#endif + +#ifndef STBI_NO_PSD +static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) +{ + int channelCount, dummy; + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + if (stbi__get32be(s) != 0x38425053) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 1) { + stbi__rewind( s ); + return 0; + } + stbi__skip(s, 6); + channelCount = stbi__get16be(s); + if (channelCount < 0 || channelCount > 16) { + stbi__rewind( s ); + return 0; + } + *y = stbi__get32be(s); + *x = stbi__get32be(s); + if (stbi__get16be(s) != 8) { + stbi__rewind( s ); + return 0; + } + if (stbi__get16be(s) != 3) { + stbi__rewind( s ); + return 0; + } + *comp = 4; + return 1; +} +#endif + +#ifndef STBI_NO_PIC +static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) +{ + int act_comp=0,num_packets=0,chained,dummy; + stbi__pic_packet packets[10]; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { + stbi__rewind(s); + return 0; + } + + stbi__skip(s, 88); + + *x = stbi__get16be(s); + *y = stbi__get16be(s); + if (stbi__at_eof(s)) { + stbi__rewind( s); + return 0; + } + if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { + stbi__rewind( s ); + return 0; + } + + stbi__skip(s, 8); + + do { + stbi__pic_packet *packet; + + if (num_packets==sizeof(packets)/sizeof(packets[0])) + return 0; + + packet = &packets[num_packets++]; + chained = stbi__get8(s); + packet->size = stbi__get8(s); + packet->type = stbi__get8(s); + packet->channel = stbi__get8(s); + act_comp |= packet->channel; + + if (stbi__at_eof(s)) { + stbi__rewind( s ); + return 0; + } + if (packet->size != 8) { + stbi__rewind( s ); + return 0; + } + } while (chained); + + *comp = (act_comp & 0x10 ? 4 : 3); + + return 1; +} +#endif + +// ************************************************************************************************* +// Portable Gray Map and Portable Pixel Map loader +// by Ken Miller +// +// PGM: http://netpbm.sourceforge.net/doc/pgm.html +// PPM: http://netpbm.sourceforge.net/doc/ppm.html +// +// Known limitations: +// Does not support comments in the header section +// Does not support ASCII image data (formats P2 and P3) +// Does not support 16-bit-per-channel + +#ifndef STBI_NO_PNM + +static int stbi__pnm_test(stbi__context *s) +{ + char p, t; + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind( s ); + return 0; + } + return 1; +} + +static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) +{ + stbi_uc *out; + STBI_NOTUSED(ri); + + if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) + return 0; + + *x = s->img_x; + *y = s->img_y; + if (comp) *comp = s->img_n; + + if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0)) + return stbi__errpuc("too large", "PNM too large"); + + out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0); + if (!out) return stbi__errpuc("outofmem", "Out of memory"); + stbi__getn(s, out, s->img_n * s->img_x * s->img_y); + + if (req_comp && req_comp != s->img_n) { + out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); + if (out == NULL) return out; // stbi__convert_format frees input on failure + } + return out; +} + +static int stbi__pnm_isspace(char c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; +} + +static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) +{ + for (;;) { + while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) + *c = (char) stbi__get8(s); + + if (stbi__at_eof(s) || *c != '#') + break; + + while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) + *c = (char) stbi__get8(s); + } +} + +static int stbi__pnm_isdigit(char c) +{ + return c >= '0' && c <= '9'; +} + +static int stbi__pnm_getinteger(stbi__context *s, char *c) +{ + int value = 0; + + while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { + value = value*10 + (*c - '0'); + *c = (char) stbi__get8(s); + } + + return value; +} + +static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) +{ + int maxv, dummy; + char c, p, t; + + if (!x) x = &dummy; + if (!y) y = &dummy; + if (!comp) comp = &dummy; + + stbi__rewind(s); + + // Get identifier + p = (char) stbi__get8(s); + t = (char) stbi__get8(s); + if (p != 'P' || (t != '5' && t != '6')) { + stbi__rewind(s); + return 0; + } + + *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm + + c = (char) stbi__get8(s); + stbi__pnm_skip_whitespace(s, &c); + + *x = stbi__pnm_getinteger(s, &c); // read width + stbi__pnm_skip_whitespace(s, &c); + + *y = stbi__pnm_getinteger(s, &c); // read height + stbi__pnm_skip_whitespace(s, &c); + + maxv = stbi__pnm_getinteger(s, &c); // read max value + + if (maxv > 255) + return stbi__err("max value > 255", "PPM image not 8-bit"); + else + return 1; +} +#endif + +static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) +{ + #ifndef STBI_NO_JPEG + if (stbi__jpeg_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNG + if (stbi__png_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_GIF + if (stbi__gif_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_BMP + if (stbi__bmp_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PSD + if (stbi__psd_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PIC + if (stbi__pic_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_PNM + if (stbi__pnm_info(s, x, y, comp)) return 1; + #endif + + #ifndef STBI_NO_HDR + if (stbi__hdr_info(s, x, y, comp)) return 1; + #endif + + // test tga last because it's a crappy test! + #ifndef STBI_NO_TGA + if (stbi__tga_info(s, x, y, comp)) + return 1; + #endif + return stbi__err("unknown image type", "Image not of any known type, or corrupt"); +} + +#ifndef STBI_NO_STDIO +STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) +{ + FILE *f = stbi__fopen(filename, "rb"); + int result; + if (!f) return stbi__err("can't fopen", "Unable to open file"); + result = stbi_info_from_file(f, x, y, comp); + fclose(f); + return result; +} + +STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) +{ + int r; + stbi__context s; + long pos = ftell(f); + stbi__start_file(&s, f); + r = stbi__info_main(&s,x,y,comp); + fseek(f,pos,SEEK_SET); + return r; +} +#endif // !STBI_NO_STDIO + +STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_mem(&s,buffer,len); + return stbi__info_main(&s,x,y,comp); +} + +STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) +{ + stbi__context s; + stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); + return stbi__info_main(&s,x,y,comp); +} + +#endif // STB_IMAGE_IMPLEMENTATION + +/* + revision history: + 2.16 (2017-07-23) all functions have 16-bit variants; + STBI_NO_STDIO works again; + compilation fixes; + fix rounding in unpremultiply; + optimize vertical flip; + disable raw_len validation; + documentation fixes + 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; + warning fixes; disable run-time SSE detection on gcc; + uniform handling of optional "return" values; + thread-safe initialization of zlib tables + 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs + 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now + 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes + 2.11 (2016-04-02) allocate large structures on the stack + remove white matting for transparent PSD + fix reported channel count for PNG & BMP + re-enable SSE2 in non-gcc 64-bit + support RGB-formatted JPEG + read 16-bit PNGs (only as 8-bit) + 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED + 2.09 (2016-01-16) allow comments in PNM files + 16-bit-per-pixel TGA (not bit-per-component) + info() for TGA could break due to .hdr handling + info() for BMP to shares code instead of sloppy parse + can use STBI_REALLOC_SIZED if allocator doesn't support realloc + code cleanup + 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA + 2.07 (2015-09-13) fix compiler warnings + partial animated GIF support + limited 16-bpc PSD support + #ifdef unused functions + bug with < 92 byte PIC,PNM,HDR,TGA + 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value + 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning + 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit + 2.03 (2015-04-12) extra corruption checking (mmozeiko) + stbi_set_flip_vertically_on_load (nguillemot) + fix NEON support; fix mingw support + 2.02 (2015-01-19) fix incorrect assert, fix warning + 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 + 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG + 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) + progressive JPEG (stb) + PGM/PPM support (Ken Miller) + STBI_MALLOC,STBI_REALLOC,STBI_FREE + GIF bugfix -- seemingly never worked + STBI_NO_*, STBI_ONLY_* + 1.48 (2014-12-14) fix incorrectly-named assert() + 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) + optimize PNG (ryg) + fix bug in interlaced PNG with user-specified channel count (stb) + 1.46 (2014-08-26) + fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG + 1.45 (2014-08-16) + fix MSVC-ARM internal compiler error by wrapping malloc + 1.44 (2014-08-07) + various warning fixes from Ronny Chevalier + 1.43 (2014-07-15) + fix MSVC-only compiler problem in code changed in 1.42 + 1.42 (2014-07-09) + don't define _CRT_SECURE_NO_WARNINGS (affects user code) + fixes to stbi__cleanup_jpeg path + added STBI_ASSERT to avoid requiring assert.h + 1.41 (2014-06-25) + fix search&replace from 1.36 that messed up comments/error messages + 1.40 (2014-06-22) + fix gcc struct-initialization warning + 1.39 (2014-06-15) + fix to TGA optimization when req_comp != number of components in TGA; + fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) + add support for BMP version 5 (more ignored fields) + 1.38 (2014-06-06) + suppress MSVC warnings on integer casts truncating values + fix accidental rename of 'skip' field of I/O + 1.37 (2014-06-04) + remove duplicate typedef + 1.36 (2014-06-03) + convert to header file single-file library + if de-iphone isn't set, load iphone images color-swapped instead of returning NULL + 1.35 (2014-05-27) + various warnings + fix broken STBI_SIMD path + fix bug where stbi_load_from_file no longer left file pointer in correct place + fix broken non-easy path for 32-bit BMP (possibly never used) + TGA optimization by Arseny Kapoulkine + 1.34 (unknown) + use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case + 1.33 (2011-07-14) + make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements + 1.32 (2011-07-13) + support for "info" function for all supported filetypes (SpartanJ) + 1.31 (2011-06-20) + a few more leak fixes, bug in PNG handling (SpartanJ) + 1.30 (2011-06-11) + added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) + removed deprecated format-specific test/load functions + removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway + error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) + fix inefficiency in decoding 32-bit BMP (David Woo) + 1.29 (2010-08-16) + various warning fixes from Aurelien Pocheville + 1.28 (2010-08-01) + fix bug in GIF palette transparency (SpartanJ) + 1.27 (2010-08-01) + cast-to-stbi_uc to fix warnings + 1.26 (2010-07-24) + fix bug in file buffering for PNG reported by SpartanJ + 1.25 (2010-07-17) + refix trans_data warning (Won Chun) + 1.24 (2010-07-12) + perf improvements reading from files on platforms with lock-heavy fgetc() + minor perf improvements for jpeg + deprecated type-specific functions so we'll get feedback if they're needed + attempt to fix trans_data warning (Won Chun) + 1.23 fixed bug in iPhone support + 1.22 (2010-07-10) + removed image *writing* support + stbi_info support from Jetro Lauha + GIF support from Jean-Marc Lienher + iPhone PNG-extensions from James Brown + warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) + 1.21 fix use of 'stbi_uc' in header (reported by jon blow) + 1.20 added support for Softimage PIC, by Tom Seddon + 1.19 bug in interlaced PNG corruption check (found by ryg) + 1.18 (2008-08-02) + fix a threading bug (local mutable static) + 1.17 support interlaced PNG + 1.16 major bugfix - stbi__convert_format converted one too many pixels + 1.15 initialize some fields for thread safety + 1.14 fix threadsafe conversion bug + header-file-only version (#define STBI_HEADER_FILE_ONLY before including) + 1.13 threadsafe + 1.12 const qualifiers in the API + 1.11 Support installable IDCT, colorspace conversion routines + 1.10 Fixes for 64-bit (don't use "unsigned long") + optimized upsampling by Fabian "ryg" Giesen + 1.09 Fix format-conversion for PSD code (bad global variables!) + 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz + 1.07 attempt to fix C++ warning/errors again + 1.06 attempt to fix C++ warning/errors again + 1.05 fix TGA loading to return correct *comp and use good luminance calc + 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free + 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR + 1.02 support for (subset of) HDR files, float interface for preferred access to them + 1.01 fix bug: possible bug in handling right-side up bmps... not sure + fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all + 1.00 interface to zlib that skips zlib header + 0.99 correct handling of alpha in palette + 0.98 TGA loader by lonesock; dynamically add loaders (untested) + 0.97 jpeg errors on too large a file; also catch another malloc failure + 0.96 fix detection of invalid v value - particleman@mollyrocket forum + 0.95 during header scan, seek to markers in case of padding + 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same + 0.93 handle jpegtran output; verbose errors + 0.92 read 4,8,16,24,32-bit BMP files of several formats + 0.91 output 24-bit Windows 3.0 BMP files + 0.90 fix a few more warnings; bump version number to approach 1.0 + 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd + 0.60 fix compiling as c++ + 0.59 fix warnings: merge Dave Moore's -Wall fixes + 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian + 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available + 0.56 fix bug: zlib uncompressed mode len vs. nlen + 0.55 fix bug: restart_interval not initialized to 0 + 0.54 allow NULL for 'int *comp' + 0.53 fix bug in png 3->4; speedup png decoding + 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments + 0.51 obey req_comp requests, 1-component jpegs return as 1-component, + on 'test' only check type, not whether we support this variant + 0.50 (2006-11-19) + first released version +*/ + + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/src/lib/geogram/third_party/stb_image/stb_image_write.h b/src/lib/geogram/third_party/stb_image/stb_image_write.h new file mode 100644 index 00000000..9d553e0d --- /dev/null +++ b/src/lib/geogram/third_party/stb_image/stb_image_write.h @@ -0,0 +1,1458 @@ +/* stb_image_write - v1.07 - public domain - http://nothings.org/stb/stb_image_write.h + writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 + no warranty implied; use at your own risk + + Before #including, + + #define STB_IMAGE_WRITE_IMPLEMENTATION + + in the file that you want to have the implementation. + + Will probably not work correctly with strict-aliasing optimizations. + +ABOUT: + + This header file is a library for writing images to C stdio. It could be + adapted to write to memory or a general streaming interface; let me know. + + The PNG output is not optimal; it is 20-50% larger than the file + written by a decent optimizing implementation. This library is designed + for source code compactness and simplicity, not optimal image file size + or run-time performance. + +BUILDING: + + You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. + You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace + malloc,realloc,free. + You can define STBIW_MEMMOVE() to replace memmove() + +USAGE: + + There are four functions, one for each image file format: + + int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); + int stbi_write_jpg(char const *filename, int w, int h, int comp, const float *data); + + There are also four equivalent functions that use an arbitrary write function. You are + expected to open/close your file-equivalent before and after calling these: + + int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); + int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); + + where the callback is: + void stbi_write_func(void *context, void *data, int size); + + You can define STBI_WRITE_NO_STDIO to disable the file variant of these + functions, so the library will not use stdio.h at all. However, this will + also disable HDR writing, because it requires stdio for formatted output. + + Each function returns 0 on failure and non-0 on success. + + The functions create an image file defined by the parameters. The image + is a rectangle of pixels stored from left-to-right, top-to-bottom. + Each pixel contains 'comp' channels of data stored interleaved with 8-bits + per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is + monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. + The *data pointer points to the first byte of the top-left-most pixel. + For PNG, "stride_in_bytes" is the distance in bytes from the first byte of + a row of pixels to the first byte of the next row of pixels. + + PNG creates output files with the same number of components as the input. + The BMP format expands Y to RGB in the file format and does not + output alpha. + + PNG supports writing rectangles of data even when the bytes storing rows of + data are not consecutive in memory (e.g. sub-rectangles of a larger image), + by supplying the stride between the beginning of adjacent rows. The other + formats do not. (Thus you cannot write a native-format BMP through the BMP + writer, both because it is in BGR order and because it may have padding + at the end of the line.) + + HDR expects linear float data. Since the format is always 32-bit rgb(e) + data, alpha (if provided) is discarded, and for monochrome data it is + replicated across all three channels. + + TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed + data, set the global variable 'stbi_write_tga_with_rle' to 0. + + JPEG does ignore alpha channels in input data; quality is between 1 and 100. + Higher quality looks better but results in a bigger image. + JPEG baseline (no JPEG progressive). + +CREDITS: + + PNG/BMP/TGA + Sean Barrett + HDR + Baldur Karlsson + TGA monochrome: + Jean-Sebastien Guay + misc enhancements: + Tim Kelsey + TGA RLE + Alan Hickman + initial file IO callback implementation + Emmanuel Julien + JPEG + Jon Olick (original jo_jpeg.cpp code) + Daniel Gibson + bugfixes: + github:Chribba + Guillaume Chereau + github:jry2 + github:romigrou + Sergio Gonzalez + Jonas Karlsson + Filip Wasil + Thatcher Ulrich + github:poppolopoppo + Patrick Boettcher + +LICENSE + + See end of file for license information. + +*/ + +#ifndef INCLUDE_STB_IMAGE_WRITE_H +#define INCLUDE_STB_IMAGE_WRITE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef STB_IMAGE_WRITE_STATIC +#define STBIWDEF static +#else +#define STBIWDEF extern +extern int stbi_write_tga_with_rle; +#endif + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); +STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality); +#endif + +typedef void stbi_write_func(void *context, void *data, int size); + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); +STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); + +#ifdef __cplusplus +} +#endif + +#endif//INCLUDE_STB_IMAGE_WRITE_H + +#ifdef STB_IMAGE_WRITE_IMPLEMENTATION + +#ifdef _WIN32 + #ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS + #endif + #ifndef _CRT_NONSTDC_NO_DEPRECATE + #define _CRT_NONSTDC_NO_DEPRECATE + #endif +#endif + +#ifndef STBI_WRITE_NO_STDIO +#include +#endif // STBI_WRITE_NO_STDIO + +#include +#include +#include +#include + +#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) +// ok +#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." +#endif + +#ifndef STBIW_MALLOC +#define STBIW_MALLOC(sz) malloc(sz) +#define STBIW_REALLOC(p,newsz) realloc(p,newsz) +#define STBIW_FREE(p) free(p) +#endif + +#ifndef STBIW_REALLOC_SIZED +#define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) +#endif + + +#ifndef STBIW_MEMMOVE +#define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) +#endif + + +#ifndef STBIW_ASSERT +#include +#define STBIW_ASSERT(x) assert(x) +#endif + +#define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) + +typedef struct +{ + stbi_write_func *func; + void *context; +} stbi__write_context; + +// initialize a callback-based context +static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) +{ + s->func = c; + s->context = context; +} + +#ifndef STBI_WRITE_NO_STDIO + +static void stbi__stdio_write(void *context, void *data, int size) +{ + fwrite(data,1,size,(FILE*) context); +} + +static int stbi__start_write_file(stbi__write_context *s, const char *filename) +{ + FILE *f = fopen(filename, "wb"); + stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); + return f != NULL; +} + +static void stbi__end_write_file(stbi__write_context *s) +{ + fclose((FILE *)s->context); +} + +#endif // !STBI_WRITE_NO_STDIO + +typedef unsigned int stbiw_uint32; +typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; + +#ifdef STB_IMAGE_WRITE_STATIC +static int stbi_write_tga_with_rle = 1; +#else +int stbi_write_tga_with_rle = 1; +#endif + +static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) +{ + while (*fmt) { + switch (*fmt++) { + case ' ': break; + case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int)); + s->func(s->context,&x,1); + break; } + case '2': { int x = va_arg(v,int); + unsigned char b[2]; + b[0] = STBIW_UCHAR(x); + b[1] = STBIW_UCHAR(x>>8); + s->func(s->context,b,2); + break; } + case '4': { stbiw_uint32 x = va_arg(v,int); + unsigned char b[4]; + b[0]=STBIW_UCHAR(x); + b[1]=STBIW_UCHAR(x>>8); + b[2]=STBIW_UCHAR(x>>16); + b[3]=STBIW_UCHAR(x>>24); + s->func(s->context,b,4); + break; } + default: + STBIW_ASSERT(0); + return; + } + } +} + +static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) +{ + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); +} + +static void stbiw__putc(stbi__write_context *s, unsigned char c) +{ + s->func(s->context, &c, 1); +} + +static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) +{ + unsigned char arr[3]; + arr[0] = a, arr[1] = b, arr[2] = c; + s->func(s->context, arr, 3); +} + +static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) +{ + unsigned char bg[3] = { 255, 0, 255}, px[3]; + int k; + + if (write_alpha < 0) + s->func(s->context, &d[comp - 1], 1); + + switch (comp) { + case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case + case 1: + if (expand_mono) + stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp + else + s->func(s->context, d, 1); // monochrome TGA + break; + case 4: + if (!write_alpha) { + // composite against pink background + for (k = 0; k < 3; ++k) + px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; + stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); + break; + } + /* FALLTHROUGH */ + case 3: + stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); + break; + } + if (write_alpha > 0) + s->func(s->context, &d[comp - 1], 1); +} + +static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) +{ + stbiw_uint32 zero = 0; + int i,j, j_end; + + if (y <= 0) + return; + + if (vdir < 0) + j_end = -1, j = y-1; + else + j_end = y, j = 0; + + for (; j != j_end; j += vdir) { + for (i=0; i < x; ++i) { + unsigned char *d = (unsigned char *) data + (j*x+i)*comp; + stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); + } + s->func(s->context, &zero, scanline_pad); + } +} + +static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) +{ + if (y < 0 || x < 0) { + return 0; + } else { + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); + stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); + return 1; + } +} + +static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) +{ + int pad = (-x*3) & 3; + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, + "11 4 22 4" "4 44 22 444444", + 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header + 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header +} + +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_bmp_core(&s, x, y, comp, data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_bmp_core(&s, x, y, comp, data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif //!STBI_WRITE_NO_STDIO + +static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) +{ + int has_alpha = (comp == 2 || comp == 4); + int colorbytes = has_alpha ? comp-1 : comp; + int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 + + if (y < 0 || x < 0) + return 0; + + if (!stbi_write_tga_with_rle) { + return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, + "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); + } else { + int i,j,k; + + stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); + + for (j = y - 1; j >= 0; --j) { + unsigned char *row = (unsigned char *) data + j * x * comp; + int len; + + for (i = 0; i < x; i += len) { + unsigned char *begin = row + i * comp; + int diff = 1; + len = 1; + + if (i < x - 1) { + ++len; + diff = memcmp(begin, row + (i + 1) * comp, comp); + if (diff) { + const unsigned char *prev = begin; + for (k = i + 2; k < x && len < 128; ++k) { + if (memcmp(prev, row + k * comp, comp)) { + prev += comp; + ++len; + } else { + --len; + break; + } + } + } else { + for (k = i + 2; k < x && len < 128; ++k) { + if (!memcmp(begin, row + k * comp, comp)) { + ++len; + } else { + break; + } + } + } + } + + if (diff) { + unsigned char header = STBIW_UCHAR(len - 1); + s->func(s->context, &header, 1); + for (k = 0; k < len; ++k) { + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); + } + } else { + unsigned char header = STBIW_UCHAR(len - 129); + s->func(s->context, &header, 1); + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); + } + } + } + } + return 1; +} + +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_tga_core(&s, x, y, comp, (void *) data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR writer +// by Baldur Karlsson + +#define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) + +void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) +{ + int exponent; + float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); + + if (maxcomp < 1e-32f) { + rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; + } else { + float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; + + rgbe[0] = (unsigned char)(linear[0] * normalize); + rgbe[1] = (unsigned char)(linear[1] * normalize); + rgbe[2] = (unsigned char)(linear[2] * normalize); + rgbe[3] = (unsigned char)(exponent + 128); + } +} + +void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) +{ + unsigned char lengthbyte = STBIW_UCHAR(length+128); + STBIW_ASSERT(length+128 <= 255); + s->func(s->context, &lengthbyte, 1); + s->func(s->context, &databyte, 1); +} + +void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) +{ + unsigned char lengthbyte = STBIW_UCHAR(length); + STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code + s->func(s->context, &lengthbyte, 1); + s->func(s->context, data, length); +} + +void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) +{ + unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; + unsigned char rgbe[4]; + float linear[3]; + int x; + + scanlineheader[2] = (width&0xff00)>>8; + scanlineheader[3] = (width&0x00ff); + + /* skip RLE for images too small or large */ + if (width < 8 || width >= 32768) { + for (x=0; x < width; x++) { + switch (ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + s->func(s->context, rgbe, 4); + } + } else { + int c,r; + /* encode into scratch buffer */ + for (x=0; x < width; x++) { + switch(ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + scratch[x + width*0] = rgbe[0]; + scratch[x + width*1] = rgbe[1]; + scratch[x + width*2] = rgbe[2]; + scratch[x + width*3] = rgbe[3]; + } + + s->func(s->context, scanlineheader, 4); + + /* RLE each component separately */ + for (c=0; c < 4; c++) { + unsigned char *comp = &scratch[width*c]; + + x = 0; + while (x < width) { + // find first run + r = x; + while (r+2 < width) { + if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) + break; + ++r; + } + if (r+2 >= width) + r = width; + // dump up to first run + while (x < r) { + int len = r-x; + if (len > 128) len = 128; + stbiw__write_dump_data(s, len, &comp[x]); + x += len; + } + // if there's a run, output it + if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd + // find next byte after run + while (r < width && comp[r] == comp[x]) + ++r; + // output run up to r + while (x < r) { + int len = r-x; + if (len > 127) len = 127; + stbiw__write_run_data(s, len, comp[x]); + x += len; + } + } + } + } + } +} + +static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) +{ + if (y <= 0 || x <= 0 || data == NULL) + return 0; + else { + // Each component is stored separately. Allocate scratch space for full output scanline. + unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); + int i, len; + char buffer[128]; + char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; + s->func(s->context, header, sizeof(header)-1); + + len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); + s->func(s->context, buffer, len); + + for(i=0; i < y; i++) + stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*i*x); + STBIW_FREE(scratch); + return 1; + } +} + +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_hdr_core(&s, x, y, comp, (float *) data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif // STBI_WRITE_NO_STDIO + + +////////////////////////////////////////////////////////////////////////////// +// +// PNG writer +// + +// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() +#define stbiw__sbraw(a) ((int *) (a) - 2) +#define stbiw__sbm(a) stbiw__sbraw(a)[0] +#define stbiw__sbn(a) stbiw__sbraw(a)[1] + +#define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) +#define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) +#define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) + +#define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) +#define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) +#define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) + +static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) +{ + int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; + void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); + STBIW_ASSERT(p); + if (p) { + if (!*arr) ((int *) p)[1] = 0; + *arr = (void *) ((int *) p + 2); + stbiw__sbm(*arr) = m; + } + return *arr; +} + +static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) +{ + while (*bitcount >= 8) { + stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); + *bitbuffer >>= 8; + *bitcount -= 8; + } + return data; +} + +static int stbiw__zlib_bitrev(int code, int codebits) +{ + int res=0; + while (codebits--) { + res = (res << 1) | (code & 1); + code >>= 1; + } + return res; +} + +static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) +{ + int i; + for (i=0; i < limit && i < 258; ++i) + if (a[i] != b[i]) break; + return i; +} + +static unsigned int stbiw__zhash(unsigned char *data) +{ + stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +#define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) +#define stbiw__zlib_add(code,codebits) \ + (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) +#define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) +// default huffman tables +#define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) +#define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) +#define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) +#define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) +#define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n)) +#define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) + +#define stbiw__ZHASH 16384 + +unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) +{ + static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; + static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; + static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; + static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; + unsigned int bitbuf=0; + int i,j, bitcount=0; + unsigned char *out = NULL; + unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(char**)); + if (quality < 5) quality = 5; + + stbiw__sbpush(out, 0x78); // DEFLATE 32K window + stbiw__sbpush(out, 0x5e); // FLEVEL = 1 + stbiw__zlib_add(1,1); // BFINAL = 1 + stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman + + for (i=0; i < stbiw__ZHASH; ++i) + hash_table[i] = NULL; + + i=0; + while (i < data_len-3) { + // hash next 3 bytes of data to be compressed + int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; + unsigned char *bestloc = 0; + unsigned char **hlist = hash_table[h]; + int n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32768) { // if entry lies within window + int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); + if (d >= best) best=d,bestloc=hlist[j]; + } + } + // when hash table entry is too long, delete half the entries + if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { + STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); + stbiw__sbn(hash_table[h]) = quality; + } + stbiw__sbpush(hash_table[h],data+i); + + if (bestloc) { + // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal + h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); + hlist = hash_table[h]; + n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32767) { + int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); + if (e > best) { // if next match is better, bail on current match + bestloc = NULL; + break; + } + } + } + } + + if (bestloc) { + int d = (int) (data+i - bestloc); // distance back + STBIW_ASSERT(d <= 32767 && best <= 258); + for (j=0; best > lengthc[j+1]-1; ++j); + stbiw__zlib_huff(j+257); + if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); + for (j=0; d > distc[j+1]-1; ++j); + stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); + if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); + i += best; + } else { + stbiw__zlib_huffb(data[i]); + ++i; + } + } + // write out final bytes + for (;i < data_len; ++i) + stbiw__zlib_huffb(data[i]); + stbiw__zlib_huff(256); // end of block + // pad with 0 bits to byte boundary + while (bitcount) + stbiw__zlib_add(0,1); + + for (i=0; i < stbiw__ZHASH; ++i) + (void) stbiw__sbfree(hash_table[i]); + STBIW_FREE(hash_table); + + { + // compute adler32 on input + unsigned int s1=1, s2=0; + int blocklen = (int) (data_len % 5552); + j=0; + while (j < data_len) { + for (i=0; i < blocklen; ++i) s1 += data[j+i], s2 += s1; + s1 %= 65521, s2 %= 65521; + j += blocklen; + blocklen = 5552; + } + stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s2)); + stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s1)); + } + *out_len = stbiw__sbn(out); + // make returned pointer freeable + STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); + return (unsigned char *) stbiw__sbraw(out); +} + +static unsigned int stbiw__crc32(unsigned char *buffer, int len) +{ + static unsigned int crc_table[256] = + { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + + unsigned int crc = ~0u; + int i; + for (i=0; i < len; ++i) + crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; + return ~crc; +} + +#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) +#define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); +#define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) + +static void stbiw__wpcrc(unsigned char **data, int len) +{ + unsigned int crc = stbiw__crc32(*data - len - 4, len+4); + stbiw__wp32(*data, crc); +} + +static unsigned char stbiw__paeth(int a, int b, int c) +{ + int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); + if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); + if (pb <= pc) return STBIW_UCHAR(b); + return STBIW_UCHAR(c); +} + +// @OPTIMIZE: provide an option that always forces left-predict or paeth predict +unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) +{ + int ctype[5] = { -1, 0, 4, 2, 6 }; + unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; + unsigned char *out,*o, *filt, *zlib; + signed char *line_buffer; + int i,j,k,p,zlen; + + if (stride_bytes == 0) + stride_bytes = x * n; + + filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; + line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } + for (j=0; j < y; ++j) { + static int mapping[] = { 0,1,2,3,4 }; + static int firstmap[] = { 0,1,0,5,6 }; + int *mymap = (j != 0) ? mapping : firstmap; + int best = 0, bestval = 0x7fffffff; + for (p=0; p < 2; ++p) { + for (k= p?best:0; k < 5; ++k) { // @TODO: clarity: rewrite this to go 0..5, and 'continue' the unwanted ones during 2nd pass + int type = mymap[k],est=0; + unsigned char *z = pixels + stride_bytes*j; + for (i=0; i < n; ++i) + switch (type) { + case 0: line_buffer[i] = z[i]; break; + case 1: line_buffer[i] = z[i]; break; + case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; + case 3: line_buffer[i] = z[i] - (z[i-stride_bytes]>>1); break; + case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-stride_bytes],0)); break; + case 5: line_buffer[i] = z[i]; break; + case 6: line_buffer[i] = z[i]; break; + } + for (i=n; i < x*n; ++i) { + switch (type) { + case 0: line_buffer[i] = z[i]; break; + case 1: line_buffer[i] = z[i] - z[i-n]; break; + case 2: line_buffer[i] = z[i] - z[i-stride_bytes]; break; + case 3: line_buffer[i] = z[i] - ((z[i-n] + z[i-stride_bytes])>>1); break; + case 4: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-stride_bytes], z[i-stride_bytes-n]); break; + case 5: line_buffer[i] = z[i] - (z[i-n]>>1); break; + case 6: line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; + } + } + if (p) break; + for (i=0; i < x*n; ++i) + est += abs((signed char) line_buffer[i]); + if (est < bestval) { bestval = est; best = k; } + } + } + // when we get here, best contains the filter type, and line_buffer contains the data + filt[j*(x*n+1)] = (unsigned char) best; + STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); + } + STBIW_FREE(line_buffer); + zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, 8); // increase 8 to get smaller but use more memory + STBIW_FREE(filt); + if (!zlib) return 0; + + // each tag requires 12 bytes of overhead + out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); + if (!out) return 0; + *out_len = 8 + 12+13 + 12+zlen + 12; + + o=out; + STBIW_MEMMOVE(o,sig,8); o+= 8; + stbiw__wp32(o, 13); // header length + stbiw__wptag(o, "IHDR"); + stbiw__wp32(o, x); + stbiw__wp32(o, y); + *o++ = 8; + *o++ = STBIW_UCHAR(ctype[n]); + *o++ = 0; + *o++ = 0; + *o++ = 0; + stbiw__wpcrc(&o,13); + + stbiw__wp32(o, zlen); + stbiw__wptag(o, "IDAT"); + STBIW_MEMMOVE(o, zlib, zlen); + o += zlen; + STBIW_FREE(zlib); + stbiw__wpcrc(&o, zlen); + + stbiw__wp32(o,0); + stbiw__wptag(o, "IEND"); + stbiw__wpcrc(&o,0); + + STBIW_ASSERT(o == out + *out_len); + + return out; +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) +{ + FILE *f; + int len; + unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + f = fopen(filename, "wb"); + if (!f) { STBIW_FREE(png); return 0; } + fwrite(png, 1, len, f); + fclose(f); + STBIW_FREE(png); + return 1; +} +#endif + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) +{ + int len; + unsigned char *png = stbi_write_png_to_mem((unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + func(context, png, len); + STBIW_FREE(png); + return 1; +} + + +/* *************************************************************************** + * + * JPEG writer + * + * This is based on Jon Olick's jo_jpeg.cpp: + * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html + */ + +static const unsigned char stbiw__jpg_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18, + 24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 }; + +static void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) { + int bitBuf = *bitBufP, bitCnt = *bitCntP; + bitCnt += bs[1]; + bitBuf |= bs[0] << (24 - bitCnt); + while(bitCnt >= 8) { + unsigned char c = (bitBuf >> 16) & 255; + stbiw__putc(s, c); + if(c == 255) { + stbiw__putc(s, 0); + } + bitBuf <<= 8; + bitCnt -= 8; + } + *bitBufP = bitBuf; + *bitCntP = bitCnt; +} + +static void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) { + float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p; + float z1, z2, z3, z4, z5, z11, z13; + + float tmp0 = d0 + d7; + float tmp7 = d0 - d7; + float tmp1 = d1 + d6; + float tmp6 = d1 - d6; + float tmp2 = d2 + d5; + float tmp5 = d2 - d5; + float tmp3 = d3 + d4; + float tmp4 = d3 - d4; + + // Even part + float tmp10 = tmp0 + tmp3; // phase 2 + float tmp13 = tmp0 - tmp3; + float tmp11 = tmp1 + tmp2; + float tmp12 = tmp1 - tmp2; + + d0 = tmp10 + tmp11; // phase 3 + d4 = tmp10 - tmp11; + + z1 = (tmp12 + tmp13) * 0.707106781f; // c4 + d2 = tmp13 + z1; // phase 5 + d6 = tmp13 - z1; + + // Odd part + tmp10 = tmp4 + tmp5; // phase 2 + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + // The rotator is modified from fig 4-8 to avoid extra negations. + z5 = (tmp10 - tmp12) * 0.382683433f; // c6 + z2 = tmp10 * 0.541196100f + z5; // c2-c6 + z4 = tmp12 * 1.306562965f + z5; // c2+c6 + z3 = tmp11 * 0.707106781f; // c4 + + z11 = tmp7 + z3; // phase 5 + z13 = tmp7 - z3; + + *d5p = z13 + z2; // phase 6 + *d3p = z13 - z2; + *d1p = z11 + z4; + *d7p = z11 - z4; + + *d0p = d0; *d2p = d2; *d4p = d4; *d6p = d6; +} + +static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) { + int tmp1 = val < 0 ? -val : val; + val = val < 0 ? val-1 : val; + bits[1] = 1; + while(tmp1 >>= 1) { + ++bits[1]; + } + bits[0] = val & ((1<0)&&(DU[end0pos]==0); --end0pos) { + } + // end0pos = first element in reverse order !=0 + if(end0pos == 0) { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); + return DU[0]; + } + for(i = 1; i <= end0pos; ++i) { + int startpos = i; + int nrzeroes; + unsigned short bits[2]; + for (; DU[i]==0 && i<=end0pos; ++i) { + } + nrzeroes = i-startpos; + if ( nrzeroes >= 16 ) { + int lng = nrzeroes>>4; + int nrmarker; + for (nrmarker=1; nrmarker <= lng; ++nrmarker) + stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes); + nrzeroes &= 15; + } + stbiw__jpg_calcBits(DU[i], bits); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); + } + if(end0pos != 63) { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); + } + return DU[0]; +} + +static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void* data, int quality) { + // Constants that don't pollute global namespace + static const unsigned char std_dc_luminance_nrcodes[] = {0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0}; + static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const unsigned char std_ac_luminance_nrcodes[] = {0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d}; + static const unsigned char std_ac_luminance_values[] = { + 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, + 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, + 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, + 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, + 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, + 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, + 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa + }; + static const unsigned char std_dc_chrominance_nrcodes[] = {0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0}; + static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const unsigned char std_ac_chrominance_nrcodes[] = {0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77}; + static const unsigned char std_ac_chrominance_values[] = { + 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, + 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, + 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, + 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, + 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, + 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, + 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa + }; + // Huffman tables + static const unsigned short YDC_HT[256][2] = { {0,2},{2,3},{3,3},{4,3},{5,3},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9}}; + static const unsigned short UVDC_HT[256][2] = { {0,2},{1,2},{2,2},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9},{1022,10},{2046,11}}; + static const unsigned short YAC_HT[256][2] = { + {10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} + }; + static const unsigned short UVAC_HT[256][2] = { + {0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} + }; + static const int YQT[] = {16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22, + 37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99}; + static const int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99, + 99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99}; + static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f, + 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f }; + + int row, col, i, k; + float fdtbl_Y[64], fdtbl_UV[64]; + unsigned char YTable[64], UVTable[64]; + + if(!data || !width || !height || comp > 4 || comp < 1) { + return 0; + } + + quality = quality ? quality : 90; + quality = quality < 1 ? 1 : quality > 100 ? 100 : quality; + quality = quality < 50 ? 5000 / quality : 200 - quality * 2; + + for(i = 0; i < 64; ++i) { + int uvti, yti = (YQT[i]*quality+50)/100; + YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti); + uvti = (UVQT[i]*quality+50)/100; + UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti); + } + + for(row = 0, k = 0; row < 8; ++row) { + for(col = 0; col < 8; ++col, ++k) { + fdtbl_Y[k] = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); + fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); + } + } + + // Write Headers + { + static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 }; + static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 }; + const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width), + 3,1,0x11,0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 }; + s->func(s->context, (void*)head0, sizeof(head0)); + s->func(s->context, (void*)YTable, sizeof(YTable)); + stbiw__putc(s, 1); + s->func(s->context, UVTable, sizeof(UVTable)); + s->func(s->context, (void*)head1, sizeof(head1)); + s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1); + s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values)); + stbiw__putc(s, 0x10); // HTYACinfo + s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1); + s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values)); + stbiw__putc(s, 1); // HTUDCinfo + s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1); + s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values)); + stbiw__putc(s, 0x11); // HTUACinfo + s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1); + s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values)); + s->func(s->context, (void*)head2, sizeof(head2)); + } + + // Encode 8x8 macroblocks + { + static const unsigned short fillBits[] = {0x7F, 7}; + const unsigned char *imageData = (const unsigned char *)data; + int DCY=0, DCU=0, DCV=0; + int bitBuf=0, bitCnt=0; + // comp == 2 is grey+alpha (alpha is ignored) + int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0; + int x, y, pos; + for(y = 0; y < height; y += 8) { + for(x = 0; x < width; x += 8) { + float YDU[64], UDU[64], VDU[64]; + for(row = y, pos = 0; row < y+8; ++row) { + for(col = x; col < x+8; ++col, ++pos) { + int p = row*width*comp + col*comp; + float r, g, b; + if(row >= height) { + p -= width*comp*(row+1 - height); + } + if(col >= width) { + p -= comp*(col+1 - width); + } + + r = imageData[p+0]; + g = imageData[p+ofsG]; + b = imageData[p+ofsB]; + YDU[pos]=+0.29900f*r+0.58700f*g+0.11400f*b-128; + UDU[pos]=-0.16874f*r-0.33126f*g+0.50000f*b; + VDU[pos]=+0.50000f*r-0.41869f*g-0.08131f*b; + } + } + + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); + DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); + } + } + + // Do the bit alignment of the EOI marker + stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits); + } + + // EOI + stbiw__putc(s, 0xFF); + stbiw__putc(s, 0xD9); + + return 1; +} + +STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality); +} + + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_jpg_core(&s, x, y, comp, data, quality); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + +#endif // STB_IMAGE_WRITE_IMPLEMENTATION + +/* Revision history + 1.07 (2017-07-24) + doc fix + 1.06 (2017-07-23) + writing JPEG (using Jon Olick's code) + 1.05 ??? + 1.04 (2017-03-03) + monochrome BMP expansion + 1.03 ??? + 1.02 (2016-04-02) + avoid allocating large structures on the stack + 1.01 (2016-01-16) + STBIW_REALLOC_SIZED: support allocators with no realloc support + avoid race-condition in crc initialization + minor compile issues + 1.00 (2015-09-14) + installable file IO function + 0.99 (2015-09-13) + warning fixes; TGA rle support + 0.98 (2015-04-08) + added STBIW_MALLOC, STBIW_ASSERT etc + 0.97 (2015-01-18) + fixed HDR asserts, rewrote HDR rle logic + 0.96 (2015-01-17) + add HDR output + fix monochrome BMP + 0.95 (2014-08-17) + add monochrome TGA output + 0.94 (2014-05-31) + rename private functions to avoid conflicts with stb_image.h + 0.93 (2014-05-27) + warning fixes + 0.92 (2010-08-01) + casts to unsigned char to fix warnings + 0.91 (2010-07-17) + first public release + 0.90 first internal release +*/ + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/src/lib/geogram/third_party/tetgen/tetgen.h b/src/lib/geogram/third_party/tetgen/tetgen.h index 125a9cae..9782711f 100755 --- a/src/lib/geogram/third_party/tetgen/tetgen.h +++ b/src/lib/geogram/third_party/tetgen/tetgen.h @@ -65,6 +65,7 @@ // [Bruno] I got too many complaints in tetgen so I "close my eyes" :-) #ifdef __GNUC__ #ifndef __ICC +#pragma GCC diagnostic ignored "-Wpragmas" #pragma GCC diagnostic ignored "-Wunknown-pragmas" #pragma GCC diagnostic ignored "-Wuninitialized" #pragma GCC diagnostic ignored "-Wunused-parameter" diff --git a/src/lib/geogram/voronoi/RVD.cpp b/src/lib/geogram/voronoi/RVD.cpp index bd6de730..ec92fa60 100644 --- a/src/lib/geogram/voronoi/RVD.cpp +++ b/src/lib/geogram/voronoi/RVD.cpp @@ -411,8 +411,8 @@ namespace { arg_scalars_ = m; spinlocks_.resize(delaunay_->nb_vertices()); parallel_for( - parallel_for_member_callback(this, &thisclass::run_thread), - 0, nb_parts() + 0, nb_parts(), + [this](index_t i) { run_thread(i); } ); } } @@ -526,8 +526,8 @@ namespace { arg_scalars_ = m; spinlocks_.resize(delaunay_->nb_vertices()); parallel_for( - parallel_for_member_callback(this, &thisclass::run_thread), - 0, nb_parts() + 0, nb_parts(), + [this](index_t i) { run_thread(i); } ); } } @@ -770,8 +770,8 @@ namespace { part(t).funcval_ = 0.0; } parallel_for( - parallel_for_member_callback(this, &thisclass::run_thread), - 0, nb_parts() + 0, nb_parts(), + [this](index_t i) { run_thread(i); } ); for(index_t t = 0; t < nb_parts(); t++) { f += part(t).funcval_; @@ -906,8 +906,8 @@ namespace { part(t).funcval_ = 0.0; } parallel_for( - parallel_for_member_callback(this, &thisclass::run_thread), - 0, nb_parts() + 0, nb_parts(), + [this](index_t i) { run_thread(i); } ); for(index_t t = 0; t < nb_parts(); t++) { f += part(t).funcval_; @@ -1050,8 +1050,8 @@ namespace { } parallel_for( - parallel_for_member_callback(this, &thisclass::run_thread), - 0, nb_parts() + 0, nb_parts(), + [this](index_t i) { run_thread(i); } ); f = 0.0; @@ -1127,8 +1127,8 @@ namespace { polygon_callback_->set_spinlocks(&spinlocks_); // Note: callback begin()/end() is called in for_each_polygon() parallel_for( - parallel_for_member_callback(this, &thisclass::run_thread), - 0, nb_parts() + 0, nb_parts(), + [this](index_t i) { run_thread(i); } ); polygon_callback_->set_spinlocks(nullptr); } @@ -1154,8 +1154,8 @@ namespace { // Note: callback begin()/end() is // called in for_each_polyhedron() parallel_for( - parallel_for_member_callback(this, &thisclass::run_thread), - 0, nb_parts() + 0, nb_parts(), + [this](index_t i) { run_thread(i); } ); polyhedron_callback_->set_spinlocks(nullptr); } diff --git a/src/lib/geogram/voronoi/RVD.cpp.new b/src/lib/geogram/voronoi/RVD.cpp.new new file mode 100644 index 00000000..d38c0ccb --- /dev/null +++ b/src/lib/geogram/voronoi/RVD.cpp.new @@ -0,0 +1,2651 @@ +/* + * Copyright (c) 2012-2014, Bruno Levy + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the ALICE Project-Team nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * If you modify this software, you should include a notice giving the + * name of the person performing the modification, the date of modification, + * and the reason for such modification. + * + * Contact: Bruno Levy + * + * Bruno.Levy@inria.fr + * http://www.loria.fr/~levy + * + * ALICE Project + * LORIA, INRIA Lorraine, + * Campus Scientifique, BP 239 + * 54506 VANDOEUVRE LES NANCY CEDEX + * FRANCE + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * There are three levels of implementation: + * Level 1: RestrictedVoronoiDiagram is the abstract API seen from client code + * Level 2: RVD_Nd_Impl implements RestrictedVoronoiDiagram + * Level 3: RVD_Nd_Impl::GenRestrictedVoronoiDiagram is + * an instantiation of GEOGen::RestrictedVoronoiDiagram (from generic_RVD.h) + * + * Warning: there are approx. 1000 lines of boring code ahead. + */ + +namespace { + + using namespace GEO; + + /** + * \brief Generic implementation of RestrictedVoronoiDiagram. + * \tparam DIM dimension + */ + template + class RVD_Nd_Impl : public GEO::RestrictedVoronoiDiagram { + + /** \brief This class type */ + typedef RVD_Nd_Impl thisclass; + + /** \brief The base class of this class */ + typedef RestrictedVoronoiDiagram baseclass; + + public: + /** \brief Implementation based on the generic version. */ + typedef GEOGen::RestrictedVoronoiDiagram + GenRestrictedVoronoiDiagram; + + /** \brief Representation of points. */ + typedef vecng Point; + + /** \brief Representation of vectors. */ + typedef vecng Vector; + + /** \brief Represents a point and its symbolic information. */ + typedef typename GenRestrictedVoronoiDiagram::Vertex Vertex; + + /** + * \brief Specifies the computation done by the threads. + */ + enum ThreadMode { + MT_NONE, /**< uninitialized */ + MT_LLOYD, /**< Lloyd iteration */ + MT_NEWTON, /**< Newton optimization */ + MT_INT_SMPLX, /**< Newton with integration simplex */ + MT_POLYG, /**< Polygon callback */ + MT_POLYH /**< Polyhedron callback */ + }; + + /** + * \brief Gets a mesh vertex from its index. + * \param[in] v index of the vertex + * \return a const reference to a Point + */ + const Point& mesh_vertex(index_t v) { + return *(const Point*) mesh_->vertices.point_ptr(v); + } + + + /** + * \brief Creates a RVD_Nd_Impl. + * + * \details The dimension is determined by \p mesh->dimension(). + * \param[in] delaunay the Delaunay triangulation + * \param[in] mesh the input mesh + * \param[in] R3_embedding gives for each vertex + * its mapping in 3D space. + * \param[in] R3_embedding_stride gives the stride between + * two consecutive vertices in R3_embedding + */ + RVD_Nd_Impl( + Delaunay* delaunay, Mesh* mesh, + const double* R3_embedding, index_t R3_embedding_stride + ) : + RestrictedVoronoiDiagram( + delaunay, mesh, R3_embedding, R3_embedding_stride + ), + RVD_(delaunay, mesh) { + use_exact_projection_ = false; + is_slave_ = false; + master_ = nullptr; + has_weights_ = false; + if(mesh->vertices.attributes().is_defined("weight")) { + vertex_weight_.bind(mesh->vertices.attributes(), "weight"); + has_weights_ = true; + } + parts_ = nullptr; + nb_parts_ = 0; + funcval_ = 0.0; + simplex_func_ = nullptr; + polygon_callback_ = nullptr; + polyhedron_callback_ = nullptr; + arg_vectors_ = nullptr; + arg_scalars_ = nullptr; + thread_mode_ = MT_NONE; + nb_triangles_ = 0; + } + + /** + * \brief Constructor for parts, used in multithreading mode. + */ + RVD_Nd_Impl() : + RestrictedVoronoiDiagram(nullptr, nullptr, nullptr, 0), + RVD_(nullptr, nullptr) { + use_exact_projection_ = false; + is_slave_ = true; + master_ = nullptr; + mesh_ = nullptr; + parts_ = nullptr; + nb_parts_ = 0; + facets_begin_ = -1; + facets_end_ = -1; + funcval_ = 0.0; + simplex_func_ = nullptr; + polygon_callback_ = nullptr; + polyhedron_callback_ = nullptr; + arg_vectors_ = nullptr; + arg_scalars_ = nullptr; + thread_mode_ = MT_NONE; + nb_triangles_ = 0; + } + + void set_delaunay(Delaunay* delaunay) override { + baseclass::set_delaunay(delaunay); + RVD_.set_delaunay(delaunay); + for(index_t p = 0; p < nb_parts_; ++p) { + parts_[p].set_delaunay(delaunay); + } + } + + void set_check_SR(bool x) override { + RVD_.set_check_SR(x); + for(index_t p = 0; p < nb_parts_; ++p) { + parts_[p].set_check_SR(x); + } + } + + void set_exact_predicates(bool x) override { + RVD_.set_exact_predicates(x); + for(index_t p = 0; p < nb_parts_; ++p) { + parts_[p].set_exact_predicates(x); + } + } + + bool exact_predicates() const override { + return RVD_.exact_predicates(); + } + + /********************************************************************/ + + /** + * \brief Place holder, "no locking" policy. + * \details NoLocks is used by algorithms templated + * by locking policy, for the single-threaded instances + * that do not need synchronization. The multi-threaded + * instances are parameterized by SpinLockArray. + */ + class NoLocks { + public: + /** + * \brief Acquires a spinlock. + * \details Does nothing in this version + * \param[in] i index of the spinlock to acquire + */ + void acquire_spinlock(index_t i) { + geo_argused(i); + } + + /** + * \brief Releases a spinlock. + * \details Does nothing in this version + * \param[in] i index of the spinlock to release + */ + void release_spinlock(index_t i) { + geo_argused(i); + } + }; + + // ____________________________________________________________________ + + /** + * \brief Implementation class for surfacic Lloyd relaxation. + * \details To be used as a template argument + * to RVD::for_each_triangle(). + * This version ignores the weights. + * + * Computes for each RVD cell: + * - mg[v] (v's Voronoi cell's total area times centroid) + * - m[v] (v's total area) + * \tparam LOCKS locking policy + * (can be one of Process::SpinLockArray, NoLocks) + */ + template + class ComputeCentroids { + public: + /** + * \brief Constructs a ComputeCentroids. + * \param[out] mg where to store the centroids + * \param[out] m where to store the masses + * \param[in] locks the array of locks + * (or NoLocks in single thread mode) + */ + ComputeCentroids( + double* mg, + double* m, + LOCKS& locks + ) : + mg_(mg), + m_(m), + locks_(locks) { + } + + /** + * \brief The callback called for each integration simplex. + * \param[in] v index of current center vertex + * \param[in] p1 first vertex of current integration simplex + * \param[in] p2 second vertex of current integration simplex + * \param[in] p3 third vertex of current integration simplex + */ + void operator() ( + index_t v, + const double* p1, + const double* p2, + const double* p3 + ) const { + double cur_m = Geom::triangle_area(p1, p2, p3, DIM); + double s = cur_m / 3.0; + locks_.acquire_spinlock(v); + m_[v] += cur_m; + double* cur_mg_out = mg_ + v * DIM; + for(coord_index_t coord = 0; coord < DIM; coord++) { + cur_mg_out[coord] += + s * (p1[coord] + p2[coord] + p3[coord]); + } + locks_.release_spinlock(v); + } + + private: + double* mg_; + double* m_; + LOCKS& locks_; + }; + + /** + * \brief Implementation class for surfacic Lloyd relaxation. + * \details To be used as a template + * argument to RVD::for_each_triangle(). + * This version takes the weights into account. + * + * Computes for each RVD cell: + * - mg[v] (v's Voronoi cell's total area times centroid) + * - m[v] (v's total area) + * \tparam LOCKS locking policy + * (can be one of Process::SpinLockArray, NoLocks) + */ + template + class ComputeCentroidsWeighted { + public: + /** + * \brief Constructs a ComputeCentroidsWeighted. + * \param[out] mg where to store the centroids + * \param[out] m where to store the masses + * \param[in] locks the array of locks + * (or NoLocks in single thread mode) + */ + ComputeCentroidsWeighted( + double* mg, + double* m, + LOCKS& locks + ) : + mg_(mg), + m_(m), + locks_(locks) { + } + + /** + * \brief The callback called for each integration simplex. + * \param[in] v index of current center vertex + * \param[in] v1 first vertex of current integration simplex + * \param[in] v2 second vertex of current integration simplex + * \param[in] v3 third vertex of current integration simplex + */ + void operator() ( + index_t v, + const Vertex& v1, + const Vertex& v2, + const Vertex& v3 + ) const { + double cur_m; + double cur_Vg[DIM]; + Geom::triangle_centroid( + v1.point(), v2.point(), v3.point(), + v1.weight(), v2.weight(), v3.weight(), + cur_Vg, cur_m, DIM + ); + locks_.acquire_spinlock(v); + m_[v] += cur_m; + double* cur_mg_out = mg_ + v * DIM; + for(coord_index_t coord = 0; coord < DIM; coord++) { + cur_mg_out[coord] += cur_Vg[coord]; + } + locks_.release_spinlock(v); + } + + private: + double* mg_; + double* m_; + LOCKS& locks_; + }; + + void compute_centroids_on_surface(double* mg, double* m) override { + create_threads(); + if(nb_parts() == 0) { + if(master_ != nullptr) { + if(has_weights_) { + RVD_.for_each_triangle( + ComputeCentroidsWeighted( + mg, m, master_->spinlocks_ + ) + ); + } else { + RVD_.for_each_triangle( + ComputeCentroids( + mg, m, master_->spinlocks_ + ) + ); + } + } else { + NoLocks nolocks; + if(has_weights_) { + RVD_.for_each_triangle( + ComputeCentroidsWeighted( + mg, m, nolocks + ) + ); + } else { + RVD_.for_each_triangle( + ComputeCentroids(mg, m, nolocks) + ); + } + } + } else { + thread_mode_ = MT_LLOYD; + arg_vectors_ = mg; + arg_scalars_ = m; + spinlocks_.resize(delaunay_->nb_vertices()); + parallel_for( + 0, nb_parts(), + std::bind(&thisclass::run_thread, this, std::placeholders::_1) + ); + } + } + + /********************************************************************/ + + /** + * \brief Implementation class for surfacic Lloyd relaxation. + * \details To be used as a template argument + * to RVD::for_each_volumetric_integration_simplex(). + * This version ignores the weights. + * + * Computes for each RVD cell: + * - mg[v] (v's Voronoi cell's total area times centroid) + * - m[v] (v's total area) + * \tparam LOCKS locking policy + * (can be one of Process::SpinLockArray, NoLocks) + */ + template + class ComputeCentroidsVolumetric { + public: + /** + * \brief Constructs a ComputeCentroidsVolumetric. + * \param[out] mg where to store the centroids + * \param[out] m where to store the masses + * \param[in] delaunay the Delaunay triangulation + * \param[in] locks the array of locks + * (or NoLocks in single thread mode) + */ + ComputeCentroidsVolumetric( + double* mg, + double* m, + const Delaunay* delaunay, + LOCKS& locks + ) : + mg_(mg), + m_(m), + delaunay_(delaunay), + locks_(locks) { + } + + /** + * \brief The callback called for each integration simplex. + * \param[in] v index of current center vertex + * \param[in] v_adj (unused here) is the index of the Voronoi cell + * adjacent to t accros facet (\p v1, \p v2, \p v3) or + * -1 if it does not exists + * \param[in] t (unused here) is the index of the current + * tetrahedron + * \param[in] t_adj (unused here) is the index of the + * tetrahedron adjacent to t accros facet (\p v1, \p v2, \p v3) + * or -1 if it does not exists + * \param[in] p0 first vertex of current integration simplex + * \param[in] p1 second vertex of current integration simplex + * \param[in] p2 third vertex of current integration simplex + * \param[in] p3 fourth vertex of current integration simplex + */ + void operator() ( + index_t v, signed_index_t v_adj, + index_t t, signed_index_t t_adj, + const double* p0, + const double* p1, + const double* p2, + const double* p3 + ) const { + geo_argused(v_adj); + geo_argused(t); + geo_argused(t_adj); + double cur_m = Geom::tetra_volume( + p0, p1, p2, p3 + ); + double s = cur_m / 4.0; + locks_.acquire_spinlock(v); + m_[v] += cur_m; + double* cur_mg_out = mg_ + v * DIM; + for(coord_index_t coord = 0; coord < DIM; coord++) { + cur_mg_out[coord] += s * ( + p0[coord] + p1[coord] + p2[coord] + p3[coord] + ); + } + locks_.release_spinlock(v); + } + + private: + double* mg_; + double* m_; + const Delaunay* delaunay_; + LOCKS& locks_; + }; + + void compute_centroids_in_volume(double* mg, double* m) override { + create_threads(); + if(nb_parts() == 0) { + if(master_ != nullptr) { + RVD_.for_each_tetrahedron( + ComputeCentroidsVolumetric( + mg, m, RVD_.delaunay(), master_->spinlocks_ + ) + ); + } else { + NoLocks nolocks; + RVD_.for_each_tetrahedron( + ComputeCentroidsVolumetric( + mg, m, RVD_.delaunay(), nolocks + ) + ); + } + } else { + thread_mode_ = MT_LLOYD; + arg_vectors_ = mg; + arg_scalars_ = m; + spinlocks_.resize(delaunay_->nb_vertices()); + parallel_for( + 0, nb_parts(), + std::bind(&thisclass::run_thread, this, std::placeholders::_1) + ); + } + } + + /********************************************************************/ + + /** + * \brief Implementation class for Newton-based restricted CVT. + * \details To be used as a template argument + * to RVD::for_each_triangle(). + * This version ignores the weights. + * + * Computes for each RVD cell: + * - g (gradient) + * - f (CVT energy) + * \tparam LOCKS locking policy + * (can be one of Process::SpinLockArray, NoLocks) + */ + template + class ComputeCVTFuncGrad { + public: + /** + * \brief Constructs a ComputeCVTFuncGrad. + * \param[in] RVD the restricted Voronoi diagram + * \param[out] f the computed function value + * \param[out] g the computed gradient of f, + * allocated by caller, and managed + * by caller + * \param[in] locks the array of locks + * (or NoLocks in single thread mode) + */ + ComputeCVTFuncGrad( + const GenRestrictedVoronoiDiagram& RVD, + double& f, + double* g, + LOCKS& locks + ) : + f_(f), + g_(g), + locks_(locks), + RVD_(RVD) { + } + + /** + * \brief The callback called for each integration simplex. + * \param[in] v index of current center vertex + * \param[in] p1 first vertex of current integration simplex + * \param[in] p2 second vertex of current integration simplex + * \param[in] p3 third vertex of current integration simplex + */ + void operator() ( + index_t v, + const double* p1, + const double* p2, + const double* p3 + ) const { + + const double* p0 = RVD_.delaunay()->vertex_ptr(v); + + double t_area = Geom::triangle_area(p1, p2, p3, DIM); + + double cur_f = 0.0; + for(index_t c = 0; c < DIM; c++) { + double u0 = p0[c] - p1[c]; + double u1 = p0[c] - p2[c]; + double u2 = p0[c] - p3[c]; + cur_f += u0 * u0; + cur_f += u1 * (u0 + u1); + cur_f += u2 * (u0 + u1 + u2); + } + + f_ += t_area * cur_f / 6.0; + + locks_.acquire_spinlock(v); + for(index_t c = 0; c < DIM; c++) { + double Gc = (1.0 / 3.0) * (p1[c] + p2[c] + p3[c]); + g_[DIM * v + c] += (2.0 * t_area) * (p0[c] - Gc); + } + locks_.release_spinlock(v); + } + + double& f_; + double* g_; + LOCKS& locks_; + const GenRestrictedVoronoiDiagram& RVD_; + }; + + /** + * \brief Implementation class for Newton-based restricted CVT. + * \details To be used as a template argument + * to RVD::for_each_triangle(). + * This version takes the weights into account. + * + * Computes for each RVD cell: + * - g (gradient) + * - f (CVT energy) + * \tparam LOCKS locking policy + * (can be one of Process::SpinLockArray, NoLocks) + */ + template + class ComputeCVTFuncGradWeighted { + public: + /** + * \brief Constructs a ComputeCVTFuncGradWeighted. + * \param[in] RVD the restricted Voronoi diagram + * \param[out] f the computed function value + * \param[out] g the computed gradient of f, + * allocated by caller, and managed by caller + * \param[in] locks the array of locks + * (or NoLocks in single thread mode) + */ + ComputeCVTFuncGradWeighted( + const GenRestrictedVoronoiDiagram& RVD, + double& f, + double* g, + LOCKS& locks + ) : + f_(f), + g_(g), + locks_(locks), + RVD_(RVD) { + } + + /** + * \brief The callback called for each integration simplex. + * \param[in] v index of current center vertex + * \param[in] v1 first vertex of current integration simplex + * \param[in] v2 second vertex of current integration simplex + * \param[in] v3 third vertex of current integration simplex + */ + void operator() ( + index_t v, + const Vertex& v1, + const Vertex& v2, + const Vertex& v3 + ) const { + + const double* p0 = RVD_.delaunay()->vertex_ptr(v); + + const double* p1 = v1.point(); + const double* p2 = v2.point(); + const double* p3 = v3.point(); + + double t_area = Geom::triangle_area(p1, p2, p3, DIM); + + double Sp = v1.weight() + v2.weight() + v3.weight(); + double rho[3], alpha[3]; + rho[0] = v1.weight(); + rho[1] = v2.weight(); + rho[2] = v3.weight(); + alpha[0] = Sp + rho[0]; + alpha[1] = Sp + rho[1]; + alpha[2] = Sp + rho[2]; + + double dotprod_00 = 0.0; + double dotprod_10 = 0.0; + double dotprod_11 = 0.0; + double dotprod_20 = 0.0; + double dotprod_21 = 0.0; + double dotprod_22 = 0.0; + for(unsigned int c = 0; c < DIM; c++) { + double sp0 = p0[c] - p1[c]; + double sp1 = p0[c] - p2[c]; + double sp2 = p0[c] - p3[c]; + dotprod_00 += sp0 * sp0; + dotprod_10 += sp1 * sp0; + dotprod_11 += sp1 * sp1; + dotprod_20 += sp2 * sp0; + dotprod_21 += sp2 * sp1; + dotprod_22 += sp2 * sp2; + } + + double cur_f = 0.0; + cur_f += (alpha[0] + rho[0]) * dotprod_00; // 0 0 + cur_f += (alpha[1] + rho[0]) * dotprod_10; // 1 0 + cur_f += (alpha[1] + rho[1]) * dotprod_11; // 1 1 + cur_f += (alpha[2] + rho[0]) * dotprod_20; // 2 0 + cur_f += (alpha[2] + rho[1]) * dotprod_21; // 2 1 + cur_f += (alpha[2] + rho[2]) * dotprod_22; // 2 2 + + f_ += t_area * cur_f / 30.0; + double* g_out = g_ + v * DIM; + locks_.acquire_spinlock(v); + for(index_t c = 0; c < DIM; c++) { + g_out[c] += (t_area / 6.0) * ( + 4.0 * Sp * p0[c] - ( + alpha[0] * p1[c] + + alpha[1] * p2[c] + + alpha[2] * p3[c] + ) + ); + } + locks_.release_spinlock(v); + } + + double& f_; + double* g_; + LOCKS& locks_; + const GenRestrictedVoronoiDiagram& RVD_; + }; + + void compute_CVT_func_grad_on_surface(double& f, double* g) override { + create_threads(); + if(nb_parts() == 0) { + if(master_ != nullptr) { + if(has_weights_) { + RVD_.for_each_triangle( + ComputeCVTFuncGradWeighted( + RVD_, f, g, master_->spinlocks_ + ) + ); + } else { + RVD_.for_each_triangle( + ComputeCVTFuncGrad( + RVD_, f, g, master_->spinlocks_ + ) + ); + } + } else { + NoLocks nolocks; + if(has_weights_) { + RVD_.for_each_triangle( + ComputeCVTFuncGradWeighted( + RVD_, f, g, nolocks + ) + ); + } else { + RVD_.for_each_triangle( + ComputeCVTFuncGrad( + RVD_, f, g, nolocks + ) + ); + } + } + } else { + thread_mode_ = MT_NEWTON; + arg_vectors_ = g; + spinlocks_.resize(delaunay_->nb_vertices()); + for(index_t t = 0; t < nb_parts(); t++) { + part(t).funcval_ = 0.0; + } + parallel_for( + 0, nb_parts(), + std::bind(&thisclass::run_thread, this, std::placeholders::_1) + ); + for(index_t t = 0; t < nb_parts(); t++) { + f += part(t).funcval_; + } + } + } + + /********************************************************************/ + + /** + * \brief Implementation class for Newton-based restricted CVT + * in volume. + * \details To be used as a template argument + * to RVD::for_each_volumetric_integration_simplex(). + * This version ignores the weights. + * + * Computes for each RVD cell: + * - g (gradient) + * - f (CVT energy) + * \tparam LOCKS locking policy + * (can be one of Process::SpinLockArray, NoLocks) + */ + template + class ComputeCVTFuncGradVolumetric { + public: + /** + * \brief Constructs a ComputeCentroidsFuncGradVolumetric. + * \param[in] RVD the restricted Voronoi diagram + * \param[out] f the computed function value + * \param[out] g the computed gradient of f, + * allocated by caller, and managed + * by caller + * \param[in] locks the array of locks + * (or NoLocks in single thread mode) + */ + ComputeCVTFuncGradVolumetric( + const GenRestrictedVoronoiDiagram& RVD, + double& f, + double* g, + LOCKS& locks + ) : + f_(f), + g_(g), + locks_(locks), + RVD_(RVD) { + } + + /** + * \brief The callback called for each integration simplex. + * \param[in] v index of current center vertex + * \param[in] v_adj (unused here) is the index of the Voronoi cell + * adjacent to t accros facet (\p v1, \p v2, \p v3) or + * -1 if it does not exists + * \param[in] t (unused here) is the index of the current + * tetrahedron + * \param[in] t_adj (unused here) is the index of the + * tetrahedron adjacent to t accros facet (\p v1, \p v2, \p v3) + * or -1 if it does not exists + * \param[in] p1 first vertex of current integration simplex + * \param[in] p2 second vertex of current integration simplex + * \param[in] p3 third vertex of current integration simplex + */ + void operator() ( + index_t v, + signed_index_t v_adj, + index_t t, + signed_index_t t_adj, + const double* p1, + const double* p2, + const double* p3 + ) const { + geo_argused(v_adj); + geo_argused(t); + geo_argused(t_adj); + const double* p0 = RVD_.delaunay()->vertex_ptr(v); + + double mi = Geom::tetra_volume(p0, p1, p2, p3); + + // fi = (mi/10)*(U.U + V.V + W.W + U.V + V.W + W.U) + // where: U = p1-p0 ; V = p2-p0 and W=p3-p0 + double fi = 0.0; + for(coord_index_t c = 0; c < DIM; ++c) { + double Uc = p1[c] - p0[c]; + double Vc = p2[c] - p0[c]; + double Wc = p3[c] - p0[c]; + fi += geo_sqr(Uc) + geo_sqr(Vc) + geo_sqr(Wc); + fi += (Uc * Vc + Vc * Wc + Wc * Uc); + } + fi *= (mi / 10.0); + f_ += fi; + + // gi = 2*mi(p0 - 1/4(p0 + p1 + p2 + p3)) + double* g_out = g_ + v * DIM; + locks_.acquire_spinlock(v); + for(coord_index_t c = 0; c < DIM; ++c) { + g_out[c] += 2.0 * mi * ( + 0.75 * p0[c] + - 0.25 * p1[c] - 0.25 * p2[c] - 0.25 * p3[c] + ); + } + locks_.release_spinlock(v); + } + + double& f_; + double* g_; + LOCKS& locks_; + const GenRestrictedVoronoiDiagram& RVD_; + }; + + void compute_CVT_func_grad_in_volume(double& f, double* g) override { + create_threads(); + if(nb_parts() == 0) { + if(master_ != nullptr) { + RVD_.for_each_volumetric_integration_simplex( + ComputeCVTFuncGradVolumetric( + RVD_, f, g, master_->spinlocks_ + ) + ); + } else { + NoLocks nolocks; + RVD_.for_each_volumetric_integration_simplex( + ComputeCVTFuncGradVolumetric( + RVD_, f, g, nolocks + ) + ); + } + } else { + thread_mode_ = MT_NEWTON; + arg_vectors_ = g; + spinlocks_.resize(delaunay_->nb_vertices()); + for(index_t t = 0; t < nb_parts(); t++) { + part(t).funcval_ = 0.0; + } + parallel_for( + 0, nb_parts(), + std::bind(&thisclass::run_thread, this, std::placeholders::_1) + ); + for(index_t t = 0; t < nb_parts(); t++) { + f += part(t).funcval_; + } + } + } + + /********************************************************************/ + + /** + * \brief Implementation class for computing function integrals + * and gradients over integration simplices. + * \details To be used as a template argument + * to RVD::for_each_triangle() and + * RVD::for_each_volumetric_integration_simplex() + */ + class ComputeCVTFuncGradIntegrationSimplex { + public: + /** + * \brief Constructs a ComputeCVTFuncGradIntegrationSimplex. + * \param[in] RVD the Restricted Voronoi Diagram + * \param[in] F the IntegrationSimplex + */ + ComputeCVTFuncGradIntegrationSimplex( + const GenRestrictedVoronoiDiagram& RVD, + IntegrationSimplex* F + ) : + f_(0.0), + RVD_(RVD), + simplex_func_(F) { + simplex_func_->reset_thread_local_storage(); + } + + /** + * \brief The callback called for each surfacic integration simplex. + * \param[in] i index of current center vertex + * \param[in] v1 first vertex of current integration simplex + * \param[in] v2 second vertex of current integration simplex + * \param[in] v3 third vertex of current integration simplex + */ + void operator() ( + index_t i, + const Vertex& v1, + const Vertex& v2, + const Vertex& v3 + ) { + f_ += simplex_func_->eval( + i,v1,v2,v3,RVD_.current_facet() + ); + } + + /** + * \brief The callback called for each volumetric + * integration simplex. + * \param[in] v index of current center vertex + * \param[in] v_adj index of the Voronoi cell adjacent to t accros + * facet (\p v1, \p v2, \p v3) or -1 if it does not exists + * \param[in] t index of the current tetrahedron + * \param[in] t_adj index of the tetrahedron adjacent to t accros + * facet (\p v1, \p v2, \p v3) or -1 if it does not exists + * \param[in] v1 first vertex of current integration simplex + * \param[in] v2 second vertex of current integration simplex + * \param[in] v3 third vertex of current integration simplex + */ + void operator() ( + index_t v, + signed_index_t v_adj, + index_t t, + signed_index_t t_adj, + const Vertex& v1, + const Vertex& v2, + const Vertex& v3 + ) { + geo_argused(v_adj); + geo_argused(t_adj); + f_ += simplex_func_->eval( + v,v1,v2,v3,t,index_t(t_adj),index_t(v_adj) + ); + } + + /** + * \brief Gets the function value. + * \return The function value accumulated so far. + */ + double f() const { + return f_; + } + + private: + double f_; + const GenRestrictedVoronoiDiagram& RVD_; + IntegrationSimplex* simplex_func_; + }; + + void compute_integration_simplex_func_grad( + double& f, double* g, IntegrationSimplex* F + ) override { + create_threads(); + if(nb_parts() == 0) { + if(master_ == nullptr) { + F->set_points_and_gradient( + delaunay()->dimension(), + delaunay()->nb_vertices(), + delaunay()->vertex_ptr(0), + g + ); + } + ComputeCVTFuncGradIntegrationSimplex C(RVD_,F); + bool sym = RVD_.symbolic(); + RVD_.set_symbolic(true); + if(F->volumetric()) { + RVD_.for_each_volumetric_integration_simplex( + C, + F->background_mesh_has_varying_attribute(), + false /* Coherent triangles */ + ); + } else { + RVD_.for_each_triangle(C); + } + RVD_.set_symbolic(sym); + funcval_ = C.f(); + f = C.f(); + } else { + spinlocks_.resize(delaunay_->nb_vertices()); + F->set_points_and_gradient( + delaunay()->dimension(), + delaunay()->nb_vertices(), + delaunay()->vertex_ptr(0), + g, + &spinlocks_ + ); + thread_mode_ = MT_INT_SMPLX; + arg_vectors_ = g; + simplex_func_ = F; + funcval_ = 0.0; + for(index_t t = 0; t < nb_parts(); t++) { + part(t).arg_vectors_ = g; + part(t).simplex_func_ = F; + part(t).funcval_ = 0.0; + } + + parallel_for( + 0, nb_parts(), + std::bind(&thisclass::run_thread, this, std::placeholders::_1) + ); + + f = 0.0; + for(index_t t = 0; t < nb_parts(); t++) { + f += part(t).funcval_; + } + } + } + + /********************************************************************/ + + /** + * \brief Adapter class used internally to implement for_each_polygon() + * \details Gets the current triangle from the RVD and passes it back + * to the callback. It is needed because GenericRVD::for_each_polygon() + * does not pass the current triangle. + */ + // TODO: pass it through all the callbacks, because it is ridiculous: + // we pass it through the first levels, then throw it, then retrieve it + // (see GenRVD) + class PolygonCallbackAction { + public: + /** + * \brief PolygonCallbackAction constructor + * \param[in] RVD a pointer to the restricted Voronoi diagram + * \param[in] callback a pointer to the PolygonCallback + */ + PolygonCallbackAction( + GenRestrictedVoronoiDiagram& RVD, + GEO::RVDPolygonCallback& callback + ) : + RVD_(RVD), + callback_(callback) { + } + + /** + * \brief Callback called for each polygon. + * \details Routes the callback to the wrapped user action class. + * \param[in] v index of current Delaunay seed + * \param[in] P intersection between current mesh facet + * and the Voronoi cell of \p v + */ + void operator() ( + index_t v, + const GEOGen::Polygon& P + ) const { + callback_(v, RVD_.current_facet(), P); + } + + protected: + GenRestrictedVoronoiDiagram& RVD_; + GEO::RVDPolygonCallback& callback_; + }; + + + virtual void compute_with_polygon_callback( + GEO::RVDPolygonCallback& polygon_callback + ) { + create_threads(); + if(nb_parts() == 0) { + PolygonCallbackAction action(RVD_,polygon_callback); + RVD_.for_each_polygon(action); + } else { + for(index_t t = 0; t < nb_parts(); t++) { + part(t).RVD_.set_symbolic(RVD_.symbolic()); + part(t).RVD_.set_connected_components_priority( + RVD_.connected_components_priority() + ); + } + spinlocks_.resize(delaunay_->nb_vertices()); + thread_mode_ = MT_POLYG; + polygon_callback_ = &polygon_callback; + polygon_callback_->set_spinlocks(&spinlocks_); + // Note: callback begin()/end() is called in for_each_polygon() + parallel_for( + 0, nb_parts(), + std::bind(&thisclass::run_thread, this, std::placeholders::_1) + ); + polygon_callback_->set_spinlocks(nullptr); + } + } + + virtual void compute_with_polyhedron_callback( + GEO::RVDPolyhedronCallback& polyhedron_callback + ) { + create_threads(); + if(nb_parts() == 0) { + RVD_.for_each_polyhedron(polyhedron_callback); + } else { + for(index_t t = 0; t < nb_parts(); t++) { + part(t).RVD_.set_symbolic(RVD_.symbolic()); + part(t).RVD_.set_connected_components_priority( + RVD_.connected_components_priority() + ); + } + spinlocks_.resize(delaunay_->nb_vertices()); + thread_mode_ = MT_POLYH; + polyhedron_callback_ = &polyhedron_callback; + polyhedron_callback_->set_spinlocks(&spinlocks_); + // Note: callback begin()/end() is + // called in for_each_polyhedron() + parallel_for( + 0, nb_parts(), + std::bind(&thisclass::run_thread, this, std::placeholders::_1) + ); + polyhedron_callback_->set_spinlocks(nullptr); + } + } + + /********************************************************************/ + + /** + * \brief Implementation class for explicitly constructing + * a surfacic mesh that corresponds to the surfacic + * restricted Voronoi diagram. + * \details To be used as a template argument + * to RVD::for_each_polygon(). The current Vornoi cell is + * reported in facet region. + * \tparam BUILDER a class that implements iterative mesh building, + * e.g., MeshBuilder. + */ + template + class BuildRVD { + public: + /** + * \brief Constructs a new BuildRVD. + * \param[in] RVD_in the restricted Voronoi diagram + * \param[in] builder the lesh builder + */ + BuildRVD( + const GenRestrictedVoronoiDiagram& RVD_in, + BUILDER& builder + ) : + RVD(RVD_in), + builder_(builder), + current_facet_(-1) { + builder_.begin_surface(); + } + + /** + * \brief The destructor + * \details Terminates the current facet + * and the current surface. + */ + ~BuildRVD() { + if(current_facet_ != -1) { + builder_.end_reference_facet(); + } + builder_.end_surface(); + } + + /** + * \brief The callback called for each restricted Voronoi cell. + * \param[in] v index of current center vertex + * \param[in] P current restricted Voronoi cell + */ + void operator() ( + index_t v, + const typename GenRestrictedVoronoiDiagram::Polygon& P + ) { + index_t f = RVD.current_facet(); + if(signed_index_t(f) != current_facet_) { + if(current_facet_ != -1) { + builder_.end_reference_facet(); + } + current_facet_ = signed_index_t(f); + builder_.begin_reference_facet(f); + } + builder_.begin_facet(v); + for(index_t i = 0; i < P.nb_vertices(); i++) { + const Vertex& ve = P.vertex(i); + builder_.add_vertex_to_facet(ve.point(), ve.sym()); + } + builder_.end_facet(); + } + + private: + const GenRestrictedVoronoiDiagram& RVD; + BUILDER& builder_; + signed_index_t current_facet_; + }; + + /** + * \brief Implementation class for explicitly constructing + * a volumetric mesh that corresponds to the volumetric + * restricted Voronoi diagram. + * \details To be used as a template argument + * to RVD::for_each_volumetric_integration_simplex(). + * The current Voronoi cell is reported in tetrahedron region. + * \note For the moment, vertices are duplicated (will be fixed + * in a future version). + */ + class BuildVolumetricRVD { + public: + /** + * Constructs a new BuildVolumetricRVD. + * \param[in] RVD the volumetric restricted Voronoi diagram + * \param[in] dim dimension of the points (can be smaller + * than actual dimension of the RVD). + * \param[out] vertices coordinates of the generated vertices + * \param[out] triangle_vertex_indices generated triangles, as + * vertex indices triplets + * \param[out] tet_vertex_indices generated tetrahedra, as + * vertex indices 4-uples + * \param[out] triangle_regions each generated triangle has + * a region index, that corresponds to the index of the + * Voronoi cell the triangle belongs to + * \param[out] tet_regions each generated tetrahedron has + * a region index, that corresponds to the index of the + * Voronoi cell the tetrahedron belongs to + * \param[in] cell_borders_only if true, only the surfacic + * borders of the volumetric cells are saved in the mesh, else + * volumetric cells are tetrahedralized. + * \pre dim <= delaunay->dimension() + */ + BuildVolumetricRVD( + GenRestrictedVoronoiDiagram& RVD, + coord_index_t dim, + vector& vertices, + vector& triangle_vertex_indices, + vector& tet_vertex_indices, + vector& triangle_regions, + vector& tet_regions, + bool cell_borders_only + ) : + delaunay_(RVD.delaunay()), + mesh_(RVD.mesh()), + dim_(dim), + vertices_(vertices), + triangle_vertex_indices_(triangle_vertex_indices), + tet_vertex_indices_(tet_vertex_indices), + triangle_regions_(triangle_regions), + tet_regions_(tet_regions), + cell_borders_only_(cell_borders_only) + { + vertices_.clear(); + triangle_vertex_indices_.clear(); + tet_vertex_indices_.clear(); + triangle_regions_.clear(); + tet_regions_.clear(); + + // The first vertices are copied from Delaunay, + // the other ones will be created during the traversal + nb_vertices_ = delaunay_->nb_vertices(); + vertices_.resize(nb_vertices_ * dim); + for(index_t v = 0; v < delaunay_->nb_vertices(); ++v) { + for(coord_index_t c = 0; c < dim; ++c) { + vertices_[v * dim + c] = delaunay_->vertex_ptr(v)[c]; + } + } + vertex_map_.set_first_vertex_index(nb_vertices_); + } + + /** + * \brief The callback called for each integration simplex. + * \param[in] v index of current center vertex + * \param[in] v_adj (unused here) is the index of the Voronoi cell + * adjacent to t accros facet (\p v1, \p v2, \p v3) or + * -1 if it does not exists + * \param[in] t (unused here) is the index of the current + * tetrahedron + * \param[in] t_adj (unused here) is the index of the + * tetrahedron adjacent to t accros facet (\p v1, \p v2, \p v3) + * or -1 if it does not exists + * \param[in] v1 first vertex of current integration simplex + * \param[in] v2 second vertex of current integration simplex + * \param[in] v3 third vertex of current integration simplex + */ + void operator() ( + index_t v, signed_index_t v_adj, + index_t t, signed_index_t t_adj, + const Vertex& v1, const Vertex& v2, const Vertex& v3 + ) { + geo_argused(v_adj); + geo_argused(t); + + if(cell_borders_only_) { + if(signed_index_t(v) > v_adj) { + index_t iv2 = find_or_create_vertex(v, v1); + index_t iv3 = find_or_create_vertex(v, v2); + index_t iv4 = find_or_create_vertex(v, v3); + triangle_vertex_indices_.push_back(iv4); + triangle_vertex_indices_.push_back(iv3); + triangle_vertex_indices_.push_back(iv2); + triangle_regions_.push_back(signed_index_t(v)); + } + } else { + index_t iv1 = v; + index_t iv2 = find_or_create_vertex(v, v1); + index_t iv3 = find_or_create_vertex(v, v2); + index_t iv4 = find_or_create_vertex(v, v3); + + // Triangle v1,v2,v3 is on border if there is + // no adjacent seed and no adjacent tet. + if(v_adj == -1 && t_adj == -1) { + triangle_vertex_indices_.push_back(iv4); + triangle_vertex_indices_.push_back(iv3); + triangle_vertex_indices_.push_back(iv2); + triangle_regions_.push_back(signed_index_t(v)); + } + + tet_vertex_indices_.push_back(iv1); + tet_vertex_indices_.push_back(iv2); + tet_vertex_indices_.push_back(iv3); + tet_vertex_indices_.push_back(iv4); + tet_regions_.push_back(signed_index_t(v)); + } + } + + /** + * \brief The callback called for each tetrahedron + * \param[in] v index of current center vertex + * \param[in] v_adj (unused here) is the index of the Voronoi cell + * adjacent to t accros facet (\p v1, \p v2, \p v3) or + * -1 if it does not exists + * \param[in] t (unused here) is the index of the current + * tetrahedron + * \param[in] t_adj (unused here) is the index of the + * tetrahedron adjacent to t accros facet (\p v1, \p v2, \p v3) + * or -1 if it does not exists + * \param[in] v1 first vertex of current tetrahedron + * \param[in] v2 second vertex of current tetrahedron + * \param[in] v3 third vertex of current tetrahedron + * \param[in] v4 fourth vertex of current tetrahedron + */ + void operator() ( + index_t v, signed_index_t v_adj, + index_t t, signed_index_t t_adj, + const Vertex& v1, const Vertex& v2, + const Vertex& v3, const Vertex& v4 + ) { + geo_argused(v_adj); + geo_argused(t); + geo_argused(t_adj); + index_t iv1 = vertices_.size() / dim_; + for(index_t c = 0; c < dim_; ++c) { + vertices_.push_back(v1.point()[c]); + } + index_t iv2 = vertices_.size() / dim_; + for(index_t c = 0; c < dim_; ++c) { + vertices_.push_back(v2.point()[c]); + } + index_t iv3 = vertices_.size() / dim_; + for(index_t c = 0; c < dim_; ++c) { + vertices_.push_back(v3.point()[c]); + } + index_t iv4 = vertices_.size() / dim_; + for(index_t c = 0; c < dim_; ++c) { + vertices_.push_back(v4.point()[c]); + } + tet_vertex_indices_.push_back(iv1); + tet_vertex_indices_.push_back(iv2); + tet_vertex_indices_.push_back(iv3); + tet_vertex_indices_.push_back(iv4); + tet_regions_.push_back(signed_index_t(v)); + } + + protected: + /** + * \brief Retrieves the index of a vertex given its symbolic + * representation. + * \param[in] center_vertex_id index of current Voronoi seed + * \param[in] v symbolic and geometric representation of the vertex + * \return the index of the vertex + */ + index_t find_or_create_vertex( + index_t center_vertex_id, const Vertex& v + ) { + index_t result = vertex_map_.find_or_create_vertex( + center_vertex_id, v.sym() + ); + if(result >= nb_vertices_) { + geo_assert(result == nb_vertices_); + nb_vertices_ = result + 1; + for(coord_index_t c = 0; c < dim_; ++c) { + vertices_.push_back(v.point()[c]); + } + } + return result; + } + + private: + const Delaunay* delaunay_; + const Mesh* mesh_; + coord_index_t dim_; + vector& vertices_; + vector& triangle_vertex_indices_; + vector& tet_vertex_indices_; + vector& triangle_regions_; + vector& tet_regions_; + RVDVertexMap vertex_map_; + index_t nb_vertices_; + bool cell_borders_only_; + }; + + void compute_RVD( + Mesh& M, coord_index_t dim, bool cell_borders_only, + bool integration_simplices + ) override { + bool sym = RVD_.symbolic(); + RVD_.set_symbolic(true); + if(volumetric_) { + if(dim == 0) { + dim = dimension(); + } + vector vertices; + vector triangle_vertices; + vector tet_vertices; + vector triangle_regions; + vector tet_regions; + if(cell_borders_only) { + RVD_.for_each_volumetric_integration_simplex( + BuildVolumetricRVD( + RVD_, dim, + vertices, + triangle_vertices, + tet_vertices, + triangle_regions, + tet_regions, + cell_borders_only + ), + false, // Do not visit inner tetrahedra. + true // Ensure that polygonal facets are triangulated + // coherently. + ); + } else { + if(integration_simplices) { + RVD_.for_each_volumetric_integration_simplex( + BuildVolumetricRVD( + RVD_, dim, + vertices, + triangle_vertices, + tet_vertices, + triangle_regions, + tet_regions, + cell_borders_only + ), + false, // Do not visit inner tetrahedra. + true // Ensure that polygonal facets are + // triangulated coherently. + ); + } else { + RVD_.for_each_tetrahedron( + BuildVolumetricRVD( + RVD_, dim, + vertices, + triangle_vertices, + tet_vertices, + triangle_regions, + tet_regions, + cell_borders_only + ) + ); + } + } + + M.clear(true); // keep attributes + + M.vertices.assign_points(vertices,dim,true); + M.facets.assign_triangle_mesh(triangle_vertices, true); + M.cells.assign_tet_mesh(tet_vertices, true); + + // TODO: use Attribute::assign(vector, steal_args) + // when it is there... + + if(M.facets.nb() != 0) { + Attribute facet_region_attr( + M.facets.attributes(), "region" + ); + for(index_t f=0; f cell_region_attr( + M.cells.attributes(), "region" + ); + for(index_t c=0; c(RVD_, builder) + ); + } + RVD_.set_symbolic(sym); + M.show_stats("RVD"); + } + + /********************************************************************/ + + void compute_RVC( + index_t i, + Mesh& M, + Mesh& result, + bool copy_symbolic_info + ) override { + Mesh* tmp_mesh = mesh_; + mesh_ = &M; + RVD_.set_mesh(&M); + typename GenRestrictedVoronoiDiagram::Polyhedron Cell(dimension()); + Cell.initialize_from_surface_mesh(&M, RVD_.symbolic()); + RVD_.intersect_cell_cell(i, Cell); + Cell.convert_to_mesh(&result, copy_symbolic_info); + mesh_ = tmp_mesh; + RVD_.set_mesh(tmp_mesh); + } + + /********************************************************************/ + + void for_each_polygon( + GEO::RVDPolygonCallback& callback, + bool symbolic, + bool connected_comp_priority, + bool parallel + ) override { + bool sym_backup = RVD_.symbolic(); + RVD_.set_symbolic(symbolic); + RVD_.set_connected_components_priority(connected_comp_priority); + callback.begin(); + if(parallel) { + compute_with_polygon_callback(callback); + } else { + PolygonCallbackAction action(RVD_,callback); + RVD_.for_each_polygon(action); + } + callback.end(); + RVD_.set_symbolic(sym_backup); + RVD_.set_connected_components_priority(false); + } + + /********************************************************************/ + + void for_each_polyhedron( + GEO::RVDPolyhedronCallback& callback, + bool symbolic, + bool connected_comp_priority, + bool parallel + ) override { + bool sym_backup = RVD_.symbolic(); + RVD_.set_symbolic(symbolic); + RVD_.set_connected_components_priority(connected_comp_priority); + callback.set_dimension(RVD_.mesh()->vertices.dimension()); + callback.begin(); + if(parallel) { + compute_with_polyhedron_callback(callback); + } else { + RVD_.for_each_polyhedron(callback); + } + callback.end(); + RVD_.set_symbolic(sym_backup); + RVD_.set_connected_components_priority(false); + } + + /********************************************************************/ + + /** + * \brief Does the actual computation for a specific part + * in multithread mode. + * \param[in] t the index of the part. + * \pre \p t < nb_parts() + */ + void run_thread(index_t t) { + geo_assert(t < nb_parts()); + thisclass& T = part(t); + switch(thread_mode_) { + case MT_LLOYD: + { + T.compute_centroids(arg_vectors_, arg_scalars_); + } break; + case MT_NEWTON: + { + T.compute_CVT_func_grad(T.funcval_, arg_vectors_); + } break; + case MT_INT_SMPLX: + { + T.compute_integration_simplex_func_grad( + T.funcval_, arg_vectors_, simplex_func_ + ); + } break; + case MT_POLYG: + { + T.compute_with_polygon_callback( + *polygon_callback_ + ); + } break; + case MT_POLYH: + { + T.compute_with_polyhedron_callback( + *polyhedron_callback_ + ); + } break; + case MT_NONE: + geo_assert_not_reached; + } + } + + bool compute_initial_sampling_on_surface( + double* p, index_t nb_points + ) override { + geo_assert(mesh_->facets.are_simplices()); + + // We do that here, since this triggers partitioning, + // that improves data locality. Then data locality is + // inherited by the generated points. + create_threads(); + + if(facets_begin_ == -1 && facets_end_ == -1) { + Logger::out("RVD") + << "Computing initial sampling on surface, using dimension=" + << index_t(dimension_) << std::endl; + } + + return mesh_generate_random_samples_on_surface( + *mesh_, p, nb_points, vertex_weight_, facets_begin_, facets_end_ + ); + } + + bool compute_initial_sampling_in_volume( + double* p, index_t nb_points + ) override { + geo_assert(mesh_->cells.nb() != 0); + + // We do that here, since this triggers partitioning, + // that improves data locality. Then data locality is + // inherited by the generated points. + create_threads(); + + if(tets_begin_ == -1 && tets_end_ == -1) { + Logger::out("RVD") + << "Computing initial sampling in volume, using dimension=" + << index_t(dimension_) << std::endl; + } + + return mesh_generate_random_samples_in_volume( + *mesh_, p, nb_points, vertex_weight_, tets_begin_, tets_end_ + ); + } + + /** + * \brief Creates the data structures for fast projection. + * \details It decomposes the surface into triangles, and + * stores the vertices in a KdTree. + */ + void prepare_projection() { + if(!mesh_vertices_.is_null()) { + return; + } + + // Step 1: get triangles + for(index_t f = 0; f < mesh_->facets.nb(); f++) { + index_t i = mesh_->facets.corners_begin(f); + for( + index_t j = i + 1; + j + 1 < mesh_->facets.corners_end(f); j++ + ) { + triangles_.push_back(mesh_->facet_corners.vertex(i)); + triangles_.push_back(mesh_->facet_corners.vertex(j)); + triangles_.push_back(mesh_->facet_corners.vertex(j + 1)); + } + } + nb_triangles_ = index_t(triangles_.size() / 3); + + // Step 2: get vertices stars + // Step 2.1: get one-ring neighborhood + vector > stars2(mesh_->vertices.nb()); + for(index_t t = 0; t < nb_triangles_; t++) { + stars2[triangles_[3 * t]].push_back(t); + stars2[triangles_[3 * t + 1]].push_back(t); + stars2[triangles_[3 * t + 2]].push_back(t); + } + + // Step 2.2: get two-ring neighborhood + stars_.resize(mesh_->vertices.nb()); + for(index_t i = 0; i < stars2.size(); i++) { + vector Ni; + for(index_t j = 0; j < stars2[i].size(); j++) { + index_t t = stars2[i][j]; + for(index_t iv = 0; iv < 3; iv++) { + index_t v = triangles_[3 * t + iv]; + if(v != i) { + Ni.push_back(v); + } + } + } + sort_unique(Ni); + for(index_t j = 0; j < Ni.size(); j++) { + index_t k = Ni[j]; + stars_[i].insert( + stars_[i].end(), stars2[k].begin(), stars2[k].end() + ); + } + sort_unique(stars_[i]); + } + + // Step 3: create search structure + mesh_vertices_ = Delaunay::create(dimension_, "NN"); + index_t nb_vertices = mesh_->vertices.nb(); + + // TODO: BUG !! mesh_vertices_ keeps a ref. to mesh_vertices + // that is destroyed when leaving this function. + vector mesh_vertices(nb_vertices * dimension_); + for(index_t i = 0; i < nb_vertices; i++) { + for(index_t coord = 0; coord < dimension_; coord++) { + mesh_vertices[i * dimension_ + coord] = + mesh_->vertices.point_ptr(i)[coord]; + } + } + mesh_vertices_->set_vertices(nb_vertices, mesh_vertices.data()); + } + + void project_points_on_surface( + index_t nb_points, double* points, vec3* nearest, bool do_project + ) override { + + prepare_projection(); + + if(use_exact_projection_) { + for(index_t p = 0; p < nb_points; p++) { + Point P(points + p * dimension_); + double d2 = Numeric::max_float64(); + for(index_t t = 0; t < nb_triangles_; t++) { + const Point& p1 = mesh_vertex(triangles_[3 * t]); + const Point& p2 = mesh_vertex(triangles_[3 * t + 1]); + const Point& p3 = mesh_vertex(triangles_[3 * t + 2]); + + double l1, l2, l3; + Point nearestP; + + double cur_d2 = Geom::point_triangle_squared_distance( + P, p1, p2, p3, nearestP, l1, l2, l3 + ); + + if(cur_d2 < d2) { + d2 = cur_d2; + const vec3& p1_R3 = + R3_embedding(triangles_[3 * t]); + const vec3& p2_R3 = + R3_embedding(triangles_[3 * t + 1]); + const vec3& p3_R3 = + R3_embedding(triangles_[3 * t + 2]); + nearest[p] = l1 * p1_R3 + l2 * p2_R3 + l3 * p3_R3; + if(do_project) { + for(coord_index_t + coord = 0; coord < dimension_; coord++) { + (points + p * dimension_)[coord] = + nearestP[coord]; + } + } + } + } + } + return; + } + + // find nearest point on surface in star of nearest vertex + for(index_t p = 0; p < nb_points; p++) { + Point P(points + p * dimension_); + index_t v = mesh_vertices_->nearest_vertex( + points + p * dimension_ + ); + double d2 = Numeric::max_float64(); + nearest[p] = R3_embedding(v); + for(index_t i = 0; i < stars_[v].size(); i++) { + index_t t = stars_[v][i]; + const Point& p1 = mesh_vertex(triangles_[3 * t]); + const Point& p2 = mesh_vertex(triangles_[3 * t + 1]); + const Point& p3 = mesh_vertex(triangles_[3 * t + 2]); + + double l1, l2, l3; + Point nearestP; + double cur_d2 = Geom::point_triangle_squared_distance( + P, p1, p2, p3, nearestP, l1, l2, l3 + ); + if(cur_d2 < d2) { + d2 = cur_d2; + const vec3& p1_R3 = R3_embedding(triangles_[3 * t]); + const vec3& p2_R3 = R3_embedding(triangles_[3 * t + 1]); + const vec3& p3_R3 = R3_embedding(triangles_[3 * t + 2]); + nearest[p] = l1 * p1_R3 + l2 * p2_R3 + l3 * p3_R3; + if(do_project) { + for(coord_index_t coord = 0; + coord < dimension_; coord++ + ) { + (points + p * dimension_)[coord] = + nearestP[coord]; + } + } + } + } + } + } + + /********************************************************************/ + + /** + * \brief Implementation class for computing the restricted Delaunay + * triangulation. + * \details To be used as a template argument + * to RVD::for_each_primal_triangle(). + */ + class GetPrimalTriangles { + public: + /** + * \brief Creates a new GetPrimalTriangles. + * \param[out] triangles where to store the triangles + */ + GetPrimalTriangles( + vector& triangles + ) : + triangles_(triangles) { + } + + /** + * \brief The callback called for each primal triangle. + * \param[in] v1 index of the first vertex + * \param[in] v2 index of the second vertex + * \param[in] v3 index of the third vertex + */ + void operator() (index_t v1, index_t v2, index_t v3) { + triangles_.push_back(v1); + triangles_.push_back(v2); + triangles_.push_back(v3); + } + + private: + vector& triangles_; + }; + + /** + * \brief Implementation class for computing the restricted Delaunay + * triangulation of the connected components. + * + * \details The difference with GetPrimalTriangles is that when a + * restricted Voronoi cell has multiple connected components, + * more triangles are generated to account for the topology. + * To be used as a template argument to RVD::for_each_polygon(). + * The RestrictedVoronoiDiagram needs to be in connected-components + * priority mode. + */ + class GetConnectedComponentsPrimalTriangles { + public: + /** Internal representation of the polygons. */ + typedef typename GenRestrictedVoronoiDiagram::Polygon Polygon; + + /** Internal representation of the vertices. */ + typedef typename GenRestrictedVoronoiDiagram::Vertex Vertex; + + static const index_t UNINITIALIZED = index_t(-1); + static const index_t MULTI_COMP = index_t(-2); + static const index_t ON_BORDER = index_t(-3); + + /** + * \brief Constructs a new GetConnectedComponentsPrimalTriangles. + * \param[in] RVD the restricted Voronoi diagram + * \param[out] triangles where to store the triangles + * \param[out] vertices where to store the vertices + * \param[in] dimension dimension of the restricted Voronoi diagram + * \param[in] mode a combination of constants defined in RDTMode + * \param[in] seed_is_locked specifies for each seed whether it + * can be moved (to RVC centroid or projected on surface). If + * left uninitialized, all the seeds can be moved. + * \param[in] AABB an axis-aligned bounding box tree defined on + * the input surface. It is used if one of (RDT_SELECT_NEAREST, + * RDT_PROJECT_ON_SURFACE) is set in \p mode. If needed and not + * specified, then a new one is created locally. + */ + GetConnectedComponentsPrimalTriangles( + const GenRestrictedVoronoiDiagram& RVD, + vector& triangles, + vector& vertices, + coord_index_t dimension, + RDTMode mode, + const std::vector& seed_is_locked, + MeshFacetsAABB* AABB = nullptr + ) : + RVD_(RVD), + dimension_(dimension), + triangles_(triangles), + vertices_(vertices), + m_(0.0), + cur_seed_(-1), + cur_vertex_(0), + use_RVC_centroids_((mode & RDT_RVC_CENTROIDS) != 0), + select_nearest_((mode & RDT_SELECT_NEAREST) != 0), + project_on_surface_((mode & RDT_PROJECT_ON_SURFACE) != 0), + seed_is_locked_(seed_is_locked), + prefer_seeds_((mode & RDT_PREFER_SEEDS) != 0), + AABB_(AABB) + { + if(prefer_seeds_) { + seed_to_vertex_.assign( + RVD.delaunay()->nb_vertices(),index_t(UNINITIALIZED) + ); + } + } + + /** + * \brief The callback called for each restricted Voronoi cell. + * \param[in] s1 index of current center vertex + * \param[in] P current restricted Voronoi cell + */ + void operator() (index_t s1, const Polygon& P) { + if(RVD_.connected_component_changed()) { + if(cur_seed_ != -1) { + end_connected_component(); + } + begin_connected_component(s1); + } + + if(prefer_seeds_ && !component_on_border_) { + // NOTE: there is one vertex shift between + // adjacent facet and adjacent seed, there + // must be something wrong in the way the + // combinatorial information is initialized, + // to be checked !!! (should be the i index + // for both) + for(index_t i=0; i= 0 && v3 >= 0) { + triangles_.push_back(v1); + triangles_.push_back(index_t(v2)); + triangles_.push_back(index_t(v3)); + } + } + } + } + + /** + * \brief The destructor + */ + ~GetConnectedComponentsPrimalTriangles() { + + bool owns_AABB = false; + if(select_nearest_ || project_on_surface_) { + if(AABB_ == nullptr) { + // Construct an axis-aligned bounding box tree, + // do not reorder the mesh (needs to be pre-reordered) + AABB_ = new MeshFacetsAABB( + *const_cast(RVD_.mesh()),false + ); + owns_AABB = true; + } + } + + if(cur_seed_ != -1) { + end_connected_component(); + } + + if(prefer_seeds_) { + if(select_nearest_) { + for(index_t s=0; svertex_ptr(s); + + + // At this step, vertex_ptr contains + // the centroid of the connected component + // of the RVC, we now determine whether it + // should be replaced by the + // seed (or by a projection onto the surface). + + const double* vertex_ptr = &(vertices_[vbase]); + + // If the seed is nearer to the surface + // than the + // centroid of the connected component of the + // restricted Voronoi cell, then use the seed. + + double seed_dist; + vec3 seed_projection; + double vertex_dist; + vec3 vertex_projection; + + AABB_->nearest_facet( + vec3(seed_ptr), seed_projection, seed_dist + ); + AABB_->nearest_facet( + vec3(vertex_ptr), + vertex_projection, vertex_dist + ); + + if(seed_dist < vertex_dist) { + if(project_on_surface_) { + for( + coord_index_t c = 0; + c < dimension_; ++c + ) { + vertices_[vbase + c] = + seed_projection[c]; + } + } else { + for(coord_index_t c = 0; + c < dimension_; ++c + ) { + vertices_[vbase + c] = seed_ptr[c]; + } + } + } else { + if(project_on_surface_) { + for( + coord_index_t c = 0; + c < dimension_; ++c + ) { + vertices_[vbase + c] = + vertex_projection[c]; + } + } + } + } + } + } else { + + // Current mode: prefer seeds and not select nearest + // Replace all points with the seeds (provided that + // they do not correspond to multiple + // connected components). + + for(index_t s=0; svertex_ptr(s); + + for(coord_index_t c = 0; c < dimension_; ++c) { + vertices_[vbase + c] = seed_ptr[c]; + } + } + } + } + } + + if( + (!prefer_seeds_ || !select_nearest_) && project_on_surface_ + ) { + for(index_t v=0; vnearest_facet(p,q,sq_dist); + vertices_[3*v ] = q.x; + vertices_[3*v+1] = q.y; + vertices_[3*v+2] = q.z; + } + } + + if(owns_AABB) { + delete AABB_; + AABB_ = nullptr; + } + } + + protected: + /** + * \brief Tests whether a given seed is locked. + */ + bool seed_is_locked(index_t s) { + return + seed_is_locked_.size() > 0 && + seed_is_locked_[s] + ; + } + + /** + * \brief Starts a new connected component. + * \param[in] s the seed the connected component + * is associated with. + */ + void begin_connected_component(index_t s) { + cur_seed_ = signed_index_t(s); + for(coord_index_t c = 0; c < dimension_; ++c) { + vertices_.push_back(0.0); + } + m_ = 0.0; + component_on_border_ = false; + } + + /** + * \brief Terminates the current connected component. + */ + void end_connected_component() { + + if( + !use_RVC_centroids_ || + seed_is_locked(index_t(cur_seed_)) || + component_on_border_ + ) { + // Copy seed + index_t vbase = cur_vertex_ * dimension_; + const double* seed_ptr = + RVD_.delaunay()->vertex_ptr(index_t(cur_seed_)); + for(coord_index_t c = 0; c < dimension_; ++c) { + vertices_[vbase + c] = seed_ptr[c]; + } + } else { + // Use restricted Voronoi + // cell component's centroid. + double scal = (m_ < 1e-30 ? 0.0 : 1.0 / m_); + index_t vbase = cur_vertex_ * dimension_; + for(coord_index_t c = 0; c < dimension_; ++c) { + vertices_[vbase + c] *= scal; + } + } + if(prefer_seeds_) { + if(component_on_border_) { + seed_to_vertex_[index_t(cur_seed_)] = ON_BORDER; + } + switch(seed_to_vertex_[index_t(cur_seed_)]) { + case UNINITIALIZED: + seed_to_vertex_[index_t(cur_seed_)] = cur_vertex_; + break; + case ON_BORDER: + break; + default: + seed_to_vertex_[index_t(cur_seed_)] = MULTI_COMP; + break; + } + } + ++cur_vertex_; + } + + private: + const GenRestrictedVoronoiDiagram& RVD_; + coord_index_t dimension_; + vector& triangles_; + vector& vertices_; + double m_; + signed_index_t cur_seed_; + index_t cur_vertex_; + bool use_RVC_centroids_; + bool select_nearest_; + bool project_on_surface_; + const std::vector& seed_is_locked_; + bool prefer_seeds_; + vector seed_to_vertex_; + bool component_on_border_; + MeshFacetsAABB* AABB_; + }; + + /** + * \brief Implementation class for computing the restricted Delaunay + * triangulation in volume mode. + * \details To be used as a template argument + * to RVD::for_each_primal_tetrahedron(). + */ + class GetPrimalTetrahedra { + public: + /** + * \brief Creates a new GetPrimalTetrahedra. + * \param[out] tetrahedra where to store the tetrahedra + */ + GetPrimalTetrahedra( + vector& tetrahedra + ) : + tetrahedra_(tetrahedra) { + } + + /** + * \brief The callback called for each primal tetrahedron. + * \param[in] v1 index of the first vertex + * \param[in] v2 index of the second vertex + * \param[in] v3 index of the third vertex + * \param[in] v4 index of the fourth vertex + */ + void operator() (index_t v1, index_t v2, index_t v3, index_t v4) { + tetrahedra_.push_back(v1); + tetrahedra_.push_back(v2); + tetrahedra_.push_back(v3); + tetrahedra_.push_back(v4); + } + + private: + vector& tetrahedra_; + }; + + void compute_RDT( + vector& simplices, + vector& embedding, + RDTMode mode, + const vector& seed_is_locked, + MeshFacetsAABB* AABB + ) override { + if(volumetric_) { + // For the moment, only simple mode is supported + simplices.clear(); + RVD_.for_each_primal_tetrahedron( + GetPrimalTetrahedra(simplices) + ); + // Reorient the tetrahedra + index_t nb_tetrahedra = simplices.size() / 4; + for(index_t t = 0; t < nb_tetrahedra; ++t) { + const double* p1 = + delaunay()->vertex_ptr(simplices[4 * t]); + const double* p2 = + delaunay()->vertex_ptr(simplices[4 * t + 1]); + const double* + p3 = delaunay()->vertex_ptr(simplices[4 * t + 2]); + const double* + p4 = delaunay()->vertex_ptr(simplices[4 * t + 3]); + if(PCK::orient_3d(p1, p2, p3, p4) < 0) { + std::swap(simplices[4 * t], simplices[4 * t + 1]); + } + } + embedding.clear(); + embedding.reserve(dimension_ * delaunay_->nb_vertices()); + for(index_t i = 0; i < delaunay_->nb_vertices(); i++) { + for(coord_index_t coord = 0; coord < dimension_; coord++) { + embedding.push_back(delaunay_->vertex_ptr(i)[coord]); + } + } + } else { + if((mode & RDT_MULTINERVE) != 0) { + simplices.clear(); + embedding.clear(); + bool sym = RVD_.symbolic(); + RVD_.set_symbolic(true); + RVD_.set_connected_components_priority(true); + RVD_.for_each_polygon( + GetConnectedComponentsPrimalTriangles( + RVD_, simplices, embedding, RVD_.dimension(), + mode, seed_is_locked, AABB + ) + ); + RVD_.set_symbolic(sym); + RVD_.set_connected_components_priority(false); + } else { + // Simple mode: compute RDT, without any post-processing + simplices.clear(); + RVD_.for_each_primal_triangle( + GetPrimalTriangles(simplices) + ); + embedding.clear(); + embedding.reserve(dimension_ * delaunay_->nb_vertices()); + for(index_t i = 0; i < delaunay_->nb_vertices(); i++) { + for( + coord_index_t coord = 0; + coord < dimension_; ++coord + ){ + embedding.push_back( + delaunay_->vertex_ptr(i)[coord] + ); + } + } + } + } + } + + void create_threads() override { + // TODO: check if number of facets is not smaller than + // number of threads + // TODO: create parts even if facets range is specified + // (and subdivide facets range) + if(is_slave_ || facets_begin_ != -1 || facets_end_ != -1) { + return; + } + index_t nb_parts_in = Process::maximum_concurrent_threads(); + if(nb_parts() != nb_parts_in) { + if(nb_parts_in == 1) { + delete_threads(); + } else { + vector facet_ptr; + vector tet_ptr; + mesh_partition( + *mesh_, MESH_PARTITION_HILBERT, + facet_ptr, tet_ptr, nb_parts_in + ); + delete_threads(); + parts_ = new thisclass[nb_parts_in]; + nb_parts_ = nb_parts_in; + for(index_t i = 0; i < nb_parts(); ++i) { + part(i).mesh_ = mesh_; + part(i).set_delaunay(delaunay_); + part(i).R3_embedding_base_ = R3_embedding_base_; + part(i).R3_embedding_stride_ = R3_embedding_stride_; + part(i).has_weights_ = has_weights_; + part(i).master_ = this; + part(i).RVD_.set_mesh(mesh_); + part(i).set_facets_range( + facet_ptr[i], facet_ptr[i + 1] + ); + part(i).set_exact_predicates(RVD_.exact_predicates()); + part(i).set_volumetric(volumetric()); + part(i).set_check_SR(RVD_.check_SR()); + } + if(mesh_->cells.nb() != 0) { + for(index_t i = 0; i < nb_parts(); ++i) { + part(i).set_tetrahedra_range( + tet_ptr[i], tet_ptr[i + 1] + ); + } + } + geo_assert(!Process::is_running_threads()); + } + } + } + + void set_volumetric(bool x) override { + volumetric_ = x; + for(index_t i = 0; i < nb_parts(); ++i) { + part(i).set_volumetric(x); + } + } + + void set_facets_range( + index_t facets_begin, index_t facets_end + ) override { + RVD_.set_facets_range(facets_begin, facets_end); + facets_begin_ = signed_index_t(facets_begin); + facets_end_ = signed_index_t(facets_end); + } + + void set_tetrahedra_range( + index_t tets_begin, index_t tets_end + ) override { + RVD_.set_tetrahedra_range(tets_begin, tets_end); + tets_begin_ = signed_index_t(tets_begin); + tets_end_ = signed_index_t(tets_end); + } + + void delete_threads() override { + delete[] parts_; + parts_ = nullptr; + nb_parts_ = 0; + } + + /** + * \brief Gets the number of parts (or number of threads). + */ + index_t nb_parts() const { + return nb_parts_; + } + + /** + * \brief Gets a given part from its index. + * \param[in] i index of the part + * \pre \p i < nb_parts() + */ + thisclass& part(index_t i) { + geo_debug_assert(i < nb_parts()); + return parts_[i]; + } + + /** + * \copydoc RestrictedVoronoiDiagram::point_allocator() + */ + GEOGen::PointAllocator* point_allocator() override { + return RVD_.point_allocator(); + } + + + protected: + + GenRestrictedVoronoiDiagram RVD_; + + // For projection + bool use_exact_projection_; + index_t nb_triangles_; + vector triangles_; + vector > stars_; + Delaunay_var mesh_vertices_; + + // One of MT_NONE, MT_LLOYD, MT_NEWTON + ThreadMode thread_mode_; + + bool is_slave_; + + // Variables for 'master' in multithreading mode + thisclass* parts_; + index_t nb_parts_; + Process::SpinLockArray spinlocks_; + + // Newton mode with int. simplex + IntegrationSimplex* simplex_func_; + + // PolygonCallback mode. + RVDPolygonCallback* polygon_callback_; + + // PolyhedronCallback mode. + RVDPolyhedronCallback* polyhedron_callback_; + + // master stores argument for compute_centroids() and + // compute_CVT_func_grad() to pass it to the parts. + double* arg_vectors_; + double* arg_scalars_; + + // Variables for 'slaves' in multithreading mode + thisclass* master_; + double funcval_; // Newton mode: function value + + protected: + /** + * \brief Destructor + */ + ~RVD_Nd_Impl() override { + delete_threads(); + } + + private: + /** \brief Forbids construction by copy. */ + RVD_Nd_Impl(const thisclass&); + + /** \brief Forbids assignment. */ + thisclass& operator= (const thisclass&); + }; +} + +/****************************************************************************/ + +namespace GEO { + + RestrictedVoronoiDiagram* RestrictedVoronoiDiagram::create( + Delaunay* delaunay, Mesh* mesh, + const double* R3_embedding, index_t R3_embedding_stride + ) { + + geo_cite("DBLP:journals/tog/EdelsbrunnerM90"); + geo_cite("DBLP:conf/compgeom/Shewchuk96"); + geo_cite("meyer:inria-00344297"); + geo_cite("DBLP:conf/gmp/YanWLL10"); + geo_cite("DBLP:journals/cad/YanWLL13"); + geo_cite("DBLP:journals/cad/Levy16"); + + delaunay->set_stores_neighbors(true); + RestrictedVoronoiDiagram* result = nullptr; + geo_assert(delaunay != nullptr); + coord_index_t dim = delaunay->dimension(); + switch(dim) { + case 2: + result = new RVD_Nd_Impl<2>( + delaunay, mesh, R3_embedding, R3_embedding_stride + ); + break; + case 3: + result = new RVD_Nd_Impl<3>( + delaunay, mesh, R3_embedding, R3_embedding_stride + ); + break; + case 4: + result = new RVD_Nd_Impl<4>( + delaunay, mesh, R3_embedding, R3_embedding_stride + ); + break; + case 6: + result = new RVD_Nd_Impl<6>( + delaunay, mesh, R3_embedding, R3_embedding_stride + ); + break; + case 8: + result = new RVD_Nd_Impl<8>( + delaunay, mesh, R3_embedding, R3_embedding_stride + ); + break; + default: + geo_assert_not_reached; + } + if(CmdLine::get_arg("algo:predicates") == "exact") { + result->set_exact_predicates(true); + } + return result; + } + + void RestrictedVoronoiDiagram::set_delaunay(Delaunay* delaunay) { + delaunay_ = delaunay; + if(delaunay_ != nullptr) { + dimension_ = delaunay->dimension(); + } else { + dimension_ = 0; + } + } + + RestrictedVoronoiDiagram::~RestrictedVoronoiDiagram() { + } + + RestrictedVoronoiDiagram::RestrictedVoronoiDiagram( + Delaunay* delaunay, Mesh* mesh, + const double* R3_embedding, index_t R3_embedding_stride + ) : + dimension_(0), + mesh_(mesh), + R3_embedding_base_(R3_embedding), + R3_embedding_stride_(R3_embedding_stride) { + set_delaunay(delaunay); + has_weights_ = false; + facets_begin_ = -1; + facets_end_ = -1; + tets_begin_ = -1; + tets_end_ = -1; + volumetric_ = false; + } + + + void RestrictedVoronoiDiagram::compute_RDT( + Mesh& RDT, + RDTMode mode, + const vector& seed_is_locked, + MeshFacetsAABB* AABB + ) { + vector simplices; + vector embedding; + compute_RDT( + simplices, embedding, + mode, seed_is_locked, + AABB + ); + if(volumetric()) { + RDT.cells.assign_tet_mesh(dimension(),embedding,simplices,true); + } else { + RDT.facets.assign_triangle_mesh( + dimension(),embedding,simplices,true + ); + if((mode & RDT_DONT_REPAIR) == 0) { + mesh_repair(RDT); // Needed to reorient triangles + } + } + } + + +} + diff --git a/src/lib/geogram/voronoi/RVD.cpp.old b/src/lib/geogram/voronoi/RVD.cpp.old new file mode 100644 index 00000000..c3942a43 --- /dev/null +++ b/src/lib/geogram/voronoi/RVD.cpp.old @@ -0,0 +1,2651 @@ +/* + * Copyright (c) 2012-2014, Bruno Levy + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the ALICE Project-Team nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * If you modify this software, you should include a notice giving the + * name of the person performing the modification, the date of modification, + * and the reason for such modification. + * + * Contact: Bruno Levy + * + * Bruno.Levy@inria.fr + * http://www.loria.fr/~levy + * + * ALICE Project + * LORIA, INRIA Lorraine, + * Campus Scientifique, BP 239 + * 54506 VANDOEUVRE LES NANCY CEDEX + * FRANCE + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * There are three levels of implementation: + * Level 1: RestrictedVoronoiDiagram is the abstract API seen from client code + * Level 2: RVD_Nd_Impl implements RestrictedVoronoiDiagram + * Level 3: RVD_Nd_Impl::GenRestrictedVoronoiDiagram is + * an instantiation of GEOGen::RestrictedVoronoiDiagram (from generic_RVD.h) + * + * Warning: there are approx. 1000 lines of boring code ahead. + */ + +namespace { + + using namespace GEO; + + /** + * \brief Generic implementation of RestrictedVoronoiDiagram. + * \tparam DIM dimension + */ + template + class RVD_Nd_Impl : public GEO::RestrictedVoronoiDiagram { + + /** \brief This class type */ + typedef RVD_Nd_Impl thisclass; + + /** \brief The base class of this class */ + typedef RestrictedVoronoiDiagram baseclass; + + public: + /** \brief Implementation based on the generic version. */ + typedef GEOGen::RestrictedVoronoiDiagram + GenRestrictedVoronoiDiagram; + + /** \brief Representation of points. */ + typedef vecng Point; + + /** \brief Representation of vectors. */ + typedef vecng Vector; + + /** \brief Represents a point and its symbolic information. */ + typedef typename GenRestrictedVoronoiDiagram::Vertex Vertex; + + /** + * \brief Specifies the computation done by the threads. + */ + enum ThreadMode { + MT_NONE, /**< uninitialized */ + MT_LLOYD, /**< Lloyd iteration */ + MT_NEWTON, /**< Newton optimization */ + MT_INT_SMPLX, /**< Newton with integration simplex */ + MT_POLYG, /**< Polygon callback */ + MT_POLYH /**< Polyhedron callback */ + }; + + /** + * \brief Gets a mesh vertex from its index. + * \param[in] v index of the vertex + * \return a const reference to a Point + */ + const Point& mesh_vertex(index_t v) { + return *(const Point*) mesh_->vertices.point_ptr(v); + } + + + /** + * \brief Creates a RVD_Nd_Impl. + * + * \details The dimension is determined by \p mesh->dimension(). + * \param[in] delaunay the Delaunay triangulation + * \param[in] mesh the input mesh + * \param[in] R3_embedding gives for each vertex + * its mapping in 3D space. + * \param[in] R3_embedding_stride gives the stride between + * two consecutive vertices in R3_embedding + */ + RVD_Nd_Impl( + Delaunay* delaunay, Mesh* mesh, + const double* R3_embedding, index_t R3_embedding_stride + ) : + RestrictedVoronoiDiagram( + delaunay, mesh, R3_embedding, R3_embedding_stride + ), + RVD_(delaunay, mesh) { + use_exact_projection_ = false; + is_slave_ = false; + master_ = nullptr; + has_weights_ = false; + if(mesh->vertices.attributes().is_defined("weight")) { + vertex_weight_.bind(mesh->vertices.attributes(), "weight"); + has_weights_ = true; + } + parts_ = nullptr; + nb_parts_ = 0; + funcval_ = 0.0; + simplex_func_ = nullptr; + polygon_callback_ = nullptr; + polyhedron_callback_ = nullptr; + arg_vectors_ = nullptr; + arg_scalars_ = nullptr; + thread_mode_ = MT_NONE; + nb_triangles_ = 0; + } + + /** + * \brief Constructor for parts, used in multithreading mode. + */ + RVD_Nd_Impl() : + RestrictedVoronoiDiagram(nullptr, nullptr, nullptr, 0), + RVD_(nullptr, nullptr) { + use_exact_projection_ = false; + is_slave_ = true; + master_ = nullptr; + mesh_ = nullptr; + parts_ = nullptr; + nb_parts_ = 0; + facets_begin_ = -1; + facets_end_ = -1; + funcval_ = 0.0; + simplex_func_ = nullptr; + polygon_callback_ = nullptr; + polyhedron_callback_ = nullptr; + arg_vectors_ = nullptr; + arg_scalars_ = nullptr; + thread_mode_ = MT_NONE; + nb_triangles_ = 0; + } + + void set_delaunay(Delaunay* delaunay) override { + baseclass::set_delaunay(delaunay); + RVD_.set_delaunay(delaunay); + for(index_t p = 0; p < nb_parts_; ++p) { + parts_[p].set_delaunay(delaunay); + } + } + + void set_check_SR(bool x) override { + RVD_.set_check_SR(x); + for(index_t p = 0; p < nb_parts_; ++p) { + parts_[p].set_check_SR(x); + } + } + + void set_exact_predicates(bool x) override { + RVD_.set_exact_predicates(x); + for(index_t p = 0; p < nb_parts_; ++p) { + parts_[p].set_exact_predicates(x); + } + } + + bool exact_predicates() const override { + return RVD_.exact_predicates(); + } + + /********************************************************************/ + + /** + * \brief Place holder, "no locking" policy. + * \details NoLocks is used by algorithms templated + * by locking policy, for the single-threaded instances + * that do not need synchronization. The multi-threaded + * instances are parameterized by SpinLockArray. + */ + class NoLocks { + public: + /** + * \brief Acquires a spinlock. + * \details Does nothing in this version + * \param[in] i index of the spinlock to acquire + */ + void acquire_spinlock(index_t i) { + geo_argused(i); + } + + /** + * \brief Releases a spinlock. + * \details Does nothing in this version + * \param[in] i index of the spinlock to release + */ + void release_spinlock(index_t i) { + geo_argused(i); + } + }; + + // ____________________________________________________________________ + + /** + * \brief Implementation class for surfacic Lloyd relaxation. + * \details To be used as a template argument + * to RVD::for_each_triangle(). + * This version ignores the weights. + * + * Computes for each RVD cell: + * - mg[v] (v's Voronoi cell's total area times centroid) + * - m[v] (v's total area) + * \tparam LOCKS locking policy + * (can be one of Process::SpinLockArray, NoLocks) + */ + template + class ComputeCentroids { + public: + /** + * \brief Constructs a ComputeCentroids. + * \param[out] mg where to store the centroids + * \param[out] m where to store the masses + * \param[in] locks the array of locks + * (or NoLocks in single thread mode) + */ + ComputeCentroids( + double* mg, + double* m, + LOCKS& locks + ) : + mg_(mg), + m_(m), + locks_(locks) { + } + + /** + * \brief The callback called for each integration simplex. + * \param[in] v index of current center vertex + * \param[in] p1 first vertex of current integration simplex + * \param[in] p2 second vertex of current integration simplex + * \param[in] p3 third vertex of current integration simplex + */ + void operator() ( + index_t v, + const double* p1, + const double* p2, + const double* p3 + ) const { + double cur_m = Geom::triangle_area(p1, p2, p3, DIM); + double s = cur_m / 3.0; + locks_.acquire_spinlock(v); + m_[v] += cur_m; + double* cur_mg_out = mg_ + v * DIM; + for(coord_index_t coord = 0; coord < DIM; coord++) { + cur_mg_out[coord] += + s * (p1[coord] + p2[coord] + p3[coord]); + } + locks_.release_spinlock(v); + } + + private: + double* mg_; + double* m_; + LOCKS& locks_; + }; + + /** + * \brief Implementation class for surfacic Lloyd relaxation. + * \details To be used as a template + * argument to RVD::for_each_triangle(). + * This version takes the weights into account. + * + * Computes for each RVD cell: + * - mg[v] (v's Voronoi cell's total area times centroid) + * - m[v] (v's total area) + * \tparam LOCKS locking policy + * (can be one of Process::SpinLockArray, NoLocks) + */ + template + class ComputeCentroidsWeighted { + public: + /** + * \brief Constructs a ComputeCentroidsWeighted. + * \param[out] mg where to store the centroids + * \param[out] m where to store the masses + * \param[in] locks the array of locks + * (or NoLocks in single thread mode) + */ + ComputeCentroidsWeighted( + double* mg, + double* m, + LOCKS& locks + ) : + mg_(mg), + m_(m), + locks_(locks) { + } + + /** + * \brief The callback called for each integration simplex. + * \param[in] v index of current center vertex + * \param[in] v1 first vertex of current integration simplex + * \param[in] v2 second vertex of current integration simplex + * \param[in] v3 third vertex of current integration simplex + */ + void operator() ( + index_t v, + const Vertex& v1, + const Vertex& v2, + const Vertex& v3 + ) const { + double cur_m; + double cur_Vg[DIM]; + Geom::triangle_centroid( + v1.point(), v2.point(), v3.point(), + v1.weight(), v2.weight(), v3.weight(), + cur_Vg, cur_m, DIM + ); + locks_.acquire_spinlock(v); + m_[v] += cur_m; + double* cur_mg_out = mg_ + v * DIM; + for(coord_index_t coord = 0; coord < DIM; coord++) { + cur_mg_out[coord] += cur_Vg[coord]; + } + locks_.release_spinlock(v); + } + + private: + double* mg_; + double* m_; + LOCKS& locks_; + }; + + void compute_centroids_on_surface(double* mg, double* m) override { + create_threads(); + if(nb_parts() == 0) { + if(master_ != nullptr) { + if(has_weights_) { + RVD_.for_each_triangle( + ComputeCentroidsWeighted( + mg, m, master_->spinlocks_ + ) + ); + } else { + RVD_.for_each_triangle( + ComputeCentroids( + mg, m, master_->spinlocks_ + ) + ); + } + } else { + NoLocks nolocks; + if(has_weights_) { + RVD_.for_each_triangle( + ComputeCentroidsWeighted( + mg, m, nolocks + ) + ); + } else { + RVD_.for_each_triangle( + ComputeCentroids(mg, m, nolocks) + ); + } + } + } else { + thread_mode_ = MT_LLOYD; + arg_vectors_ = mg; + arg_scalars_ = m; + spinlocks_.resize(delaunay_->nb_vertices()); + parallel_for( + 0, nb_parts(), + parallel_for_member_callback(this, &thisclass::run_thread) + ); + } + } + + /********************************************************************/ + + /** + * \brief Implementation class for surfacic Lloyd relaxation. + * \details To be used as a template argument + * to RVD::for_each_volumetric_integration_simplex(). + * This version ignores the weights. + * + * Computes for each RVD cell: + * - mg[v] (v's Voronoi cell's total area times centroid) + * - m[v] (v's total area) + * \tparam LOCKS locking policy + * (can be one of Process::SpinLockArray, NoLocks) + */ + template + class ComputeCentroidsVolumetric { + public: + /** + * \brief Constructs a ComputeCentroidsVolumetric. + * \param[out] mg where to store the centroids + * \param[out] m where to store the masses + * \param[in] delaunay the Delaunay triangulation + * \param[in] locks the array of locks + * (or NoLocks in single thread mode) + */ + ComputeCentroidsVolumetric( + double* mg, + double* m, + const Delaunay* delaunay, + LOCKS& locks + ) : + mg_(mg), + m_(m), + delaunay_(delaunay), + locks_(locks) { + } + + /** + * \brief The callback called for each integration simplex. + * \param[in] v index of current center vertex + * \param[in] v_adj (unused here) is the index of the Voronoi cell + * adjacent to t accros facet (\p v1, \p v2, \p v3) or + * -1 if it does not exists + * \param[in] t (unused here) is the index of the current + * tetrahedron + * \param[in] t_adj (unused here) is the index of the + * tetrahedron adjacent to t accros facet (\p v1, \p v2, \p v3) + * or -1 if it does not exists + * \param[in] p0 first vertex of current integration simplex + * \param[in] p1 second vertex of current integration simplex + * \param[in] p2 third vertex of current integration simplex + * \param[in] p3 fourth vertex of current integration simplex + */ + void operator() ( + index_t v, signed_index_t v_adj, + index_t t, signed_index_t t_adj, + const double* p0, + const double* p1, + const double* p2, + const double* p3 + ) const { + geo_argused(v_adj); + geo_argused(t); + geo_argused(t_adj); + double cur_m = Geom::tetra_volume( + p0, p1, p2, p3 + ); + double s = cur_m / 4.0; + locks_.acquire_spinlock(v); + m_[v] += cur_m; + double* cur_mg_out = mg_ + v * DIM; + for(coord_index_t coord = 0; coord < DIM; coord++) { + cur_mg_out[coord] += s * ( + p0[coord] + p1[coord] + p2[coord] + p3[coord] + ); + } + locks_.release_spinlock(v); + } + + private: + double* mg_; + double* m_; + const Delaunay* delaunay_; + LOCKS& locks_; + }; + + void compute_centroids_in_volume(double* mg, double* m) override { + create_threads(); + if(nb_parts() == 0) { + if(master_ != nullptr) { + RVD_.for_each_tetrahedron( + ComputeCentroidsVolumetric( + mg, m, RVD_.delaunay(), master_->spinlocks_ + ) + ); + } else { + NoLocks nolocks; + RVD_.for_each_tetrahedron( + ComputeCentroidsVolumetric( + mg, m, RVD_.delaunay(), nolocks + ) + ); + } + } else { + thread_mode_ = MT_LLOYD; + arg_vectors_ = mg; + arg_scalars_ = m; + spinlocks_.resize(delaunay_->nb_vertices()); + parallel_for( + 0, nb_parts(), + parallel_for_member_callback(this, &thisclass::run_thread) + ); + } + } + + /********************************************************************/ + + /** + * \brief Implementation class for Newton-based restricted CVT. + * \details To be used as a template argument + * to RVD::for_each_triangle(). + * This version ignores the weights. + * + * Computes for each RVD cell: + * - g (gradient) + * - f (CVT energy) + * \tparam LOCKS locking policy + * (can be one of Process::SpinLockArray, NoLocks) + */ + template + class ComputeCVTFuncGrad { + public: + /** + * \brief Constructs a ComputeCVTFuncGrad. + * \param[in] RVD the restricted Voronoi diagram + * \param[out] f the computed function value + * \param[out] g the computed gradient of f, + * allocated by caller, and managed + * by caller + * \param[in] locks the array of locks + * (or NoLocks in single thread mode) + */ + ComputeCVTFuncGrad( + const GenRestrictedVoronoiDiagram& RVD, + double& f, + double* g, + LOCKS& locks + ) : + f_(f), + g_(g), + locks_(locks), + RVD_(RVD) { + } + + /** + * \brief The callback called for each integration simplex. + * \param[in] v index of current center vertex + * \param[in] p1 first vertex of current integration simplex + * \param[in] p2 second vertex of current integration simplex + * \param[in] p3 third vertex of current integration simplex + */ + void operator() ( + index_t v, + const double* p1, + const double* p2, + const double* p3 + ) const { + + const double* p0 = RVD_.delaunay()->vertex_ptr(v); + + double t_area = Geom::triangle_area(p1, p2, p3, DIM); + + double cur_f = 0.0; + for(index_t c = 0; c < DIM; c++) { + double u0 = p0[c] - p1[c]; + double u1 = p0[c] - p2[c]; + double u2 = p0[c] - p3[c]; + cur_f += u0 * u0; + cur_f += u1 * (u0 + u1); + cur_f += u2 * (u0 + u1 + u2); + } + + f_ += t_area * cur_f / 6.0; + + locks_.acquire_spinlock(v); + for(index_t c = 0; c < DIM; c++) { + double Gc = (1.0 / 3.0) * (p1[c] + p2[c] + p3[c]); + g_[DIM * v + c] += (2.0 * t_area) * (p0[c] - Gc); + } + locks_.release_spinlock(v); + } + + double& f_; + double* g_; + LOCKS& locks_; + const GenRestrictedVoronoiDiagram& RVD_; + }; + + /** + * \brief Implementation class for Newton-based restricted CVT. + * \details To be used as a template argument + * to RVD::for_each_triangle(). + * This version takes the weights into account. + * + * Computes for each RVD cell: + * - g (gradient) + * - f (CVT energy) + * \tparam LOCKS locking policy + * (can be one of Process::SpinLockArray, NoLocks) + */ + template + class ComputeCVTFuncGradWeighted { + public: + /** + * \brief Constructs a ComputeCVTFuncGradWeighted. + * \param[in] RVD the restricted Voronoi diagram + * \param[out] f the computed function value + * \param[out] g the computed gradient of f, + * allocated by caller, and managed by caller + * \param[in] locks the array of locks + * (or NoLocks in single thread mode) + */ + ComputeCVTFuncGradWeighted( + const GenRestrictedVoronoiDiagram& RVD, + double& f, + double* g, + LOCKS& locks + ) : + f_(f), + g_(g), + locks_(locks), + RVD_(RVD) { + } + + /** + * \brief The callback called for each integration simplex. + * \param[in] v index of current center vertex + * \param[in] v1 first vertex of current integration simplex + * \param[in] v2 second vertex of current integration simplex + * \param[in] v3 third vertex of current integration simplex + */ + void operator() ( + index_t v, + const Vertex& v1, + const Vertex& v2, + const Vertex& v3 + ) const { + + const double* p0 = RVD_.delaunay()->vertex_ptr(v); + + const double* p1 = v1.point(); + const double* p2 = v2.point(); + const double* p3 = v3.point(); + + double t_area = Geom::triangle_area(p1, p2, p3, DIM); + + double Sp = v1.weight() + v2.weight() + v3.weight(); + double rho[3], alpha[3]; + rho[0] = v1.weight(); + rho[1] = v2.weight(); + rho[2] = v3.weight(); + alpha[0] = Sp + rho[0]; + alpha[1] = Sp + rho[1]; + alpha[2] = Sp + rho[2]; + + double dotprod_00 = 0.0; + double dotprod_10 = 0.0; + double dotprod_11 = 0.0; + double dotprod_20 = 0.0; + double dotprod_21 = 0.0; + double dotprod_22 = 0.0; + for(unsigned int c = 0; c < DIM; c++) { + double sp0 = p0[c] - p1[c]; + double sp1 = p0[c] - p2[c]; + double sp2 = p0[c] - p3[c]; + dotprod_00 += sp0 * sp0; + dotprod_10 += sp1 * sp0; + dotprod_11 += sp1 * sp1; + dotprod_20 += sp2 * sp0; + dotprod_21 += sp2 * sp1; + dotprod_22 += sp2 * sp2; + } + + double cur_f = 0.0; + cur_f += (alpha[0] + rho[0]) * dotprod_00; // 0 0 + cur_f += (alpha[1] + rho[0]) * dotprod_10; // 1 0 + cur_f += (alpha[1] + rho[1]) * dotprod_11; // 1 1 + cur_f += (alpha[2] + rho[0]) * dotprod_20; // 2 0 + cur_f += (alpha[2] + rho[1]) * dotprod_21; // 2 1 + cur_f += (alpha[2] + rho[2]) * dotprod_22; // 2 2 + + f_ += t_area * cur_f / 30.0; + double* g_out = g_ + v * DIM; + locks_.acquire_spinlock(v); + for(index_t c = 0; c < DIM; c++) { + g_out[c] += (t_area / 6.0) * ( + 4.0 * Sp * p0[c] - ( + alpha[0] * p1[c] + + alpha[1] * p2[c] + + alpha[2] * p3[c] + ) + ); + } + locks_.release_spinlock(v); + } + + double& f_; + double* g_; + LOCKS& locks_; + const GenRestrictedVoronoiDiagram& RVD_; + }; + + void compute_CVT_func_grad_on_surface(double& f, double* g) override { + create_threads(); + if(nb_parts() == 0) { + if(master_ != nullptr) { + if(has_weights_) { + RVD_.for_each_triangle( + ComputeCVTFuncGradWeighted( + RVD_, f, g, master_->spinlocks_ + ) + ); + } else { + RVD_.for_each_triangle( + ComputeCVTFuncGrad( + RVD_, f, g, master_->spinlocks_ + ) + ); + } + } else { + NoLocks nolocks; + if(has_weights_) { + RVD_.for_each_triangle( + ComputeCVTFuncGradWeighted( + RVD_, f, g, nolocks + ) + ); + } else { + RVD_.for_each_triangle( + ComputeCVTFuncGrad( + RVD_, f, g, nolocks + ) + ); + } + } + } else { + thread_mode_ = MT_NEWTON; + arg_vectors_ = g; + spinlocks_.resize(delaunay_->nb_vertices()); + for(index_t t = 0; t < nb_parts(); t++) { + part(t).funcval_ = 0.0; + } + parallel_for( + 0, nb_parts(), + parallel_for_member_callback(this, &thisclass::run_thread) + ); + for(index_t t = 0; t < nb_parts(); t++) { + f += part(t).funcval_; + } + } + } + + /********************************************************************/ + + /** + * \brief Implementation class for Newton-based restricted CVT + * in volume. + * \details To be used as a template argument + * to RVD::for_each_volumetric_integration_simplex(). + * This version ignores the weights. + * + * Computes for each RVD cell: + * - g (gradient) + * - f (CVT energy) + * \tparam LOCKS locking policy + * (can be one of Process::SpinLockArray, NoLocks) + */ + template + class ComputeCVTFuncGradVolumetric { + public: + /** + * \brief Constructs a ComputeCentroidsFuncGradVolumetric. + * \param[in] RVD the restricted Voronoi diagram + * \param[out] f the computed function value + * \param[out] g the computed gradient of f, + * allocated by caller, and managed + * by caller + * \param[in] locks the array of locks + * (or NoLocks in single thread mode) + */ + ComputeCVTFuncGradVolumetric( + const GenRestrictedVoronoiDiagram& RVD, + double& f, + double* g, + LOCKS& locks + ) : + f_(f), + g_(g), + locks_(locks), + RVD_(RVD) { + } + + /** + * \brief The callback called for each integration simplex. + * \param[in] v index of current center vertex + * \param[in] v_adj (unused here) is the index of the Voronoi cell + * adjacent to t accros facet (\p v1, \p v2, \p v3) or + * -1 if it does not exists + * \param[in] t (unused here) is the index of the current + * tetrahedron + * \param[in] t_adj (unused here) is the index of the + * tetrahedron adjacent to t accros facet (\p v1, \p v2, \p v3) + * or -1 if it does not exists + * \param[in] p1 first vertex of current integration simplex + * \param[in] p2 second vertex of current integration simplex + * \param[in] p3 third vertex of current integration simplex + */ + void operator() ( + index_t v, + signed_index_t v_adj, + index_t t, + signed_index_t t_adj, + const double* p1, + const double* p2, + const double* p3 + ) const { + geo_argused(v_adj); + geo_argused(t); + geo_argused(t_adj); + const double* p0 = RVD_.delaunay()->vertex_ptr(v); + + double mi = Geom::tetra_volume(p0, p1, p2, p3); + + // fi = (mi/10)*(U.U + V.V + W.W + U.V + V.W + W.U) + // where: U = p1-p0 ; V = p2-p0 and W=p3-p0 + double fi = 0.0; + for(coord_index_t c = 0; c < DIM; ++c) { + double Uc = p1[c] - p0[c]; + double Vc = p2[c] - p0[c]; + double Wc = p3[c] - p0[c]; + fi += geo_sqr(Uc) + geo_sqr(Vc) + geo_sqr(Wc); + fi += (Uc * Vc + Vc * Wc + Wc * Uc); + } + fi *= (mi / 10.0); + f_ += fi; + + // gi = 2*mi(p0 - 1/4(p0 + p1 + p2 + p3)) + double* g_out = g_ + v * DIM; + locks_.acquire_spinlock(v); + for(coord_index_t c = 0; c < DIM; ++c) { + g_out[c] += 2.0 * mi * ( + 0.75 * p0[c] + - 0.25 * p1[c] - 0.25 * p2[c] - 0.25 * p3[c] + ); + } + locks_.release_spinlock(v); + } + + double& f_; + double* g_; + LOCKS& locks_; + const GenRestrictedVoronoiDiagram& RVD_; + }; + + void compute_CVT_func_grad_in_volume(double& f, double* g) override { + create_threads(); + if(nb_parts() == 0) { + if(master_ != nullptr) { + RVD_.for_each_volumetric_integration_simplex( + ComputeCVTFuncGradVolumetric( + RVD_, f, g, master_->spinlocks_ + ) + ); + } else { + NoLocks nolocks; + RVD_.for_each_volumetric_integration_simplex( + ComputeCVTFuncGradVolumetric( + RVD_, f, g, nolocks + ) + ); + } + } else { + thread_mode_ = MT_NEWTON; + arg_vectors_ = g; + spinlocks_.resize(delaunay_->nb_vertices()); + for(index_t t = 0; t < nb_parts(); t++) { + part(t).funcval_ = 0.0; + } + parallel_for( + 0, nb_parts(), + parallel_for_member_callback(this, &thisclass::run_thread) + ); + for(index_t t = 0; t < nb_parts(); t++) { + f += part(t).funcval_; + } + } + } + + /********************************************************************/ + + /** + * \brief Implementation class for computing function integrals + * and gradients over integration simplices. + * \details To be used as a template argument + * to RVD::for_each_triangle() and + * RVD::for_each_volumetric_integration_simplex() + */ + class ComputeCVTFuncGradIntegrationSimplex { + public: + /** + * \brief Constructs a ComputeCVTFuncGradIntegrationSimplex. + * \param[in] RVD the Restricted Voronoi Diagram + * \param[in] F the IntegrationSimplex + */ + ComputeCVTFuncGradIntegrationSimplex( + const GenRestrictedVoronoiDiagram& RVD, + IntegrationSimplex* F + ) : + f_(0.0), + RVD_(RVD), + simplex_func_(F) { + simplex_func_->reset_thread_local_storage(); + } + + /** + * \brief The callback called for each surfacic integration simplex. + * \param[in] i index of current center vertex + * \param[in] v1 first vertex of current integration simplex + * \param[in] v2 second vertex of current integration simplex + * \param[in] v3 third vertex of current integration simplex + */ + void operator() ( + index_t i, + const Vertex& v1, + const Vertex& v2, + const Vertex& v3 + ) { + f_ += simplex_func_->eval( + i,v1,v2,v3,RVD_.current_facet() + ); + } + + /** + * \brief The callback called for each volumetric + * integration simplex. + * \param[in] v index of current center vertex + * \param[in] v_adj index of the Voronoi cell adjacent to t accros + * facet (\p v1, \p v2, \p v3) or -1 if it does not exists + * \param[in] t index of the current tetrahedron + * \param[in] t_adj index of the tetrahedron adjacent to t accros + * facet (\p v1, \p v2, \p v3) or -1 if it does not exists + * \param[in] v1 first vertex of current integration simplex + * \param[in] v2 second vertex of current integration simplex + * \param[in] v3 third vertex of current integration simplex + */ + void operator() ( + index_t v, + signed_index_t v_adj, + index_t t, + signed_index_t t_adj, + const Vertex& v1, + const Vertex& v2, + const Vertex& v3 + ) { + geo_argused(v_adj); + geo_argused(t_adj); + f_ += simplex_func_->eval( + v,v1,v2,v3,t,index_t(t_adj),index_t(v_adj) + ); + } + + /** + * \brief Gets the function value. + * \return The function value accumulated so far. + */ + double f() const { + return f_; + } + + private: + double f_; + const GenRestrictedVoronoiDiagram& RVD_; + IntegrationSimplex* simplex_func_; + }; + + void compute_integration_simplex_func_grad( + double& f, double* g, IntegrationSimplex* F + ) override { + create_threads(); + if(nb_parts() == 0) { + if(master_ == nullptr) { + F->set_points_and_gradient( + delaunay()->dimension(), + delaunay()->nb_vertices(), + delaunay()->vertex_ptr(0), + g + ); + } + ComputeCVTFuncGradIntegrationSimplex C(RVD_,F); + bool sym = RVD_.symbolic(); + RVD_.set_symbolic(true); + if(F->volumetric()) { + RVD_.for_each_volumetric_integration_simplex( + C, + F->background_mesh_has_varying_attribute(), + false /* Coherent triangles */ + ); + } else { + RVD_.for_each_triangle(C); + } + RVD_.set_symbolic(sym); + funcval_ = C.f(); + f = C.f(); + } else { + spinlocks_.resize(delaunay_->nb_vertices()); + F->set_points_and_gradient( + delaunay()->dimension(), + delaunay()->nb_vertices(), + delaunay()->vertex_ptr(0), + g, + &spinlocks_ + ); + thread_mode_ = MT_INT_SMPLX; + arg_vectors_ = g; + simplex_func_ = F; + funcval_ = 0.0; + for(index_t t = 0; t < nb_parts(); t++) { + part(t).arg_vectors_ = g; + part(t).simplex_func_ = F; + part(t).funcval_ = 0.0; + } + + parallel_for( + 0, nb_parts(), + parallel_for_member_callback(this, &thisclass::run_thread) + ); + + f = 0.0; + for(index_t t = 0; t < nb_parts(); t++) { + f += part(t).funcval_; + } + } + } + + /********************************************************************/ + + /** + * \brief Adapter class used internally to implement for_each_polygon() + * \details Gets the current triangle from the RVD and passes it back + * to the callback. It is needed because GenericRVD::for_each_polygon() + * does not pass the current triangle. + */ + // TODO: pass it through all the callbacks, because it is ridiculous: + // we pass it through the first levels, then throw it, then retrieve it + // (see GenRVD) + class PolygonCallbackAction { + public: + /** + * \brief PolygonCallbackAction constructor + * \param[in] RVD a pointer to the restricted Voronoi diagram + * \param[in] callback a pointer to the PolygonCallback + */ + PolygonCallbackAction( + GenRestrictedVoronoiDiagram& RVD, + GEO::RVDPolygonCallback& callback + ) : + RVD_(RVD), + callback_(callback) { + } + + /** + * \brief Callback called for each polygon. + * \details Routes the callback to the wrapped user action class. + * \param[in] v index of current Delaunay seed + * \param[in] P intersection between current mesh facet + * and the Voronoi cell of \p v + */ + void operator() ( + index_t v, + const GEOGen::Polygon& P + ) const { + callback_(v, RVD_.current_facet(), P); + } + + protected: + GenRestrictedVoronoiDiagram& RVD_; + GEO::RVDPolygonCallback& callback_; + }; + + + virtual void compute_with_polygon_callback( + GEO::RVDPolygonCallback& polygon_callback + ) { + create_threads(); + if(nb_parts() == 0) { + PolygonCallbackAction action(RVD_,polygon_callback); + RVD_.for_each_polygon(action); + } else { + for(index_t t = 0; t < nb_parts(); t++) { + part(t).RVD_.set_symbolic(RVD_.symbolic()); + part(t).RVD_.set_connected_components_priority( + RVD_.connected_components_priority() + ); + } + spinlocks_.resize(delaunay_->nb_vertices()); + thread_mode_ = MT_POLYG; + polygon_callback_ = &polygon_callback; + polygon_callback_->set_spinlocks(&spinlocks_); + // Note: callback begin()/end() is called in for_each_polygon() + parallel_for( + 0, nb_parts(), + parallel_for_member_callback(this, &thisclass::run_thread) + ); + polygon_callback_->set_spinlocks(nullptr); + } + } + + virtual void compute_with_polyhedron_callback( + GEO::RVDPolyhedronCallback& polyhedron_callback + ) { + create_threads(); + if(nb_parts() == 0) { + RVD_.for_each_polyhedron(polyhedron_callback); + } else { + for(index_t t = 0; t < nb_parts(); t++) { + part(t).RVD_.set_symbolic(RVD_.symbolic()); + part(t).RVD_.set_connected_components_priority( + RVD_.connected_components_priority() + ); + } + spinlocks_.resize(delaunay_->nb_vertices()); + thread_mode_ = MT_POLYH; + polyhedron_callback_ = &polyhedron_callback; + polyhedron_callback_->set_spinlocks(&spinlocks_); + // Note: callback begin()/end() is + // called in for_each_polyhedron() + parallel_for( + 0, nb_parts(), + parallel_for_member_callback(this, &thisclass::run_thread) + ); + polyhedron_callback_->set_spinlocks(nullptr); + } + } + + /********************************************************************/ + + /** + * \brief Implementation class for explicitly constructing + * a surfacic mesh that corresponds to the surfacic + * restricted Voronoi diagram. + * \details To be used as a template argument + * to RVD::for_each_polygon(). The current Vornoi cell is + * reported in facet region. + * \tparam BUILDER a class that implements iterative mesh building, + * e.g., MeshBuilder. + */ + template + class BuildRVD { + public: + /** + * \brief Constructs a new BuildRVD. + * \param[in] RVD_in the restricted Voronoi diagram + * \param[in] builder the lesh builder + */ + BuildRVD( + const GenRestrictedVoronoiDiagram& RVD_in, + BUILDER& builder + ) : + RVD(RVD_in), + builder_(builder), + current_facet_(-1) { + builder_.begin_surface(); + } + + /** + * \brief The destructor + * \details Terminates the current facet + * and the current surface. + */ + ~BuildRVD() { + if(current_facet_ != -1) { + builder_.end_reference_facet(); + } + builder_.end_surface(); + } + + /** + * \brief The callback called for each restricted Voronoi cell. + * \param[in] v index of current center vertex + * \param[in] P current restricted Voronoi cell + */ + void operator() ( + index_t v, + const typename GenRestrictedVoronoiDiagram::Polygon& P + ) { + index_t f = RVD.current_facet(); + if(signed_index_t(f) != current_facet_) { + if(current_facet_ != -1) { + builder_.end_reference_facet(); + } + current_facet_ = signed_index_t(f); + builder_.begin_reference_facet(f); + } + builder_.begin_facet(v); + for(index_t i = 0; i < P.nb_vertices(); i++) { + const Vertex& ve = P.vertex(i); + builder_.add_vertex_to_facet(ve.point(), ve.sym()); + } + builder_.end_facet(); + } + + private: + const GenRestrictedVoronoiDiagram& RVD; + BUILDER& builder_; + signed_index_t current_facet_; + }; + + /** + * \brief Implementation class for explicitly constructing + * a volumetric mesh that corresponds to the volumetric + * restricted Voronoi diagram. + * \details To be used as a template argument + * to RVD::for_each_volumetric_integration_simplex(). + * The current Voronoi cell is reported in tetrahedron region. + * \note For the moment, vertices are duplicated (will be fixed + * in a future version). + */ + class BuildVolumetricRVD { + public: + /** + * Constructs a new BuildVolumetricRVD. + * \param[in] RVD the volumetric restricted Voronoi diagram + * \param[in] dim dimension of the points (can be smaller + * than actual dimension of the RVD). + * \param[out] vertices coordinates of the generated vertices + * \param[out] triangle_vertex_indices generated triangles, as + * vertex indices triplets + * \param[out] tet_vertex_indices generated tetrahedra, as + * vertex indices 4-uples + * \param[out] triangle_regions each generated triangle has + * a region index, that corresponds to the index of the + * Voronoi cell the triangle belongs to + * \param[out] tet_regions each generated tetrahedron has + * a region index, that corresponds to the index of the + * Voronoi cell the tetrahedron belongs to + * \param[in] cell_borders_only if true, only the surfacic + * borders of the volumetric cells are saved in the mesh, else + * volumetric cells are tetrahedralized. + * \pre dim <= delaunay->dimension() + */ + BuildVolumetricRVD( + GenRestrictedVoronoiDiagram& RVD, + coord_index_t dim, + vector& vertices, + vector& triangle_vertex_indices, + vector& tet_vertex_indices, + vector& triangle_regions, + vector& tet_regions, + bool cell_borders_only + ) : + delaunay_(RVD.delaunay()), + mesh_(RVD.mesh()), + dim_(dim), + vertices_(vertices), + triangle_vertex_indices_(triangle_vertex_indices), + tet_vertex_indices_(tet_vertex_indices), + triangle_regions_(triangle_regions), + tet_regions_(tet_regions), + cell_borders_only_(cell_borders_only) + { + vertices_.clear(); + triangle_vertex_indices_.clear(); + tet_vertex_indices_.clear(); + triangle_regions_.clear(); + tet_regions_.clear(); + + // The first vertices are copied from Delaunay, + // the other ones will be created during the traversal + nb_vertices_ = delaunay_->nb_vertices(); + vertices_.resize(nb_vertices_ * dim); + for(index_t v = 0; v < delaunay_->nb_vertices(); ++v) { + for(coord_index_t c = 0; c < dim; ++c) { + vertices_[v * dim + c] = delaunay_->vertex_ptr(v)[c]; + } + } + vertex_map_.set_first_vertex_index(nb_vertices_); + } + + /** + * \brief The callback called for each integration simplex. + * \param[in] v index of current center vertex + * \param[in] v_adj (unused here) is the index of the Voronoi cell + * adjacent to t accros facet (\p v1, \p v2, \p v3) or + * -1 if it does not exists + * \param[in] t (unused here) is the index of the current + * tetrahedron + * \param[in] t_adj (unused here) is the index of the + * tetrahedron adjacent to t accros facet (\p v1, \p v2, \p v3) + * or -1 if it does not exists + * \param[in] v1 first vertex of current integration simplex + * \param[in] v2 second vertex of current integration simplex + * \param[in] v3 third vertex of current integration simplex + */ + void operator() ( + index_t v, signed_index_t v_adj, + index_t t, signed_index_t t_adj, + const Vertex& v1, const Vertex& v2, const Vertex& v3 + ) { + geo_argused(v_adj); + geo_argused(t); + + if(cell_borders_only_) { + if(signed_index_t(v) > v_adj) { + index_t iv2 = find_or_create_vertex(v, v1); + index_t iv3 = find_or_create_vertex(v, v2); + index_t iv4 = find_or_create_vertex(v, v3); + triangle_vertex_indices_.push_back(iv4); + triangle_vertex_indices_.push_back(iv3); + triangle_vertex_indices_.push_back(iv2); + triangle_regions_.push_back(signed_index_t(v)); + } + } else { + index_t iv1 = v; + index_t iv2 = find_or_create_vertex(v, v1); + index_t iv3 = find_or_create_vertex(v, v2); + index_t iv4 = find_or_create_vertex(v, v3); + + // Triangle v1,v2,v3 is on border if there is + // no adjacent seed and no adjacent tet. + if(v_adj == -1 && t_adj == -1) { + triangle_vertex_indices_.push_back(iv4); + triangle_vertex_indices_.push_back(iv3); + triangle_vertex_indices_.push_back(iv2); + triangle_regions_.push_back(signed_index_t(v)); + } + + tet_vertex_indices_.push_back(iv1); + tet_vertex_indices_.push_back(iv2); + tet_vertex_indices_.push_back(iv3); + tet_vertex_indices_.push_back(iv4); + tet_regions_.push_back(signed_index_t(v)); + } + } + + /** + * \brief The callback called for each tetrahedron + * \param[in] v index of current center vertex + * \param[in] v_adj (unused here) is the index of the Voronoi cell + * adjacent to t accros facet (\p v1, \p v2, \p v3) or + * -1 if it does not exists + * \param[in] t (unused here) is the index of the current + * tetrahedron + * \param[in] t_adj (unused here) is the index of the + * tetrahedron adjacent to t accros facet (\p v1, \p v2, \p v3) + * or -1 if it does not exists + * \param[in] v1 first vertex of current tetrahedron + * \param[in] v2 second vertex of current tetrahedron + * \param[in] v3 third vertex of current tetrahedron + * \param[in] v4 fourth vertex of current tetrahedron + */ + void operator() ( + index_t v, signed_index_t v_adj, + index_t t, signed_index_t t_adj, + const Vertex& v1, const Vertex& v2, + const Vertex& v3, const Vertex& v4 + ) { + geo_argused(v_adj); + geo_argused(t); + geo_argused(t_adj); + index_t iv1 = vertices_.size() / dim_; + for(index_t c = 0; c < dim_; ++c) { + vertices_.push_back(v1.point()[c]); + } + index_t iv2 = vertices_.size() / dim_; + for(index_t c = 0; c < dim_; ++c) { + vertices_.push_back(v2.point()[c]); + } + index_t iv3 = vertices_.size() / dim_; + for(index_t c = 0; c < dim_; ++c) { + vertices_.push_back(v3.point()[c]); + } + index_t iv4 = vertices_.size() / dim_; + for(index_t c = 0; c < dim_; ++c) { + vertices_.push_back(v4.point()[c]); + } + tet_vertex_indices_.push_back(iv1); + tet_vertex_indices_.push_back(iv2); + tet_vertex_indices_.push_back(iv3); + tet_vertex_indices_.push_back(iv4); + tet_regions_.push_back(signed_index_t(v)); + } + + protected: + /** + * \brief Retrieves the index of a vertex given its symbolic + * representation. + * \param[in] center_vertex_id index of current Voronoi seed + * \param[in] v symbolic and geometric representation of the vertex + * \return the index of the vertex + */ + index_t find_or_create_vertex( + index_t center_vertex_id, const Vertex& v + ) { + index_t result = vertex_map_.find_or_create_vertex( + center_vertex_id, v.sym() + ); + if(result >= nb_vertices_) { + geo_assert(result == nb_vertices_); + nb_vertices_ = result + 1; + for(coord_index_t c = 0; c < dim_; ++c) { + vertices_.push_back(v.point()[c]); + } + } + return result; + } + + private: + const Delaunay* delaunay_; + const Mesh* mesh_; + coord_index_t dim_; + vector& vertices_; + vector& triangle_vertex_indices_; + vector& tet_vertex_indices_; + vector& triangle_regions_; + vector& tet_regions_; + RVDVertexMap vertex_map_; + index_t nb_vertices_; + bool cell_borders_only_; + }; + + void compute_RVD( + Mesh& M, coord_index_t dim, bool cell_borders_only, + bool integration_simplices + ) override { + bool sym = RVD_.symbolic(); + RVD_.set_symbolic(true); + if(volumetric_) { + if(dim == 0) { + dim = dimension(); + } + vector vertices; + vector triangle_vertices; + vector tet_vertices; + vector triangle_regions; + vector tet_regions; + if(cell_borders_only) { + RVD_.for_each_volumetric_integration_simplex( + BuildVolumetricRVD( + RVD_, dim, + vertices, + triangle_vertices, + tet_vertices, + triangle_regions, + tet_regions, + cell_borders_only + ), + false, // Do not visit inner tetrahedra. + true // Ensure that polygonal facets are triangulated + // coherently. + ); + } else { + if(integration_simplices) { + RVD_.for_each_volumetric_integration_simplex( + BuildVolumetricRVD( + RVD_, dim, + vertices, + triangle_vertices, + tet_vertices, + triangle_regions, + tet_regions, + cell_borders_only + ), + false, // Do not visit inner tetrahedra. + true // Ensure that polygonal facets are + // triangulated coherently. + ); + } else { + RVD_.for_each_tetrahedron( + BuildVolumetricRVD( + RVD_, dim, + vertices, + triangle_vertices, + tet_vertices, + triangle_regions, + tet_regions, + cell_borders_only + ) + ); + } + } + + M.clear(true); // keep attributes + + M.vertices.assign_points(vertices,dim,true); + M.facets.assign_triangle_mesh(triangle_vertices, true); + M.cells.assign_tet_mesh(tet_vertices, true); + + // TODO: use Attribute::assign(vector, steal_args) + // when it is there... + + if(M.facets.nb() != 0) { + Attribute facet_region_attr( + M.facets.attributes(), "region" + ); + for(index_t f=0; f cell_region_attr( + M.cells.attributes(), "region" + ); + for(index_t c=0; c(RVD_, builder) + ); + } + RVD_.set_symbolic(sym); + M.show_stats("RVD"); + } + + /********************************************************************/ + + void compute_RVC( + index_t i, + Mesh& M, + Mesh& result, + bool copy_symbolic_info + ) override { + Mesh* tmp_mesh = mesh_; + mesh_ = &M; + RVD_.set_mesh(&M); + typename GenRestrictedVoronoiDiagram::Polyhedron Cell(dimension()); + Cell.initialize_from_surface_mesh(&M, RVD_.symbolic()); + RVD_.intersect_cell_cell(i, Cell); + Cell.convert_to_mesh(&result, copy_symbolic_info); + mesh_ = tmp_mesh; + RVD_.set_mesh(tmp_mesh); + } + + /********************************************************************/ + + void for_each_polygon( + GEO::RVDPolygonCallback& callback, + bool symbolic, + bool connected_comp_priority, + bool parallel + ) override { + bool sym_backup = RVD_.symbolic(); + RVD_.set_symbolic(symbolic); + RVD_.set_connected_components_priority(connected_comp_priority); + callback.begin(); + if(parallel) { + compute_with_polygon_callback(callback); + } else { + PolygonCallbackAction action(RVD_,callback); + RVD_.for_each_polygon(action); + } + callback.end(); + RVD_.set_symbolic(sym_backup); + RVD_.set_connected_components_priority(false); + } + + /********************************************************************/ + + void for_each_polyhedron( + GEO::RVDPolyhedronCallback& callback, + bool symbolic, + bool connected_comp_priority, + bool parallel + ) override { + bool sym_backup = RVD_.symbolic(); + RVD_.set_symbolic(symbolic); + RVD_.set_connected_components_priority(connected_comp_priority); + callback.set_dimension(RVD_.mesh()->vertices.dimension()); + callback.begin(); + if(parallel) { + compute_with_polyhedron_callback(callback); + } else { + RVD_.for_each_polyhedron(callback); + } + callback.end(); + RVD_.set_symbolic(sym_backup); + RVD_.set_connected_components_priority(false); + } + + /********************************************************************/ + + /** + * \brief Does the actual computation for a specific part + * in multithread mode. + * \param[in] t the index of the part. + * \pre \p t < nb_parts() + */ + void run_thread(index_t t) { + geo_assert(t < nb_parts()); + thisclass& T = part(t); + switch(thread_mode_) { + case MT_LLOYD: + { + T.compute_centroids(arg_vectors_, arg_scalars_); + } break; + case MT_NEWTON: + { + T.compute_CVT_func_grad(T.funcval_, arg_vectors_); + } break; + case MT_INT_SMPLX: + { + T.compute_integration_simplex_func_grad( + T.funcval_, arg_vectors_, simplex_func_ + ); + } break; + case MT_POLYG: + { + T.compute_with_polygon_callback( + *polygon_callback_ + ); + } break; + case MT_POLYH: + { + T.compute_with_polyhedron_callback( + *polyhedron_callback_ + ); + } break; + case MT_NONE: + geo_assert_not_reached; + } + } + + bool compute_initial_sampling_on_surface( + double* p, index_t nb_points + ) override { + geo_assert(mesh_->facets.are_simplices()); + + // We do that here, since this triggers partitioning, + // that improves data locality. Then data locality is + // inherited by the generated points. + create_threads(); + + if(facets_begin_ == -1 && facets_end_ == -1) { + Logger::out("RVD") + << "Computing initial sampling on surface, using dimension=" + << index_t(dimension_) << std::endl; + } + + return mesh_generate_random_samples_on_surface( + *mesh_, p, nb_points, vertex_weight_, facets_begin_, facets_end_ + ); + } + + bool compute_initial_sampling_in_volume( + double* p, index_t nb_points + ) override { + geo_assert(mesh_->cells.nb() != 0); + + // We do that here, since this triggers partitioning, + // that improves data locality. Then data locality is + // inherited by the generated points. + create_threads(); + + if(tets_begin_ == -1 && tets_end_ == -1) { + Logger::out("RVD") + << "Computing initial sampling in volume, using dimension=" + << index_t(dimension_) << std::endl; + } + + return mesh_generate_random_samples_in_volume( + *mesh_, p, nb_points, vertex_weight_, tets_begin_, tets_end_ + ); + } + + /** + * \brief Creates the data structures for fast projection. + * \details It decomposes the surface into triangles, and + * stores the vertices in a KdTree. + */ + void prepare_projection() { + if(!mesh_vertices_.is_null()) { + return; + } + + // Step 1: get triangles + for(index_t f = 0; f < mesh_->facets.nb(); f++) { + index_t i = mesh_->facets.corners_begin(f); + for( + index_t j = i + 1; + j + 1 < mesh_->facets.corners_end(f); j++ + ) { + triangles_.push_back(mesh_->facet_corners.vertex(i)); + triangles_.push_back(mesh_->facet_corners.vertex(j)); + triangles_.push_back(mesh_->facet_corners.vertex(j + 1)); + } + } + nb_triangles_ = index_t(triangles_.size() / 3); + + // Step 2: get vertices stars + // Step 2.1: get one-ring neighborhood + vector > stars2(mesh_->vertices.nb()); + for(index_t t = 0; t < nb_triangles_; t++) { + stars2[triangles_[3 * t]].push_back(t); + stars2[triangles_[3 * t + 1]].push_back(t); + stars2[triangles_[3 * t + 2]].push_back(t); + } + + // Step 2.2: get two-ring neighborhood + stars_.resize(mesh_->vertices.nb()); + for(index_t i = 0; i < stars2.size(); i++) { + vector Ni; + for(index_t j = 0; j < stars2[i].size(); j++) { + index_t t = stars2[i][j]; + for(index_t iv = 0; iv < 3; iv++) { + index_t v = triangles_[3 * t + iv]; + if(v != i) { + Ni.push_back(v); + } + } + } + sort_unique(Ni); + for(index_t j = 0; j < Ni.size(); j++) { + index_t k = Ni[j]; + stars_[i].insert( + stars_[i].end(), stars2[k].begin(), stars2[k].end() + ); + } + sort_unique(stars_[i]); + } + + // Step 3: create search structure + mesh_vertices_ = Delaunay::create(dimension_, "NN"); + index_t nb_vertices = mesh_->vertices.nb(); + + // TODO: BUG !! mesh_vertices_ keeps a ref. to mesh_vertices + // that is destroyed when leaving this function. + vector mesh_vertices(nb_vertices * dimension_); + for(index_t i = 0; i < nb_vertices; i++) { + for(index_t coord = 0; coord < dimension_; coord++) { + mesh_vertices[i * dimension_ + coord] = + mesh_->vertices.point_ptr(i)[coord]; + } + } + mesh_vertices_->set_vertices(nb_vertices, mesh_vertices.data()); + } + + void project_points_on_surface( + index_t nb_points, double* points, vec3* nearest, bool do_project + ) override { + + prepare_projection(); + + if(use_exact_projection_) { + for(index_t p = 0; p < nb_points; p++) { + Point P(points + p * dimension_); + double d2 = Numeric::max_float64(); + for(index_t t = 0; t < nb_triangles_; t++) { + const Point& p1 = mesh_vertex(triangles_[3 * t]); + const Point& p2 = mesh_vertex(triangles_[3 * t + 1]); + const Point& p3 = mesh_vertex(triangles_[3 * t + 2]); + + double l1, l2, l3; + Point nearestP; + + double cur_d2 = Geom::point_triangle_squared_distance( + P, p1, p2, p3, nearestP, l1, l2, l3 + ); + + if(cur_d2 < d2) { + d2 = cur_d2; + const vec3& p1_R3 = + R3_embedding(triangles_[3 * t]); + const vec3& p2_R3 = + R3_embedding(triangles_[3 * t + 1]); + const vec3& p3_R3 = + R3_embedding(triangles_[3 * t + 2]); + nearest[p] = l1 * p1_R3 + l2 * p2_R3 + l3 * p3_R3; + if(do_project) { + for(coord_index_t + coord = 0; coord < dimension_; coord++) { + (points + p * dimension_)[coord] = + nearestP[coord]; + } + } + } + } + } + return; + } + + // find nearest point on surface in star of nearest vertex + for(index_t p = 0; p < nb_points; p++) { + Point P(points + p * dimension_); + index_t v = mesh_vertices_->nearest_vertex( + points + p * dimension_ + ); + double d2 = Numeric::max_float64(); + nearest[p] = R3_embedding(v); + for(index_t i = 0; i < stars_[v].size(); i++) { + index_t t = stars_[v][i]; + const Point& p1 = mesh_vertex(triangles_[3 * t]); + const Point& p2 = mesh_vertex(triangles_[3 * t + 1]); + const Point& p3 = mesh_vertex(triangles_[3 * t + 2]); + + double l1, l2, l3; + Point nearestP; + double cur_d2 = Geom::point_triangle_squared_distance( + P, p1, p2, p3, nearestP, l1, l2, l3 + ); + if(cur_d2 < d2) { + d2 = cur_d2; + const vec3& p1_R3 = R3_embedding(triangles_[3 * t]); + const vec3& p2_R3 = R3_embedding(triangles_[3 * t + 1]); + const vec3& p3_R3 = R3_embedding(triangles_[3 * t + 2]); + nearest[p] = l1 * p1_R3 + l2 * p2_R3 + l3 * p3_R3; + if(do_project) { + for(coord_index_t coord = 0; + coord < dimension_; coord++ + ) { + (points + p * dimension_)[coord] = + nearestP[coord]; + } + } + } + } + } + } + + /********************************************************************/ + + /** + * \brief Implementation class for computing the restricted Delaunay + * triangulation. + * \details To be used as a template argument + * to RVD::for_each_primal_triangle(). + */ + class GetPrimalTriangles { + public: + /** + * \brief Creates a new GetPrimalTriangles. + * \param[out] triangles where to store the triangles + */ + GetPrimalTriangles( + vector& triangles + ) : + triangles_(triangles) { + } + + /** + * \brief The callback called for each primal triangle. + * \param[in] v1 index of the first vertex + * \param[in] v2 index of the second vertex + * \param[in] v3 index of the third vertex + */ + void operator() (index_t v1, index_t v2, index_t v3) { + triangles_.push_back(v1); + triangles_.push_back(v2); + triangles_.push_back(v3); + } + + private: + vector& triangles_; + }; + + /** + * \brief Implementation class for computing the restricted Delaunay + * triangulation of the connected components. + * + * \details The difference with GetPrimalTriangles is that when a + * restricted Voronoi cell has multiple connected components, + * more triangles are generated to account for the topology. + * To be used as a template argument to RVD::for_each_polygon(). + * The RestrictedVoronoiDiagram needs to be in connected-components + * priority mode. + */ + class GetConnectedComponentsPrimalTriangles { + public: + /** Internal representation of the polygons. */ + typedef typename GenRestrictedVoronoiDiagram::Polygon Polygon; + + /** Internal representation of the vertices. */ + typedef typename GenRestrictedVoronoiDiagram::Vertex Vertex; + + static const index_t UNINITIALIZED = index_t(-1); + static const index_t MULTI_COMP = index_t(-2); + static const index_t ON_BORDER = index_t(-3); + + /** + * \brief Constructs a new GetConnectedComponentsPrimalTriangles. + * \param[in] RVD the restricted Voronoi diagram + * \param[out] triangles where to store the triangles + * \param[out] vertices where to store the vertices + * \param[in] dimension dimension of the restricted Voronoi diagram + * \param[in] mode a combination of constants defined in RDTMode + * \param[in] seed_is_locked specifies for each seed whether it + * can be moved (to RVC centroid or projected on surface). If + * left uninitialized, all the seeds can be moved. + * \param[in] AABB an axis-aligned bounding box tree defined on + * the input surface. It is used if one of (RDT_SELECT_NEAREST, + * RDT_PROJECT_ON_SURFACE) is set in \p mode. If needed and not + * specified, then a new one is created locally. + */ + GetConnectedComponentsPrimalTriangles( + const GenRestrictedVoronoiDiagram& RVD, + vector& triangles, + vector& vertices, + coord_index_t dimension, + RDTMode mode, + const std::vector& seed_is_locked, + MeshFacetsAABB* AABB = nullptr + ) : + RVD_(RVD), + dimension_(dimension), + triangles_(triangles), + vertices_(vertices), + m_(0.0), + cur_seed_(-1), + cur_vertex_(0), + use_RVC_centroids_((mode & RDT_RVC_CENTROIDS) != 0), + select_nearest_((mode & RDT_SELECT_NEAREST) != 0), + project_on_surface_((mode & RDT_PROJECT_ON_SURFACE) != 0), + seed_is_locked_(seed_is_locked), + prefer_seeds_((mode & RDT_PREFER_SEEDS) != 0), + AABB_(AABB) + { + if(prefer_seeds_) { + seed_to_vertex_.assign( + RVD.delaunay()->nb_vertices(),index_t(UNINITIALIZED) + ); + } + } + + /** + * \brief The callback called for each restricted Voronoi cell. + * \param[in] s1 index of current center vertex + * \param[in] P current restricted Voronoi cell + */ + void operator() (index_t s1, const Polygon& P) { + if(RVD_.connected_component_changed()) { + if(cur_seed_ != -1) { + end_connected_component(); + } + begin_connected_component(s1); + } + + if(prefer_seeds_ && !component_on_border_) { + // NOTE: there is one vertex shift between + // adjacent facet and adjacent seed, there + // must be something wrong in the way the + // combinatorial information is initialized, + // to be checked !!! (should be the i index + // for both) + for(index_t i=0; i= 0 && v3 >= 0) { + triangles_.push_back(v1); + triangles_.push_back(index_t(v2)); + triangles_.push_back(index_t(v3)); + } + } + } + } + + /** + * \brief The destructor + */ + ~GetConnectedComponentsPrimalTriangles() { + + bool owns_AABB = false; + if(select_nearest_ || project_on_surface_) { + if(AABB_ == nullptr) { + // Construct an axis-aligned bounding box tree, + // do not reorder the mesh (needs to be pre-reordered) + AABB_ = new MeshFacetsAABB( + *const_cast(RVD_.mesh()),false + ); + owns_AABB = true; + } + } + + if(cur_seed_ != -1) { + end_connected_component(); + } + + if(prefer_seeds_) { + if(select_nearest_) { + for(index_t s=0; svertex_ptr(s); + + + // At this step, vertex_ptr contains + // the centroid of the connected component + // of the RVC, we now determine whether it + // should be replaced by the + // seed (or by a projection onto the surface). + + const double* vertex_ptr = &(vertices_[vbase]); + + // If the seed is nearer to the surface + // than the + // centroid of the connected component of the + // restricted Voronoi cell, then use the seed. + + double seed_dist; + vec3 seed_projection; + double vertex_dist; + vec3 vertex_projection; + + AABB_->nearest_facet( + vec3(seed_ptr), seed_projection, seed_dist + ); + AABB_->nearest_facet( + vec3(vertex_ptr), + vertex_projection, vertex_dist + ); + + if(seed_dist < vertex_dist) { + if(project_on_surface_) { + for( + coord_index_t c = 0; + c < dimension_; ++c + ) { + vertices_[vbase + c] = + seed_projection[c]; + } + } else { + for(coord_index_t c = 0; + c < dimension_; ++c + ) { + vertices_[vbase + c] = seed_ptr[c]; + } + } + } else { + if(project_on_surface_) { + for( + coord_index_t c = 0; + c < dimension_; ++c + ) { + vertices_[vbase + c] = + vertex_projection[c]; + } + } + } + } + } + } else { + + // Current mode: prefer seeds and not select nearest + // Replace all points with the seeds (provided that + // they do not correspond to multiple + // connected components). + + for(index_t s=0; svertex_ptr(s); + + for(coord_index_t c = 0; c < dimension_; ++c) { + vertices_[vbase + c] = seed_ptr[c]; + } + } + } + } + } + + if( + (!prefer_seeds_ || !select_nearest_) && project_on_surface_ + ) { + for(index_t v=0; vnearest_facet(p,q,sq_dist); + vertices_[3*v ] = q.x; + vertices_[3*v+1] = q.y; + vertices_[3*v+2] = q.z; + } + } + + if(owns_AABB) { + delete AABB_; + AABB_ = nullptr; + } + } + + protected: + /** + * \brief Tests whether a given seed is locked. + */ + bool seed_is_locked(index_t s) { + return + seed_is_locked_.size() > 0 && + seed_is_locked_[s] + ; + } + + /** + * \brief Starts a new connected component. + * \param[in] s the seed the connected component + * is associated with. + */ + void begin_connected_component(index_t s) { + cur_seed_ = signed_index_t(s); + for(coord_index_t c = 0; c < dimension_; ++c) { + vertices_.push_back(0.0); + } + m_ = 0.0; + component_on_border_ = false; + } + + /** + * \brief Terminates the current connected component. + */ + void end_connected_component() { + + if( + !use_RVC_centroids_ || + seed_is_locked(index_t(cur_seed_)) || + component_on_border_ + ) { + // Copy seed + index_t vbase = cur_vertex_ * dimension_; + const double* seed_ptr = + RVD_.delaunay()->vertex_ptr(index_t(cur_seed_)); + for(coord_index_t c = 0; c < dimension_; ++c) { + vertices_[vbase + c] = seed_ptr[c]; + } + } else { + // Use restricted Voronoi + // cell component's centroid. + double scal = (m_ < 1e-30 ? 0.0 : 1.0 / m_); + index_t vbase = cur_vertex_ * dimension_; + for(coord_index_t c = 0; c < dimension_; ++c) { + vertices_[vbase + c] *= scal; + } + } + if(prefer_seeds_) { + if(component_on_border_) { + seed_to_vertex_[index_t(cur_seed_)] = ON_BORDER; + } + switch(seed_to_vertex_[index_t(cur_seed_)]) { + case UNINITIALIZED: + seed_to_vertex_[index_t(cur_seed_)] = cur_vertex_; + break; + case ON_BORDER: + break; + default: + seed_to_vertex_[index_t(cur_seed_)] = MULTI_COMP; + break; + } + } + ++cur_vertex_; + } + + private: + const GenRestrictedVoronoiDiagram& RVD_; + coord_index_t dimension_; + vector& triangles_; + vector& vertices_; + double m_; + signed_index_t cur_seed_; + index_t cur_vertex_; + bool use_RVC_centroids_; + bool select_nearest_; + bool project_on_surface_; + const std::vector& seed_is_locked_; + bool prefer_seeds_; + vector seed_to_vertex_; + bool component_on_border_; + MeshFacetsAABB* AABB_; + }; + + /** + * \brief Implementation class for computing the restricted Delaunay + * triangulation in volume mode. + * \details To be used as a template argument + * to RVD::for_each_primal_tetrahedron(). + */ + class GetPrimalTetrahedra { + public: + /** + * \brief Creates a new GetPrimalTetrahedra. + * \param[out] tetrahedra where to store the tetrahedra + */ + GetPrimalTetrahedra( + vector& tetrahedra + ) : + tetrahedra_(tetrahedra) { + } + + /** + * \brief The callback called for each primal tetrahedron. + * \param[in] v1 index of the first vertex + * \param[in] v2 index of the second vertex + * \param[in] v3 index of the third vertex + * \param[in] v4 index of the fourth vertex + */ + void operator() (index_t v1, index_t v2, index_t v3, index_t v4) { + tetrahedra_.push_back(v1); + tetrahedra_.push_back(v2); + tetrahedra_.push_back(v3); + tetrahedra_.push_back(v4); + } + + private: + vector& tetrahedra_; + }; + + void compute_RDT( + vector& simplices, + vector& embedding, + RDTMode mode, + const vector& seed_is_locked, + MeshFacetsAABB* AABB + ) override { + if(volumetric_) { + // For the moment, only simple mode is supported + simplices.clear(); + RVD_.for_each_primal_tetrahedron( + GetPrimalTetrahedra(simplices) + ); + // Reorient the tetrahedra + index_t nb_tetrahedra = simplices.size() / 4; + for(index_t t = 0; t < nb_tetrahedra; ++t) { + const double* p1 = + delaunay()->vertex_ptr(simplices[4 * t]); + const double* p2 = + delaunay()->vertex_ptr(simplices[4 * t + 1]); + const double* + p3 = delaunay()->vertex_ptr(simplices[4 * t + 2]); + const double* + p4 = delaunay()->vertex_ptr(simplices[4 * t + 3]); + if(PCK::orient_3d(p1, p2, p3, p4) < 0) { + std::swap(simplices[4 * t], simplices[4 * t + 1]); + } + } + embedding.clear(); + embedding.reserve(dimension_ * delaunay_->nb_vertices()); + for(index_t i = 0; i < delaunay_->nb_vertices(); i++) { + for(coord_index_t coord = 0; coord < dimension_; coord++) { + embedding.push_back(delaunay_->vertex_ptr(i)[coord]); + } + } + } else { + if((mode & RDT_MULTINERVE) != 0) { + simplices.clear(); + embedding.clear(); + bool sym = RVD_.symbolic(); + RVD_.set_symbolic(true); + RVD_.set_connected_components_priority(true); + RVD_.for_each_polygon( + GetConnectedComponentsPrimalTriangles( + RVD_, simplices, embedding, RVD_.dimension(), + mode, seed_is_locked, AABB + ) + ); + RVD_.set_symbolic(sym); + RVD_.set_connected_components_priority(false); + } else { + // Simple mode: compute RDT, without any post-processing + simplices.clear(); + RVD_.for_each_primal_triangle( + GetPrimalTriangles(simplices) + ); + embedding.clear(); + embedding.reserve(dimension_ * delaunay_->nb_vertices()); + for(index_t i = 0; i < delaunay_->nb_vertices(); i++) { + for( + coord_index_t coord = 0; + coord < dimension_; ++coord + ){ + embedding.push_back( + delaunay_->vertex_ptr(i)[coord] + ); + } + } + } + } + } + + void create_threads() override { + // TODO: check if number of facets is not smaller than + // number of threads + // TODO: create parts even if facets range is specified + // (and subdivide facets range) + if(is_slave_ || facets_begin_ != -1 || facets_end_ != -1) { + return; + } + index_t nb_parts_in = Process::maximum_concurrent_threads(); + if(nb_parts() != nb_parts_in) { + if(nb_parts_in == 1) { + delete_threads(); + } else { + vector facet_ptr; + vector tet_ptr; + mesh_partition( + *mesh_, MESH_PARTITION_HILBERT, + facet_ptr, tet_ptr, nb_parts_in + ); + delete_threads(); + parts_ = new thisclass[nb_parts_in]; + nb_parts_ = nb_parts_in; + for(index_t i = 0; i < nb_parts(); ++i) { + part(i).mesh_ = mesh_; + part(i).set_delaunay(delaunay_); + part(i).R3_embedding_base_ = R3_embedding_base_; + part(i).R3_embedding_stride_ = R3_embedding_stride_; + part(i).has_weights_ = has_weights_; + part(i).master_ = this; + part(i).RVD_.set_mesh(mesh_); + part(i).set_facets_range( + facet_ptr[i], facet_ptr[i + 1] + ); + part(i).set_exact_predicates(RVD_.exact_predicates()); + part(i).set_volumetric(volumetric()); + part(i).set_check_SR(RVD_.check_SR()); + } + if(mesh_->cells.nb() != 0) { + for(index_t i = 0; i < nb_parts(); ++i) { + part(i).set_tetrahedra_range( + tet_ptr[i], tet_ptr[i + 1] + ); + } + } + geo_assert(!Process::is_running_threads()); + } + } + } + + void set_volumetric(bool x) override { + volumetric_ = x; + for(index_t i = 0; i < nb_parts(); ++i) { + part(i).set_volumetric(x); + } + } + + void set_facets_range( + index_t facets_begin, index_t facets_end + ) override { + RVD_.set_facets_range(facets_begin, facets_end); + facets_begin_ = signed_index_t(facets_begin); + facets_end_ = signed_index_t(facets_end); + } + + void set_tetrahedra_range( + index_t tets_begin, index_t tets_end + ) override { + RVD_.set_tetrahedra_range(tets_begin, tets_end); + tets_begin_ = signed_index_t(tets_begin); + tets_end_ = signed_index_t(tets_end); + } + + void delete_threads() override { + delete[] parts_; + parts_ = nullptr; + nb_parts_ = 0; + } + + /** + * \brief Gets the number of parts (or number of threads). + */ + index_t nb_parts() const { + return nb_parts_; + } + + /** + * \brief Gets a given part from its index. + * \param[in] i index of the part + * \pre \p i < nb_parts() + */ + thisclass& part(index_t i) { + geo_debug_assert(i < nb_parts()); + return parts_[i]; + } + + /** + * \copydoc RestrictedVoronoiDiagram::point_allocator() + */ + GEOGen::PointAllocator* point_allocator() override { + return RVD_.point_allocator(); + } + + + protected: + + GenRestrictedVoronoiDiagram RVD_; + + // For projection + bool use_exact_projection_; + index_t nb_triangles_; + vector triangles_; + vector > stars_; + Delaunay_var mesh_vertices_; + + // One of MT_NONE, MT_LLOYD, MT_NEWTON + ThreadMode thread_mode_; + + bool is_slave_; + + // Variables for 'master' in multithreading mode + thisclass* parts_; + index_t nb_parts_; + Process::SpinLockArray spinlocks_; + + // Newton mode with int. simplex + IntegrationSimplex* simplex_func_; + + // PolygonCallback mode. + RVDPolygonCallback* polygon_callback_; + + // PolyhedronCallback mode. + RVDPolyhedronCallback* polyhedron_callback_; + + // master stores argument for compute_centroids() and + // compute_CVT_func_grad() to pass it to the parts. + double* arg_vectors_; + double* arg_scalars_; + + // Variables for 'slaves' in multithreading mode + thisclass* master_; + double funcval_; // Newton mode: function value + + protected: + /** + * \brief Destructor + */ + ~RVD_Nd_Impl() override { + delete_threads(); + } + + private: + /** \brief Forbids construction by copy. */ + RVD_Nd_Impl(const thisclass&); + + /** \brief Forbids assignment. */ + thisclass& operator= (const thisclass&); + }; +} + +/****************************************************************************/ + +namespace GEO { + + RestrictedVoronoiDiagram* RestrictedVoronoiDiagram::create( + Delaunay* delaunay, Mesh* mesh, + const double* R3_embedding, index_t R3_embedding_stride + ) { + + geo_cite("DBLP:journals/tog/EdelsbrunnerM90"); + geo_cite("DBLP:conf/compgeom/Shewchuk96"); + geo_cite("meyer:inria-00344297"); + geo_cite("DBLP:conf/gmp/YanWLL10"); + geo_cite("DBLP:journals/cad/YanWLL13"); + geo_cite("DBLP:journals/cad/Levy16"); + + delaunay->set_stores_neighbors(true); + RestrictedVoronoiDiagram* result = nullptr; + geo_assert(delaunay != nullptr); + coord_index_t dim = delaunay->dimension(); + switch(dim) { + case 2: + result = new RVD_Nd_Impl<2>( + delaunay, mesh, R3_embedding, R3_embedding_stride + ); + break; + case 3: + result = new RVD_Nd_Impl<3>( + delaunay, mesh, R3_embedding, R3_embedding_stride + ); + break; + case 4: + result = new RVD_Nd_Impl<4>( + delaunay, mesh, R3_embedding, R3_embedding_stride + ); + break; + case 6: + result = new RVD_Nd_Impl<6>( + delaunay, mesh, R3_embedding, R3_embedding_stride + ); + break; + case 8: + result = new RVD_Nd_Impl<8>( + delaunay, mesh, R3_embedding, R3_embedding_stride + ); + break; + default: + geo_assert_not_reached; + } + if(CmdLine::get_arg("algo:predicates") == "exact") { + result->set_exact_predicates(true); + } + return result; + } + + void RestrictedVoronoiDiagram::set_delaunay(Delaunay* delaunay) { + delaunay_ = delaunay; + if(delaunay_ != nullptr) { + dimension_ = delaunay->dimension(); + } else { + dimension_ = 0; + } + } + + RestrictedVoronoiDiagram::~RestrictedVoronoiDiagram() { + } + + RestrictedVoronoiDiagram::RestrictedVoronoiDiagram( + Delaunay* delaunay, Mesh* mesh, + const double* R3_embedding, index_t R3_embedding_stride + ) : + dimension_(0), + mesh_(mesh), + R3_embedding_base_(R3_embedding), + R3_embedding_stride_(R3_embedding_stride) { + set_delaunay(delaunay); + has_weights_ = false; + facets_begin_ = -1; + facets_end_ = -1; + tets_begin_ = -1; + tets_end_ = -1; + volumetric_ = false; + } + + + void RestrictedVoronoiDiagram::compute_RDT( + Mesh& RDT, + RDTMode mode, + const vector& seed_is_locked, + MeshFacetsAABB* AABB + ) { + vector simplices; + vector embedding; + compute_RDT( + simplices, embedding, + mode, seed_is_locked, + AABB + ); + if(volumetric()) { + RDT.cells.assign_tet_mesh(dimension(),embedding,simplices,true); + } else { + RDT.facets.assign_triangle_mesh( + dimension(),embedding,simplices,true + ); + if((mode & RDT_DONT_REPAIR) == 0) { + mesh_repair(RDT); // Needed to reorient triangles + } + } + } + + +} + diff --git a/src/lib/geogram/voronoi/convex_cell.h b/src/lib/geogram/voronoi/convex_cell.h index 7009a852..ee4eb38d 100755 --- a/src/lib/geogram/voronoi/convex_cell.h +++ b/src/lib/geogram/voronoi/convex_cell.h @@ -47,10 +47,10 @@ #define GEOGRAM_VORONOI_CONVEX_CELL #ifndef STANDALONE_CONVEX_CELL -# include -# include -# include -# include +#include +#include +#include +#include #endif #include diff --git a/src/lib/geogram_gfx/GLUP/GLUP.cpp b/src/lib/geogram_gfx/GLUP/GLUP.cpp index d090fdce..50538481 100644 --- a/src/lib/geogram_gfx/GLUP/GLUP.cpp +++ b/src/lib/geogram_gfx/GLUP/GLUP.cpp @@ -261,8 +261,9 @@ void glupBindUniformState(GLUPuint program) { } -#if defined(GEO_OS_EMSCRIPTEN) || defined(GEO_OS_APPLE) -#else +#if !defined(GEO_OS_EMSCRIPTEN) && \ + !defined(GEO_OS_APPLE) && \ + !defined(GEO_OS_ANDROID) /** * \brief Tests whether tessellation shaders are supported by OpenGL. diff --git a/src/lib/geogram_gfx/ImGui_ext/file_dialog.cpp b/src/lib/geogram_gfx/ImGui_ext/file_dialog.cpp index 326ab907..83409ef0 100644 --- a/src/lib/geogram_gfx/ImGui_ext/file_dialog.cpp +++ b/src/lib/geogram_gfx/ImGui_ext/file_dialog.cpp @@ -330,11 +330,11 @@ namespace GEO { ImGui::Begin( (std::string( save_mode_ ? "Save as...##" : "Load...##" - )+String::to_string(this)).c_str(), + )+String::to_string(this)).c_str(), &visible_, ImGuiWindowFlags_NoCollapse ); - + if(ImGui::Button("parent")) { set_directory("../"); } diff --git a/src/lib/geogram_gfx/ImGui_ext/imgui_ext.cpp b/src/lib/geogram_gfx/ImGui_ext/imgui_ext.cpp index 2504f626..04db7b55 100644 --- a/src/lib/geogram_gfx/ImGui_ext/imgui_ext.cpp +++ b/src/lib/geogram_gfx/ImGui_ext/imgui_ext.cpp @@ -266,6 +266,7 @@ namespace ImGui { } GEO::FileDialog* dlg = file_dialogs[label]; dlg->draw(); + std::string result = dlg->get_and_reset_selected_file(); if(result != "") { if(result.length() + 1 >= filename_buff_len) { diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/glup_compat.h b/src/lib/geogram_gfx/third_party/ImGui/OLD/glup_compat.h deleted file mode 100644 index ed07aedf..00000000 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/glup_compat.h +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Macros and functions for facilitating interfacing - * ImGui with WebGL when used with emscripten. - * This file is not part of the initial ImGui distribution. - * [Bruno Levy 11/26/2017] - */ - -#ifndef H_GLUP_COMPAT_H -#define H_GLUP_COMPAT_H - -#ifndef GL_POLYGON_MODE -# define GL_POLYGON_MODE 0x0B40 -#endif - -#ifndef GL_FILL -# define GL_FILL 0x1B02 -#endif - -#ifndef GL_VERTEX_ARRAY_BINDING -# define GL_VERTEX_ARRAY_BINDING 0x85B5 -#endif - -#ifndef GL_SAMPLER_BINDING -# define GL_SAMPLER_BINDING 0x8919 -#endif - -#ifndef GL_UNPACK_ROW_LENGTH -# define GL_UNPACK_ROW_LENGTH 0x0CF2 -#endif - -namespace { - - inline void glup_glGetIntegerv(GLenum name, GLint* value) { - if(name == GL_VERTEX_ARRAY_BINDING) { - *value = glupGetVertexArrayBinding(); - } -#if defined(__EMSCRIPTEN__) || defined(__APPLE__) - else if(name == GL_SAMPLER_BINDING) { - *value = 0; - } -#endif - -#if defined(__EMSCRIPTEN__) || defined(__ANDROID__) - else if(name == GL_POLYGON_MODE) { - *value = 0; - } -#endif - else { - glGetIntegerv(name, value); - } - } - -#if defined(__EMSCRIPTEN__) || defined(__ANDROID__) -# ifdef glPolygonMode -# undef glPolygonMode -# endif - inline void glPolygonMode(GLenum, GLenum) { - } -#endif - -#if defined(__EMSCRIPTEN__) || defined(__APPLE__) -# ifdef glBindSampler -# undef glBindSampler -# endif - inline void glBindSampler(GLenum, GLuint) { - } -#endif -} - - -#ifdef glGetIntegerv -# undef glGetIntegerv -#endif -#define glGetIntegerv glup_glGetIntegerv - -#ifdef glBindVertexArray -# undef glBindVertexArray -#endif -#define glBindVertexArray glupBindVertexArray - -#ifdef glGenVertexArrays -# undef glGenVertexArrays -#endif -#define glGenVertexArrays glupGenVertexArrays - -#ifdef __EMSCRIPTEN__ -inline void glup_glPixelStorei(GLenum name, GLint value) { - if(name != GL_UNPACK_ROW_LENGTH) { - glPixelStorei(name, value); - } -} -# define glPixelStorei glup_glPixelStorei -#endif - -#endif // Include guard diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/imconfig.h b/src/lib/geogram_gfx/third_party/ImGui/OLD/imconfig.h deleted file mode 100644 index a19bb6ce..00000000 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/imconfig.h +++ /dev/null @@ -1,80 +0,0 @@ -//----------------------------------------------------------------------------- -// COMPILE-TIME OPTIONS FOR DEAR IMGUI -// Runtime options (clipboard callbacks, enabling various features, etc.) can generally be set via the ImGuiIO structure. -// You can use ImGui::SetAllocatorFunctions() before calling ImGui::CreateContext() to rewire memory allocation functions. -//----------------------------------------------------------------------------- -// A) You may edit imconfig.h (and not overwrite it when updating imgui, or maintain a patch/branch with your modifications to imconfig.h) -// B) or add configuration directives in your own file and compile with #define IMGUI_USER_CONFIG "myfilename.h" -// If you do so you need to make sure that configuration settings are defined consistently _everywhere_ dear imgui is used, which include -// the imgui*.cpp files but also _any_ of your code that uses imgui. This is because some compile-time options have an affect on data structures. -// Defining those options in imconfig.h will ensure every compilation unit gets to see the same data structure layouts. -// Call IMGUI_CHECKVERSION() from your .cpp files to verify that the data structures your files are using are matching the ones imgui.cpp is using. -//----------------------------------------------------------------------------- - -#pragma once - -// [Bruno] I'm now using c++11... -#include -#ifdef NULL -#undef NULL -#define NULL nullptr -#endif - -//---- Define assertion handler. Defaults to calling assert(). -//#define IM_ASSERT(_EXPR) MyAssert(_EXPR) -//#define IM_ASSERT(_EXPR) ((void)(_EXPR)) // Disable asserts - -//---- Define attributes of all API symbols declarations, e.g. for DLL under Windows. -// [Bruno] export ImGUI symbols. -#include -#define IMGUI_API GEOGRAM_GFX_API - -//---- Don't define obsolete functions/enums names. Consider enabling from time to time after updating to avoid using soon-to-be obsolete function/names. -//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS - -//---- Don't implement demo windows functionality (ShowDemoWindow()/ShowStyleEditor()/ShowUserGuide() methods will be empty) -//---- It is very strongly recommended to NOT disable the demo windows during development. Please read the comments in imgui_demo.cpp. -//#define IMGUI_DISABLE_DEMO_WINDOWS - -//---- Don't implement some functions to reduce linkage requirements. -//#define IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS // [Win32] Don't implement default clipboard handler. Won't use and link with OpenClipboard/GetClipboardData/CloseClipboard etc. -//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] Don't implement default IME handler. Won't use and link with ImmGetContext/ImmSetCompositionWindow. -//#define IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself if you don't want to link with vsnprintf. -//#define IMGUI_DISABLE_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 wrapper so you can implement them yourself. Declare your prototypes in imconfig.h. -//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free(). You will need to call ImGui::SetAllocatorFunctions(). - -//---- Include imgui_user.h at the end of imgui.h as a convenience -//#define IMGUI_INCLUDE_IMGUI_USER_H - -//---- Pack colors to BGRA8 instead of RGBA8 (if you needed to convert from one to another anyway) -//#define IMGUI_USE_BGRA_PACKED_COLOR - -//---- Avoid multiple STB libraries implementations, or redefine path/filenames to prioritize another version -// By default the embedded implementations are declared static and not available outside of imgui cpp files. -//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h" -//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h" -//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION -//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION - -//---- Define constructor and implicit cast operators to convert back<>forth from your math types and ImVec2/ImVec4. -// This will be inlined as part of ImVec2 and ImVec4 class declarations. -/* -#define IM_VEC2_CLASS_EXTRA \ - ImVec2(const MyVec2& f) { x = f.x; y = f.y; } \ - operator MyVec2() const { return MyVec2(x,y); } - -#define IM_VEC4_CLASS_EXTRA \ - ImVec4(const MyVec4& f) { x = f.x; y = f.y; z = f.z; w = f.w; } \ - operator MyVec4() const { return MyVec4(x,y,z,w); } -*/ - -//---- Use 32-bit vertex indices (default is 16-bit) to allow meshes with more than 64K vertices. Render function needs to support it. -//#define ImDrawIdx unsigned int - -//---- Tip: You can add extra functions within the ImGui:: namespace, here or in your own headers files. -/* -namespace ImGui -{ - void MyFunction(const char* name, const MyMatrix44& v); -} -*/ diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui.cpp b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui.cpp deleted file mode 100644 index fb196524..00000000 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui.cpp +++ /dev/null @@ -1,13952 +0,0 @@ -// dear imgui, v1.62 -// (main code and documentation) - -// Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp for demo code. -// Newcomers, read 'Programmer guide' below for notes on how to setup Dear ImGui in your codebase. -// Get latest version at https://github.com/ocornut/imgui -// Releases change-log at https://github.com/ocornut/imgui/releases -// Gallery (please post your screenshots/video there!): https://github.com/ocornut/imgui/issues/1269 -// Developed by Omar Cornut and every direct or indirect contributors to the GitHub. -// This library is free but I need your support to sustain development and maintenance. -// If you work for a company, please consider financial support, see README. For individuals: https://www.patreon.com/imgui - -// It is recommended that you don't modify imgui.cpp! It will become difficult for you to update the library. -// Note that 'ImGui::' being a namespace, you can add functions into the namespace from your own source files, without -// modifying imgui.h or imgui.cpp. You may include imgui_internal.h to access internal data structures, but it doesn't -// come with any guarantee of forward compatibility. Discussing your changes on the GitHub Issue Tracker may lead you -// to a better solution or official support for them. - -/* - - Index - - MISSION STATEMENT - - END-USER GUIDE - - PROGRAMMER GUIDE (read me!) - - Read first - - How to update to a newer version of Dear ImGui - - Getting started with integrating Dear ImGui in your code/engine - - Using gamepad/keyboard navigation controls [BETA] - - API BREAKING CHANGES (read me when you update!) - - ISSUES & TODO LIST - - FREQUENTLY ASKED QUESTIONS (FAQ), TIPS - - How can I tell whether to dispatch mouse/keyboard to imgui or to my application? - - How can I display an image? What is ImTextureID, how does it works? - - How can I have multiple widgets with the same label or without a label? A primer on labels and the ID Stack. - - How can I load a different font than the default? - - How can I easily use icons in my application? - - How can I load multiple fonts? - - How can I display and input non-latin characters such as Chinese, Japanese, Korean, Cyrillic? - - How can I use the drawing facilities without an ImGui window? (using ImDrawList API) - - I integrated Dear ImGui in my engine and the text or lines are blurry.. - - I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around.. - - How can I help? - - ISSUES & TODO-LIST - - CODE - - - MISSION STATEMENT - ================= - - - Easy to use to create code-driven and data-driven tools - - Easy to use to create ad hoc short-lived tools and long-lived, more elaborate tools - - Easy to hack and improve - - Minimize screen real-estate usage - - Minimize setup and maintenance - - Minimize state storage on user side - - Portable, minimize dependencies, run on target (consoles, phones, etc.) - - Efficient runtime and memory consumption (NB- we do allocate when "growing" content e.g. creating a window, - opening a tree node for the first time, etc. but a typical frame should not allocate anything) - - Designed for developers and content-creators, not the typical end-user! Some of the weaknesses includes: - - Doesn't look fancy, doesn't animate - - Limited layout features, intricate layouts are typically crafted in code - - - END-USER GUIDE - ============== - - - Double-click on title bar to collapse window. - - Click upper right corner to close a window, available when 'bool* p_open' is passed to ImGui::Begin(). - - Click and drag on lower right corner to resize window (double-click to auto fit window to its contents). - - Click and drag on any empty space to move window. - - TAB/SHIFT+TAB to cycle through keyboard editable fields. - - CTRL+Click on a slider or drag box to input value as text. - - Use mouse wheel to scroll. - - Text editor: - - Hold SHIFT or use mouse to select text. - - CTRL+Left/Right to word jump. - - CTRL+Shift+Left/Right to select words. - - CTRL+A our Double-Click to select all. - - CTRL+X,CTRL+C,CTRL+V to use OS clipboard/ - - CTRL+Z,CTRL+Y to undo/redo. - - ESCAPE to revert text to its original value. - - You can apply arithmetic operators +,*,/ on numerical values. Use +- to subtract (because - would set a negative value!) - - Controls are automatically adjusted for OSX to match standard OSX text editing operations. - - General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard. - - General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://goo.gl/9LgVZW - - - PROGRAMMER GUIDE - ================ - - READ FIRST - - - Read the FAQ below this section! - - Your code creates the UI, if your code doesn't run the UI is gone! The UI can be highly dynamic, there are no construction - or destruction steps, less data retention on your side, less state duplication, less state synchronization, less bugs. - - Call and read ImGui::ShowDemoWindow() for demo code demonstrating most features. - - You can learn about immediate-mode gui principles at http://www.johno.se/book/imgui.html or watch http://mollyrocket.com/861 - - HOW TO UPDATE TO A NEWER VERSION OF DEAR IMGUI - - - Overwrite all the sources files except for imconfig.h (if you have made modification to your copy of imconfig.h) - - Read the "API BREAKING CHANGES" section (below). This is where we list occasional API breaking changes. - If a function/type has been renamed / or marked obsolete, try to fix the name in your code before it is permanently removed - from the public API. If you have a problem with a missing function/symbols, search for its name in the code, there will - likely be a comment about it. Please report any issue to the GitHub page! - - Try to keep your copy of dear imgui reasonably up to date. - - GETTING STARTED WITH INTEGRATING DEAR IMGUI IN YOUR CODE/ENGINE - - - Run and study the examples and demo to get acquainted with the library. - - Add the Dear ImGui source files to your projects or using your preferred build system. - It is recommended you build the .cpp files as part of your project and not as a library. - - You can later customize the imconfig.h file to tweak some compilation time behavior, such as integrating imgui types with your own maths types. - - You may be able to grab and copy a ready made imgui_impl_*** file from the examples/ folder. - - When using Dear ImGui, your programming IDE is your friend: follow the declaration of variables, functions and types to find comments about them. - - Dear ImGui never touches or knows about your GPU state. The only function that knows about GPU is the draw function that you provide. - Effectively it means you can create widgets at any time in your code, regardless of considerations of being in "update" vs "render" - phases of your own application. All rendering informatioe are stored into command-lists that you will retrieve after calling ImGui::Render(). - - Refer to the bindings and demo applications in the examples/ folder for instruction on how to setup your code. - - THIS IS HOW A SIMPLE APPLICATION MAY LOOK LIKE - - // Application init - // Create a context - ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); - // TODO: Fill optional settings of the io structure later. - // TODO: Load fonts if you don't want to use the default font. - - // Build and load the texture atlas into a texture - unsigned char* pixels = NULL; - int width, height; - io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); - // At this point you've got the texture data and you need to upload that your your graphic system: - MyTexture* texture = MyEngine::CreateTextureFromMemoryPixels(pixels, width, height, TEXTURE_TYPE_RGBA32) - // Store your texture pointer/identifier (in whatever formatyour engine uses) in 'io.Fonts->TexID'. - // This will be passed back to your via the renderer. Read FAQ for details about ImTextureID. - io.Fonts->TexID = (void*)texture; - - // Application main loop - while (true) - { - // Setup low-level inputs (e.g. on Win32, GetKeyboardState(), or write to those fields from your Windows message loop handlers, etc.) - ImGuiIO& io = ImGui::GetIO(); - io.DeltaTime = 1.0f/60.0f; - io.DisplaySize.x = 1920.0f; - io.DisplaySize.y = 1280.0f; - io.MousePos = my_mouse_pos; - io.MouseDown[0] = my_mouse_buttons[0]; - io.MouseDown[1] = my_mouse_buttons[1]; - - // Call NewFrame(), after this point you can use ImGui::* functions anytime - // (So you want to try calling Newframe() as early as you can in your mainloop to be able to use imgui everywhere) - ImGui::NewFrame(); - - // Most of your application code here - ImGui::Text("Hello, world!"); - MyGameUpdate(); // may use any ImGui functions, e.g. ImGui::Begin("My window"); ImGui::Text("Hello, world!"); ImGui::End(); - MyGameRender(); // may use any ImGui functions as well! - - // Render imgui, swap buffers - // (You want to try calling EndFrame/Render as late as you can, to be able to use imgui in your own game rendering code) - ImGui::EndFrame(); - ImGui::Render(); - MyImGuiRenderFunction(ImGui::GetDrawData()); - SwapBuffers(); - } - - // Shutdown - ImGui::DestroyContext(); - - THIS HOW A SIMPLE RENDERING FUNCTION MAY LOOK LIKE - - void void MyImGuiRenderFunction(ImDrawData* draw_data) - { - // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled - // TODO: Setup viewport using draw_data->DisplaySize - // TODO: Setup orthographic projection matrix cover draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize - // TODO: Setup shader: vertex { float2 pos, float2 uv, u32 color }, fragment shader sample color from 1 texture, multiply by vertex color. - for (int n = 0; n < draw_data->CmdListsCount; n++) - { - const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; // vertex buffer generated by ImGui - const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; // index buffer generated by ImGui - for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) - { - const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; - if (pcmd->UserCallback) - { - pcmd->UserCallback(cmd_list, pcmd); - } - else - { - // The texture for the draw call is specified by pcmd->TextureId. - // The vast majority of draw calls with use the imgui texture atlas, which value you have set yourself during initialization. - MyEngineBindTexture(pcmd->TextureId); - - // We are using scissoring to clip some objects. All low-level graphics API should supports it. - // - If your engine doesn't support scissoring yet, you may ignore this at first. You will get some small glitches - // (some elements visible outside their bounds) but you can fix that once everywhere else works! - // - Clipping coordinates are provided in imgui coordinates space (from draw_data->DisplayPos to draw_data->DisplayPos + draw_data->DisplaySize) - // In a single viewport application, draw_data->DisplayPos will always be (0,0) and draw_data->DisplaySize will always be == io.DisplaySize. - // However, in the interest of supporting multi-viewport applications in the future, always subtract draw_data->DisplayPos from - // clipping bounds to convert them to your viewport space. - // - Note that pcmd->ClipRect contains Min+Max bounds. Some graphics API may use Min+Max, other may use Min+Size (size being Max-Min) - ImVec2 pos = draw_data->DisplayPos; - MyEngineScissor((int)(pcmd->ClipRect.x - pos.x), (int)(pcmd->ClipRect.y - pos.y), (int)(pcmd->ClipRect.z - pos.x), (int)(pcmd->ClipRect.w - pos.y)); - - // Render 'pcmd->ElemCount/3' indexed triangles. - // By default the indices ImDrawIdx are 16-bits, you can change them to 32-bits if your engine doesn't support 16-bits indices. - MyEngineDrawIndexedTriangles(pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer, vtx_buffer); - } - idx_buffer += pcmd->ElemCount; - } - } - } - - - The examples/ folders contains many functional implementation of the pseudo-code above. - - When calling NewFrame(), the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags are updated. - They tell you if ImGui intends to use your inputs. When a flag is set you want to hide the corresponding inputs from the rest of your application. - In both cases you need to pass on the inputs to imgui. Read the FAQ below for more information about those flags. - - Please read the FAQ above. Amusingly, it is called a FAQ because people frequently have the same issues! - - USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS [BETA] - - - The gamepad/keyboard navigation is in Beta. Ask questions and report issues at https://github.com/ocornut/imgui/issues/787 - - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable. - - Gamepad: - - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. - - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + fill the io.NavInputs[] fields before calling NewFrame(). - Note that io.NavInputs[] is cleared by EndFrame(). - - See 'enum ImGuiNavInput_' in imgui.h for a description of inputs. For each entry of io.NavInputs[], set the following values: - 0.0f= not held. 1.0f= fully held. Pass intermediate 0.0f..1.0f values for analog triggers/sticks. - - We uses a simple >0.0f test for activation testing, and won't attempt to test for a dead-zone. - Your code will probably need to transform your raw inputs (such as e.g. remapping your 0.2..0.9 raw input range to 0.0..1.0 imgui range, etc.). - - You can download PNG/PSD files depicting the gamepad controls for common controllers at: goo.gl/9LgVZW. - - If you need to share inputs between your game and the imgui parts, the easiest approach is to go all-or-nothing, with a buttons combo - to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved. - - Keyboard: - - Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. - NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays. - - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag - will be set. For more advanced uses, you may want to read from: - - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set. - - io.NavVisible: true when the navigation cursor is visible (and usually goes false when mouse is used). - - or query focus information with e.g. IsWindowFocused(ImGuiFocusedFlags_AnyWindow), IsItemFocused() etc. functions. - Please reach out if you think the game vs navigation input sharing could be improved. - - Mouse: - - PS4 users: Consider emulating a mouse cursor with DualShock4 touch pad or a spare analog stick as a mouse-emulation fallback. - - Consoles/Tablet/Phone users: Consider using a Synergy 1.x server (on your PC) + uSynergy.c (on your console/tablet/phone app) to share your PC mouse/keyboard. - - On a TV/console system where readability may be lower or mouse inputs may be awkward, you may want to set the ImGuiConfigFlags_NavEnableSetMousePos flag. - Enabling ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos instructs dear imgui to move your mouse cursor along with navigation movements. - When enabled, the NewFrame() function may alter 'io.MousePos' and set 'io.WantSetMousePos' to notify you that it wants the mouse cursor to be moved. - When that happens your back-end NEEDS to move the OS or underlying mouse cursor on the next frame. Some of the binding in examples/ do that. - (If you set the NavEnableSetMousePos flag but don't honor 'io.WantSetMousePos' properly, imgui will misbehave as it will see your mouse as moving back and forth!) - (In a setup when you may not have easy control over the mouse cursor, e.g. uSynergy.c doesn't expose moving remote mouse cursor, you may want - to set a boolean to ignore your other external mouse positions until the external source is moved again.) - - - API BREAKING CHANGES - ==================== - - Occasionally introducing changes that are breaking the API. We try to make the breakage minor and easy to fix. - Below is a change-log of API breaking changes only. If you are using one of the functions listed, expect to have to fix some code. - When you are not sure about a old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. - You can read releases logs https://github.com/ocornut/imgui/releases for more details. - - - 2018/06/06 (1.62) - renamed GetGlyphRangesChinese() to GetGlyphRangesChineseFull() to distinguish other variants and discourage using the full set. - - 2018/06/06 (1.62) - TreeNodeEx()/TreeNodeBehavior(): the ImGuiTreeNodeFlags_CollapsingHeader helper now include the ImGuiTreeNodeFlags_NoTreePushOnOpen flag. See Changelog for details. - - 2018/05/03 (1.61) - DragInt(): the default compile-time format string has been changed from "%.0f" to "%d", as we are not using integers internally any more. - If you used DragInt() with custom format strings, make sure you change them to use %d or an integer-compatible format. - To honor backward-compatibility, the DragInt() code will currently parse and modify format strings to replace %*f with %d, giving time to users to upgrade their code. - If you have IMGUI_DISABLE_OBSOLETE_FUNCTIONS enabled, the code will instead assert! You may run a reg-exp search on your codebase for e.g. "DragInt.*%f" to help you find them. - - 2018/04/28 (1.61) - obsoleted InputFloat() functions taking an optional "int decimal_precision" in favor of an equivalent and more flexible "const char* format", - consistent with other functions. Kept redirection functions (will obsolete). - - 2018/04/09 (1.61) - IM_DELETE() helper function added in 1.60 doesn't clear the input _pointer_ reference, more consistent with expectation and allows passing r-value. - - 2018/03/20 (1.60) - renamed io.WantMoveMouse to io.WantSetMousePos for consistency and ease of understanding (was added in 1.52, _not_ used by core and only honored by some binding ahead of merging the Nav branch). - - 2018/03/12 (1.60) - removed ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered as the closing cross uses regular button colors now. - - 2018/03/08 (1.60) - changed ImFont::DisplayOffset.y to default to 0 instead of +1. Fixed rounding of Ascent/Descent to match TrueType renderer. If you were adding or subtracting to ImFont::DisplayOffset check if your fonts are correctly aligned vertically. - - 2018/03/03 (1.60) - renamed ImGuiStyleVar_Count_ to ImGuiStyleVar_COUNT and ImGuiMouseCursor_Count_ to ImGuiMouseCursor_COUNT for consistency with other public enums. - - 2018/02/18 (1.60) - BeginDragDropSource(): temporarily removed the optional mouse_button=0 parameter because it is not really usable in many situations at the moment. - - 2018/02/16 (1.60) - obsoleted the io.RenderDrawListsFn callback, you can call your graphics engine render function after ImGui::Render(). Use ImGui::GetDrawData() to retrieve the ImDrawData* to display. - - 2018/02/07 (1.60) - reorganized context handling to be more explicit, - - YOU NOW NEED TO CALL ImGui::CreateContext() AT THE BEGINNING OF YOUR APP, AND CALL ImGui::DestroyContext() AT THE END. - - removed Shutdown() function, as DestroyContext() serve this purpose. - - you may pass a ImFontAtlas* pointer to CreateContext() to share a font atlas between contexts. Otherwise CreateContext() will create its own font atlas instance. - - removed allocator parameters from CreateContext(), they are now setup with SetAllocatorFunctions(), and shared by all contexts. - - removed the default global context and font atlas instance, which were confusing for users of DLL reloading and users of multiple contexts. - - 2018/01/31 (1.60) - moved sample TTF files from extra_fonts/ to misc/fonts/. If you loaded files directly from the imgui repo you may need to update your paths. - - 2018/01/11 (1.60) - obsoleted IsAnyWindowHovered() in favor of IsWindowHovered(ImGuiHoveredFlags_AnyWindow). Kept redirection function (will obsolete). - - 2018/01/11 (1.60) - obsoleted IsAnyWindowFocused() in favor of IsWindowFocused(ImGuiFocusedFlags_AnyWindow). Kept redirection function (will obsolete). - - 2018/01/03 (1.60) - renamed ImGuiSizeConstraintCallback to ImGuiSizeCallback, ImGuiSizeConstraintCallbackData to ImGuiSizeCallbackData. - - 2017/12/29 (1.60) - removed CalcItemRectClosestPoint() which was weird and not really used by anyone except demo code. If you need it it's easy to replicate on your side. - - 2017/12/24 (1.53) - renamed the emblematic ShowTestWindow() function to ShowDemoWindow(). Kept redirection function (will obsolete). - - 2017/12/21 (1.53) - ImDrawList: renamed style.AntiAliasedShapes to style.AntiAliasedFill for consistency and as a way to explicitly break code that manipulate those flag at runtime. You can now manipulate ImDrawList::Flags - - 2017/12/21 (1.53) - ImDrawList: removed 'bool anti_aliased = true' final parameter of ImDrawList::AddPolyline() and ImDrawList::AddConvexPolyFilled(). Prefer manipulating ImDrawList::Flags if you need to toggle them during the frame. - - 2017/12/14 (1.53) - using the ImGuiWindowFlags_NoScrollWithMouse flag on a child window forwards the mouse wheel event to the parent window, unless either ImGuiWindowFlags_NoInputs or ImGuiWindowFlags_NoScrollbar are also set. - - 2017/12/13 (1.53) - renamed GetItemsLineHeightWithSpacing() to GetFrameHeightWithSpacing(). Kept redirection function (will obsolete). - - 2017/12/13 (1.53) - obsoleted IsRootWindowFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootWindow). Kept redirection function (will obsolete). - - obsoleted IsRootWindowOrAnyChildFocused() in favor of using IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows). Kept redirection function (will obsolete). - - 2017/12/12 (1.53) - renamed ImGuiTreeNodeFlags_AllowOverlapMode to ImGuiTreeNodeFlags_AllowItemOverlap. Kept redirection enum (will obsolete). - - 2017/12/10 (1.53) - removed SetNextWindowContentWidth(), prefer using SetNextWindowContentSize(). Kept redirection function (will obsolete). - - 2017/11/27 (1.53) - renamed ImGuiTextBuffer::append() helper to appendf(), appendv() to appendfv(). If you copied the 'Log' demo in your code, it uses appendv() so that needs to be renamed. - - 2017/11/18 (1.53) - Style, Begin: removed ImGuiWindowFlags_ShowBorders window flag. Borders are now fully set up in the ImGuiStyle structure (see e.g. style.FrameBorderSize, style.WindowBorderSize). Use ImGui::ShowStyleEditor() to look them up. - Please note that the style system will keep evolving (hopefully stabilizing in Q1 2018), and so custom styles will probably subtly break over time. It is recommended you use the StyleColorsClassic(), StyleColorsDark(), StyleColorsLight() functions. - - 2017/11/18 (1.53) - Style: removed ImGuiCol_ComboBg in favor of combo boxes using ImGuiCol_PopupBg for consistency. - - 2017/11/18 (1.53) - Style: renamed ImGuiCol_ChildWindowBg to ImGuiCol_ChildBg. - - 2017/11/18 (1.53) - Style: renamed style.ChildWindowRounding to style.ChildRounding, ImGuiStyleVar_ChildWindowRounding to ImGuiStyleVar_ChildRounding. - - 2017/11/02 (1.53) - obsoleted IsRootWindowOrAnyChildHovered() in favor of using IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows); - - 2017/10/24 (1.52) - renamed IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCS/IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCS to IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS/IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS for consistency. - - 2017/10/20 (1.52) - changed IsWindowHovered() default parameters behavior to return false if an item is active in another window (e.g. click-dragging item from another window to this window). You can use the newly introduced IsWindowHovered() flags to requests this specific behavior if you need it. - - 2017/10/20 (1.52) - marked IsItemHoveredRect()/IsMouseHoveringWindow() as obsolete, in favor of using the newly introduced flags for IsItemHovered() and IsWindowHovered(). See https://github.com/ocornut/imgui/issues/1382 for details. - removed the IsItemRectHovered()/IsWindowRectHovered() names introduced in 1.51 since they were merely more consistent names for the two functions we are now obsoleting. - - 2017/10/17 (1.52) - marked the old 5-parameters version of Begin() as obsolete (still available). Use SetNextWindowSize()+Begin() instead! - - 2017/10/11 (1.52) - renamed AlignFirstTextHeightToWidgets() to AlignTextToFramePadding(). Kept inline redirection function (will obsolete). - - 2017/09/25 (1.52) - removed SetNextWindowPosCenter() because SetNextWindowPos() now has the optional pivot information to do the same and more. Kept redirection function (will obsolete). - - 2017/08/25 (1.52) - io.MousePos needs to be set to ImVec2(-FLT_MAX,-FLT_MAX) when mouse is unavailable/missing. Previously ImVec2(-1,-1) was enough but we now accept negative mouse coordinates. In your binding if you need to support unavailable mouse, make sure to replace "io.MousePos = ImVec2(-1,-1)" with "io.MousePos = ImVec2(-FLT_MAX,-FLT_MAX)". - - 2017/08/22 (1.51) - renamed IsItemHoveredRect() to IsItemRectHovered(). Kept inline redirection function (will obsolete). -> (1.52) use IsItemHovered(ImGuiHoveredFlags_RectOnly)! - - renamed IsMouseHoveringAnyWindow() to IsAnyWindowHovered() for consistency. Kept inline redirection function (will obsolete). - - renamed IsMouseHoveringWindow() to IsWindowRectHovered() for consistency. Kept inline redirection function (will obsolete). - - 2017/08/20 (1.51) - renamed GetStyleColName() to GetStyleColorName() for consistency. - - 2017/08/20 (1.51) - added PushStyleColor(ImGuiCol idx, ImU32 col) overload, which _might_ cause an "ambiguous call" compilation error if you are using ImColor() with implicit cast. Cast to ImU32 or ImVec4 explicily to fix. - - 2017/08/15 (1.51) - marked the weird IMGUI_ONCE_UPON_A_FRAME helper macro as obsolete. prefer using the more explicit ImGuiOnceUponAFrame. - - 2017/08/15 (1.51) - changed parameter order for BeginPopupContextWindow() from (const char*,int buttons,bool also_over_items) to (const char*,int buttons,bool also_over_items). Note that most calls relied on default parameters completely. - - 2017/08/13 (1.51) - renamed ImGuiCol_Columns*** to ImGuiCol_Separator***. Kept redirection enums (will obsolete). - - 2017/08/11 (1.51) - renamed ImGuiSetCond_*** types and flags to ImGuiCond_***. Kept redirection enums (will obsolete). - - 2017/08/09 (1.51) - removed ValueColor() helpers, they are equivalent to calling Text(label) + SameLine() + ColorButton(). - - 2017/08/08 (1.51) - removed ColorEditMode() and ImGuiColorEditMode in favor of ImGuiColorEditFlags and parameters to the various Color*() functions. The SetColorEditOptions() allows to initialize default but the user can still change them with right-click context menu. - - changed prototype of 'ColorEdit4(const char* label, float col[4], bool show_alpha = true)' to 'ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0)', where passing flags = 0x01 is a safe no-op (hello dodgy backward compatibility!). - check and run the demo window, under "Color/Picker Widgets", to understand the various new options. - - changed prototype of rarely used 'ColorButton(ImVec4 col, bool small_height = false, bool outline_border = true)' to 'ColorButton(const char* desc_id, ImVec4 col, ImGuiColorEditFlags flags = 0, ImVec2 size = ImVec2(0,0))' - - 2017/07/20 (1.51) - removed IsPosHoveringAnyWindow(ImVec2), which was partly broken and misleading. ASSERT + redirect user to io.WantCaptureMouse - - 2017/05/26 (1.50) - removed ImFontConfig::MergeGlyphCenterV in favor of a more multipurpose ImFontConfig::GlyphOffset. - - 2017/05/01 (1.50) - renamed ImDrawList::PathFill() (rarely used directly) to ImDrawList::PathFillConvex() for clarity. - - 2016/11/06 (1.50) - BeginChild(const char*) now applies the stack id to the provided label, consistently with other functions as it should always have been. It shouldn't affect you unless (extremely unlikely) you were appending multiple times to a same child from different locations of the stack id. If that's the case, generate an id with GetId() and use it instead of passing string to BeginChild(). - - 2016/10/15 (1.50) - avoid 'void* user_data' parameter to io.SetClipboardTextFn/io.GetClipboardTextFn pointers. We pass io.ClipboardUserData to it. - - 2016/09/25 (1.50) - style.WindowTitleAlign is now a ImVec2 (ImGuiAlign enum was removed). set to (0.5f,0.5f) for horizontal+vertical centering, (0.0f,0.0f) for upper-left, etc. - - 2016/07/30 (1.50) - SameLine(x) with x>0.0f is now relative to left of column/group if any, and not always to left of window. This was sort of always the intent and hopefully breakage should be minimal. - - 2016/05/12 (1.49) - title bar (using ImGuiCol_TitleBg/ImGuiCol_TitleBgActive colors) isn't rendered over a window background (ImGuiCol_WindowBg color) anymore. - If your TitleBg/TitleBgActive alpha was 1.0f or you are using the default theme it will not affect you. - If your TitleBg/TitleBgActive alpha was <1.0f you need to tweak your custom theme to readjust for the fact that we don't draw a WindowBg background behind the title bar. - This helper function will convert an old TitleBg/TitleBgActive color into a new one with the same visual output, given the OLD color and the OLD WindowBg color. - ImVec4 ConvertTitleBgCol(const ImVec4& win_bg_col, const ImVec4& title_bg_col) - { - float new_a = 1.0f - ((1.0f - win_bg_col.w) * (1.0f - title_bg_col.w)), k = title_bg_col.w / new_a; - return ImVec4((win_bg_col.x * win_bg_col.w + title_bg_col.x) * k, (win_bg_col.y * win_bg_col.w + title_bg_col.y) * k, (win_bg_col.z * win_bg_col.w + title_bg_col.z) * k, new_a); - } - If this is confusing, pick the RGB value from title bar from an old screenshot and apply this as TitleBg/TitleBgActive. Or you may just create TitleBgActive from a tweaked TitleBg color. - - 2016/05/07 (1.49) - removed confusing set of GetInternalState(), GetInternalStateSize(), SetInternalState() functions. Now using CreateContext(), DestroyContext(), GetCurrentContext(), SetCurrentContext(). - - 2016/05/02 (1.49) - renamed SetNextTreeNodeOpened() to SetNextTreeNodeOpen(), no redirection. - - 2016/05/01 (1.49) - obsoleted old signature of CollapsingHeader(const char* label, const char* str_id = NULL, bool display_frame = true, bool default_open = false) as extra parameters were badly designed and rarely used. You can replace the "default_open = true" flag in new API with CollapsingHeader(label, ImGuiTreeNodeFlags_DefaultOpen). - - 2016/04/26 (1.49) - changed ImDrawList::PushClipRect(ImVec4 rect) to ImDraw::PushClipRect(Imvec2 min,ImVec2 max,bool intersect_with_current_clip_rect=false). Note that higher-level ImGui::PushClipRect() is preferable because it will clip at logic/widget level, whereas ImDrawList::PushClipRect() only affect your renderer. - - 2016/04/03 (1.48) - removed style.WindowFillAlphaDefault setting which was redundant. Bake default BG alpha inside style.Colors[ImGuiCol_WindowBg] and all other Bg color values. (ref github issue #337). - - 2016/04/03 (1.48) - renamed ImGuiCol_TooltipBg to ImGuiCol_PopupBg, used by popups/menus and tooltips. popups/menus were previously using ImGuiCol_WindowBg. (ref github issue #337) - - 2016/03/21 (1.48) - renamed GetWindowFont() to GetFont(), GetWindowFontSize() to GetFontSize(). Kept inline redirection function (will obsolete). - - 2016/03/02 (1.48) - InputText() completion/history/always callbacks: if you modify the text buffer manually (without using DeleteChars()/InsertChars() helper) you need to maintain the BufTextLen field. added an assert. - - 2016/01/23 (1.48) - fixed not honoring exact width passed to PushItemWidth(), previously it would add extra FramePadding.x*2 over that width. if you had manual pixel-perfect alignment in place it might affect you. - - 2015/12/27 (1.48) - fixed ImDrawList::AddRect() which used to render a rectangle 1 px too large on each axis. - - 2015/12/04 (1.47) - renamed Color() helpers to ValueColor() - dangerously named, rarely used and probably to be made obsolete. - - 2015/08/29 (1.45) - with the addition of horizontal scrollbar we made various fixes to inconsistencies with dealing with cursor position. - GetCursorPos()/SetCursorPos() functions now include the scrolled amount. It shouldn't affect the majority of users, but take note that SetCursorPosX(100.0f) puts you at +100 from the starting x position which may include scrolling, not at +100 from the window left side. - GetContentRegionMax()/GetWindowContentRegionMin()/GetWindowContentRegionMax() functions allow include the scrolled amount. Typically those were used in cases where no scrolling would happen so it may not be a problem, but watch out! - - 2015/08/29 (1.45) - renamed style.ScrollbarWidth to style.ScrollbarSize - - 2015/08/05 (1.44) - split imgui.cpp into extra files: imgui_demo.cpp imgui_draw.cpp imgui_internal.h that you need to add to your project. - - 2015/07/18 (1.44) - fixed angles in ImDrawList::PathArcTo(), PathArcToFast() (introduced in 1.43) being off by an extra PI for no justifiable reason - - 2015/07/14 (1.43) - add new ImFontAtlas::AddFont() API. For the old AddFont***, moved the 'font_no' parameter of ImFontAtlas::AddFont** functions to the ImFontConfig structure. - you need to render your textured triangles with bilinear filtering to benefit from sub-pixel positioning of text. - - 2015/07/08 (1.43) - switched rendering data to use indexed rendering. this is saving a fair amount of CPU/GPU and enables us to get anti-aliasing for a marginal cost. - this necessary change will break your rendering function! the fix should be very easy. sorry for that :( - - if you are using a vanilla copy of one of the imgui_impl_XXXX.cpp provided in the example, you just need to update your copy and you can ignore the rest. - - the signature of the io.RenderDrawListsFn handler has changed! - old: ImGui_XXXX_RenderDrawLists(ImDrawList** const cmd_lists, int cmd_lists_count) - new: ImGui_XXXX_RenderDrawLists(ImDrawData* draw_data). - argument: 'cmd_lists' becomes 'draw_data->CmdLists', 'cmd_lists_count' becomes 'draw_data->CmdListsCount' - ImDrawList: 'commands' becomes 'CmdBuffer', 'vtx_buffer' becomes 'VtxBuffer', 'IdxBuffer' is new. - ImDrawCmd: 'vtx_count' becomes 'ElemCount', 'clip_rect' becomes 'ClipRect', 'user_callback' becomes 'UserCallback', 'texture_id' becomes 'TextureId'. - - each ImDrawList now contains both a vertex buffer and an index buffer. For each command, render ElemCount/3 triangles using indices from the index buffer. - - if you REALLY cannot render indexed primitives, you can call the draw_data->DeIndexAllBuffers() method to de-index the buffers. This is slow and a waste of CPU/GPU. Prefer using indexed rendering! - - refer to code in the examples/ folder or ask on the GitHub if you are unsure of how to upgrade. please upgrade! - - 2015/07/10 (1.43) - changed SameLine() parameters from int to float. - - 2015/07/02 (1.42) - renamed SetScrollPosHere() to SetScrollFromCursorPos(). Kept inline redirection function (will obsolete). - - 2015/07/02 (1.42) - renamed GetScrollPosY() to GetScrollY(). Necessary to reduce confusion along with other scrolling functions, because positions (e.g. cursor position) are not equivalent to scrolling amount. - - 2015/06/14 (1.41) - changed ImageButton() default bg_col parameter from (0,0,0,1) (black) to (0,0,0,0) (transparent) - makes a difference when texture have transparence - - 2015/06/14 (1.41) - changed Selectable() API from (label, selected, size) to (label, selected, flags, size). Size override should have been rarely be used. Sorry! - - 2015/05/31 (1.40) - renamed GetWindowCollapsed() to IsWindowCollapsed() for consistency. Kept inline redirection function (will obsolete). - - 2015/05/31 (1.40) - renamed IsRectClipped() to IsRectVisible() for consistency. Note that return value is opposite! Kept inline redirection function (will obsolete). - - 2015/05/27 (1.40) - removed the third 'repeat_if_held' parameter from Button() - sorry! it was rarely used and inconsistent. Use PushButtonRepeat(true) / PopButtonRepeat() to enable repeat on desired buttons. - - 2015/05/11 (1.40) - changed BeginPopup() API, takes a string identifier instead of a bool. ImGui needs to manage the open/closed state of popups. Call OpenPopup() to actually set the "open" state of a popup. BeginPopup() returns true if the popup is opened. - - 2015/05/03 (1.40) - removed style.AutoFitPadding, using style.WindowPadding makes more sense (the default values were already the same). - - 2015/04/13 (1.38) - renamed IsClipped() to IsRectClipped(). Kept inline redirection function until 1.50. - - 2015/04/09 (1.38) - renamed ImDrawList::AddArc() to ImDrawList::AddArcFast() for compatibility with future API - - 2015/04/03 (1.38) - removed ImGuiCol_CheckHovered, ImGuiCol_CheckActive, replaced with the more general ImGuiCol_FrameBgHovered, ImGuiCol_FrameBgActive. - - 2014/04/03 (1.38) - removed support for passing -FLT_MAX..+FLT_MAX as the range for a SliderFloat(). Use DragFloat() or Inputfloat() instead. - - 2015/03/17 (1.36) - renamed GetItemBoxMin()/GetItemBoxMax()/IsMouseHoveringBox() to GetItemRectMin()/GetItemRectMax()/IsMouseHoveringRect(). Kept inline redirection function until 1.50. - - 2015/03/15 (1.36) - renamed style.TreeNodeSpacing to style.IndentSpacing, ImGuiStyleVar_TreeNodeSpacing to ImGuiStyleVar_IndentSpacing - - 2015/03/13 (1.36) - renamed GetWindowIsFocused() to IsWindowFocused(). Kept inline redirection function until 1.50. - - 2015/03/08 (1.35) - renamed style.ScrollBarWidth to style.ScrollbarWidth (casing) - - 2015/02/27 (1.34) - renamed OpenNextNode(bool) to SetNextTreeNodeOpened(bool, ImGuiSetCond). Kept inline redirection function until 1.50. - - 2015/02/27 (1.34) - renamed ImGuiSetCondition_*** to ImGuiSetCond_***, and _FirstUseThisSession becomes _Once. - - 2015/02/11 (1.32) - changed text input callback ImGuiTextEditCallback return type from void-->int. reserved for future use, return 0 for now. - - 2015/02/10 (1.32) - renamed GetItemWidth() to CalcItemWidth() to clarify its evolving behavior - - 2015/02/08 (1.31) - renamed GetTextLineSpacing() to GetTextLineHeightWithSpacing() - - 2015/02/01 (1.31) - removed IO.MemReallocFn (unused) - - 2015/01/19 (1.30) - renamed ImGuiStorage::GetIntPtr()/GetFloatPtr() to GetIntRef()/GetIntRef() because Ptr was conflicting with actual pointer storage functions. - - 2015/01/11 (1.30) - big font/image API change! now loads TTF file. allow for multiple fonts. no need for a PNG loader. - (1.30) - removed GetDefaultFontData(). uses io.Fonts->GetTextureData*() API to retrieve uncompressed pixels. - font init: const void* png_data; unsigned int png_size; ImGui::GetDefaultFontData(NULL, NULL, &png_data, &png_size); <..Upload texture to GPU..> - became: unsigned char* pixels; int width, height; io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); <..Upload texture to GPU>; io.Fonts->TexId = YourTextureIdentifier; - you now more flexibility to load multiple TTF fonts and manage the texture buffer for internal needs. - it is now recommended that you sample the font texture with bilinear interpolation. - (1.30) - added texture identifier in ImDrawCmd passed to your render function (we can now render images). make sure to set io.Fonts->TexID. - (1.30) - removed IO.PixelCenterOffset (unnecessary, can be handled in user projection matrix) - (1.30) - removed ImGui::IsItemFocused() in favor of ImGui::IsItemActive() which handles all widgets - - 2014/12/10 (1.18) - removed SetNewWindowDefaultPos() in favor of new generic API SetNextWindowPos(pos, ImGuiSetCondition_FirstUseEver) - - 2014/11/28 (1.17) - moved IO.Font*** options to inside the IO.Font-> structure (FontYOffset, FontTexUvForWhite, FontBaseScale, FontFallbackGlyph) - - 2014/11/26 (1.17) - reworked syntax of IMGUI_ONCE_UPON_A_FRAME helper macro to increase compiler compatibility - - 2014/11/07 (1.15) - renamed IsHovered() to IsItemHovered() - - 2014/10/02 (1.14) - renamed IMGUI_INCLUDE_IMGUI_USER_CPP to IMGUI_INCLUDE_IMGUI_USER_INL and imgui_user.cpp to imgui_user.inl (more IDE friendly) - - 2014/09/25 (1.13) - removed 'text_end' parameter from IO.SetClipboardTextFn (the string is now always zero-terminated for simplicity) - - 2014/09/24 (1.12) - renamed SetFontScale() to SetWindowFontScale() - - 2014/09/24 (1.12) - moved IM_MALLOC/IM_REALLOC/IM_FREE preprocessor defines to IO.MemAllocFn/IO.MemReallocFn/IO.MemFreeFn - - 2014/08/30 (1.09) - removed IO.FontHeight (now computed automatically) - - 2014/08/30 (1.09) - moved IMGUI_FONT_TEX_UV_FOR_WHITE preprocessor define to IO.FontTexUvForWhite - - 2014/08/28 (1.09) - changed the behavior of IO.PixelCenterOffset following various rendering fixes - - - ISSUES & TODO-LIST - ================== - See TODO.txt - - - FREQUENTLY ASKED QUESTIONS (FAQ), TIPS - ====================================== - - Q: How can I tell whether to dispatch mouse/keyboard to imgui or to my application? - A: You can read the 'io.WantCaptureMouse', 'io.WantCaptureKeyboard' and 'io.WantTextInput' flags from the ImGuiIO structure (e.g. if (ImGui::GetIO().WantCaptureMouse) { ... } ) - - When 'io.WantCaptureMouse' is set, imgui wants to use your mouse state, and you may want to discard/hide the inputs from the rest of your application. - - When 'io.WantCaptureKeyboard' is set, imgui wants to use your keyboard state, and you may want to discard/hide the inputs from the rest of your application. - - When 'io.WantTextInput' is set to may want to notify your OS to popup an on-screen keyboard, if available (e.g. on a mobile phone, or console OS). - Note: you should always pass your mouse/keyboard inputs to imgui, even when the io.WantCaptureXXX flag are set false. - This is because imgui needs to detect that you clicked in the void to unfocus its windows. - Note: The 'io.WantCaptureMouse' is more accurate that any attempt to "check if the mouse is hovering a window" (don't do that!). - It handle mouse dragging correctly (both dragging that started over your application or over an imgui window) and handle e.g. modal windows blocking inputs. - Those flags are updated by ImGui::NewFrame(). Preferably read the flags after calling NewFrame() if you can afford it, but reading them before is also - perfectly fine, as the bool toggle fairly rarely. If you have on a touch device, you might find use for an early call to NewFrameUpdateHoveredWindowAndCaptureFlags(). - Note: Text input widget releases focus on "Return KeyDown", so the subsequent "Return KeyUp" event that your application receive will typically - have 'io.WantCaptureKeyboard=false'. Depending on your application logic it may or not be inconvenient. You might want to track which key-downs - were targeted for Dear ImGui, e.g. with an array of bool, and filter out the corresponding key-ups.) - - Q: How can I display an image? What is ImTextureID, how does it works? - A: Short explanation: - - You may use functions such as ImGui::Image(), ImGui::ImageButton() or lower-level ImDrawList::AddImage() to emit draw calls that will use your own textures. - - Actual textures are identified in a way that is up to the user/engine. - - Loading image files from the disk and turning them into a texture is not within the scope of Dear ImGui (for a good reason). - Please read documentations or tutorials on your graphics API to understand how to display textures on the screen before moving onward. - - Long explanation: - - Dear ImGui's job is to create "meshes", defined in a renderer-agnostic format made of draw commands and vertices. - At the end of the frame those meshes (ImDrawList) will be displayed by your rendering function. They are made up of textured polygons and the code - to render them is generally fairly short (a few dozen lines). In the examples/ folder we provide functions for popular graphics API (OpenGL, DirectX, etc.). - - Each rendering function decides on a data type to represent "textures". The concept of what is a "texture" is entirely tied to your underlying engine/graphics API. - We carry the information to identify a "texture" in the ImTextureID type. - ImTextureID is nothing more that a void*, aka 4/8 bytes worth of data: just enough to store 1 pointer or 1 integer of your choice. - Dear ImGui doesn't know or understand what you are storing in ImTextureID, it merely pass ImTextureID values until they reach your rendering function. - - In the examples/ bindings, for each graphics API binding we decided on a type that is likely to be a good representation for specifying - an image from the end-user perspective. This is what the _examples_ rendering functions are using: - - OpenGL: ImTextureID = GLuint (see ImGui_ImplGlfwGL3_RenderDrawData() function in imgui_impl_glfw_gl3.cpp) - DirectX9: ImTextureID = LPDIRECT3DTEXTURE9 (see ImGui_ImplDX9_RenderDrawData() function in imgui_impl_dx9.cpp) - DirectX11: ImTextureID = ID3D11ShaderResourceView* (see ImGui_ImplDX11_RenderDrawData() function in imgui_impl_dx11.cpp) - DirectX12: ImTextureID = D3D12_GPU_DESCRIPTOR_HANDLE (see ImGui_ImplDX12_RenderDrawData() function in imgui_impl_dx12.cpp) - - For example, in the OpenGL example binding we store raw OpenGL texture identifier (GLuint) inside ImTextureID. - Whereas in the DirectX11 example binding we store a pointer to ID3D11ShaderResourceView inside ImTextureID, which is a higher-level structure - tying together both the texture and information about its format and how to read it. - - If you have a custom engine built over e.g. OpenGL, instead of passing GLuint around you may decide to use a high-level data type to carry information about - the texture as well as how to display it (shaders, etc.). The decision of what to use as ImTextureID can always be made better knowing how your codebase - is designed. If your engine has high-level data types for "textures" and "material" then you may want to use them. - If you are starting with OpenGL or DirectX or Vulkan and haven't built much of a rendering engine over them, keeping the default ImTextureID - representation suggested by the example bindings is probably the best choice. - (Advanced users may also decide to keep a low-level type in ImTextureID, and use ImDrawList callback and pass information to their renderer) - - User code may do: - - // Cast our texture type to ImTextureID / void* - MyTexture* texture = g_CoffeeTableTexture; - ImGui::Image((void*)texture, ImVec2(texture->Width, texture->Height)); - - The renderer function called after ImGui::Render() will receive that same value that the user code passed: - - // Cast ImTextureID / void* stored in the draw command as our texture type - MyTexture* texture = (MyTexture*)pcmd->TextureId; - MyEngineBindTexture2D(texture); - - Once you understand this design you will understand that loading image files and turning them into displayable textures is not within the scope of Dear ImGui. - This is by design and is actually a good thing, because it means your code has full control over your data types and how you display them. - If you want to display an image file (e.g. PNG file) into the screen, please refer to documentation and tutorials for the graphics API you are using. - - Here's a simplified OpenGL example using stb_image.h: - - // Use stb_image.h to load a PNG from disk and turn it into raw RGBA pixel data: - #define STB_IMAGE_IMPLEMENTATION - #include - [...] - int my_image_width, my_image_height; - unsigned char* my_image_data = stbi_load("my_image.png", &my_image_width, &my_image_height, NULL, 4); - - // Turn the RGBA pixel data into an OpenGL texture: - GLuint my_opengl_texture; - glGenTextures(1, &my_opengl_texture); - glBindTexture(GL_TEXTURE_2D, my_opengl_texture); - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image_width, image_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data); - - // Now that we have an OpenGL texture, assuming our imgui rendering function (imgui_impl_xxx.cpp file) takes GLuint as ImTextureID, we can display it: - ImGui::Image((void*)(intptr_t)my_opengl_texture, ImVec2(my_image_width, my_image_height)); - - C/C++ tip: a void* is pointer-sized storage. You may safely store any pointer or integer into it by casting your value to ImTexture / void*, and vice-versa. - Because both end-points (user code and rendering function) are under your control, you know exactly what is stored inside the ImTexture / void*. - Examples: - - GLuint my_tex = XXX; - void* my_void_ptr; - my_void_ptr = (void*)(intptr_t)my_tex; // cast a GLuint into a void* (we don't take its address! we literally store the value inside the pointer) - my_tex = (GLuint)(intptr_t)my_void_ptr; // cast a void* into a GLuint - - ID3D11ShaderResourceView* my_dx11_srv = XXX; - void* my_void_ptr; - my_void_ptr = (void*)my_dx11_srv; // cast a ID3D11ShaderResourceView* into an opaque void* - my_dx11_srv = (ID3D11ShaderResourceView*)my_void_ptr; // cast a void* into a ID3D11ShaderResourceView* - - Finally, you may call ImGui::ShowMetricsWindow() to explore/visualize/understand how the ImDrawList are generated. - - Q: How can I have multiple widgets with the same label or without a label? - Q: I have multiple widgets with the same label, and only the first one works. Why is that? - A: A primer on labels and the ID Stack... - - Dear ImGui internally need to uniquely identify UI elements. - Elements that are typically not clickable (such as calls to the Text functions) don't need an ID. - Interactive widgets (such as calls to Button buttons) need a unique ID. - Unique ID are used internally to track active widgets and occasionally associate state to widgets. - Unique ID are implicitly built from the hash of multiple elements that identify the "path" to the UI element. - - - Unique ID are often derived from a string label: - - Button("OK"); // Label = "OK", ID = hash of (..., "OK") - Button("Cancel"); // Label = "Cancel", ID = hash of (..., "Cancel") - - - ID are uniquely scoped within windows, tree nodes, etc. which all pushes to the ID stack. Having - two buttons labeled "OK" in different windows or different tree locations is fine. - We used "..." above to signify whatever was already pushed to the ID stack previously: - - Begin("MyWindow"); - Button("OK"); // Label = "OK", ID = hash of ("MyWindow", "OK") - End(); - - - If you have a same ID twice in the same location, you'll have a conflict: - - Button("OK"); - Button("OK"); // ID collision! Interacting with either button will trigger the first one. - - Fear not! this is easy to solve and there are many ways to solve it! - - - Solving ID conflict in a simple/local context: - When passing a label you can optionally specify extra ID information within string itself. - Use "##" to pass a complement to the ID that won't be visible to the end-user. - This helps solving the simple collision cases when you know e.g. at compilation time which items - are going to be created: - - Begin("MyWindow"); - Button("Play"); // Label = "Play", ID = hash of ("MyWindow", "Play") - Button("Play##foo1"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo1") // Different from above - Button("Play##foo2"); // Label = "Play", ID = hash of ("MyWindow", "Play##foo2") // Different from above - End(); - - - If you want to completely hide the label, but still need an ID: - - Checkbox("##On", &b); // Label = "", ID = hash of (..., "##On") // No visible label! - - - Occasionally/rarely you might want change a label while preserving a constant ID. This allows - you to animate labels. For example you may want to include varying information in a window title bar, - but windows are uniquely identified by their ID. Use "###" to pass a label that isn't part of ID: - - Button("Hello###ID"; // Label = "Hello", ID = hash of (..., "ID") - Button("World###ID"; // Label = "World", ID = hash of (..., "ID") // Same as above, even though the label looks different - - sprintf(buf, "My game (%f FPS)###MyGame", fps); - Begin(buf); // Variable label, ID = hash of "MyGame" - - - Solving ID conflict in a more general manner: - Use PushID() / PopID() to create scopes and manipulate the ID stack, as to avoid ID conflicts - within the same window. This is the most convenient way of distinguishing ID when iterating and - creating many UI elements programmatically. - You can push a pointer, a string or an integer value into the ID stack. - Remember that ID are formed from the concatenation of _everything_ in the ID stack! - - Begin("Window"); - for (int i = 0; i < 100; i++) - { - PushID(i); // Push i to the id tack - Button("Click"); // Label = "Click", ID = Hash of ("Window", i, "Click") - PopID(); - } - for (int i = 0; i < 100; i++) - { - MyObject* obj = Objects[i]; - PushID(obj); - Button("Click"); // Label = "Click", ID = Hash of ("Window", obj pointer, "Click") - PopID(); - } - for (int i = 0; i < 100; i++) - { - MyObject* obj = Objects[i]; - PushID(obj->Name); - Button("Click"); // Label = "Click", ID = Hash of ("Window", obj->Name, "Click") - PopID(); - } - End(); - - - More example showing that you can stack multiple prefixes into the ID stack: - - Button("Click"); // Label = "Click", ID = hash of (..., "Click") - PushID("node"); - Button("Click"); // Label = "Click", ID = hash of (..., "node", "Click") - PushID(my_ptr); - Button("Click"); // Label = "Click", ID = hash of (..., "node", my_ptr, "Click") - PopID(); - PopID(); - - - Tree nodes implicitly creates a scope for you by calling PushID(). - - Button("Click"); // Label = "Click", ID = hash of (..., "Click") - if (TreeNode("node")) - { - Button("Click"); // Label = "Click", ID = hash of (..., "node", "Click") - TreePop(); - } - - - When working with trees, ID are used to preserve the open/close state of each tree node. - Depending on your use cases you may want to use strings, indices or pointers as ID. - e.g. when following a single pointer that may change over time, using a static string as ID - will preserve your node open/closed state when the targeted object change. - e.g. when displaying a list of objects, using indices or pointers as ID will preserve the - node open/closed state differently. See what makes more sense in your situation! - - Q: How can I load a different font than the default? - A: Use the font atlas to load the TTF/OTF file you want: - ImGuiIO& io = ImGui::GetIO(); - io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels); - io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8() - (default is ProggyClean.ttf, rendered at size 13, embedded in dear imgui's source code) - - New programmers: remember that in C/C++ and most programming languages if you want to use a - backslash \ within a string literal, you need to write it double backslash "\\": - io.Fonts->AddFontFromFileTTF("MyDataFolder\MyFontFile.ttf", size_in_pixels); // WRONG (you are escape the M here!) - io.Fonts->AddFontFromFileTTF("MyDataFolder\\MyFontFile.ttf", size_in_pixels); // CORRECT - io.Fonts->AddFontFromFileTTF("MyDataFolder/MyFontFile.ttf", size_in_pixels); // ALSO CORRECT - - Q: How can I easily use icons in my application? - A: The most convenient and practical way is to merge an icon font such as FontAwesome inside you - main font. Then you can refer to icons within your strings. Read 'How can I load multiple fonts?' - and the file 'misc/fonts/README.txt' for instructions and useful header files. - - Q: How can I load multiple fonts? - A: Use the font atlas to pack them into a single texture: - (Read misc/fonts/README.txt and the code in ImFontAtlas for more details.) - - ImGuiIO& io = ImGui::GetIO(); - ImFont* font0 = io.Fonts->AddFontDefault(); - ImFont* font1 = io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels); - ImFont* font2 = io.Fonts->AddFontFromFileTTF("myfontfile2.ttf", size_in_pixels); - io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8() - // the first loaded font gets used by default - // use ImGui::PushFont()/ImGui::PopFont() to change the font at runtime - - // Options - ImFontConfig config; - config.OversampleH = 3; - config.OversampleV = 1; - config.GlyphOffset.y -= 2.0f; // Move everything by 2 pixels up - config.GlyphExtraSpacing.x = 1.0f; // Increase spacing between characters - io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, &config); - - // Combine multiple fonts into one (e.g. for icon fonts) - ImWchar ranges[] = { 0xf000, 0xf3ff, 0 }; - ImFontConfig config; - config.MergeMode = true; - io.Fonts->AddFontDefault(); - io.Fonts->LoadFromFileTTF("fontawesome-webfont.ttf", 16.0f, &config, ranges); // Merge icon font - io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, NULL, &config, io.Fonts->GetGlyphRangesJapanese()); // Merge japanese glyphs - - Q: How can I display and input non-Latin characters such as Chinese, Japanese, Korean, Cyrillic? - A: When loading a font, pass custom Unicode ranges to specify the glyphs to load. - - // Add default Japanese ranges - io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, io.Fonts->GetGlyphRangesJapanese()); - - // Or create your own custom ranges (e.g. for a game you can feed your entire game script and only build the characters the game need) - ImVector ranges; - ImFontAtlas::GlyphRangesBuilder builder; - builder.AddText("Hello world"); // Add a string (here "Hello world" contains 7 unique characters) - builder.AddChar(0x7262); // Add a specific character - builder.AddRanges(io.Fonts->GetGlyphRangesJapanese()); // Add one of the default ranges - builder.BuildRanges(&ranges); // Build the final result (ordered ranges with all the unique characters submitted) - io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, ranges.Data); - - All your strings needs to use UTF-8 encoding. In C++11 you can encode a string literal in UTF-8 - by using the u8"hello" syntax. Specifying literal in your source code using a local code page - (such as CP-923 for Japanese or CP-1251 for Cyrillic) will NOT work! - Otherwise you can convert yourself to UTF-8 or load text data from file already saved as UTF-8. - - Text input: it is up to your application to pass the right character code by calling io.AddInputCharacter(). - The applications in examples/ are doing that. - Windows: you can use the WM_CHAR or WM_UNICHAR or WM_IME_CHAR message (depending if your app is built using Unicode or MultiByte mode). - You may also use MultiByteToWideChar() or ToUnicode() to retrieve Unicode codepoints from MultiByte characters or keyboard state. - Windows: if your language is relying on an Input Method Editor (IME), you copy the HWND of your window to io.ImeWindowHandle in order for - the default implementation of io.ImeSetInputScreenPosFn() to set your Microsoft IME position correctly. - - Q: How can I use the drawing facilities without an ImGui window? (using ImDrawList API) - A: - You can create a dummy window. Call SetNextWindowBgAlpha(0.0f), call Begin() with NoTitleBar|NoResize|NoMove|NoScrollbar|NoSavedSettings|NoInputs flags. - Then you can retrieve the ImDrawList* via GetWindowDrawList() and draw to it in any way you like. - - You can call ImGui::GetOverlayDrawList() and use this draw list to display contents over every other imgui windows. - - You can create your own ImDrawList instance. You'll need to initialize them ImGui::GetDrawListSharedData(), or create your own ImDrawListSharedData. - - Q: I integrated Dear ImGui in my engine and the text or lines are blurry.. - A: In your Render function, try translating your projection matrix by (0.5f,0.5f) or (0.375f,0.375f). - Also make sure your orthographic projection matrix and io.DisplaySize matches your actual framebuffer dimension. - - Q: I integrated Dear ImGui in my engine and some elements are clipping or disappearing when I move windows around.. - A: You are probably mishandling the clipping rectangles in your render function. - Rectangles provided by ImGui are defined as (x1=left,y1=top,x2=right,y2=bottom) and NOT as (x1,y1,width,height). - - Q: How can I help? - A: - If you are experienced with Dear ImGui and C++, look at the github issues, or TODO.txt and see how you want/can help! - - Convince your company to fund development time! Individual users: you can also become a Patron (patreon.com/imgui) or donate on PayPal! See README. - - Disclose your usage of dear imgui via a dev blog post, a tweet, a screenshot, a mention somewhere etc. - You may post screenshot or links in the gallery threads (github.com/ocornut/imgui/issues/1269). Visuals are ideal as they inspire other programmers. - But even without visuals, disclosing your use of dear imgui help the library grow credibility, and help other teams and programmers with taking decisions. - - If you have issues or if you need to hack into the library, even if you don't expect any support it is useful that you share your issues (on github or privately). - - - tip: you can call Begin() multiple times with the same name during the same frame, it will keep appending to the same window. - this is also useful to set yourself in the context of another window (to get/set other settings) - - tip: you can create widgets without a Begin()/End() block, they will go in an implicit window called "Debug". - - tip: the ImGuiOnceUponAFrame helper will allow run the block of code only once a frame. You can use it to quickly add custom UI in the middle - of a deep nested inner loop in your code. - - tip: you can call Render() multiple times (e.g for VR renders). - - tip: call and read the ShowDemoWindow() code in imgui_demo.cpp for more example of how to use ImGui! - -*/ - -#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) -#define _CRT_SECURE_NO_WARNINGS -#endif - -#include "imgui.h" -#define IMGUI_DEFINE_MATH_OPERATORS -#include "imgui_internal.h" - -#include // toupper, isprint -#include // vsnprintf, sscanf, printf -#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier -#include // intptr_t -#else -#include // intptr_t -#endif - -#define IMGUI_DEBUG_NAV_SCORING 0 -#define IMGUI_DEBUG_NAV_RECTS 0 - -// Visual Studio warnings -#ifdef _MSC_VER -#pragma warning (disable: 4127) // condition expression is constant -#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff) -#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen -#endif - -// Clang warnings with -Weverything -#ifdef __clang__ -#pragma clang diagnostic ignored "-Wunknown-pragmas" // warning : unknown warning group '-Wformat-pedantic *' // not all warnings are known by all clang versions.. so ignoring warnings triggers new warnings on some configuration. great! -#pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse. -#pragma clang diagnostic ignored "-Wfloat-equal" // warning : comparing floating point with == or != is unsafe // storing and comparing against same constants (typically 0.0f) is ok. -#pragma clang diagnostic ignored "-Wformat-nonliteral" // warning : format string is not a string literal // passing non-literal to vsnformat(). yes, user passing incorrect format strings can crash the code. -#pragma clang diagnostic ignored "-Wexit-time-destructors" // warning : declaration requires an exit-time destructor // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals. -#pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference it. -#pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness // -#pragma clang diagnostic ignored "-Wformat-pedantic" // warning : format specifies type 'void *' but the argument has type 'xxxx *' // unreasonable, would lead to casting every %p arg to void*. probably enabled by -pedantic. -#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int' -#elif defined(__GNUC__) -#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used -#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size -#pragma GCC diagnostic ignored "-Wformat" // warning: format '%p' expects argument of type 'void*', but argument 6 has type 'ImGuiWindow*' -#pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function -#pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value -#pragma GCC diagnostic ignored "-Wformat-nonliteral" // warning: format not a string literal, format string not checked -#pragma GCC diagnostic ignored "-Wstrict-overflow" // warning: assuming signed overflow does not occur when assuming that (X - c) > X is always false -#if __GNUC__ >= 8 -#pragma GCC diagnostic ignored "-Wclass-memaccess" // warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead -#endif -#endif - -// Enforce cdecl calling convention for functions called by the standard library, in case compilation settings changed the default to e.g. __vectorcall -#ifdef _MSC_VER -#define IMGUI_CDECL __cdecl -#else -#define IMGUI_CDECL -#endif - -static const ImS32 IM_S32_MIN = 0x80000000; // INT_MIN; -static const ImS32 IM_S32_MAX = 0x7FFFFFFF; // INT_MAX; -static const ImU32 IM_U32_MIN = 0; -static const ImU32 IM_U32_MAX = 0xFFFFFFFF; -static const ImS64 IM_S64_MIN = -9223372036854775807ll - 1ll; -static const ImS64 IM_S64_MAX = 9223372036854775807ll; -static const ImU64 IM_U64_MIN = 0; -static const ImU64 IM_U64_MAX = 0xFFFFFFFFFFFFFFFFull; - -//------------------------------------------------------------------------- -// Forward Declarations -//------------------------------------------------------------------------- - -static bool IsKeyPressedMap(ImGuiKey key, bool repeat = true); - -static ImFont* GetDefaultFont(); -static void SetCurrentWindow(ImGuiWindow* window); -static void SetWindowScrollX(ImGuiWindow* window, float new_scroll_x); -static void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y); -static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond); -static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond); -static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond); -static ImGuiWindow* FindHoveredWindow(); -static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags); -static void CheckStacksSize(ImGuiWindow* window, bool write); -static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges); - -static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* draw_list); -static void AddWindowToDrawData(ImVector* out_list, ImGuiWindow* window); -static void AddWindowToSortedBuffer(ImVector* out_sorted_windows, ImGuiWindow* window); - -static ImGuiWindowSettings* AddWindowSettings(const char* name); - -static ImRect GetViewportRect(); - -static void ClosePopupToLevel(int remaining); - -static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data); -static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end); -static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false); - -static inline int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* data_ptr, const char* format); -static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg_1, const void* arg_2); -static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* format); - -namespace ImGui -{ -static void NavUpdate(); -static void NavUpdateWindowing(); -static void NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id); - -static void UpdateMovingWindow(); -static void UpdateMouseInputs(); -static void UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]); -static void FocusFrontMostActiveWindow(ImGuiWindow* ignore_window); - -// Template widget behaviors -template -static bool DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const TYPE v_min, const TYPE v_max, const char* format, float power); - -template -static bool SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, TYPE* v, const TYPE v_min, const TYPE v_max, const char* format, float power, ImGuiSliderFlags flags); -} - -//----------------------------------------------------------------------------- -// Platform dependent default implementations -//----------------------------------------------------------------------------- - -static const char* GetClipboardTextFn_DefaultImpl(void* user_data); -static void SetClipboardTextFn_DefaultImpl(void* user_data, const char* text); -static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y); - -//----------------------------------------------------------------------------- -// Context -//----------------------------------------------------------------------------- - -// Current context pointer. Implicitly used by all ImGui functions. Always assumed to be != NULL. -// CreateContext() will automatically set this pointer if it is NULL. Change to a different context by calling ImGui::SetCurrentContext(). -// If you use DLL hotreloading you might need to call SetCurrentContext() after reloading code from this file. -// ImGui functions are not thread-safe because of this pointer. If you want thread-safety to allow N threads to access N different contexts, you can: -// - Change this variable to use thread local storage. You may #define GImGui in imconfig.h for that purpose. Future development aim to make this context pointer explicit to all calls. Also read https://github.com/ocornut/imgui/issues/586 -// - Having multiple instances of the ImGui code compiled inside different namespace (easiest/safest, if you have a finite number of contexts) -#ifndef GImGui -ImGuiContext* GImGui = NULL; -#endif - -// Memory Allocator functions. Use SetAllocatorFunctions() to change them. -// If you use DLL hotreloading you might need to call SetAllocatorFunctions() after reloading code from this file. -// Otherwise, you probably don't want to modify them mid-program, and if you use global/static e.g. ImVector<> instances you may need to keep them accessible during program destruction. -#ifndef IMGUI_DISABLE_DEFAULT_ALLOCATORS -static void* MallocWrapper(size_t size, void* user_data) { (void)user_data; return malloc(size); } -static void FreeWrapper(void* ptr, void* user_data) { (void)user_data; free(ptr); } -#else -static void* MallocWrapper(size_t size, void* user_data) { (void)user_data; (void)size; IM_ASSERT(0); return NULL; } -static void FreeWrapper(void* ptr, void* user_data) { (void)user_data; (void)ptr; IM_ASSERT(0); } -#endif - -static void* (*GImAllocatorAllocFunc)(size_t size, void* user_data) = MallocWrapper; -static void (*GImAllocatorFreeFunc)(void* ptr, void* user_data) = FreeWrapper; -static void* GImAllocatorUserData = NULL; -static size_t GImAllocatorActiveAllocationsCount = 0; - -//----------------------------------------------------------------------------- -// User facing structures -//----------------------------------------------------------------------------- - -ImGuiStyle::ImGuiStyle() -{ - Alpha = 1.0f; // Global alpha applies to everything in ImGui - WindowPadding = ImVec2(8,8); // Padding within a window - WindowRounding = 7.0f; // Radius of window corners rounding. Set to 0.0f to have rectangular windows - WindowBorderSize = 1.0f; // Thickness of border around windows. Generally set to 0.0f or 1.0f. Other values not well tested. - WindowMinSize = ImVec2(32,32); // Minimum window size - WindowTitleAlign = ImVec2(0.0f,0.5f);// Alignment for title bar text - ChildRounding = 0.0f; // Radius of child window corners rounding. Set to 0.0f to have rectangular child windows - ChildBorderSize = 1.0f; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. Other values not well tested. - PopupRounding = 0.0f; // Radius of popup window corners rounding. Set to 0.0f to have rectangular child windows - PopupBorderSize = 1.0f; // Thickness of border around popup or tooltip windows. Generally set to 0.0f or 1.0f. Other values not well tested. - FramePadding = ImVec2(4,3); // Padding within a framed rectangle (used by most widgets) - FrameRounding = 0.0f; // Radius of frame corners rounding. Set to 0.0f to have rectangular frames (used by most widgets). - FrameBorderSize = 0.0f; // Thickness of border around frames. Generally set to 0.0f or 1.0f. Other values not well tested. - ItemSpacing = ImVec2(8,4); // Horizontal and vertical spacing between widgets/lines - ItemInnerSpacing = ImVec2(4,4); // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label) - TouchExtraPadding = ImVec2(0,0); // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much! - IndentSpacing = 21.0f; // Horizontal spacing when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2). - ColumnsMinSpacing = 6.0f; // Minimum horizontal spacing between two columns - ScrollbarSize = 16.0f; // Width of the vertical scrollbar, Height of the horizontal scrollbar - ScrollbarRounding = 9.0f; // Radius of grab corners rounding for scrollbar - GrabMinSize = 10.0f; // Minimum width/height of a grab box for slider/scrollbar - GrabRounding = 0.0f; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. - ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text. - DisplayWindowPadding = ImVec2(20,20); // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows. - DisplaySafeAreaPadding = ImVec2(3,3); // If you cannot see the edge of your screen (e.g. on a TV) increase the safe area padding. Covers popups/tooltips as well regular windows. - MouseCursorScale = 1.0f; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. - AntiAliasedLines = true; // Enable anti-aliasing on lines/borders. Disable if you are really short on CPU/GPU. - AntiAliasedFill = true; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.) - CurveTessellationTol = 1.25f; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. - - // Default theme - ImGui::StyleColorsDark(this); -} - -// To scale your entire UI (e.g. if you want your app to use High DPI or generally be DPI aware) you may use this helper function. Scaling the fonts is done separately and is up to you. -// Important: This operation is lossy because we round all sizes to integer. If you need to change your scale multiples, call this over a freshly initialized ImGuiStyle structure rather than scaling multiple times. -void ImGuiStyle::ScaleAllSizes(float scale_factor) -{ - WindowPadding = ImFloor(WindowPadding * scale_factor); - WindowRounding = ImFloor(WindowRounding * scale_factor); - WindowMinSize = ImFloor(WindowMinSize * scale_factor); - ChildRounding = ImFloor(ChildRounding * scale_factor); - PopupRounding = ImFloor(PopupRounding * scale_factor); - FramePadding = ImFloor(FramePadding * scale_factor); - FrameRounding = ImFloor(FrameRounding * scale_factor); - ItemSpacing = ImFloor(ItemSpacing * scale_factor); - ItemInnerSpacing = ImFloor(ItemInnerSpacing * scale_factor); - TouchExtraPadding = ImFloor(TouchExtraPadding * scale_factor); - IndentSpacing = ImFloor(IndentSpacing * scale_factor); - ColumnsMinSpacing = ImFloor(ColumnsMinSpacing * scale_factor); - ScrollbarSize = ImFloor(ScrollbarSize * scale_factor); - ScrollbarRounding = ImFloor(ScrollbarRounding * scale_factor); - GrabMinSize = ImFloor(GrabMinSize * scale_factor); - GrabRounding = ImFloor(GrabRounding * scale_factor); - DisplayWindowPadding = ImFloor(DisplayWindowPadding * scale_factor); - DisplaySafeAreaPadding = ImFloor(DisplaySafeAreaPadding * scale_factor); - MouseCursorScale = ImFloor(MouseCursorScale * scale_factor); -} - -ImGuiIO::ImGuiIO() -{ - // Most fields are initialized with zero - memset(this, 0, sizeof(*this)); - - // Settings - ConfigFlags = 0x00; - BackendFlags = 0x00; - DisplaySize = ImVec2(-1.0f, -1.0f); - DeltaTime = 1.0f/60.0f; - IniSavingRate = 5.0f; - IniFilename = "imgui.ini"; - LogFilename = "imgui_log.txt"; - MouseDoubleClickTime = 0.30f; - MouseDoubleClickMaxDist = 6.0f; - for (int i = 0; i < ImGuiKey_COUNT; i++) - KeyMap[i] = -1; - KeyRepeatDelay = 0.250f; - KeyRepeatRate = 0.050f; - UserData = NULL; - - Fonts = NULL; - FontGlobalScale = 1.0f; - FontDefault = NULL; - FontAllowUserScaling = false; - DisplayFramebufferScale = ImVec2(1.0f, 1.0f); - DisplayVisibleMin = DisplayVisibleMax = ImVec2(0.0f, 0.0f); - - // Advanced/subtle behaviors -#ifdef __APPLE__ - OptMacOSXBehaviors = true; // Set Mac OS X style defaults based on __APPLE__ compile time flag -#else - OptMacOSXBehaviors = false; -#endif - OptCursorBlink = true; - - // Settings (User Functions) - GetClipboardTextFn = GetClipboardTextFn_DefaultImpl; // Platform dependent default implementations - SetClipboardTextFn = SetClipboardTextFn_DefaultImpl; - ClipboardUserData = NULL; - ImeSetInputScreenPosFn = ImeSetInputScreenPosFn_DefaultImpl; - ImeWindowHandle = NULL; - -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - RenderDrawListsFn = NULL; -#endif - - // Input (NB: we already have memset zero the entire structure) - MousePos = ImVec2(-FLT_MAX, -FLT_MAX); - MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX); - MouseDragThreshold = 6.0f; - for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f; - for (int i = 0; i < IM_ARRAYSIZE(KeysDownDuration); i++) KeysDownDuration[i] = KeysDownDurationPrev[i] = -1.0f; - for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f; -} - -// Pass in translated ASCII characters for text input. -// - with glfw you can get those from the callback set in glfwSetCharCallback() -// - on Windows you can get those using ToAscii+keyboard state, or via the WM_CHAR message -void ImGuiIO::AddInputCharacter(ImWchar c) -{ - const int n = ImStrlenW(InputCharacters); - if (n + 1 < IM_ARRAYSIZE(InputCharacters)) - { - InputCharacters[n] = c; - InputCharacters[n+1] = '\0'; - } -} - -void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars) -{ - // We can't pass more wchars than ImGuiIO::InputCharacters[] can hold so don't convert more - const int wchars_buf_len = sizeof(ImGuiIO::InputCharacters) / sizeof(ImWchar); - ImWchar wchars[wchars_buf_len]; - ImTextStrFromUtf8(wchars, wchars_buf_len, utf8_chars, NULL); - for (int i = 0; i < wchars_buf_len && wchars[i] != 0; i++) - AddInputCharacter(wchars[i]); -} - -//----------------------------------------------------------------------------- -// HELPERS -//----------------------------------------------------------------------------- - -#define IM_STATIC_ASSERT(_COND) typedef char static_assertion_##__line__[(_COND)?1:-1] -#define IM_F32_TO_INT8_UNBOUND(_VAL) ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f))) // Unsaturated, for display purpose -#define IM_F32_TO_INT8_SAT(_VAL) ((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255 - -ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p) -{ - ImVec2 ap = p - a; - ImVec2 ab_dir = b - a; - float dot = ap.x * ab_dir.x + ap.y * ab_dir.y; - if (dot < 0.0f) - return a; - float ab_len_sqr = ab_dir.x * ab_dir.x + ab_dir.y * ab_dir.y; - if (dot > ab_len_sqr) - return b; - return a + ab_dir * dot / ab_len_sqr; -} - -bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p) -{ - bool b1 = ((p.x - b.x) * (a.y - b.y) - (p.y - b.y) * (a.x - b.x)) < 0.0f; - bool b2 = ((p.x - c.x) * (b.y - c.y) - (p.y - c.y) * (b.x - c.x)) < 0.0f; - bool b3 = ((p.x - a.x) * (c.y - a.y) - (p.y - a.y) * (c.x - a.x)) < 0.0f; - return ((b1 == b2) && (b2 == b3)); -} - -void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w) -{ - ImVec2 v0 = b - a; - ImVec2 v1 = c - a; - ImVec2 v2 = p - a; - const float denom = v0.x * v1.y - v1.x * v0.y; - out_v = (v2.x * v1.y - v1.x * v2.y) / denom; - out_w = (v0.x * v2.y - v2.x * v0.y) / denom; - out_u = 1.0f - out_v - out_w; -} - -ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p) -{ - ImVec2 proj_ab = ImLineClosestPoint(a, b, p); - ImVec2 proj_bc = ImLineClosestPoint(b, c, p); - ImVec2 proj_ca = ImLineClosestPoint(c, a, p); - float dist2_ab = ImLengthSqr(p - proj_ab); - float dist2_bc = ImLengthSqr(p - proj_bc); - float dist2_ca = ImLengthSqr(p - proj_ca); - float m = ImMin(dist2_ab, ImMin(dist2_bc, dist2_ca)); - if (m == dist2_ab) - return proj_ab; - if (m == dist2_bc) - return proj_bc; - return proj_ca; -} - -int ImStricmp(const char* str1, const char* str2) -{ - int d; - while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; } - return d; -} - -int ImStrnicmp(const char* str1, const char* str2, size_t count) -{ - int d = 0; - while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; } - return d; -} - -void ImStrncpy(char* dst, const char* src, size_t count) -{ - if (count < 1) return; - strncpy(dst, src, count); - dst[count-1] = 0; -} - -char* ImStrdup(const char *str) -{ - size_t len = strlen(str) + 1; - void* buf = ImGui::MemAlloc(len); - return (char*)memcpy(buf, (const void*)str, len); -} - -const char* ImStrchrRange(const char* str, const char* str_end, char c) -{ - for ( ; str < str_end; str++) - if (*str == c) - return str; - return NULL; -} - -int ImStrlenW(const ImWchar* str) -{ - int n = 0; - while (*str++) n++; - return n; -} - -const ImWchar* ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin) // find beginning-of-line -{ - while (buf_mid_line > buf_begin && buf_mid_line[-1] != '\n') - buf_mid_line--; - return buf_mid_line; -} - -const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end) -{ - if (!needle_end) - needle_end = needle + strlen(needle); - - const char un0 = (char)toupper(*needle); - while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end)) - { - if (toupper(*haystack) == un0) - { - const char* b = needle + 1; - for (const char* a = haystack + 1; b < needle_end; a++, b++) - if (toupper(*a) != toupper(*b)) - break; - if (b == needle_end) - return haystack; - } - haystack++; - } - return NULL; -} - -// Trim str by offsetting contents when there's leading data + writing a \0 at the trailing position. We use this in situation where the cost is negligible. -void ImStrTrimBlanks(char* buf) -{ - char* p = buf; - while (p[0] == ' ' || p[0] == '\t') // Leading blanks - p++; - char* p_start = p; - while (*p != 0) // Find end of string - p++; - while (p > p_start && (p[-1] == ' ' || p[-1] == '\t')) // Trailing blanks - p--; - if (p_start != buf) // Copy memory if we had leading blanks - memmove(buf, p_start, p - p_start); - buf[p - p_start] = 0; // Zero terminate -} - -template -static const char* ImAtoi(const char* src, TYPE* output) -{ - int negative = 0; - if (*src == '-') { negative = 1; src++; } - if (*src == '+') { src++; } - TYPE v = 0; - while (*src >= '0' && *src <= '9') - v = (v * 10) + (*src++ - '0'); - *output = negative ? -v : v; - return src; -} - -// A) MSVC version appears to return -1 on overflow, whereas glibc appears to return total count (which may be >= buf_size). -// Ideally we would test for only one of those limits at runtime depending on the behavior the vsnprintf(), but trying to deduct it at compile time sounds like a pandora can of worm. -// B) When buf==NULL vsnprintf() will return the output size. -#ifndef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS -int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - int w = vsnprintf(buf, buf_size, fmt, args); - va_end(args); - if (buf == NULL) - return w; - if (w == -1 || w >= (int)buf_size) - w = (int)buf_size - 1; - buf[w] = 0; - return w; -} - -int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args) -{ - int w = vsnprintf(buf, buf_size, fmt, args); - if (buf == NULL) - return w; - if (w == -1 || w >= (int)buf_size) - w = (int)buf_size - 1; - buf[w] = 0; - return w; -} -#endif // #ifdef IMGUI_DISABLE_FORMAT_STRING_FUNCTIONS - -// Pass data_size==0 for zero-terminated strings -// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements. -ImU32 ImHash(const void* data, int data_size, ImU32 seed) -{ - static ImU32 crc32_lut[256] = { 0 }; - if (!crc32_lut[1]) - { - const ImU32 polynomial = 0xEDB88320; - for (ImU32 i = 0; i < 256; i++) - { - ImU32 crc = i; - for (ImU32 j = 0; j < 8; j++) - crc = (crc >> 1) ^ (ImU32(-int(crc & 1)) & polynomial); - crc32_lut[i] = crc; - } - } - - seed = ~seed; - ImU32 crc = seed; - const unsigned char* current = (const unsigned char*)data; - - if (data_size > 0) - { - // Known size - while (data_size--) - crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *current++]; - } - else - { - // Zero-terminated string - while (unsigned char c = *current++) - { - // We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed. - // Because this syntax is rarely used we are optimizing for the common case. - // - If we reach ### in the string we discard the hash so far and reset to the seed. - // - We don't do 'current += 2; continue;' after handling ### to keep the code smaller. - if (c == '#' && current[0] == '#' && current[1] == '#') - crc = seed; - crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c]; - } - } - return ~crc; -} - -//----------------------------------------------------------------------------- -// ImText* helpers -//----------------------------------------------------------------------------- - -// Convert UTF-8 to 32-bits character, process single character input. -// Based on stb_from_utf8() from github.com/nothings/stb/ -// We handle UTF-8 decoding error by skipping forward. -int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end) -{ - unsigned int c = (unsigned int)-1; - const unsigned char* str = (const unsigned char*)in_text; - if (!(*str & 0x80)) - { - c = (unsigned int)(*str++); - *out_char = c; - return 1; - } - if ((*str & 0xe0) == 0xc0) - { - *out_char = 0xFFFD; // will be invalid but not end of string - if (in_text_end && in_text_end - (const char*)str < 2) return 1; - if (*str < 0xc2) return 2; - c = (unsigned int)((*str++ & 0x1f) << 6); - if ((*str & 0xc0) != 0x80) return 2; - c += (*str++ & 0x3f); - *out_char = c; - return 2; - } - if ((*str & 0xf0) == 0xe0) - { - *out_char = 0xFFFD; // will be invalid but not end of string - if (in_text_end && in_text_end - (const char*)str < 3) return 1; - if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return 3; - if (*str == 0xed && str[1] > 0x9f) return 3; // str[1] < 0x80 is checked below - c = (unsigned int)((*str++ & 0x0f) << 12); - if ((*str & 0xc0) != 0x80) return 3; - c += (unsigned int)((*str++ & 0x3f) << 6); - if ((*str & 0xc0) != 0x80) return 3; - c += (*str++ & 0x3f); - *out_char = c; - return 3; - } - if ((*str & 0xf8) == 0xf0) - { - *out_char = 0xFFFD; // will be invalid but not end of string - if (in_text_end && in_text_end - (const char*)str < 4) return 1; - if (*str > 0xf4) return 4; - if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return 4; - if (*str == 0xf4 && str[1] > 0x8f) return 4; // str[1] < 0x80 is checked below - c = (unsigned int)((*str++ & 0x07) << 18); - if ((*str & 0xc0) != 0x80) return 4; - c += (unsigned int)((*str++ & 0x3f) << 12); - if ((*str & 0xc0) != 0x80) return 4; - c += (unsigned int)((*str++ & 0x3f) << 6); - if ((*str & 0xc0) != 0x80) return 4; - c += (*str++ & 0x3f); - // utf-8 encodings of values used in surrogate pairs are invalid - if ((c & 0xFFFFF800) == 0xD800) return 4; - *out_char = c; - return 4; - } - *out_char = 0; - return 0; -} - -int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_text_remaining) -{ - ImWchar* buf_out = buf; - ImWchar* buf_end = buf + buf_size; - while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text) - { - unsigned int c; - in_text += ImTextCharFromUtf8(&c, in_text, in_text_end); - if (c == 0) - break; - if (c < 0x10000) // FIXME: Losing characters that don't fit in 2 bytes - *buf_out++ = (ImWchar)c; - } - *buf_out = 0; - if (in_text_remaining) - *in_text_remaining = in_text; - return (int)(buf_out - buf); -} - -int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end) -{ - int char_count = 0; - while ((!in_text_end || in_text < in_text_end) && *in_text) - { - unsigned int c; - in_text += ImTextCharFromUtf8(&c, in_text, in_text_end); - if (c == 0) - break; - if (c < 0x10000) - char_count++; - } - return char_count; -} - -// Based on stb_to_utf8() from github.com/nothings/stb/ -static inline int ImTextCharToUtf8(char* buf, int buf_size, unsigned int c) -{ - if (c < 0x80) - { - buf[0] = (char)c; - return 1; - } - if (c < 0x800) - { - if (buf_size < 2) return 0; - buf[0] = (char)(0xc0 + (c >> 6)); - buf[1] = (char)(0x80 + (c & 0x3f)); - return 2; - } - if (c >= 0xdc00 && c < 0xe000) - { - return 0; - } - if (c >= 0xd800 && c < 0xdc00) - { - if (buf_size < 4) return 0; - buf[0] = (char)(0xf0 + (c >> 18)); - buf[1] = (char)(0x80 + ((c >> 12) & 0x3f)); - buf[2] = (char)(0x80 + ((c >> 6) & 0x3f)); - buf[3] = (char)(0x80 + ((c ) & 0x3f)); - return 4; - } - //else if (c < 0x10000) - { - if (buf_size < 3) return 0; - buf[0] = (char)(0xe0 + (c >> 12)); - buf[1] = (char)(0x80 + ((c>> 6) & 0x3f)); - buf[2] = (char)(0x80 + ((c ) & 0x3f)); - return 3; - } -} - -static inline int ImTextCountUtf8BytesFromChar(unsigned int c) -{ - if (c < 0x80) return 1; - if (c < 0x800) return 2; - if (c >= 0xdc00 && c < 0xe000) return 0; - if (c >= 0xd800 && c < 0xdc00) return 4; - return 3; -} - -int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end) -{ - char* buf_out = buf; - const char* buf_end = buf + buf_size; - while (buf_out < buf_end-1 && (!in_text_end || in_text < in_text_end) && *in_text) - { - unsigned int c = (unsigned int)(*in_text++); - if (c < 0x80) - *buf_out++ = (char)c; - else - buf_out += ImTextCharToUtf8(buf_out, (int)(buf_end-buf_out-1), c); - } - *buf_out = 0; - return (int)(buf_out - buf); -} - -int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end) -{ - int bytes_count = 0; - while ((!in_text_end || in_text < in_text_end) && *in_text) - { - unsigned int c = (unsigned int)(*in_text++); - if (c < 0x80) - bytes_count++; - else - bytes_count += ImTextCountUtf8BytesFromChar(c); - } - return bytes_count; -} - -ImVec4 ImGui::ColorConvertU32ToFloat4(ImU32 in) -{ - float s = 1.0f/255.0f; - return ImVec4( - ((in >> IM_COL32_R_SHIFT) & 0xFF) * s, - ((in >> IM_COL32_G_SHIFT) & 0xFF) * s, - ((in >> IM_COL32_B_SHIFT) & 0xFF) * s, - ((in >> IM_COL32_A_SHIFT) & 0xFF) * s); -} - -ImU32 ImGui::ColorConvertFloat4ToU32(const ImVec4& in) -{ - ImU32 out; - out = ((ImU32)IM_F32_TO_INT8_SAT(in.x)) << IM_COL32_R_SHIFT; - out |= ((ImU32)IM_F32_TO_INT8_SAT(in.y)) << IM_COL32_G_SHIFT; - out |= ((ImU32)IM_F32_TO_INT8_SAT(in.z)) << IM_COL32_B_SHIFT; - out |= ((ImU32)IM_F32_TO_INT8_SAT(in.w)) << IM_COL32_A_SHIFT; - return out; -} - -ImU32 ImGui::GetColorU32(ImGuiCol idx, float alpha_mul) -{ - ImGuiStyle& style = GImGui->Style; - ImVec4 c = style.Colors[idx]; - c.w *= style.Alpha * alpha_mul; - return ColorConvertFloat4ToU32(c); -} - -ImU32 ImGui::GetColorU32(const ImVec4& col) -{ - ImGuiStyle& style = GImGui->Style; - ImVec4 c = col; - c.w *= style.Alpha; - return ColorConvertFloat4ToU32(c); -} - -const ImVec4& ImGui::GetStyleColorVec4(ImGuiCol idx) -{ - ImGuiStyle& style = GImGui->Style; - return style.Colors[idx]; -} - -ImU32 ImGui::GetColorU32(ImU32 col) -{ - float style_alpha = GImGui->Style.Alpha; - if (style_alpha >= 1.0f) - return col; - ImU32 a = (col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT; - a = (ImU32)(a * style_alpha); // We don't need to clamp 0..255 because Style.Alpha is in 0..1 range. - return (col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT); -} - -// Convert rgb floats ([0-1],[0-1],[0-1]) to hsv floats ([0-1],[0-1],[0-1]), from Foley & van Dam p592 -// Optimized http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv -void ImGui::ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v) -{ - float K = 0.f; - if (g < b) - { - ImSwap(g, b); - K = -1.f; - } - if (r < g) - { - ImSwap(r, g); - K = -2.f / 6.f - K; - } - - const float chroma = r - (g < b ? g : b); - out_h = ImFabs(K + (g - b) / (6.f * chroma + 1e-20f)); - out_s = chroma / (r + 1e-20f); - out_v = r; -} - -// Convert hsv floats ([0-1],[0-1],[0-1]) to rgb floats ([0-1],[0-1],[0-1]), from Foley & van Dam p593 -// also http://en.wikipedia.org/wiki/HSL_and_HSV -void ImGui::ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b) -{ - if (s == 0.0f) - { - // gray - out_r = out_g = out_b = v; - return; - } - - h = ImFmod(h, 1.0f) / (60.0f/360.0f); - int i = (int)h; - float f = h - (float)i; - float p = v * (1.0f - s); - float q = v * (1.0f - s * f); - float t = v * (1.0f - s * (1.0f - f)); - - switch (i) - { - case 0: out_r = v; out_g = t; out_b = p; break; - case 1: out_r = q; out_g = v; out_b = p; break; - case 2: out_r = p; out_g = v; out_b = t; break; - case 3: out_r = p; out_g = q; out_b = v; break; - case 4: out_r = t; out_g = p; out_b = v; break; - case 5: default: out_r = v; out_g = p; out_b = q; break; - } -} - -FILE* ImFileOpen(const char* filename, const char* mode) -{ -#if defined(_WIN32) && !defined(__CYGWIN__) - // We need a fopen() wrapper because MSVC/Windows fopen doesn't handle UTF-8 filenames. Converting both strings from UTF-8 to wchar format (using a single allocation, because we can) - const int filename_wsize = ImTextCountCharsFromUtf8(filename, NULL) + 1; - const int mode_wsize = ImTextCountCharsFromUtf8(mode, NULL) + 1; - ImVector buf; - buf.resize(filename_wsize + mode_wsize); - ImTextStrFromUtf8(&buf[0], filename_wsize, filename, NULL); - ImTextStrFromUtf8(&buf[filename_wsize], mode_wsize, mode, NULL); - return _wfopen((wchar_t*)&buf[0], (wchar_t*)&buf[filename_wsize]); -#else - return fopen(filename, mode); -#endif -} - -// Load file content into memory -// Memory allocated with ImGui::MemAlloc(), must be freed by user using ImGui::MemFree() -void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, size_t* out_file_size, int padding_bytes) -{ - IM_ASSERT(filename && file_open_mode); - if (out_file_size) - *out_file_size = 0; - - FILE* f; - if ((f = ImFileOpen(filename, file_open_mode)) == NULL) - return NULL; - - long file_size_signed; - if (fseek(f, 0, SEEK_END) || (file_size_signed = ftell(f)) == -1 || fseek(f, 0, SEEK_SET)) - { - fclose(f); - return NULL; - } - - size_t file_size = (size_t)file_size_signed; - void* file_data = ImGui::MemAlloc(file_size + padding_bytes); - if (file_data == NULL) - { - fclose(f); - return NULL; - } - if (fread(file_data, 1, file_size, f) != file_size) - { - fclose(f); - ImGui::MemFree(file_data); - return NULL; - } - if (padding_bytes > 0) - memset((void *)(((char*)file_data) + file_size), 0, (size_t)padding_bytes); - - fclose(f); - if (out_file_size) - *out_file_size = file_size; - - return file_data; -} - -//----------------------------------------------------------------------------- -// ImGuiStorage -// Helper: Key->value storage -//----------------------------------------------------------------------------- - -// std::lower_bound but without the bullshit -static ImVector::iterator LowerBound(ImVector& data, ImGuiID key) -{ - ImVector::iterator first = data.begin(); - ImVector::iterator last = data.end(); - size_t count = (size_t)(last - first); - while (count > 0) - { - size_t count2 = count >> 1; - ImVector::iterator mid = first + count2; - if (mid->key < key) - { - first = ++mid; - count -= count2 + 1; - } - else - { - count = count2; - } - } - return first; -} - -// For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once. -void ImGuiStorage::BuildSortByKey() -{ - struct StaticFunc - { - static int IMGUI_CDECL PairCompareByID(const void* lhs, const void* rhs) - { - // We can't just do a subtraction because qsort uses signed integers and subtracting our ID doesn't play well with that. - if (((const Pair*)lhs)->key > ((const Pair*)rhs)->key) return +1; - if (((const Pair*)lhs)->key < ((const Pair*)rhs)->key) return -1; - return 0; - } - }; - if (Data.Size > 1) - qsort(Data.Data, (size_t)Data.Size, sizeof(Pair), StaticFunc::PairCompareByID); -} - -int ImGuiStorage::GetInt(ImGuiID key, int default_val) const -{ - ImVector::iterator it = LowerBound(const_cast&>(Data), key); - if (it == Data.end() || it->key != key) - return default_val; - return it->val_i; -} - -bool ImGuiStorage::GetBool(ImGuiID key, bool default_val) const -{ - return GetInt(key, default_val ? 1 : 0) != 0; -} - -float ImGuiStorage::GetFloat(ImGuiID key, float default_val) const -{ - ImVector::iterator it = LowerBound(const_cast&>(Data), key); - if (it == Data.end() || it->key != key) - return default_val; - return it->val_f; -} - -void* ImGuiStorage::GetVoidPtr(ImGuiID key) const -{ - ImVector::iterator it = LowerBound(const_cast&>(Data), key); - if (it == Data.end() || it->key != key) - return NULL; - return it->val_p; -} - -// References are only valid until a new value is added to the storage. Calling a Set***() function or a Get***Ref() function invalidates the pointer. -int* ImGuiStorage::GetIntRef(ImGuiID key, int default_val) -{ - ImVector::iterator it = LowerBound(Data, key); - if (it == Data.end() || it->key != key) - it = Data.insert(it, Pair(key, default_val)); - return &it->val_i; -} - -bool* ImGuiStorage::GetBoolRef(ImGuiID key, bool default_val) -{ - return (bool*)GetIntRef(key, default_val ? 1 : 0); -} - -float* ImGuiStorage::GetFloatRef(ImGuiID key, float default_val) -{ - ImVector::iterator it = LowerBound(Data, key); - if (it == Data.end() || it->key != key) - it = Data.insert(it, Pair(key, default_val)); - return &it->val_f; -} - -void** ImGuiStorage::GetVoidPtrRef(ImGuiID key, void* default_val) -{ - ImVector::iterator it = LowerBound(Data, key); - if (it == Data.end() || it->key != key) - it = Data.insert(it, Pair(key, default_val)); - return &it->val_p; -} - -// FIXME-OPT: Need a way to reuse the result of lower_bound when doing GetInt()/SetInt() - not too bad because it only happens on explicit interaction (maximum one a frame) -void ImGuiStorage::SetInt(ImGuiID key, int val) -{ - ImVector::iterator it = LowerBound(Data, key); - if (it == Data.end() || it->key != key) - { - Data.insert(it, Pair(key, val)); - return; - } - it->val_i = val; -} - -void ImGuiStorage::SetBool(ImGuiID key, bool val) -{ - SetInt(key, val ? 1 : 0); -} - -void ImGuiStorage::SetFloat(ImGuiID key, float val) -{ - ImVector::iterator it = LowerBound(Data, key); - if (it == Data.end() || it->key != key) - { - Data.insert(it, Pair(key, val)); - return; - } - it->val_f = val; -} - -void ImGuiStorage::SetVoidPtr(ImGuiID key, void* val) -{ - ImVector::iterator it = LowerBound(Data, key); - if (it == Data.end() || it->key != key) - { - Data.insert(it, Pair(key, val)); - return; - } - it->val_p = val; -} - -void ImGuiStorage::SetAllInt(int v) -{ - for (int i = 0; i < Data.Size; i++) - Data[i].val_i = v; -} - -//----------------------------------------------------------------------------- -// ImGuiTextFilter -//----------------------------------------------------------------------------- - -// Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]" -ImGuiTextFilter::ImGuiTextFilter(const char* default_filter) -{ - if (default_filter) - { - ImStrncpy(InputBuf, default_filter, IM_ARRAYSIZE(InputBuf)); - Build(); - } - else - { - InputBuf[0] = 0; - CountGrep = 0; - } -} - -bool ImGuiTextFilter::Draw(const char* label, float width) -{ - if (width != 0.0f) - ImGui::PushItemWidth(width); - bool value_changed = ImGui::InputText(label, InputBuf, IM_ARRAYSIZE(InputBuf)); - if (width != 0.0f) - ImGui::PopItemWidth(); - if (value_changed) - Build(); - return value_changed; -} - -void ImGuiTextFilter::TextRange::split(char separator, ImVector& out) -{ - out.resize(0); - const char* wb = b; - const char* we = wb; - while (we < e) - { - if (*we == separator) - { - out.push_back(TextRange(wb, we)); - wb = we + 1; - } - we++; - } - if (wb != we) - out.push_back(TextRange(wb, we)); -} - -void ImGuiTextFilter::Build() -{ - Filters.resize(0); - TextRange input_range(InputBuf, InputBuf+strlen(InputBuf)); - input_range.split(',', Filters); - - CountGrep = 0; - for (int i = 0; i != Filters.Size; i++) - { - Filters[i].trim_blanks(); - if (Filters[i].empty()) - continue; - if (Filters[i].front() != '-') - CountGrep += 1; - } -} - -bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const -{ - if (Filters.empty()) - return true; - - if (text == NULL) - text = ""; - - for (int i = 0; i != Filters.Size; i++) - { - const TextRange& f = Filters[i]; - if (f.empty()) - continue; - if (f.front() == '-') - { - // Subtract - if (ImStristr(text, text_end, f.begin()+1, f.end()) != NULL) - return false; - } - else - { - // Grep - if (ImStristr(text, text_end, f.begin(), f.end()) != NULL) - return true; - } - } - - // Implicit * grep - if (CountGrep == 0) - return true; - - return false; -} - -//----------------------------------------------------------------------------- -// ImGuiTextBuffer -//----------------------------------------------------------------------------- - -// On some platform vsnprintf() takes va_list by reference and modifies it. -// va_copy is the 'correct' way to copy a va_list but Visual Studio prior to 2013 doesn't have it. -#ifndef va_copy -#define va_copy(dest, src) (dest = src) -#endif - -// Helper: Text buffer for logging/accumulating text -void ImGuiTextBuffer::appendfv(const char* fmt, va_list args) -{ - va_list args_copy; - va_copy(args_copy, args); - - int len = ImFormatStringV(NULL, 0, fmt, args); // FIXME-OPT: could do a first pass write attempt, likely successful on first pass. - if (len <= 0) - { - va_end(args_copy); - return; - } - - const int write_off = Buf.Size; - const int needed_sz = write_off + len; - if (write_off + len >= Buf.Capacity) - { - int double_capacity = Buf.Capacity * 2; - Buf.reserve(needed_sz > double_capacity ? needed_sz : double_capacity); - } - - Buf.resize(needed_sz); - ImFormatStringV(&Buf[write_off - 1], (size_t)len + 1, fmt, args_copy); - va_end(args_copy); -} - -void ImGuiTextBuffer::appendf(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - appendfv(fmt, args); - va_end(args); -} - -//----------------------------------------------------------------------------- -// ImGuiSimpleColumns (internal use only) -//----------------------------------------------------------------------------- - -ImGuiMenuColumns::ImGuiMenuColumns() -{ - Count = 0; - Spacing = Width = NextWidth = 0.0f; - memset(Pos, 0, sizeof(Pos)); - memset(NextWidths, 0, sizeof(NextWidths)); -} - -void ImGuiMenuColumns::Update(int count, float spacing, bool clear) -{ - IM_ASSERT(Count <= IM_ARRAYSIZE(Pos)); - Count = count; - Width = NextWidth = 0.0f; - Spacing = spacing; - if (clear) memset(NextWidths, 0, sizeof(NextWidths)); - for (int i = 0; i < Count; i++) - { - if (i > 0 && NextWidths[i] > 0.0f) - Width += Spacing; - Pos[i] = (float)(int)Width; - Width += NextWidths[i]; - NextWidths[i] = 0.0f; - } -} - -float ImGuiMenuColumns::DeclColumns(float w0, float w1, float w2) // not using va_arg because they promote float to double -{ - NextWidth = 0.0f; - NextWidths[0] = ImMax(NextWidths[0], w0); - NextWidths[1] = ImMax(NextWidths[1], w1); - NextWidths[2] = ImMax(NextWidths[2], w2); - for (int i = 0; i < 3; i++) - NextWidth += NextWidths[i] + ((i > 0 && NextWidths[i] > 0.0f) ? Spacing : 0.0f); - return ImMax(Width, NextWidth); -} - -float ImGuiMenuColumns::CalcExtraSpace(float avail_w) -{ - return ImMax(0.0f, avail_w - Width); -} - -//----------------------------------------------------------------------------- -// ImGuiListClipper -//----------------------------------------------------------------------------- - -static void SetCursorPosYAndSetupDummyPrevLine(float pos_y, float line_height) -{ - // Set cursor position and a few other things so that SetScrollHere() and Columns() can work when seeking cursor. - // FIXME: It is problematic that we have to do that here, because custom/equivalent end-user code would stumble on the same issue. - // The clipper should probably have a 4th step to display the last item in a regular manner. - ImGui::SetCursorPosY(pos_y); - ImGuiWindow* window = ImGui::GetCurrentWindow(); - window->DC.CursorPosPrevLine.y = window->DC.CursorPos.y - line_height; // Setting those fields so that SetScrollHere() can properly function after the end of our clipper usage. - window->DC.PrevLineHeight = (line_height - GImGui->Style.ItemSpacing.y); // If we end up needing more accurate data (to e.g. use SameLine) we may as well make the clipper have a fourth step to let user process and display the last item in their list. - if (window->DC.ColumnsSet) - window->DC.ColumnsSet->LineMinY = window->DC.CursorPos.y; // Setting this so that cell Y position are set properly -} - -// Use case A: Begin() called from constructor with items_height<0, then called again from Sync() in StepNo 1 -// Use case B: Begin() called from constructor with items_height>0 -// FIXME-LEGACY: Ideally we should remove the Begin/End functions but they are part of the legacy API we still support. This is why some of the code in Step() calling Begin() and reassign some fields, spaghetti style. -void ImGuiListClipper::Begin(int count, float items_height) -{ - StartPosY = ImGui::GetCursorPosY(); - ItemsHeight = items_height; - ItemsCount = count; - StepNo = 0; - DisplayEnd = DisplayStart = -1; - if (ItemsHeight > 0.0f) - { - ImGui::CalcListClipping(ItemsCount, ItemsHeight, &DisplayStart, &DisplayEnd); // calculate how many to clip/display - if (DisplayStart > 0) - SetCursorPosYAndSetupDummyPrevLine(StartPosY + DisplayStart * ItemsHeight, ItemsHeight); // advance cursor - StepNo = 2; - } -} - -void ImGuiListClipper::End() -{ - if (ItemsCount < 0) - return; - // In theory here we should assert that ImGui::GetCursorPosY() == StartPosY + DisplayEnd * ItemsHeight, but it feels saner to just seek at the end and not assert/crash the user. - if (ItemsCount < INT_MAX) - SetCursorPosYAndSetupDummyPrevLine(StartPosY + ItemsCount * ItemsHeight, ItemsHeight); // advance cursor - ItemsCount = -1; - StepNo = 3; -} - -bool ImGuiListClipper::Step() -{ - if (ItemsCount == 0 || ImGui::GetCurrentWindowRead()->SkipItems) - { - ItemsCount = -1; - return false; - } - if (StepNo == 0) // Step 0: the clipper let you process the first element, regardless of it being visible or not, so we can measure the element height. - { - DisplayStart = 0; - DisplayEnd = 1; - StartPosY = ImGui::GetCursorPosY(); - StepNo = 1; - return true; - } - if (StepNo == 1) // Step 1: the clipper infer height from first element, calculate the actual range of elements to display, and position the cursor before the first element. - { - if (ItemsCount == 1) { ItemsCount = -1; return false; } - float items_height = ImGui::GetCursorPosY() - StartPosY; - IM_ASSERT(items_height > 0.0f); // If this triggers, it means Item 0 hasn't moved the cursor vertically - Begin(ItemsCount-1, items_height); - DisplayStart++; - DisplayEnd++; - StepNo = 3; - return true; - } - if (StepNo == 2) // Step 2: dummy step only required if an explicit items_height was passed to constructor or Begin() and user still call Step(). Does nothing and switch to Step 3. - { - IM_ASSERT(DisplayStart >= 0 && DisplayEnd >= 0); - StepNo = 3; - return true; - } - if (StepNo == 3) // Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), advance the cursor to the end of the list and then returns 'false' to end the loop. - End(); - return false; -} - -//----------------------------------------------------------------------------- -// ImGuiWindow -//----------------------------------------------------------------------------- - -ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name) - : DrawListInst(&context->DrawListSharedData) -{ - Name = ImStrdup(name); - ID = ImHash(name, 0); - IDStack.push_back(ID); - Flags = 0; - Pos = ImVec2(0.0f, 0.0f); - Size = SizeFull = ImVec2(0.0f, 0.0f); - SizeContents = SizeContentsExplicit = ImVec2(0.0f, 0.0f); - WindowPadding = ImVec2(0.0f, 0.0f); - WindowRounding = 0.0f; - WindowBorderSize = 0.0f; - MoveId = GetID("#MOVE"); - ChildId = 0; - Scroll = ImVec2(0.0f, 0.0f); - ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); - ScrollTargetCenterRatio = ImVec2(0.5f, 0.5f); - ScrollbarSizes = ImVec2(0.0f, 0.0f); - ScrollbarX = ScrollbarY = false; - Active = WasActive = false; - WriteAccessed = false; - Collapsed = false; - CollapseToggleWanted = false; - SkipItems = false; - Appearing = false; - CloseButton = false; - BeginOrderWithinParent = -1; - BeginOrderWithinContext = -1; - BeginCount = 0; - PopupId = 0; - AutoFitFramesX = AutoFitFramesY = -1; - AutoFitOnlyGrows = false; - AutoFitChildAxises = 0x00; - AutoPosLastDirection = ImGuiDir_None; - HiddenFrames = 0; - SetWindowPosAllowFlags = SetWindowSizeAllowFlags = SetWindowCollapsedAllowFlags = ImGuiCond_Always | ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing; - SetWindowPosVal = SetWindowPosPivot = ImVec2(FLT_MAX, FLT_MAX); - - LastFrameActive = -1; - ItemWidthDefault = 0.0f; - FontWindowScale = 1.0f; - - DrawList = &DrawListInst; - DrawList->_OwnerName = Name; - ParentWindow = NULL; - RootWindow = NULL; - RootWindowForTitleBarHighlight = NULL; - RootWindowForTabbing = NULL; - RootWindowForNav = NULL; - - NavLastIds[0] = NavLastIds[1] = 0; - NavRectRel[0] = NavRectRel[1] = ImRect(); - NavLastChildNavWindow = NULL; - - FocusIdxAllCounter = FocusIdxTabCounter = -1; - FocusIdxAllRequestCurrent = FocusIdxTabRequestCurrent = INT_MAX; - FocusIdxAllRequestNext = FocusIdxTabRequestNext = INT_MAX; -} - -ImGuiWindow::~ImGuiWindow() -{ - IM_ASSERT(DrawList == &DrawListInst); - IM_DELETE(Name); - for (int i = 0; i != ColumnsStorage.Size; i++) - ColumnsStorage[i].~ImGuiColumnsSet(); -} - -ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end) -{ - ImGuiID seed = IDStack.back(); - ImGuiID id = ImHash(str, str_end ? (int)(str_end - str) : 0, seed); - ImGui::KeepAliveID(id); - return id; -} - -ImGuiID ImGuiWindow::GetID(const void* ptr) -{ - ImGuiID seed = IDStack.back(); - ImGuiID id = ImHash(&ptr, sizeof(void*), seed); - ImGui::KeepAliveID(id); - return id; -} - -ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end) -{ - ImGuiID seed = IDStack.back(); - return ImHash(str, str_end ? (int)(str_end - str) : 0, seed); -} - -// This is only used in rare/specific situations to manufacture an ID out of nowhere. -ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs) -{ - ImGuiID seed = IDStack.back(); - const int r_rel[4] = { (int)(r_abs.Min.x - Pos.x), (int)(r_abs.Min.y - Pos.y), (int)(r_abs.Max.x - Pos.x), (int)(r_abs.Max.y - Pos.y) }; - ImGuiID id = ImHash(&r_rel, sizeof(r_rel), seed); - ImGui::KeepAliveID(id); - return id; -} - -//----------------------------------------------------------------------------- -// Internal API exposed in imgui_internal.h -//----------------------------------------------------------------------------- - -static void SetCurrentWindow(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - g.CurrentWindow = window; - if (window) - g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize(); -} - -static void SetNavID(ImGuiID id, int nav_layer) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.NavWindow); - IM_ASSERT(nav_layer == 0 || nav_layer == 1); - g.NavId = id; - g.NavWindow->NavLastIds[nav_layer] = id; -} - -static void SetNavIDWithRectRel(ImGuiID id, int nav_layer, const ImRect& rect_rel) -{ - ImGuiContext& g = *GImGui; - SetNavID(id, nav_layer); - g.NavWindow->NavRectRel[nav_layer] = rect_rel; - g.NavMousePosDirty = true; - g.NavDisableHighlight = false; - g.NavDisableMouseHover = true; -} - -void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - g.ActiveIdIsJustActivated = (g.ActiveId != id); - if (g.ActiveIdIsJustActivated) - { - g.ActiveIdTimer = 0.0f; - g.ActiveIdValueChanged = false; - if (id != 0) - { - g.LastActiveId = id; - g.LastActiveIdTimer = 0.0f; - } - } - g.ActiveId = id; - g.ActiveIdAllowNavDirFlags = 0; - g.ActiveIdAllowOverlap = false; - g.ActiveIdWindow = window; - if (id) - { - g.ActiveIdIsAlive = true; - g.ActiveIdSource = (g.NavActivateId == id || g.NavInputId == id || g.NavJustTabbedId == id || g.NavJustMovedToId == id) ? ImGuiInputSource_Nav : ImGuiInputSource_Mouse; - } -} - -void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(id != 0); - - // Assume that SetFocusID() is called in the context where its NavLayer is the current layer, which is the case everywhere we call it. - const int nav_layer = window->DC.NavLayerCurrent; - if (g.NavWindow != window) - g.NavInitRequest = false; - g.NavId = id; - g.NavWindow = window; - g.NavLayer = nav_layer; - window->NavLastIds[nav_layer] = id; - if (window->DC.LastItemId == id) - window->NavRectRel[nav_layer] = ImRect(window->DC.LastItemRect.Min - window->Pos, window->DC.LastItemRect.Max - window->Pos); - - if (g.ActiveIdSource == ImGuiInputSource_Nav) - g.NavDisableMouseHover = true; - else - g.NavDisableHighlight = true; -} - -void ImGui::ClearActiveID() -{ - SetActiveID(0, NULL); -} - -void ImGui::SetHoveredID(ImGuiID id) -{ - ImGuiContext& g = *GImGui; - g.HoveredId = id; - g.HoveredIdAllowOverlap = false; - g.HoveredIdTimer = (id != 0 && g.HoveredIdPreviousFrame == id) ? (g.HoveredIdTimer + g.IO.DeltaTime) : 0.0f; -} - -ImGuiID ImGui::GetHoveredID() -{ - ImGuiContext& g = *GImGui; - return g.HoveredId ? g.HoveredId : g.HoveredIdPreviousFrame; -} - -void ImGui::KeepAliveID(ImGuiID id) -{ - ImGuiContext& g = *GImGui; - if (g.ActiveId == id) - g.ActiveIdIsAlive = true; - if (g.ActiveIdPreviousFrame == id) - g.ActiveIdPreviousFrameIsAlive = true; -} - -void ImGui::MarkItemValueChanged(ImGuiID id) -{ - // This marking is solely to be able to provide info for IsItemDeactivatedAfterChange(). - // ActiveId might have been released by the time we call this (as in the typical press/release button behavior) but still need need to fill the data. - (void)id; // Avoid unused variable warnings when asserts are compiled out. - ImGuiContext& g = *GImGui; - IM_ASSERT(g.ActiveId == id || g.ActiveId == 0); - g.ActiveIdValueChanged = true; -} - -static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flags) -{ - // An active popup disable hovering on other windows (apart from its own children) - // FIXME-OPT: This could be cached/stored within the window. - ImGuiContext& g = *GImGui; - if (g.NavWindow) - if (ImGuiWindow* focused_root_window = g.NavWindow->RootWindow) - if (focused_root_window->WasActive && focused_root_window != window->RootWindow) - { - // For the purpose of those flags we differentiate "standard popup" from "modal popup" - // NB: The order of those two tests is important because Modal windows are also Popups. - if (focused_root_window->Flags & ImGuiWindowFlags_Modal) - return false; - if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup)) - return false; - } - - return true; -} - -// Advance cursor given item size for layout. -void ImGui::ItemSize(const ImVec2& size, float text_offset_y) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (window->SkipItems) - return; - - // Always align ourselves on pixel boundaries - const float line_height = ImMax(window->DC.CurrentLineHeight, size.y); - const float text_base_offset = ImMax(window->DC.CurrentLineTextBaseOffset, text_offset_y); - //if (g.IO.KeyAlt) window->DrawList->AddRect(window->DC.CursorPos, window->DC.CursorPos + ImVec2(size.x, line_height), IM_COL32(255,0,0,200)); // [DEBUG] - window->DC.CursorPosPrevLine = ImVec2(window->DC.CursorPos.x + size.x, window->DC.CursorPos.y); - window->DC.CursorPos = ImVec2((float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX), (float)(int)(window->DC.CursorPos.y + line_height + g.Style.ItemSpacing.y)); - window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x); - window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y); - //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG] - - window->DC.PrevLineHeight = line_height; - window->DC.PrevLineTextBaseOffset = text_base_offset; - window->DC.CurrentLineHeight = window->DC.CurrentLineTextBaseOffset = 0.0f; - - // Horizontal layout mode - if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) - SameLine(); -} - -void ImGui::ItemSize(const ImRect& bb, float text_offset_y) -{ - ItemSize(bb.GetSize(), text_offset_y); -} - -static ImGuiDir inline NavScoreItemGetQuadrant(float dx, float dy) -{ - if (ImFabs(dx) > ImFabs(dy)) - return (dx > 0.0f) ? ImGuiDir_Right : ImGuiDir_Left; - return (dy > 0.0f) ? ImGuiDir_Down : ImGuiDir_Up; -} - -static float inline NavScoreItemDistInterval(float a0, float a1, float b0, float b1) -{ - if (a1 < b0) - return a1 - b0; - if (b1 < a0) - return a0 - b1; - return 0.0f; -} - -static void inline NavClampRectToVisibleAreaForMoveDir(ImGuiDir move_dir, ImRect& r, const ImRect& clip_rect) -{ - if (move_dir == ImGuiDir_Left || move_dir == ImGuiDir_Right) - { - r.Min.y = ImClamp(r.Min.y, clip_rect.Min.y, clip_rect.Max.y); - r.Max.y = ImClamp(r.Max.y, clip_rect.Min.y, clip_rect.Max.y); - } - else - { - r.Min.x = ImClamp(r.Min.x, clip_rect.Min.x, clip_rect.Max.x); - r.Max.x = ImClamp(r.Max.x, clip_rect.Min.x, clip_rect.Max.x); - } -} - -// Scoring function for directional navigation. Based on https://gist.github.com/rygorous/6981057 -static bool NavScoreItem(ImGuiNavMoveResult* result, ImRect cand) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (g.NavLayer != window->DC.NavLayerCurrent) - return false; - - const ImRect& curr = g.NavScoringRectScreen; // Current modified source rect (NB: we've applied Max.x = Min.x in NavUpdate() to inhibit the effect of having varied item width) - g.NavScoringCount++; - - // When entering through a NavFlattened border, we consider child window items as fully clipped for scoring - if (window->ParentWindow == g.NavWindow) - { - IM_ASSERT((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened); - if (!window->ClipRect.Contains(cand)) - return false; - cand.ClipWithFull(window->ClipRect); // This allows the scored item to not overlap other candidates in the parent window - } - - // We perform scoring on items bounding box clipped by the current clipping rectangle on the other axis (clipping on our movement axis would give us equal scores for all clipped items) - // For example, this ensure that items in one column are not reached when moving vertically from items in another column. - NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect); - - // Compute distance between boxes - // FIXME-NAV: Introducing biases for vertical navigation, needs to be removed. - float dbx = NavScoreItemDistInterval(cand.Min.x, cand.Max.x, curr.Min.x, curr.Max.x); - float dby = NavScoreItemDistInterval(ImLerp(cand.Min.y, cand.Max.y, 0.2f), ImLerp(cand.Min.y, cand.Max.y, 0.8f), ImLerp(curr.Min.y, curr.Max.y, 0.2f), ImLerp(curr.Min.y, curr.Max.y, 0.8f)); // Scale down on Y to keep using box-distance for vertically touching items - if (dby != 0.0f && dbx != 0.0f) - dbx = (dbx/1000.0f) + ((dbx > 0.0f) ? +1.0f : -1.0f); - float dist_box = ImFabs(dbx) + ImFabs(dby); - - // Compute distance between centers (this is off by a factor of 2, but we only compare center distances with each other so it doesn't matter) - float dcx = (cand.Min.x + cand.Max.x) - (curr.Min.x + curr.Max.x); - float dcy = (cand.Min.y + cand.Max.y) - (curr.Min.y + curr.Max.y); - float dist_center = ImFabs(dcx) + ImFabs(dcy); // L1 metric (need this for our connectedness guarantee) - - // Determine which quadrant of 'curr' our candidate item 'cand' lies in based on distance - ImGuiDir quadrant; - float dax = 0.0f, day = 0.0f, dist_axial = 0.0f; - if (dbx != 0.0f || dby != 0.0f) - { - // For non-overlapping boxes, use distance between boxes - dax = dbx; - day = dby; - dist_axial = dist_box; - quadrant = NavScoreItemGetQuadrant(dbx, dby); - } - else if (dcx != 0.0f || dcy != 0.0f) - { - // For overlapping boxes with different centers, use distance between centers - dax = dcx; - day = dcy; - dist_axial = dist_center; - quadrant = NavScoreItemGetQuadrant(dcx, dcy); - } - else - { - // Degenerate case: two overlapping buttons with same center, break ties arbitrarily (note that LastItemId here is really the _previous_ item order, but it doesn't matter) - quadrant = (window->DC.LastItemId < g.NavId) ? ImGuiDir_Left : ImGuiDir_Right; - } - -#if IMGUI_DEBUG_NAV_SCORING - char buf[128]; - if (ImGui::IsMouseHoveringRect(cand.Min, cand.Max)) - { - ImFormatString(buf, IM_ARRAYSIZE(buf), "dbox (%.2f,%.2f->%.4f)\ndcen (%.2f,%.2f->%.4f)\nd (%.2f,%.2f->%.4f)\nnav %c, quadrant %c", dbx, dby, dist_box, dcx, dcy, dist_center, dax, day, dist_axial, "WENS"[g.NavMoveDir], "WENS"[quadrant]); - ImDrawList* draw_list = ImGui::GetOverlayDrawList(); - draw_list->AddRect(curr.Min, curr.Max, IM_COL32(255,200,0,100)); - draw_list->AddRect(cand.Min, cand.Max, IM_COL32(255,255,0,200)); - draw_list->AddRectFilled(cand.Max-ImVec2(4,4), cand.Max+ImGui::CalcTextSize(buf)+ImVec2(4,4), IM_COL32(40,0,0,150)); - draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Max, ~0U, buf); - } - else if (g.IO.KeyCtrl) // Hold to preview score in matching quadrant. Press C to rotate. - { - if (IsKeyPressedMap(ImGuiKey_C)) { g.NavMoveDirLast = (ImGuiDir)((g.NavMoveDirLast + 1) & 3); g.IO.KeysDownDuration[g.IO.KeyMap[ImGuiKey_C]] = 0.01f; } - if (quadrant == g.NavMoveDir) - { - ImFormatString(buf, IM_ARRAYSIZE(buf), "%.0f/%.0f", dist_box, dist_center); - ImDrawList* draw_list = ImGui::GetOverlayDrawList(); - draw_list->AddRectFilled(cand.Min, cand.Max, IM_COL32(255, 0, 0, 200)); - draw_list->AddText(g.IO.FontDefault, 13.0f, cand.Min, IM_COL32(255, 255, 255, 255), buf); - } - } - #endif - - // Is it in the quadrant we're interesting in moving to? - bool new_best = false; - if (quadrant == g.NavMoveDir) - { - // Does it beat the current best candidate? - if (dist_box < result->DistBox) - { - result->DistBox = dist_box; - result->DistCenter = dist_center; - return true; - } - if (dist_box == result->DistBox) - { - // Try using distance between center points to break ties - if (dist_center < result->DistCenter) - { - result->DistCenter = dist_center; - new_best = true; - } - else if (dist_center == result->DistCenter) - { - // Still tied! we need to be extra-careful to make sure everything gets linked properly. We consistently break ties by symbolically moving "later" items - // (with higher index) to the right/downwards by an infinitesimal amount since we the current "best" button already (so it must have a lower index), - // this is fairly easy. This rule ensures that all buttons with dx==dy==0 will end up being linked in order of appearance along the x axis. - if (((g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) ? dby : dbx) < 0.0f) // moving bj to the right/down decreases distance - new_best = true; - } - } - } - - // Axial check: if 'curr' has no link at all in some direction and 'cand' lies roughly in that direction, add a tentative link. This will only be kept if no "real" matches - // are found, so it only augments the graph produced by the above method using extra links. (important, since it doesn't guarantee strong connectedness) - // This is just to avoid buttons having no links in a particular direction when there's a suitable neighbor. you get good graphs without this too. - // 2017/09/29: FIXME: This now currently only enabled inside menu bars, ideally we'd disable it everywhere. Menus in particular need to catch failure. For general navigation it feels awkward. - // Disabling it may lead to disconnected graphs when nodes are very spaced out on different axis. Perhaps consider offering this as an option? - if (result->DistBox == FLT_MAX && dist_axial < result->DistAxial) // Check axial match - if (g.NavLayer == 1 && !(g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) - if ((g.NavMoveDir == ImGuiDir_Left && dax < 0.0f) || (g.NavMoveDir == ImGuiDir_Right && dax > 0.0f) || (g.NavMoveDir == ImGuiDir_Up && day < 0.0f) || (g.NavMoveDir == ImGuiDir_Down && day > 0.0f)) - { - result->DistAxial = dist_axial; - new_best = true; - } - - return new_best; -} - -static void NavSaveLastChildNavWindow(ImGuiWindow* child_window) -{ - ImGuiWindow* parent_window = child_window; - while (parent_window && (parent_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (parent_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) - parent_window = parent_window->ParentWindow; - if (parent_window && parent_window != child_window) - parent_window->NavLastChildNavWindow = child_window; -} - -// Call when we are expected to land on Layer 0 after FocusWindow() -static ImGuiWindow* NavRestoreLastChildNavWindow(ImGuiWindow* window) -{ - return window->NavLastChildNavWindow ? window->NavLastChildNavWindow : window; -} - -static void NavRestoreLayer(int layer) -{ - ImGuiContext& g = *GImGui; - g.NavLayer = layer; - if (layer == 0) - g.NavWindow = NavRestoreLastChildNavWindow(g.NavWindow); - if (layer == 0 && g.NavWindow->NavLastIds[0] != 0) - SetNavIDWithRectRel(g.NavWindow->NavLastIds[0], layer, g.NavWindow->NavRectRel[0]); - else - ImGui::NavInitWindow(g.NavWindow, true); -} - -static inline void NavUpdateAnyRequestFlag() -{ - ImGuiContext& g = *GImGui; - g.NavAnyRequest = g.NavMoveRequest || g.NavInitRequest || (IMGUI_DEBUG_NAV_SCORING && g.NavWindow != NULL); - if (g.NavAnyRequest) - IM_ASSERT(g.NavWindow != NULL); -} - -static bool NavMoveRequestButNoResultYet() -{ - ImGuiContext& g = *GImGui; - return g.NavMoveRequest && g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0; -} - -void ImGui::NavMoveRequestCancel() -{ - ImGuiContext& g = *GImGui; - g.NavMoveRequest = false; - NavUpdateAnyRequestFlag(); -} - -// We get there when either NavId == id, or when g.NavAnyRequest is set (which is updated by NavUpdateAnyRequestFlag above) -static void ImGui::NavProcessItem(ImGuiWindow* window, const ImRect& nav_bb, const ImGuiID id) -{ - ImGuiContext& g = *GImGui; - //if (!g.IO.NavActive) // [2017/10/06] Removed this possibly redundant test but I am not sure of all the side-effects yet. Some of the feature here will need to work regardless of using a _NoNavInputs flag. - // return; - - const ImGuiItemFlags item_flags = window->DC.ItemFlags; - const ImRect nav_bb_rel(nav_bb.Min - window->Pos, nav_bb.Max - window->Pos); - - // Process Init Request - if (g.NavInitRequest && g.NavLayer == window->DC.NavLayerCurrent) - { - // Even if 'ImGuiItemFlags_NoNavDefaultFocus' is on (typically collapse/close button) we record the first ResultId so they can be used as a fallback - if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus) || g.NavInitResultId == 0) - { - g.NavInitResultId = id; - g.NavInitResultRectRel = nav_bb_rel; - } - if (!(item_flags & ImGuiItemFlags_NoNavDefaultFocus)) - { - g.NavInitRequest = false; // Found a match, clear request - NavUpdateAnyRequestFlag(); - } - } - - // Process Move Request (scoring for navigation) - // FIXME-NAV: Consider policy for double scoring (scoring from NavScoringRectScreen + scoring from a rect wrapped according to current wrapping policy) - if ((g.NavId != id || (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & ImGuiItemFlags_NoNav)) - { - ImGuiNavMoveResult* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; -#if IMGUI_DEBUG_NAV_SCORING - // [DEBUG] Score all items in NavWindow at all times - if (!g.NavMoveRequest) - g.NavMoveDir = g.NavMoveDirLast; - bool new_best = NavScoreItem(result, nav_bb) && g.NavMoveRequest; -#else - bool new_best = g.NavMoveRequest && NavScoreItem(result, nav_bb); -#endif - if (new_best) - { - result->ID = id; - result->Window = window; - result->RectRel = nav_bb_rel; - } - - const float VISIBLE_RATIO = 0.70f; - if ((g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) && window->ClipRect.Overlaps(nav_bb)) - if (ImClamp(nav_bb.Max.y, window->ClipRect.Min.y, window->ClipRect.Max.y) - ImClamp(nav_bb.Min.y, window->ClipRect.Min.y, window->ClipRect.Max.y) >= (nav_bb.Max.y - nav_bb.Min.y) * VISIBLE_RATIO) - if (NavScoreItem(&g.NavMoveResultLocalVisibleSet, nav_bb)) - { - result = &g.NavMoveResultLocalVisibleSet; - result->ID = id; - result->Window = window; - result->RectRel = nav_bb_rel; - } - } - - // Update window-relative bounding box of navigated item - if (g.NavId == id) - { - g.NavWindow = window; // Always refresh g.NavWindow, because some operations such as FocusItem() don't have a window. - g.NavLayer = window->DC.NavLayerCurrent; - g.NavIdIsAlive = true; - g.NavIdTabCounter = window->FocusIdxTabCounter; - window->NavRectRel[window->DC.NavLayerCurrent] = nav_bb_rel; // Store item bounding box (relative to window position) - } -} - -// Declare item bounding box for clipping and interaction. -// Note that the size can be different than the one provided to ItemSize(). Typically, widgets that spread over available surface -// declare their minimum size requirement to ItemSize() and then use a larger region for drawing/interaction, which is passed to ItemAdd(). -bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - if (id != 0) - { - // Navigation processing runs prior to clipping early-out - // (a) So that NavInitRequest can be honored, for newly opened windows to select a default widget - // (b) So that we can scroll up/down past clipped items. This adds a small O(N) cost to regular navigation requests unfortunately, but it is still limited to one window. - // it may not scale very well for windows with ten of thousands of item, but at least NavMoveRequest is only set on user interaction, aka maximum once a frame. - // We could early out with "if (is_clipped && !g.NavInitRequest) return false;" but when we wouldn't be able to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick) - window->DC.NavLayerActiveMaskNext |= window->DC.NavLayerCurrentMask; - if (g.NavId == id || g.NavAnyRequest) - if (g.NavWindow->RootWindowForNav == window->RootWindowForNav) - if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened)) - NavProcessItem(window, nav_bb_arg ? *nav_bb_arg : bb, id); - } - - window->DC.LastItemId = id; - window->DC.LastItemRect = bb; - window->DC.LastItemStatusFlags = 0; - - // Clipping test - const bool is_clipped = IsClippedEx(bb, id, false); - if (is_clipped) - return false; - //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG] - - // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them) - if (IsMouseHoveringRect(bb.Min, bb.Max)) - window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HoveredRect; - return true; -} - -// This is roughly matching the behavior of internal-facing ItemHoverable() -// - we allow hovering to be true when ActiveId==window->MoveID, so that clicking on non-interactive items such as a Text() item still returns true with IsItemHovered() -// - this should work even for non-interactive items that have no ID, so we cannot use LastItemId -bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (g.NavDisableMouseHover && !g.NavDisableHighlight) - return IsItemFocused(); - - // Test for bounding box overlap, as updated as ItemAdd() - if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect)) - return false; - IM_ASSERT((flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) == 0); // Flags not supported by this function - - // Test if we are hovering the right window (our window could be behind another window) - // [2017/10/16] Reverted commit 344d48be3 and testing RootWindow instead. I believe it is correct to NOT test for RootWindow but this leaves us unable to use IsItemHovered() after EndChild() itself. - // Until a solution is found I believe reverting to the test from 2017/09/27 is safe since this was the test that has been running for a long while. - //if (g.HoveredWindow != window) - // return false; - if (g.HoveredRootWindow != window->RootWindow && !(flags & ImGuiHoveredFlags_AllowWhenOverlapped)) - return false; - - // Test if another item is active (e.g. being dragged) - if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) - if (g.ActiveId != 0 && g.ActiveId != window->DC.LastItemId && !g.ActiveIdAllowOverlap && g.ActiveId != window->MoveId) - return false; - - // Test if interactions on this window are blocked by an active popup or modal - if (!IsWindowContentHoverable(window, flags)) - return false; - - // Test if the item is disabled - if (window->DC.ItemFlags & ImGuiItemFlags_Disabled) - return false; - - // Special handling for the 1st item after Begin() which represent the title bar. When the window is collapsed (SkipItems==true) that last item will never be overwritten so we need to detect tht case. - if (window->DC.LastItemId == window->MoveId && window->WriteAccessed) - return false; - return true; -} - -// Internal facing ItemHoverable() used when submitting widgets. Differs slightly from IsItemHovered(). -bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id) -{ - ImGuiContext& g = *GImGui; - if (g.HoveredId != 0 && g.HoveredId != id && !g.HoveredIdAllowOverlap) - return false; - - ImGuiWindow* window = g.CurrentWindow; - if (g.HoveredWindow != window) - return false; - if (g.ActiveId != 0 && g.ActiveId != id && !g.ActiveIdAllowOverlap) - return false; - if (!IsMouseHoveringRect(bb.Min, bb.Max)) - return false; - if (g.NavDisableMouseHover || !IsWindowContentHoverable(window, ImGuiHoveredFlags_None)) - return false; - if (window->DC.ItemFlags & ImGuiItemFlags_Disabled) - return false; - - SetHoveredID(id); - return true; -} - -bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (!bb.Overlaps(window->ClipRect)) - if (id == 0 || id != g.ActiveId) - if (clip_even_when_logged || !g.LogEnabled) - return true; - return false; -} - -bool ImGui::FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop) -{ - ImGuiContext& g = *GImGui; - - const bool allow_keyboard_focus = (window->DC.ItemFlags & (ImGuiItemFlags_AllowKeyboardFocus | ImGuiItemFlags_Disabled)) == ImGuiItemFlags_AllowKeyboardFocus; - window->FocusIdxAllCounter++; - if (allow_keyboard_focus) - window->FocusIdxTabCounter++; - - // Process keyboard input at this point: TAB/Shift-TAB to tab out of the currently focused item. - // Note that we can always TAB out of a widget that doesn't allow tabbing in. - if (tab_stop && (g.ActiveId == id) && window->FocusIdxAllRequestNext == INT_MAX && window->FocusIdxTabRequestNext == INT_MAX && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab)) - window->FocusIdxTabRequestNext = window->FocusIdxTabCounter + (g.IO.KeyShift ? (allow_keyboard_focus ? -1 : 0) : +1); // Modulo on index will be applied at the end of frame once we've got the total counter of items. - - if (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent) - return true; - if (allow_keyboard_focus && window->FocusIdxTabCounter == window->FocusIdxTabRequestCurrent) - { - g.NavJustTabbedId = id; - return true; - } - - return false; -} - -void ImGui::FocusableItemUnregister(ImGuiWindow* window) -{ - window->FocusIdxAllCounter--; - window->FocusIdxTabCounter--; -} - -ImVec2 ImGui::CalcItemSize(ImVec2 size, float default_x, float default_y) -{ - ImGuiContext& g = *GImGui; - ImVec2 content_max; - if (size.x < 0.0f || size.y < 0.0f) - content_max = g.CurrentWindow->Pos + GetContentRegionMax(); - if (size.x <= 0.0f) - size.x = (size.x == 0.0f) ? default_x : ImMax(content_max.x - g.CurrentWindow->DC.CursorPos.x, 4.0f) + size.x; - if (size.y <= 0.0f) - size.y = (size.y == 0.0f) ? default_y : ImMax(content_max.y - g.CurrentWindow->DC.CursorPos.y, 4.0f) + size.y; - return size; -} - -float ImGui::CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x) -{ - if (wrap_pos_x < 0.0f) - return 0.0f; - - ImGuiWindow* window = GetCurrentWindowRead(); - if (wrap_pos_x == 0.0f) - wrap_pos_x = GetContentRegionMax().x + window->Pos.x; - else if (wrap_pos_x > 0.0f) - wrap_pos_x += window->Pos.x - window->Scroll.x; // wrap_pos_x is provided is window local space - - return ImMax(wrap_pos_x - pos.x, 1.0f); -} - -//----------------------------------------------------------------------------- - -void* ImGui::MemAlloc(size_t size) -{ - GImAllocatorActiveAllocationsCount++; - return GImAllocatorAllocFunc(size, GImAllocatorUserData); -} - -void ImGui::MemFree(void* ptr) -{ - if (ptr) GImAllocatorActiveAllocationsCount--; - return GImAllocatorFreeFunc(ptr, GImAllocatorUserData); -} - -const char* ImGui::GetClipboardText() -{ - return GImGui->IO.GetClipboardTextFn ? GImGui->IO.GetClipboardTextFn(GImGui->IO.ClipboardUserData) : ""; -} - -void ImGui::SetClipboardText(const char* text) -{ - if (GImGui->IO.SetClipboardTextFn) - GImGui->IO.SetClipboardTextFn(GImGui->IO.ClipboardUserData, text); -} - -const char* ImGui::GetVersion() -{ - return IMGUI_VERSION; -} - -// Internal state access - if you want to share ImGui state between modules (e.g. DLL) or allocate it yourself -// Note that we still point to some static data and members (such as GFontAtlas), so the state instance you end up using will point to the static data within its module -ImGuiContext* ImGui::GetCurrentContext() -{ - return GImGui; -} - -void ImGui::SetCurrentContext(ImGuiContext* ctx) -{ -#ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC - IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this. -#else - GImGui = ctx; -#endif -} - -// Helper function to verify that the type sizes are matching between the calling file's compilation unit and imgui.cpp's compilation unit -// If the user has inconsistent compilation settings, imgui configuration #define, packing pragma, etc. you may see different structures from what imgui.cpp sees which is highly problematic. -bool ImGui::DebugCheckVersionAndDataLayout(const char* version, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_vert) -{ - bool error = false; - if (strcmp(version, IMGUI_VERSION)!=0) { error = true; IM_ASSERT(strcmp(version,IMGUI_VERSION)==0 && "Mismatch version string!"); } - if (sz_io != sizeof(ImGuiIO)) { error = true; IM_ASSERT(sz_io == sizeof(ImGuiIO) && "Mismatched struct layout!"); } - if (sz_style != sizeof(ImGuiStyle)) { error = true; IM_ASSERT(sz_style == sizeof(ImGuiStyle) && "Mismatched struct layout!"); } - if (sz_vec2 != sizeof(ImVec2)) { error = true; IM_ASSERT(sz_vec2 == sizeof(ImVec2) && "Mismatched struct layout!"); } - if (sz_vec4 != sizeof(ImVec4)) { error = true; IM_ASSERT(sz_vec4 == sizeof(ImVec4) && "Mismatched struct layout!"); } - if (sz_vert != sizeof(ImDrawVert)) { error = true; IM_ASSERT(sz_vert == sizeof(ImDrawVert) && "Mismatched struct layout!"); } - return !error; -} - -void ImGui::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void(*free_func)(void* ptr, void* user_data), void* user_data) -{ - GImAllocatorAllocFunc = alloc_func; - GImAllocatorFreeFunc = free_func; - GImAllocatorUserData = user_data; -} - -ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas) -{ - ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas); - if (GImGui == NULL) - SetCurrentContext(ctx); - Initialize(ctx); - return ctx; -} - -void ImGui::DestroyContext(ImGuiContext* ctx) -{ - if (ctx == NULL) - ctx = GImGui; - Shutdown(ctx); - if (GImGui == ctx) - SetCurrentContext(NULL); - IM_DELETE(ctx); -} - -ImGuiIO& ImGui::GetIO() -{ - IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?"); - return GImGui->IO; -} - -ImGuiStyle& ImGui::GetStyle() -{ - IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?"); - return GImGui->Style; -} - -// Same value as passed to the old io.RenderDrawListsFn function. Valid after Render() and until the next call to NewFrame() -ImDrawData* ImGui::GetDrawData() -{ - ImGuiContext& g = *GImGui; - return g.DrawData.Valid ? &g.DrawData : NULL; -} - -float ImGui::GetTime() -{ - return GImGui->Time; -} - -int ImGui::GetFrameCount() -{ - return GImGui->FrameCount; -} - -ImDrawList* ImGui::GetOverlayDrawList() -{ - return &GImGui->OverlayDrawList; -} - -ImDrawListSharedData* ImGui::GetDrawListSharedData() -{ - return &GImGui->DrawListSharedData; -} - -// This needs to be called before we submit any widget (aka in or before Begin) -void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(window == g.NavWindow); - bool init_for_nav = false; - if (!(window->Flags & ImGuiWindowFlags_NoNavInputs)) - if (!(window->Flags & ImGuiWindowFlags_ChildWindow) || (window->Flags & ImGuiWindowFlags_Popup) || (window->NavLastIds[0] == 0) || force_reinit) - init_for_nav = true; - if (init_for_nav) - { - SetNavID(0, g.NavLayer); - g.NavInitRequest = true; - g.NavInitRequestFromMove = false; - g.NavInitResultId = 0; - g.NavInitResultRectRel = ImRect(); - NavUpdateAnyRequestFlag(); - } - else - { - g.NavId = window->NavLastIds[0]; - } -} - -static ImVec2 NavCalcPreferredRefPos() -{ - ImGuiContext& g = *GImGui; - if (g.NavDisableHighlight || !g.NavDisableMouseHover || !g.NavWindow) - return ImFloor(g.IO.MousePos); - - // When navigation is active and mouse is disabled, decide on an arbitrary position around the bottom left of the currently navigated item - const ImRect& rect_rel = g.NavWindow->NavRectRel[g.NavLayer]; - ImVec2 pos = g.NavWindow->Pos + ImVec2(rect_rel.Min.x + ImMin(g.Style.FramePadding.x*4, rect_rel.GetWidth()), rect_rel.Max.y - ImMin(g.Style.FramePadding.y, rect_rel.GetHeight())); - ImRect visible_rect = GetViewportRect(); - return ImFloor(ImClamp(pos, visible_rect.Min, visible_rect.Max)); // ImFloor() is important because non-integer mouse position application in back-end might be lossy and result in undesirable non-zero delta. -} - -static int FindWindowIndex(ImGuiWindow* window) // FIXME-OPT O(N) -{ - ImGuiContext& g = *GImGui; - for (int i = g.Windows.Size-1; i >= 0; i--) - if (g.Windows[i] == window) - return i; - return -1; -} - -static ImGuiWindow* FindWindowNavigable(int i_start, int i_stop, int dir) // FIXME-OPT O(N) -{ - ImGuiContext& g = *GImGui; - for (int i = i_start; i >= 0 && i < g.Windows.Size && i != i_stop; i += dir) - if (ImGui::IsWindowNavFocusable(g.Windows[i])) - return g.Windows[i]; - return NULL; -} - -float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode) -{ - ImGuiContext& g = *GImGui; - if (mode == ImGuiInputReadMode_Down) - return g.IO.NavInputs[n]; // Instant, read analog input (0.0f..1.0f, as provided by user) - - const float t = g.IO.NavInputsDownDuration[n]; - if (t < 0.0f && mode == ImGuiInputReadMode_Released) // Return 1.0f when just released, no repeat, ignore analog input. - return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f); - if (t < 0.0f) - return 0.0f; - if (mode == ImGuiInputReadMode_Pressed) // Return 1.0f when just pressed, no repeat, ignore analog input. - return (t == 0.0f) ? 1.0f : 0.0f; - if (mode == ImGuiInputReadMode_Repeat) - return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.80f); - if (mode == ImGuiInputReadMode_RepeatSlow) - return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 1.00f, g.IO.KeyRepeatRate * 2.00f); - if (mode == ImGuiInputReadMode_RepeatFast) - return (float)CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, g.IO.KeyRepeatDelay * 0.80f, g.IO.KeyRepeatRate * 0.30f); - return 0.0f; -} - -// Equivalent of IsKeyDown() for NavInputs[] -static bool IsNavInputDown(ImGuiNavInput n) -{ - return GImGui->IO.NavInputs[n] > 0.0f; -} - -// Equivalent of IsKeyPressed() for NavInputs[] -static bool IsNavInputPressed(ImGuiNavInput n, ImGuiInputReadMode mode) -{ - return ImGui::GetNavInputAmount(n, mode) > 0.0f; -} - -static bool IsNavInputPressedAnyOfTwo(ImGuiNavInput n1, ImGuiNavInput n2, ImGuiInputReadMode mode) -{ - return (ImGui::GetNavInputAmount(n1, mode) + ImGui::GetNavInputAmount(n2, mode)) > 0.0f; -} - -ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor, float fast_factor) -{ - ImVec2 delta(0.0f, 0.0f); - if (dir_sources & ImGuiNavDirSourceFlags_Keyboard) - delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft_, mode), GetNavInputAmount(ImGuiNavInput_KeyDown_, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_, mode)); - if (dir_sources & ImGuiNavDirSourceFlags_PadDPad) - delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode) - GetNavInputAmount(ImGuiNavInput_DpadLeft, mode), GetNavInputAmount(ImGuiNavInput_DpadDown, mode) - GetNavInputAmount(ImGuiNavInput_DpadUp, mode)); - if (dir_sources & ImGuiNavDirSourceFlags_PadLStick) - delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode)); - if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow)) - delta *= slow_factor; - if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast)) - delta *= fast_factor; - return delta; -} - -static void NavUpdateWindowingHighlightWindow(int focus_change_dir) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.NavWindowingTarget); - if (g.NavWindowingTarget->Flags & ImGuiWindowFlags_Modal) - return; - - const int i_current = FindWindowIndex(g.NavWindowingTarget); - ImGuiWindow* window_target = FindWindowNavigable(i_current + focus_change_dir, -INT_MAX, focus_change_dir); - if (!window_target) - window_target = FindWindowNavigable((focus_change_dir < 0) ? (g.Windows.Size - 1) : 0, i_current, focus_change_dir); - g.NavWindowingTarget = window_target; - g.NavWindowingToggleLayer = false; -} - -// Window management mode (hold to: change focus/move/resize, tap to: toggle menu layer) -static void ImGui::NavUpdateWindowing() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* apply_focus_window = NULL; - bool apply_toggle_layer = false; - - bool start_windowing_with_gamepad = !g.NavWindowingTarget && IsNavInputPressed(ImGuiNavInput_Menu, ImGuiInputReadMode_Pressed); - bool start_windowing_with_keyboard = !g.NavWindowingTarget && g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab) && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard); - if (start_windowing_with_gamepad || start_windowing_with_keyboard) - if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavigable(g.Windows.Size - 1, -INT_MAX, -1)) - { - g.NavWindowingTarget = window->RootWindowForTabbing; - g.NavWindowingHighlightTimer = g.NavWindowingHighlightAlpha = 0.0f; - g.NavWindowingToggleLayer = start_windowing_with_keyboard ? false : true; - g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_NavKeyboard : ImGuiInputSource_NavGamepad; - } - - // Gamepad update - g.NavWindowingHighlightTimer += g.IO.DeltaTime; - if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavGamepad) - { - // Highlight only appears after a brief time holding the button, so that a fast tap on PadMenu (to toggle NavLayer) doesn't add visual noise - g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingHighlightTimer - 0.20f) / 0.05f)); - - // Select window to focus - const int focus_change_dir = (int)IsNavInputPressed(ImGuiNavInput_FocusPrev, ImGuiInputReadMode_RepeatSlow) - (int)IsNavInputPressed(ImGuiNavInput_FocusNext, ImGuiInputReadMode_RepeatSlow); - if (focus_change_dir != 0) - { - NavUpdateWindowingHighlightWindow(focus_change_dir); - g.NavWindowingHighlightAlpha = 1.0f; - } - - // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered front-most) - if (!IsNavInputDown(ImGuiNavInput_Menu)) - { - g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore. - if (g.NavWindowingToggleLayer && g.NavWindow) - apply_toggle_layer = true; - else if (!g.NavWindowingToggleLayer) - apply_focus_window = g.NavWindowingTarget; - g.NavWindowingTarget = NULL; - } - } - - // Keyboard: Focus - if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_NavKeyboard) - { - // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise - g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingHighlightTimer - 0.15f) / 0.04f)); // 1.0f - if (IsKeyPressedMap(ImGuiKey_Tab, true)) - NavUpdateWindowingHighlightWindow(g.IO.KeyShift ? +1 : -1); - if (!g.IO.KeyCtrl) - apply_focus_window = g.NavWindowingTarget; - } - - // Keyboard: Press and Release ALT to toggle menu layer - // FIXME: We lack an explicit IO variable for "is the imgui window focused", so compare mouse validity to detect the common case of back-end clearing releases all keys on ALT-TAB - if ((g.ActiveId == 0 || g.ActiveIdAllowOverlap) && IsNavInputPressed(ImGuiNavInput_KeyMenu_, ImGuiInputReadMode_Released)) - if (IsMousePosValid(&g.IO.MousePos) == IsMousePosValid(&g.IO.MousePosPrev)) - apply_toggle_layer = true; - - // Move window - if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove)) - { - ImVec2 move_delta; - if (g.NavInputSource == ImGuiInputSource_NavKeyboard && !g.IO.KeyShift) - move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down); - if (g.NavInputSource == ImGuiInputSource_NavGamepad) - move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down); - if (move_delta.x != 0.0f || move_delta.y != 0.0f) - { - const float NAV_MOVE_SPEED = 800.0f; - const float move_speed = ImFloor(NAV_MOVE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); // FIXME: Doesn't code variable framerate very well - g.NavWindowingTarget->Pos += move_delta * move_speed; - g.NavDisableMouseHover = true; - MarkIniSettingsDirty(g.NavWindowingTarget); - } - } - - // Apply final focus - if (apply_focus_window && (g.NavWindow == NULL || apply_focus_window != g.NavWindow->RootWindowForTabbing)) - { - g.NavDisableHighlight = false; - g.NavDisableMouseHover = true; - apply_focus_window = NavRestoreLastChildNavWindow(apply_focus_window); - ClosePopupsOverWindow(apply_focus_window); - FocusWindow(apply_focus_window); - if (apply_focus_window->NavLastIds[0] == 0) - NavInitWindow(apply_focus_window, false); - - // If the window only has a menu layer, select it directly - if (apply_focus_window->DC.NavLayerActiveMask == (1 << 1)) - g.NavLayer = 1; - } - if (apply_focus_window) - g.NavWindowingTarget = NULL; - - // Apply menu/layer toggle - if (apply_toggle_layer && g.NavWindow) - { - ImGuiWindow* new_nav_window = g.NavWindow; - while ((new_nav_window->DC.NavLayerActiveMask & (1 << 1)) == 0 && (new_nav_window->Flags & ImGuiWindowFlags_ChildWindow) != 0 && (new_nav_window->Flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_ChildMenu)) == 0) - new_nav_window = new_nav_window->ParentWindow; - if (new_nav_window != g.NavWindow) - { - ImGuiWindow* old_nav_window = g.NavWindow; - FocusWindow(new_nav_window); - new_nav_window->NavLastChildNavWindow = old_nav_window; - } - g.NavDisableHighlight = false; - g.NavDisableMouseHover = true; - NavRestoreLayer((g.NavWindow->DC.NavLayerActiveMask & (1 << 1)) ? (g.NavLayer ^ 1) : 0); - } -} - -// Scroll to keep newly navigated item fully into view -// NB: We modify rect_rel by the amount we scrolled for, so it is immediately updated. -static void NavScrollToBringItemIntoView(ImGuiWindow* window, const ImRect& item_rect) -{ - ImRect window_rect(window->InnerMainRect.Min - ImVec2(1, 1), window->InnerMainRect.Max + ImVec2(1, 1)); - //g.OverlayDrawList.AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG] - if (window_rect.Contains(item_rect)) - return; - - ImGuiContext& g = *GImGui; - if (window->ScrollbarX && item_rect.Min.x < window_rect.Min.x) - { - window->ScrollTarget.x = item_rect.Min.x - window->Pos.x + window->Scroll.x - g.Style.ItemSpacing.x; - window->ScrollTargetCenterRatio.x = 0.0f; - } - else if (window->ScrollbarX && item_rect.Max.x >= window_rect.Max.x) - { - window->ScrollTarget.x = item_rect.Max.x - window->Pos.x + window->Scroll.x + g.Style.ItemSpacing.x; - window->ScrollTargetCenterRatio.x = 1.0f; - } - if (item_rect.Min.y < window_rect.Min.y) - { - window->ScrollTarget.y = item_rect.Min.y - window->Pos.y + window->Scroll.y - g.Style.ItemSpacing.y; - window->ScrollTargetCenterRatio.y = 0.0f; - } - else if (item_rect.Max.y >= window_rect.Max.y) - { - window->ScrollTarget.y = item_rect.Max.y - window->Pos.y + window->Scroll.y + g.Style.ItemSpacing.y; - window->ScrollTargetCenterRatio.y = 1.0f; - } -} - -static void ImGui::NavUpdate() -{ - ImGuiContext& g = *GImGui; - g.IO.WantSetMousePos = false; - -#if 0 - if (g.NavScoringCount > 0) printf("[%05d] NavScoringCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.FrameCount, g.NavScoringCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); -#endif - - bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; - bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; - - // Set input source as Gamepad when buttons are pressed before we map Keyboard (some features differs when used with Gamepad vs Keyboard) - if (nav_gamepad_active) - if (g.IO.NavInputs[ImGuiNavInput_Activate] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Input] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Cancel] > 0.0f || g.IO.NavInputs[ImGuiNavInput_Menu] > 0.0f) - g.NavInputSource = ImGuiInputSource_NavGamepad; - - // Update Keyboard->Nav inputs mapping - if (nav_keyboard_active) - { - #define NAV_MAP_KEY(_KEY, _NAV_INPUT) if (IsKeyDown(g.IO.KeyMap[_KEY])) { g.IO.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_NavKeyboard; } - NAV_MAP_KEY(ImGuiKey_Space, ImGuiNavInput_Activate ); - NAV_MAP_KEY(ImGuiKey_Enter, ImGuiNavInput_Input ); - NAV_MAP_KEY(ImGuiKey_Escape, ImGuiNavInput_Cancel ); - NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ ); - NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_); - NAV_MAP_KEY(ImGuiKey_UpArrow, ImGuiNavInput_KeyUp_ ); - NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ ); - if (g.IO.KeyCtrl) g.IO.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f; - if (g.IO.KeyShift) g.IO.NavInputs[ImGuiNavInput_TweakFast] = 1.0f; - if (g.IO.KeyAlt) g.IO.NavInputs[ImGuiNavInput_KeyMenu_] = 1.0f; - #undef NAV_MAP_KEY - } - - memcpy(g.IO.NavInputsDownDurationPrev, g.IO.NavInputsDownDuration, sizeof(g.IO.NavInputsDownDuration)); - for (int i = 0; i < IM_ARRAYSIZE(g.IO.NavInputs); i++) - g.IO.NavInputsDownDuration[i] = (g.IO.NavInputs[i] > 0.0f) ? (g.IO.NavInputsDownDuration[i] < 0.0f ? 0.0f : g.IO.NavInputsDownDuration[i] + g.IO.DeltaTime) : -1.0f; - - // Process navigation init request (select first/default focus) - if (g.NavInitResultId != 0 && (!g.NavDisableHighlight || g.NavInitRequestFromMove)) - { - // Apply result from previous navigation init request (will typically select the first item, unless SetItemDefaultFocus() has been called) - IM_ASSERT(g.NavWindow); - if (g.NavInitRequestFromMove) - SetNavIDWithRectRel(g.NavInitResultId, g.NavLayer, g.NavInitResultRectRel); - else - SetNavID(g.NavInitResultId, g.NavLayer); - g.NavWindow->NavRectRel[g.NavLayer] = g.NavInitResultRectRel; - } - g.NavInitRequest = false; - g.NavInitRequestFromMove = false; - g.NavInitResultId = 0; - g.NavJustMovedToId = 0; - - // Process navigation move request - if (g.NavMoveRequest && (g.NavMoveResultLocal.ID != 0 || g.NavMoveResultOther.ID != 0)) - { - // Select which result to use - ImGuiNavMoveResult* result = (g.NavMoveResultLocal.ID != 0) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; - - // PageUp/PageDown behavior first jumps to the bottom/top mostly visible item, _otherwise_ use the result from the previous/next page. - if (g.NavMoveRequestFlags & ImGuiNavMoveFlags_AlsoScoreVisibleSet) - if (g.NavMoveResultLocalVisibleSet.ID != 0 && g.NavMoveResultLocalVisibleSet.ID != g.NavId) - result = &g.NavMoveResultLocalVisibleSet; - - // Maybe entering a flattened child from the outside? In this case solve the tie using the regular scoring rules. - if (result != &g.NavMoveResultOther && g.NavMoveResultOther.ID != 0 && g.NavMoveResultOther.Window->ParentWindow == g.NavWindow) - if ((g.NavMoveResultOther.DistBox < result->DistBox) || (g.NavMoveResultOther.DistBox == result->DistBox && g.NavMoveResultOther.DistCenter < result->DistCenter)) - result = &g.NavMoveResultOther; - IM_ASSERT(g.NavWindow && result->Window); - - // Scroll to keep newly navigated item fully into view. - if (g.NavLayer == 0) - { - ImRect rect_abs = ImRect(result->RectRel.Min + result->Window->Pos, result->RectRel.Max + result->Window->Pos); - NavScrollToBringItemIntoView(result->Window, rect_abs); - - // Estimate upcoming scroll so we can offset our result position so mouse position can be applied immediately after in NavUpdate() - ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(result->Window, false); - ImVec2 delta_scroll = result->Window->Scroll - next_scroll; - result->RectRel.Translate(delta_scroll); - - // Also scroll parent window to keep us into view if necessary (we could/should technically recurse back the whole the parent hierarchy). - if (result->Window->Flags & ImGuiWindowFlags_ChildWindow) - NavScrollToBringItemIntoView(result->Window->ParentWindow, ImRect(rect_abs.Min + delta_scroll, rect_abs.Max + delta_scroll)); - } - - // Apply result from previous frame navigation directional move request - ClearActiveID(); - g.NavWindow = result->Window; - SetNavIDWithRectRel(result->ID, g.NavLayer, result->RectRel); - g.NavJustMovedToId = result->ID; - g.NavMoveFromClampedRefRect = false; - } - - // When a forwarded move request failed, we restore the highlight that we disabled during the forward frame - if (g.NavMoveRequestForward == ImGuiNavForward_ForwardActive) - { - IM_ASSERT(g.NavMoveRequest); - if (g.NavMoveResultLocal.ID == 0 && g.NavMoveResultOther.ID == 0) - g.NavDisableHighlight = false; - g.NavMoveRequestForward = ImGuiNavForward_None; - } - - // Apply application mouse position movement, after we had a chance to process move request result. - if (g.NavMousePosDirty && g.NavIdIsAlive) - { - // Set mouse position given our knowledge of the navigated item position from last frame - if ((g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) && (g.IO.BackendFlags & ImGuiBackendFlags_HasSetMousePos)) - { - if (!g.NavDisableHighlight && g.NavDisableMouseHover && g.NavWindow) - { - g.IO.MousePos = g.IO.MousePosPrev = NavCalcPreferredRefPos(); - g.IO.WantSetMousePos = true; - } - } - g.NavMousePosDirty = false; - } - g.NavIdIsAlive = false; - g.NavJustTabbedId = 0; - IM_ASSERT(g.NavLayer == 0 || g.NavLayer == 1); - - // Store our return window (for returning from Layer 1 to Layer 0) and clear it as soon as we step back in our own Layer 0 - if (g.NavWindow) - NavSaveLastChildNavWindow(g.NavWindow); - if (g.NavWindow && g.NavWindow->NavLastChildNavWindow != NULL && g.NavLayer == 0) - g.NavWindow->NavLastChildNavWindow = NULL; - - NavUpdateWindowing(); - - // Set output flags for user application - g.IO.NavActive = (nav_keyboard_active || nav_gamepad_active) && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs); - g.IO.NavVisible = (g.IO.NavActive && g.NavId != 0 && !g.NavDisableHighlight) || (g.NavWindowingTarget != NULL) || g.NavInitRequest; - - // Process NavCancel input (to close a popup, get back to parent, clear focus) - if (IsNavInputPressed(ImGuiNavInput_Cancel, ImGuiInputReadMode_Pressed)) - { - if (g.ActiveId != 0) - { - ClearActiveID(); - } - else if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow) && !(g.NavWindow->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->ParentWindow) - { - // Exit child window - ImGuiWindow* child_window = g.NavWindow; - ImGuiWindow* parent_window = g.NavWindow->ParentWindow; - IM_ASSERT(child_window->ChildId != 0); - FocusWindow(parent_window); - SetNavID(child_window->ChildId, 0); - g.NavIdIsAlive = false; - if (g.NavDisableMouseHover) - g.NavMousePosDirty = true; - } - else if (g.OpenPopupStack.Size > 0) - { - // Close open popup/menu - if (!(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal)) - ClosePopupToLevel(g.OpenPopupStack.Size - 1); - } - else if (g.NavLayer != 0) - { - // Leave the "menu" layer - NavRestoreLayer(0); - } - else - { - // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were - if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow))) - g.NavWindow->NavLastIds[0] = 0; - g.NavId = 0; - } - } - - // Process manual activation request - g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = 0; - if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) - { - bool activate_down = IsNavInputDown(ImGuiNavInput_Activate); - bool activate_pressed = activate_down && IsNavInputPressed(ImGuiNavInput_Activate, ImGuiInputReadMode_Pressed); - if (g.ActiveId == 0 && activate_pressed) - g.NavActivateId = g.NavId; - if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_down) - g.NavActivateDownId = g.NavId; - if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && activate_pressed) - g.NavActivatePressedId = g.NavId; - if ((g.ActiveId == 0 || g.ActiveId == g.NavId) && IsNavInputPressed(ImGuiNavInput_Input, ImGuiInputReadMode_Pressed)) - g.NavInputId = g.NavId; - } - if (g.NavWindow && (g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) - g.NavDisableHighlight = true; - if (g.NavActivateId != 0) - IM_ASSERT(g.NavActivateDownId == g.NavActivateId); - g.NavMoveRequest = false; - - // Process programmatic activation request - if (g.NavNextActivateId != 0) - g.NavActivateId = g.NavActivateDownId = g.NavActivatePressedId = g.NavInputId = g.NavNextActivateId; - g.NavNextActivateId = 0; - - // Initiate directional inputs request - const int allowed_dir_flags = (g.ActiveId == 0) ? ~0 : g.ActiveIdAllowNavDirFlags; - if (g.NavMoveRequestForward == ImGuiNavForward_None) - { - g.NavMoveDir = ImGuiDir_None; - g.NavMoveRequestFlags = 0; - if (g.NavWindow && !g.NavWindowingTarget && allowed_dir_flags && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) - { - if ((allowed_dir_flags & (1<Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget && g.NavLayer == 0) - { - ImGuiWindow* window = g.NavWindow; - bool page_up_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageUp]) && (allowed_dir_flags & (1 << ImGuiDir_Up)); - bool page_down_held = IsKeyDown(g.IO.KeyMap[ImGuiKey_PageDown]) && (allowed_dir_flags & (1 << ImGuiDir_Down)); - if ((page_up_held && !page_down_held) || (page_down_held && !page_up_held)) - { - if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll) - { - // Fallback manual-scroll when window has no navigable item - if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true)) - SetWindowScrollY(window, window->Scroll.y - window->InnerClipRect.GetHeight()); - else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true)) - SetWindowScrollY(window, window->Scroll.y + window->InnerClipRect.GetHeight()); - } - else - { - const ImRect& nav_rect_rel = window->NavRectRel[g.NavLayer]; - const float page_offset_y = ImMax(0.0f, window->InnerClipRect.GetHeight() - window->CalcFontSize() * 1.0f + nav_rect_rel.GetHeight()); - if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageUp], true)) - { - nav_scoring_rect_offset_y = -page_offset_y; - g.NavMoveDir = ImGuiDir_Down; // Because our scoring rect is offset, we intentionally request the opposite direction (so we can always land on the last item) - g.NavMoveClipDir = ImGuiDir_Up; - g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; - } - else if (IsKeyPressed(g.IO.KeyMap[ImGuiKey_PageDown], true)) - { - nav_scoring_rect_offset_y = +page_offset_y; - g.NavMoveDir = ImGuiDir_Up; // Because our scoring rect is offset, we intentionally request the opposite direction (so we can always land on the last item) - g.NavMoveClipDir = ImGuiDir_Down; - g.NavMoveRequestFlags = ImGuiNavMoveFlags_AllowCurrentNavId | ImGuiNavMoveFlags_AlsoScoreVisibleSet; - } - } - } - } - - if (g.NavMoveDir != ImGuiDir_None) - { - g.NavMoveRequest = true; - g.NavMoveDirLast = g.NavMoveDir; - } - - // If we initiate a movement request and have no current NavId, we initiate a InitDefautRequest that will be used as a fallback if the direction fails to find a match - if (g.NavMoveRequest && g.NavId == 0) - { - g.NavInitRequest = g.NavInitRequestFromMove = true; - g.NavInitResultId = 0; - g.NavDisableHighlight = false; - } - - NavUpdateAnyRequestFlag(); - - // Scrolling - if (g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.NavWindowingTarget) - { - // *Fallback* manual-scroll with Nav directional keys when window has no navigable item - ImGuiWindow* window = g.NavWindow; - const float scroll_speed = ImFloor(window->CalcFontSize() * 100 * g.IO.DeltaTime + 0.5f); // We need round the scrolling speed because sub-pixel scroll isn't reliably supported. - if (window->DC.NavLayerActiveMask == 0x00 && window->DC.NavHasScroll && g.NavMoveRequest) - { - if (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) - SetWindowScrollX(window, ImFloor(window->Scroll.x + ((g.NavMoveDir == ImGuiDir_Left) ? -1.0f : +1.0f) * scroll_speed)); - if (g.NavMoveDir == ImGuiDir_Up || g.NavMoveDir == ImGuiDir_Down) - SetWindowScrollY(window, ImFloor(window->Scroll.y + ((g.NavMoveDir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed)); - } - - // *Normal* Manual scroll with NavScrollXXX keys - // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds. - ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiInputReadMode_Down, 1.0f/10.0f, 10.0f); - if (scroll_dir.x != 0.0f && window->ScrollbarX) - { - SetWindowScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed)); - g.NavMoveFromClampedRefRect = true; - } - if (scroll_dir.y != 0.0f) - { - SetWindowScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed)); - g.NavMoveFromClampedRefRect = true; - } - } - - // Reset search results - g.NavMoveResultLocal.Clear(); - g.NavMoveResultLocalVisibleSet.Clear(); - g.NavMoveResultOther.Clear(); - - // When we have manually scrolled (without using navigation) and NavId becomes out of bounds, we project its bounding box to the visible area to restart navigation within visible items - if (g.NavMoveRequest && g.NavMoveFromClampedRefRect && g.NavLayer == 0) - { - ImGuiWindow* window = g.NavWindow; - ImRect window_rect_rel(window->InnerMainRect.Min - window->Pos - ImVec2(1,1), window->InnerMainRect.Max - window->Pos + ImVec2(1,1)); - if (!window_rect_rel.Contains(window->NavRectRel[g.NavLayer])) - { - float pad = window->CalcFontSize() * 0.5f; - window_rect_rel.Expand(ImVec2(-ImMin(window_rect_rel.GetWidth(), pad), -ImMin(window_rect_rel.GetHeight(), pad))); // Terrible approximation for the intent of starting navigation from first fully visible item - window->NavRectRel[g.NavLayer].ClipWith(window_rect_rel); - g.NavId = 0; - } - g.NavMoveFromClampedRefRect = false; - } - - // For scoring we use a single segment on the left side our current item bounding box (not touching the edge to avoid box overlap with zero-spaced items) - ImRect nav_rect_rel = (g.NavWindow && !g.NavWindow->NavRectRel[g.NavLayer].IsInverted()) ? g.NavWindow->NavRectRel[g.NavLayer] : ImRect(0,0,0,0); - g.NavScoringRectScreen = g.NavWindow ? ImRect(g.NavWindow->Pos + nav_rect_rel.Min, g.NavWindow->Pos + nav_rect_rel.Max) : GetViewportRect(); - g.NavScoringRectScreen.TranslateY(nav_scoring_rect_offset_y); - g.NavScoringRectScreen.Min.x = ImMin(g.NavScoringRectScreen.Min.x + 1.0f, g.NavScoringRectScreen.Max.x); - g.NavScoringRectScreen.Max.x = g.NavScoringRectScreen.Min.x; - IM_ASSERT(!g.NavScoringRectScreen.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem(). - //g.OverlayDrawList.AddRect(g.NavScoringRectScreen.Min, g.NavScoringRectScreen.Max, IM_COL32(255,200,0,255)); // [DEBUG] - g.NavScoringCount = 0; -#if IMGUI_DEBUG_NAV_RECTS - if (g.NavWindow) { for (int layer = 0; layer < 2; layer++) GetOverlayDrawList()->AddRect(g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Min, g.NavWindow->Pos + g.NavWindow->NavRectRel[layer].Max, IM_COL32(255,200,0,255)); } // [DEBUG] - if (g.NavWindow) { ImU32 col = (g.NavWindow->HiddenFrames == 0) ? IM_COL32(255,0,255,255) : IM_COL32(255,0,0,255); ImVec2 p = NavCalcPreferredRefPos(); char buf[32]; ImFormatString(buf, 32, "%d", g.NavLayer); g.OverlayDrawList.AddCircleFilled(p, 3.0f, col); g.OverlayDrawList.AddText(NULL, 13.0f, p + ImVec2(8,-4), col, buf); } -#endif -} - -static void ImGui::UpdateMovingWindow() -{ - ImGuiContext& g = *GImGui; - if (g.MovingWindow != NULL) - { - // We actually want to move the root window. g.MovingWindow == window we clicked on (could be a child window). - // We track it to preserve Focus and so that generally ActiveIdWindow == MovingWindow and ActiveId == MovingWindow->MoveId for consistency. - KeepAliveID(g.ActiveId); - IM_ASSERT(g.MovingWindow && g.MovingWindow->RootWindow); - ImGuiWindow* moving_window = g.MovingWindow->RootWindow; - if (g.IO.MouseDown[0] && IsMousePosValid(&g.IO.MousePos)) - { - ImVec2 pos = g.IO.MousePos - g.ActiveIdClickOffset; - if (moving_window->Pos.x != pos.x || moving_window->Pos.y != pos.y) - { - MarkIniSettingsDirty(moving_window); - SetWindowPos(moving_window, pos, ImGuiCond_Always); - } - FocusWindow(g.MovingWindow); - } - else - { - ClearActiveID(); - g.MovingWindow = NULL; - } - } - else - { - // When clicking/dragging from a window that has the _NoMove flag, we still set the ActiveId in order to prevent hovering others. - if (g.ActiveIdWindow && g.ActiveIdWindow->MoveId == g.ActiveId) - { - KeepAliveID(g.ActiveId); - if (!g.IO.MouseDown[0]) - ClearActiveID(); - } - } -} - -static void ImGui::UpdateMouseInputs() -{ - ImGuiContext& g = *GImGui; - - // If mouse just appeared or disappeared (usually denoted by -FLT_MAX component, but in reality we test for -256000.0f) we cancel out movement in MouseDelta - if (IsMousePosValid(&g.IO.MousePos) && IsMousePosValid(&g.IO.MousePosPrev)) - g.IO.MouseDelta = g.IO.MousePos - g.IO.MousePosPrev; - else - g.IO.MouseDelta = ImVec2(0.0f, 0.0f); - if (g.IO.MouseDelta.x != 0.0f || g.IO.MouseDelta.y != 0.0f) - g.NavDisableMouseHover = false; - - g.IO.MousePosPrev = g.IO.MousePos; - for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++) - { - g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f; - g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f; - g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i]; - g.IO.MouseDownDuration[i] = g.IO.MouseDown[i] ? (g.IO.MouseDownDuration[i] < 0.0f ? 0.0f : g.IO.MouseDownDuration[i] + g.IO.DeltaTime) : -1.0f; - g.IO.MouseDoubleClicked[i] = false; - if (g.IO.MouseClicked[i]) - { - if (g.Time - g.IO.MouseClickedTime[i] < g.IO.MouseDoubleClickTime) - { - ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); - if (ImLengthSqr(delta_from_click_pos) < g.IO.MouseDoubleClickMaxDist * g.IO.MouseDoubleClickMaxDist) - g.IO.MouseDoubleClicked[i] = true; - g.IO.MouseClickedTime[i] = -FLT_MAX; // so the third click isn't turned into a double-click - } - else - { - g.IO.MouseClickedTime[i] = g.Time; - } - g.IO.MouseClickedPos[i] = g.IO.MousePos; - g.IO.MouseDragMaxDistanceAbs[i] = ImVec2(0.0f, 0.0f); - g.IO.MouseDragMaxDistanceSqr[i] = 0.0f; - } - else if (g.IO.MouseDown[i]) - { - // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold - ImVec2 delta_from_click_pos = IsMousePosValid(&g.IO.MousePos) ? (g.IO.MousePos - g.IO.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); - g.IO.MouseDragMaxDistanceSqr[i] = ImMax(g.IO.MouseDragMaxDistanceSqr[i], ImLengthSqr(delta_from_click_pos)); - g.IO.MouseDragMaxDistanceAbs[i].x = ImMax(g.IO.MouseDragMaxDistanceAbs[i].x, delta_from_click_pos.x < 0.0f ? -delta_from_click_pos.x : delta_from_click_pos.x); - g.IO.MouseDragMaxDistanceAbs[i].y = ImMax(g.IO.MouseDragMaxDistanceAbs[i].y, delta_from_click_pos.y < 0.0f ? -delta_from_click_pos.y : delta_from_click_pos.y); - } - if (g.IO.MouseClicked[i]) // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation - g.NavDisableMouseHover = false; - } -} - -// The reason this is exposed in imgui_internal.h is: on touch-based system that don't have hovering, we want to dispatch inputs to the right target (imgui vs imgui+app) -void ImGui::NewFrameUpdateHoveredWindowAndCaptureFlags() -{ - ImGuiContext& g = *GImGui; - - // Find the window hovered by mouse: - // - Child windows can extend beyond the limit of their parent so we need to derive HoveredRootWindow from HoveredWindow. - // - When moving a window we can skip the search, which also conveniently bypasses the fact that window->WindowRectClipped is lagging as this point of the frame. - // - We also support the moved window toggling the NoInputs flag after moving has started in order to be able to detect windows below it, which is useful for e.g. docking mechanisms. - g.HoveredWindow = (g.MovingWindow && !(g.MovingWindow->Flags & ImGuiWindowFlags_NoInputs)) ? g.MovingWindow : FindHoveredWindow(); - g.HoveredRootWindow = g.HoveredWindow ? g.HoveredWindow->RootWindow : NULL; - - // Modal windows prevents cursor from hovering behind them. - ImGuiWindow* modal_window = GetFrontMostPopupModal(); - if (modal_window) - if (g.HoveredRootWindow && !IsWindowChildOf(g.HoveredRootWindow, modal_window)) - g.HoveredRootWindow = g.HoveredWindow = NULL; - - // Disabled mouse? - if (g.IO.ConfigFlags & ImGuiConfigFlags_NoMouse) - g.HoveredWindow = g.HoveredRootWindow = NULL; - - // We track click ownership. When clicked outside of a window the click is owned by the application and won't report hovering nor request capture even while dragging over our windows afterward. - int mouse_earliest_button_down = -1; - bool mouse_any_down = false; - for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++) - { - if (g.IO.MouseClicked[i]) - g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (!g.OpenPopupStack.empty()); - mouse_any_down |= g.IO.MouseDown[i]; - if (g.IO.MouseDown[i]) - if (mouse_earliest_button_down == -1 || g.IO.MouseClickedTime[i] < g.IO.MouseClickedTime[mouse_earliest_button_down]) - mouse_earliest_button_down = i; - } - const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down]; - - // If mouse was first clicked outside of ImGui bounds we also cancel out hovering. - // FIXME: For patterns of drag and drop across OS windows, we may need to rework/remove this test (first committed 311c0ca9 on 2015/02) - const bool mouse_dragging_extern_payload = g.DragDropActive && (g.DragDropSourceFlags & ImGuiDragDropFlags_SourceExtern) != 0; - if (!mouse_avail_to_imgui && !mouse_dragging_extern_payload) - g.HoveredWindow = g.HoveredRootWindow = NULL; - - // Update io.WantCaptureMouse for the user application (true = dispatch mouse info to imgui, false = dispatch mouse info to imgui + app) - if (g.WantCaptureMouseNextFrame != -1) - g.IO.WantCaptureMouse = (g.WantCaptureMouseNextFrame != 0); - else - g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (!g.OpenPopupStack.empty()); - - // Update io.WantCaptureKeyboard for the user application (true = dispatch keyboard info to imgui, false = dispatch keyboard info to imgui + app) - if (g.WantCaptureKeyboardNextFrame != -1) - g.IO.WantCaptureKeyboard = (g.WantCaptureKeyboardNextFrame != 0); - else - g.IO.WantCaptureKeyboard = (g.ActiveId != 0) || (modal_window != NULL); - if (g.IO.NavActive && (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavNoCaptureKeyboard)) - g.IO.WantCaptureKeyboard = true; - - // Update io.WantTextInput flag, this is to allow systems without a keyboard (e.g. mobile, hand-held) to show a software keyboard if possible - g.IO.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false; -} - -void ImGui::NewFrame() -{ - IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() or ImGui::SetCurrentContext()?"); - ImGuiContext& g = *GImGui; - - // Check user data - // (We pass an error message in the assert expression to make it visible to programmers who are not using a debugger, as most assert handlers display their argument) - IM_ASSERT(g.Initialized); - IM_ASSERT(g.IO.DeltaTime >= 0.0f && "Need a positive DeltaTime (zero is tolerated but will cause some timing issues)"); - IM_ASSERT(g.IO.DisplaySize.x >= 0.0f && g.IO.DisplaySize.y >= 0.0f && "Invalid DisplaySize value"); - IM_ASSERT(g.IO.Fonts->Fonts.Size > 0 && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?"); - IM_ASSERT(g.IO.Fonts->Fonts[0]->IsLoaded() && "Font Atlas not built. Did you call io.Fonts->GetTexDataAsRGBA32() / GetTexDataAsAlpha8() ?"); - IM_ASSERT(g.Style.CurveTessellationTol > 0.0f && "Invalid style setting"); - IM_ASSERT(g.Style.Alpha >= 0.0f && g.Style.Alpha <= 1.0f && "Invalid style setting. Alpha cannot be negative (allows us to avoid a few clamps in color computations)"); - IM_ASSERT((g.FrameCount == 0 || g.FrameCountEnded == g.FrameCount) && "Forgot to call Render() or EndFrame() at the end of the previous frame?"); - for (int n = 0; n < ImGuiKey_COUNT; n++) - IM_ASSERT(g.IO.KeyMap[n] >= -1 && g.IO.KeyMap[n] < IM_ARRAYSIZE(g.IO.KeysDown) && "io.KeyMap[] contains an out of bound value (need to be 0..512, or -1 for unmapped key)"); - - // Perform simple check for required key mapping (we intentionally do NOT check all keys to not pressure user into setting up everything, but Space is required and was only recently added in 1.60 WIP) - if (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) - IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation."); - - // Load settings on first frame (if not explicitly loaded manually before) - if (!g.SettingsLoaded) - { - IM_ASSERT(g.SettingsWindows.empty()); - if (g.IO.IniFilename) - LoadIniSettingsFromDisk(g.IO.IniFilename); - g.SettingsLoaded = true; - } - - // Save settings (with a delay after the last modification, so we don't spam disk too much) - if (g.SettingsDirtyTimer > 0.0f) - { - g.SettingsDirtyTimer -= g.IO.DeltaTime; - if (g.SettingsDirtyTimer <= 0.0f) - { - if (g.IO.IniFilename != NULL) - SaveIniSettingsToDisk(g.IO.IniFilename); - else - g.IO.WantSaveIniSettings = true; // Let user know they can call SaveIniSettingsToMemory(). user will need to clear io.WantSaveIniSettings themselves. - g.SettingsDirtyTimer = 0.0f; - } - } - - g.Time += g.IO.DeltaTime; - g.FrameCount += 1; - g.TooltipOverrideCount = 0; - g.WindowsActiveCount = 0; - - SetCurrentFont(GetDefaultFont()); - IM_ASSERT(g.Font->IsLoaded()); - g.DrawListSharedData.ClipRectFullscreen = ImVec4(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y); - g.DrawListSharedData.CurveTessellationTol = g.Style.CurveTessellationTol; - - g.OverlayDrawList.Clear(); - g.OverlayDrawList.PushTextureID(g.IO.Fonts->TexID); - g.OverlayDrawList.PushClipRectFullScreen(); - g.OverlayDrawList.Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0); - - // Mark rendering data as invalid to prevent user who may have a handle on it to use it - g.DrawData.Clear(); - - // Clear reference to active widget if the widget isn't alive anymore - if (!g.HoveredIdPreviousFrame) - g.HoveredIdTimer = 0.0f; - g.HoveredIdPreviousFrame = g.HoveredId; - g.HoveredId = 0; - g.HoveredIdAllowOverlap = false; - if (!g.ActiveIdIsAlive && g.ActiveIdPreviousFrame == g.ActiveId && g.ActiveId != 0) - ClearActiveID(); - if (g.ActiveId) - g.ActiveIdTimer += g.IO.DeltaTime; - g.LastActiveIdTimer += g.IO.DeltaTime; - g.ActiveIdPreviousFrame = g.ActiveId; - g.ActiveIdPreviousFrameWindow = g.ActiveIdWindow; - g.ActiveIdPreviousFrameValueChanged = g.ActiveIdValueChanged; - g.ActiveIdIsAlive = g.ActiveIdPreviousFrameIsAlive = false; - g.ActiveIdIsJustActivated = false; - if (g.ScalarAsInputTextId && g.ActiveId != g.ScalarAsInputTextId) - g.ScalarAsInputTextId = 0; - - // Elapse drag & drop payload - if (g.DragDropActive && g.DragDropPayload.DataFrameCount + 1 < g.FrameCount) - { - ClearDragDrop(); - g.DragDropPayloadBufHeap.clear(); - memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal)); - } - g.DragDropAcceptIdPrev = g.DragDropAcceptIdCurr; - g.DragDropAcceptIdCurr = 0; - g.DragDropAcceptIdCurrRectSurface = FLT_MAX; - - // Update keyboard input state - memcpy(g.IO.KeysDownDurationPrev, g.IO.KeysDownDuration, sizeof(g.IO.KeysDownDuration)); - for (int i = 0; i < IM_ARRAYSIZE(g.IO.KeysDown); i++) - g.IO.KeysDownDuration[i] = g.IO.KeysDown[i] ? (g.IO.KeysDownDuration[i] < 0.0f ? 0.0f : g.IO.KeysDownDuration[i] + g.IO.DeltaTime) : -1.0f; - - // Update gamepad/keyboard directional navigation - NavUpdate(); - - // Update mouse input state - UpdateMouseInputs(); - - // Calculate frame-rate for the user, as a purely luxurious feature - g.FramerateSecPerFrameAccum += g.IO.DeltaTime - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx]; - g.FramerateSecPerFrame[g.FramerateSecPerFrameIdx] = g.IO.DeltaTime; - g.FramerateSecPerFrameIdx = (g.FramerateSecPerFrameIdx + 1) % IM_ARRAYSIZE(g.FramerateSecPerFrame); - g.IO.Framerate = (g.FramerateSecPerFrameAccum > 0.0f) ? (1.0f / (g.FramerateSecPerFrameAccum / (float)IM_ARRAYSIZE(g.FramerateSecPerFrame))) : FLT_MAX; - - // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering) - UpdateMovingWindow(); - NewFrameUpdateHoveredWindowAndCaptureFlags(); - - if (GetFrontMostPopupModal() != NULL) - g.ModalWindowDarkeningRatio = ImMin(g.ModalWindowDarkeningRatio + g.IO.DeltaTime * 6.0f, 1.0f); - else - g.ModalWindowDarkeningRatio = 0.0f; - - g.MouseCursor = ImGuiMouseCursor_Arrow; - g.WantCaptureMouseNextFrame = g.WantCaptureKeyboardNextFrame = g.WantTextInputNextFrame = -1; - g.PlatformImePos = ImVec2(1.0f, 1.0f); // OS Input Method Editor showing on top-left of our window by default - - // Mouse wheel scrolling, scale - if (g.HoveredWindow && !g.HoveredWindow->Collapsed && (g.IO.MouseWheel != 0.0f || g.IO.MouseWheelH != 0.0f)) - { - // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent (unless either ImGuiWindowFlags_NoInputs or ImGuiWindowFlags_NoScrollbar are also set). - ImGuiWindow* window = g.HoveredWindow; - ImGuiWindow* scroll_window = window; - while ((scroll_window->Flags & ImGuiWindowFlags_ChildWindow) && (scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoScrollbar) && !(scroll_window->Flags & ImGuiWindowFlags_NoInputs) && scroll_window->ParentWindow) - scroll_window = scroll_window->ParentWindow; - const bool scroll_allowed = !(scroll_window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(scroll_window->Flags & ImGuiWindowFlags_NoInputs); - - if (g.IO.MouseWheel != 0.0f) - { - if (g.IO.KeyCtrl && g.IO.FontAllowUserScaling) - { - // Zoom / Scale window - const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f); - const float scale = new_font_scale / window->FontWindowScale; - window->FontWindowScale = new_font_scale; - - const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size; - window->Pos += offset; - window->Size *= scale; - window->SizeFull *= scale; - } - else if (!g.IO.KeyCtrl && scroll_allowed) - { - // Mouse wheel vertical scrolling - float scroll_amount = 5 * scroll_window->CalcFontSize(); - scroll_amount = (float)(int)ImMin(scroll_amount, (scroll_window->ContentsRegionRect.GetHeight() + scroll_window->WindowPadding.y * 2.0f) * 0.67f); - SetWindowScrollY(scroll_window, scroll_window->Scroll.y - g.IO.MouseWheel * scroll_amount); - } - } - if (g.IO.MouseWheelH != 0.0f && scroll_allowed) - { - // Mouse wheel horizontal scrolling (for hardware that supports it) - float scroll_amount = scroll_window->CalcFontSize(); - if (!g.IO.KeyCtrl && !(window->Flags & ImGuiWindowFlags_NoScrollWithMouse)) - SetWindowScrollX(window, window->Scroll.x - g.IO.MouseWheelH * scroll_amount); - } - } - - // Pressing TAB activate widget focus - if (g.ActiveId == 0 && g.NavWindow != NULL && g.NavWindow->Active && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs) && !g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_Tab, false)) - { - if (g.NavId != 0 && g.NavIdTabCounter != INT_MAX) - g.NavWindow->FocusIdxTabRequestNext = g.NavIdTabCounter + 1 + (g.IO.KeyShift ? -1 : 1); - else - g.NavWindow->FocusIdxTabRequestNext = g.IO.KeyShift ? -1 : 0; - } - g.NavIdTabCounter = INT_MAX; - - // Mark all windows as not visible - for (int i = 0; i != g.Windows.Size; i++) - { - ImGuiWindow* window = g.Windows[i]; - window->WasActive = window->Active; - window->Active = false; - window->WriteAccessed = false; - } - - // Closing the focused window restore focus to the first active root window in descending z-order - if (g.NavWindow && !g.NavWindow->WasActive) - FocusFrontMostActiveWindow(NULL); - - // No window should be open at the beginning of the frame. - // But in order to allow the user to call NewFrame() multiple times without calling Render(), we are doing an explicit clear. - g.CurrentWindowStack.resize(0); - g.CurrentPopupStack.resize(0); - ClosePopupsOverWindow(g.NavWindow); - - // Create implicit window - we will only render it if the user has added something to it. - // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags. - SetNextWindowSize(ImVec2(400,400), ImGuiCond_FirstUseEver); - Begin("Debug##Default"); -} - -static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) -{ - ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHash(name, 0)); - if (!settings) - settings = AddWindowSettings(name); - return (void*)settings; -} - -static void SettingsHandlerWindow_ReadLine(ImGuiContext*, ImGuiSettingsHandler*, void* entry, const char* line) -{ - ImGuiWindowSettings* settings = (ImGuiWindowSettings*)entry; - float x, y; - int i; - if (sscanf(line, "Pos=%f,%f", &x, &y) == 2) settings->Pos = ImVec2(x, y); - else if (sscanf(line, "Size=%f,%f", &x, &y) == 2) settings->Size = ImMax(ImVec2(x, y), GImGui->Style.WindowMinSize); - else if (sscanf(line, "Collapsed=%d", &i) == 1) settings->Collapsed = (i != 0); -} - -static void SettingsHandlerWindow_WriteAll(ImGuiContext* imgui_ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* buf) -{ - // Gather data from windows that were active during this session - ImGuiContext& g = *imgui_ctx; - for (int i = 0; i != g.Windows.Size; i++) - { - ImGuiWindow* window = g.Windows[i]; - if (window->Flags & ImGuiWindowFlags_NoSavedSettings) - continue; - ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID); - if (!settings) - settings = AddWindowSettings(window->Name); - settings->Pos = window->Pos; - settings->Size = window->SizeFull; - settings->Collapsed = window->Collapsed; - } - - // Write a buffer - // If a window wasn't opened in this session we preserve its settings - buf->reserve(buf->size() + g.SettingsWindows.Size * 96); // ballpark reserve - for (int i = 0; i != g.SettingsWindows.Size; i++) - { - const ImGuiWindowSettings* settings = &g.SettingsWindows[i]; - if (settings->Pos.x == FLT_MAX) - continue; - const char* name = settings->Name; - if (const char* p = strstr(name, "###")) // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID() - name = p; - buf->appendf("[%s][%s]\n", handler->TypeName, name); - buf->appendf("Pos=%d,%d\n", (int)settings->Pos.x, (int)settings->Pos.y); - buf->appendf("Size=%d,%d\n", (int)settings->Size.x, (int)settings->Size.y); - buf->appendf("Collapsed=%d\n", settings->Collapsed); - buf->appendf("\n"); - } -} - -void ImGui::Initialize(ImGuiContext* context) -{ - ImGuiContext& g = *context; - IM_ASSERT(!g.Initialized && !g.SettingsLoaded); - - // Add .ini handle for ImGuiWindow type - ImGuiSettingsHandler ini_handler; - ini_handler.TypeName = "Window"; - ini_handler.TypeHash = ImHash("Window", 0, 0); - ini_handler.ReadOpenFn = SettingsHandlerWindow_ReadOpen; - ini_handler.ReadLineFn = SettingsHandlerWindow_ReadLine; - ini_handler.WriteAllFn = SettingsHandlerWindow_WriteAll; - g.SettingsHandlers.push_front(ini_handler); - - g.Initialized = true; -} - -// This function is merely here to free heap allocations. -void ImGui::Shutdown(ImGuiContext* context) -{ - // The fonts atlas can be used prior to calling NewFrame(), so we clear it even if g.Initialized is FALSE (which would happen if we never called NewFrame) - ImGuiContext& g = *context; - if (g.IO.Fonts && g.FontAtlasOwnedByContext) - IM_DELETE(g.IO.Fonts); - g.IO.Fonts = NULL; - - // Cleanup of other data are conditional on actually having initialized ImGui. - if (!g.Initialized) - return; - - // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file) - if (g.SettingsLoaded && g.IO.IniFilename != NULL) - SaveIniSettingsToDisk(g.IO.IniFilename); - - // Clear everything else - for (int i = 0; i < g.Windows.Size; i++) - IM_DELETE(g.Windows[i]); - g.Windows.clear(); - g.WindowsSortBuffer.clear(); - g.CurrentWindow = NULL; - g.CurrentWindowStack.clear(); - g.WindowsById.Clear(); - g.NavWindow = NULL; - g.HoveredWindow = NULL; - g.HoveredRootWindow = NULL; - g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL; - g.MovingWindow = NULL; - g.ColorModifiers.clear(); - g.StyleModifiers.clear(); - g.FontStack.clear(); - g.OpenPopupStack.clear(); - g.CurrentPopupStack.clear(); - g.DrawDataBuilder.ClearFreeMemory(); - g.OverlayDrawList.ClearFreeMemory(); - g.PrivateClipboard.clear(); - g.InputTextState.Text.clear(); - g.InputTextState.InitialText.clear(); - g.InputTextState.TempTextBuffer.clear(); - - for (int i = 0; i < g.SettingsWindows.Size; i++) - IM_DELETE(g.SettingsWindows[i].Name); - g.SettingsWindows.clear(); - g.SettingsHandlers.clear(); - - if (g.LogFile && g.LogFile != stdout) - { - fclose(g.LogFile); - g.LogFile = NULL; - } - g.LogClipboard.clear(); - - g.Initialized = false; -} - -ImGuiWindowSettings* ImGui::FindWindowSettings(ImGuiID id) -{ - ImGuiContext& g = *GImGui; - for (int i = 0; i != g.SettingsWindows.Size; i++) - if (g.SettingsWindows[i].Id == id) - return &g.SettingsWindows[i]; - return NULL; -} - -static ImGuiWindowSettings* AddWindowSettings(const char* name) -{ - ImGuiContext& g = *GImGui; - g.SettingsWindows.push_back(ImGuiWindowSettings()); - ImGuiWindowSettings* settings = &g.SettingsWindows.back(); - settings->Name = ImStrdup(name); - settings->Id = ImHash(name, 0); - return settings; -} - -void ImGui::LoadIniSettingsFromDisk(const char* ini_filename) -{ - size_t file_data_size = 0; - char* file_data = (char*)ImFileLoadToMemory(ini_filename, "rb", &file_data_size); - if (!file_data) - return; - LoadIniSettingsFromMemory(file_data, (size_t)file_data_size); - ImGui::MemFree(file_data); -} - -ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name) -{ - ImGuiContext& g = *GImGui; - const ImGuiID type_hash = ImHash(type_name, 0, 0); - for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) - if (g.SettingsHandlers[handler_n].TypeHash == type_hash) - return &g.SettingsHandlers[handler_n]; - return NULL; -} - -// Zero-tolerance, no error reporting, cheap .ini parsing -void ImGui::LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.Initialized); - IM_ASSERT(g.SettingsLoaded == false && g.FrameCount == 0); - - // For user convenience, we allow passing a non zero-terminated string (hence the ini_size parameter). - // For our convenience and to make the code simpler, we'll also write zero-terminators within the buffer. So let's create a writable copy.. - if (ini_size == 0) - ini_size = strlen(ini_data); - char* buf = (char*)ImGui::MemAlloc(ini_size + 1); - char* buf_end = buf + ini_size; - memcpy(buf, ini_data, ini_size); - buf[ini_size] = 0; - - void* entry_data = NULL; - ImGuiSettingsHandler* entry_handler = NULL; - - char* line_end = NULL; - for (char* line = buf; line < buf_end; line = line_end + 1) - { - // Skip new lines markers, then find end of the line - while (*line == '\n' || *line == '\r') - line++; - line_end = line; - while (line_end < buf_end && *line_end != '\n' && *line_end != '\r') - line_end++; - line_end[0] = 0; - - if (line[0] == '[' && line_end > line && line_end[-1] == ']') - { - // Parse "[Type][Name]". Note that 'Name' can itself contains [] characters, which is acceptable with the current format and parsing code. - line_end[-1] = 0; - const char* name_end = line_end - 1; - const char* type_start = line + 1; - char* type_end = (char*)(intptr_t)ImStrchrRange(type_start, name_end, ']'); - const char* name_start = type_end ? ImStrchrRange(type_end + 1, name_end, '[') : NULL; - if (!type_end || !name_start) - { - name_start = type_start; // Import legacy entries that have no type - type_start = "Window"; - } - else - { - *type_end = 0; // Overwrite first ']' - name_start++; // Skip second '[' - } - entry_handler = FindSettingsHandler(type_start); - entry_data = entry_handler ? entry_handler->ReadOpenFn(&g, entry_handler, name_start) : NULL; - } - else if (entry_handler != NULL && entry_data != NULL) - { - // Let type handler parse the line - entry_handler->ReadLineFn(&g, entry_handler, entry_data, line); - } - } - ImGui::MemFree(buf); - g.SettingsLoaded = true; -} - -void ImGui::SaveIniSettingsToDisk(const char* ini_filename) -{ - ImGuiContext& g = *GImGui; - g.SettingsDirtyTimer = 0.0f; - if (!ini_filename) - return; - - size_t ini_data_size = 0; - const char* ini_data = SaveIniSettingsToMemory(&ini_data_size); - FILE* f = ImFileOpen(ini_filename, "wt"); - if (!f) - return; - fwrite(ini_data, sizeof(char), ini_data_size, f); - fclose(f); -} - -// Call registered handlers (e.g. SettingsHandlerWindow_WriteAll() + custom handlers) to write their stuff into a text buffer -const char* ImGui::SaveIniSettingsToMemory(size_t* out_size) -{ - ImGuiContext& g = *GImGui; - g.SettingsDirtyTimer = 0.0f; - g.SettingsIniData.Buf.resize(0); - g.SettingsIniData.Buf.push_back(0); - for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) - { - ImGuiSettingsHandler* handler = &g.SettingsHandlers[handler_n]; - handler->WriteAllFn(&g, handler, &g.SettingsIniData); - } - if (out_size) - *out_size = (size_t)g.SettingsIniData.size(); - return g.SettingsIniData.c_str(); -} - -void ImGui::MarkIniSettingsDirty() -{ - ImGuiContext& g = *GImGui; - if (g.SettingsDirtyTimer <= 0.0f) - g.SettingsDirtyTimer = g.IO.IniSavingRate; -} - -void ImGui::MarkIniSettingsDirty(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - if (!(window->Flags & ImGuiWindowFlags_NoSavedSettings)) - if (g.SettingsDirtyTimer <= 0.0f) - g.SettingsDirtyTimer = g.IO.IniSavingRate; -} - -// FIXME: Add a more explicit sort order in the window structure. -static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs) -{ - const ImGuiWindow* const a = *(const ImGuiWindow* const *)lhs; - const ImGuiWindow* const b = *(const ImGuiWindow* const *)rhs; - if (int d = (a->Flags & ImGuiWindowFlags_Popup) - (b->Flags & ImGuiWindowFlags_Popup)) - return d; - if (int d = (a->Flags & ImGuiWindowFlags_Tooltip) - (b->Flags & ImGuiWindowFlags_Tooltip)) - return d; - return (a->BeginOrderWithinParent - b->BeginOrderWithinParent); -} - -static void AddWindowToSortedBuffer(ImVector* out_sorted_windows, ImGuiWindow* window) -{ - out_sorted_windows->push_back(window); - if (window->Active) - { - int count = window->DC.ChildWindows.Size; - if (count > 1) - qsort(window->DC.ChildWindows.begin(), (size_t)count, sizeof(ImGuiWindow*), ChildWindowComparer); - for (int i = 0; i < count; i++) - { - ImGuiWindow* child = window->DC.ChildWindows[i]; - if (child->Active) - AddWindowToSortedBuffer(out_sorted_windows, child); - } - } -} - -static void AddDrawListToDrawData(ImVector* out_list, ImDrawList* draw_list) -{ - if (draw_list->CmdBuffer.empty()) - return; - - // Remove trailing command if unused - ImDrawCmd& last_cmd = draw_list->CmdBuffer.back(); - if (last_cmd.ElemCount == 0 && last_cmd.UserCallback == NULL) - { - draw_list->CmdBuffer.pop_back(); - if (draw_list->CmdBuffer.empty()) - return; - } - - // Draw list sanity check. Detect mismatch between PrimReserve() calls and incrementing _VtxCurrentIdx, _VtxWritePtr etc. May trigger for you if you are using PrimXXX functions incorrectly. - IM_ASSERT(draw_list->VtxBuffer.Size == 0 || draw_list->_VtxWritePtr == draw_list->VtxBuffer.Data + draw_list->VtxBuffer.Size); - IM_ASSERT(draw_list->IdxBuffer.Size == 0 || draw_list->_IdxWritePtr == draw_list->IdxBuffer.Data + draw_list->IdxBuffer.Size); - IM_ASSERT((int)draw_list->_VtxCurrentIdx == draw_list->VtxBuffer.Size); - - // Check that draw_list doesn't use more vertices than indexable (default ImDrawIdx = unsigned short = 2 bytes = 64K vertices per ImDrawList = per window) - // If this assert triggers because you are drawing lots of stuff manually: - // A) Make sure you are coarse clipping, because ImDrawList let all your vertices pass. You can use the Metrics window to inspect draw list contents. - // B) If you need/want meshes with more than 64K vertices, uncomment the '#define ImDrawIdx unsigned int' line in imconfig.h to set the index size to 4 bytes. - // You'll need to handle the 4-bytes indices to your renderer. For example, the OpenGL example code detect index size at compile-time by doing: - // glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset); - // Your own engine or render API may use different parameters or function calls to specify index sizes. 2 and 4 bytes indices are generally supported by most API. - // C) If for some reason you cannot use 4 bytes indices or don't want to, a workaround is to call BeginChild()/EndChild() before reaching the 64K limit to split your draw commands in multiple draw lists. - if (sizeof(ImDrawIdx) == 2) - IM_ASSERT(draw_list->_VtxCurrentIdx < (1 << 16) && "Too many vertices in ImDrawList using 16-bit indices. Read comment above"); - - out_list->push_back(draw_list); -} - -static void AddWindowToDrawData(ImVector* out_render_list, ImGuiWindow* window) -{ - AddDrawListToDrawData(out_render_list, window->DrawList); - for (int i = 0; i < window->DC.ChildWindows.Size; i++) - { - ImGuiWindow* child = window->DC.ChildWindows[i]; - if (child->Active && child->HiddenFrames == 0) // clipped children may have been marked not active - AddWindowToDrawData(out_render_list, child); - } -} - -static void AddWindowToDrawDataSelectLayer(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - g.IO.MetricsActiveWindows++; - if (window->Flags & ImGuiWindowFlags_Tooltip) - AddWindowToDrawData(&g.DrawDataBuilder.Layers[1], window); - else - AddWindowToDrawData(&g.DrawDataBuilder.Layers[0], window); -} - -void ImDrawDataBuilder::FlattenIntoSingleLayer() -{ - int n = Layers[0].Size; - int size = n; - for (int i = 1; i < IM_ARRAYSIZE(Layers); i++) - size += Layers[i].Size; - Layers[0].resize(size); - for (int layer_n = 1; layer_n < IM_ARRAYSIZE(Layers); layer_n++) - { - ImVector& layer = Layers[layer_n]; - if (layer.empty()) - continue; - memcpy(&Layers[0][n], &layer[0], layer.Size * sizeof(ImDrawList*)); - n += layer.Size; - layer.resize(0); - } -} - -static void SetupDrawData(ImVector* draw_lists, ImDrawData* out_draw_data) -{ - ImGuiIO& io = ImGui::GetIO(); - out_draw_data->Valid = true; - out_draw_data->CmdLists = (draw_lists->Size > 0) ? draw_lists->Data : NULL; - out_draw_data->CmdListsCount = draw_lists->Size; - out_draw_data->TotalVtxCount = out_draw_data->TotalIdxCount = 0; - out_draw_data->DisplayPos = ImVec2(0.0f, 0.0f); - out_draw_data->DisplaySize = io.DisplaySize; - for (int n = 0; n < draw_lists->Size; n++) - { - out_draw_data->TotalVtxCount += draw_lists->Data[n]->VtxBuffer.Size; - out_draw_data->TotalIdxCount += draw_lists->Data[n]->IdxBuffer.Size; - } -} - -// When using this function it is sane to ensure that float are perfectly rounded to integer values, to that e.g. (int)(max.x-min.x) in user's render produce correct result. -void ImGui::PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect) -{ - ImGuiWindow* window = GetCurrentWindow(); - window->DrawList->PushClipRect(clip_rect_min, clip_rect_max, intersect_with_current_clip_rect); - window->ClipRect = window->DrawList->_ClipRectStack.back(); -} - -void ImGui::PopClipRect() -{ - ImGuiWindow* window = GetCurrentWindow(); - window->DrawList->PopClipRect(); - window->ClipRect = window->DrawList->_ClipRectStack.back(); -} - -// This is normally called by Render(). You may want to call it directly if you want to avoid calling Render() but the gain will be very minimal. -void ImGui::EndFrame() -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.Initialized); // Forgot to call ImGui::NewFrame() - if (g.FrameCountEnded == g.FrameCount) // Don't process EndFrame() multiple times. - return; - - // Notify OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME) - if ( - g.FrameCountEnded == -1 || // [Ben Li & Bruno Levy], avoids an FPE because PlatformImeLastPos is not initialized - (g.IO.ImeSetInputScreenPosFn && ImLengthSqr(g.PlatformImeLastPos - g.PlatformImePos) > 0.0001f) - ) { - g.IO.ImeSetInputScreenPosFn((int)g.PlatformImePos.x, (int)g.PlatformImePos.y); - g.PlatformImeLastPos = g.PlatformImePos; - } - - // Hide implicit "Debug" window if it hasn't been used - IM_ASSERT(g.CurrentWindowStack.Size == 1); // Mismatched Begin()/End() calls, did you forget to call end on g.CurrentWindow->Name? - if (g.CurrentWindow && !g.CurrentWindow->WriteAccessed) - g.CurrentWindow->Active = false; - End(); - - if (g.ActiveId == 0 && g.HoveredId == 0) - { - if (!g.NavWindow || !g.NavWindow->Appearing) // Unless we just made a window/popup appear - { - // Click to focus window and start moving (after we're done with all our widgets) - if (g.IO.MouseClicked[0]) - { - if (g.HoveredRootWindow != NULL) - { - // Set ActiveId even if the _NoMove flag is set, without it dragging away from a window with _NoMove would activate hover on other windows. - FocusWindow(g.HoveredWindow); - SetActiveID(g.HoveredWindow->MoveId, g.HoveredWindow); - g.NavDisableHighlight = true; - g.ActiveIdClickOffset = g.IO.MousePos - g.HoveredRootWindow->Pos; - if (!(g.HoveredWindow->Flags & ImGuiWindowFlags_NoMove) && !(g.HoveredRootWindow->Flags & ImGuiWindowFlags_NoMove)) - g.MovingWindow = g.HoveredWindow; - } - else if (g.NavWindow != NULL && GetFrontMostPopupModal() == NULL) - { - // Clicking on void disable focus - FocusWindow(NULL); - } - } - - // With right mouse button we close popups without changing focus - // (The left mouse button path calls FocusWindow which will lead NewFrame->ClosePopupsOverWindow to trigger) - if (g.IO.MouseClicked[1]) - { - // Find the top-most window between HoveredWindow and the front most Modal Window. - // This is where we can trim the popup stack. - ImGuiWindow* modal = GetFrontMostPopupModal(); - bool hovered_window_above_modal = false; - if (modal == NULL) - hovered_window_above_modal = true; - for (int i = g.Windows.Size - 1; i >= 0 && hovered_window_above_modal == false; i--) - { - ImGuiWindow* window = g.Windows[i]; - if (window == modal) - break; - if (window == g.HoveredWindow) - hovered_window_above_modal = true; - } - ClosePopupsOverWindow(hovered_window_above_modal ? g.HoveredWindow : modal); - } - } - } - - // Sort the window list so that all child windows are after their parent - // We cannot do that on FocusWindow() because childs may not exist yet - g.WindowsSortBuffer.resize(0); - g.WindowsSortBuffer.reserve(g.Windows.Size); - for (int i = 0; i != g.Windows.Size; i++) - { - ImGuiWindow* window = g.Windows[i]; - if (window->Active && (window->Flags & ImGuiWindowFlags_ChildWindow)) // if a child is active its parent will add it - continue; - AddWindowToSortedBuffer(&g.WindowsSortBuffer, window); - } - - IM_ASSERT(g.Windows.Size == g.WindowsSortBuffer.Size); // we done something wrong - g.Windows.swap(g.WindowsSortBuffer); - - // Clear Input data for next frame - g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f; - memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters)); - memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs)); - - g.FrameCountEnded = g.FrameCount; -} - -void ImGui::Render() -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.Initialized); // Forgot to call ImGui::NewFrame() - - if (g.FrameCountEnded != g.FrameCount) - ImGui::EndFrame(); - g.FrameCountRendered = g.FrameCount; - - // Gather windows to render - g.IO.MetricsRenderVertices = g.IO.MetricsRenderIndices = g.IO.MetricsActiveWindows = 0; - g.DrawDataBuilder.Clear(); - ImGuiWindow* window_to_render_front_most = (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) ? g.NavWindowingTarget : NULL; - for (int n = 0; n != g.Windows.Size; n++) - { - ImGuiWindow* window = g.Windows[n]; - if (window->Active && window->HiddenFrames == 0 && (window->Flags & ImGuiWindowFlags_ChildWindow) == 0 && window != window_to_render_front_most) - AddWindowToDrawDataSelectLayer(window); - } - if (window_to_render_front_most && window_to_render_front_most->Active && window_to_render_front_most->HiddenFrames == 0) // NavWindowingTarget is always temporarily displayed as the front-most window - AddWindowToDrawDataSelectLayer(window_to_render_front_most); - g.DrawDataBuilder.FlattenIntoSingleLayer(); - - // Draw software mouse cursor if requested - ImVec2 offset, size, uv[4]; - if (g.IO.MouseDrawCursor && g.IO.Fonts->GetMouseCursorTexData(g.MouseCursor, &offset, &size, &uv[0], &uv[2])) - { - const ImVec2 pos = g.IO.MousePos - offset; - const ImTextureID tex_id = g.IO.Fonts->TexID; - const float sc = g.Style.MouseCursorScale; - g.OverlayDrawList.PushTextureID(tex_id); - g.OverlayDrawList.AddImage(tex_id, pos + ImVec2(1,0)*sc, pos+ImVec2(1,0)*sc + size*sc, uv[2], uv[3], IM_COL32(0,0,0,48)); // Shadow - g.OverlayDrawList.AddImage(tex_id, pos + ImVec2(2,0)*sc, pos+ImVec2(2,0)*sc + size*sc, uv[2], uv[3], IM_COL32(0,0,0,48)); // Shadow - g.OverlayDrawList.AddImage(tex_id, pos, pos + size*sc, uv[2], uv[3], IM_COL32(0,0,0,255)); // Black border - g.OverlayDrawList.AddImage(tex_id, pos, pos + size*sc, uv[0], uv[1], IM_COL32(255,255,255,255)); // White fill - g.OverlayDrawList.PopTextureID(); - } - if (!g.OverlayDrawList.VtxBuffer.empty()) - AddDrawListToDrawData(&g.DrawDataBuilder.Layers[0], &g.OverlayDrawList); - - // Setup ImDrawData structure for end-user - SetupDrawData(&g.DrawDataBuilder.Layers[0], &g.DrawData); - g.IO.MetricsRenderVertices = g.DrawData.TotalVtxCount; - g.IO.MetricsRenderIndices = g.DrawData.TotalIdxCount; - - // Render. If user hasn't set a callback then they may retrieve the draw data via GetDrawData() -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - if (g.DrawData.CmdListsCount > 0 && g.IO.RenderDrawListsFn != NULL) - g.IO.RenderDrawListsFn(&g.DrawData); -#endif -} - -const char* ImGui::FindRenderedTextEnd(const char* text, const char* text_end) -{ - const char* text_display_end = text; - if (!text_end) - text_end = (const char*)-1; - - while (text_display_end < text_end && *text_display_end != '\0' && (text_display_end[0] != '#' || text_display_end[1] != '#')) - text_display_end++; - return text_display_end; -} - -// Pass text data straight to log (without being displayed) -void ImGui::LogText(const char* fmt, ...) -{ - ImGuiContext& g = *GImGui; - if (!g.LogEnabled) - return; - - va_list args; - va_start(args, fmt); - if (g.LogFile) - vfprintf(g.LogFile, fmt, args); - else - g.LogClipboard.appendfv(fmt, args); - va_end(args); -} - -// Internal version that takes a position to decide on newline placement and pad items according to their depth. -// We split text into individual lines to add current tree level padding -static void LogRenderedText(const ImVec2* ref_pos, const char* text, const char* text_end = NULL) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - if (!text_end) - text_end = ImGui::FindRenderedTextEnd(text, text_end); - - const bool log_new_line = ref_pos && (ref_pos->y > window->DC.LogLinePosY + 1); - if (ref_pos) - window->DC.LogLinePosY = ref_pos->y; - - const char* text_remaining = text; - if (g.LogStartDepth > window->DC.TreeDepth) // Re-adjust padding if we have popped out of our starting depth - g.LogStartDepth = window->DC.TreeDepth; - const int tree_depth = (window->DC.TreeDepth - g.LogStartDepth); - for (;;) - { - // Split the string. Each new line (after a '\n') is followed by spacing corresponding to the current depth of our log entry. - const char* line_end = text_remaining; - while (line_end < text_end) - if (*line_end == '\n') - break; - else - line_end++; - if (line_end >= text_end) - line_end = NULL; - - const bool is_first_line = (text == text_remaining); - bool is_last_line = false; - if (line_end == NULL) - { - is_last_line = true; - line_end = text_end; - } - if (line_end != NULL && !(is_last_line && (line_end - text_remaining)==0)) - { - const int char_count = (int)(line_end - text_remaining); - if (log_new_line || !is_first_line) - ImGui::LogText(IM_NEWLINE "%*s%.*s", tree_depth*4, "", char_count, text_remaining); - else - ImGui::LogText(" %.*s", char_count, text_remaining); - } - - if (is_last_line) - break; - text_remaining = line_end + 1; - } -} - -// Internal ImGui functions to render text -// RenderText***() functions calls ImDrawList::AddText() calls ImBitmapFont::RenderText() -void ImGui::RenderText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - // Hide anything after a '##' string - const char* text_display_end; - if (hide_text_after_hash) - { - text_display_end = FindRenderedTextEnd(text, text_end); - } - else - { - if (!text_end) - text_end = text + strlen(text); // FIXME-OPT - text_display_end = text_end; - } - - if (text != text_display_end) - { - window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end); - if (g.LogEnabled) - LogRenderedText(&pos, text, text_display_end); - } -} - -void ImGui::RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - if (!text_end) - text_end = text + strlen(text); // FIXME-OPT - - if (text != text_end) - { - window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width); - if (g.LogEnabled) - LogRenderedText(&pos, text, text_end); - } -} - -// Default clip_rect uses (pos_min,pos_max) -// Handle clipping on CPU immediately (vs typically let the GPU clip the triangles that are overlapping the clipping rectangle edges) -void ImGui::RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align, const ImRect* clip_rect) -{ - // Hide anything after a '##' string - const char* text_display_end = FindRenderedTextEnd(text, text_end); - const int text_len = (int)(text_display_end - text); - if (text_len == 0) - return; - - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - // Perform CPU side clipping for single clipped element to avoid using scissor state - ImVec2 pos = pos_min; - const ImVec2 text_size = text_size_if_known ? *text_size_if_known : CalcTextSize(text, text_display_end, false, 0.0f); - - const ImVec2* clip_min = clip_rect ? &clip_rect->Min : &pos_min; - const ImVec2* clip_max = clip_rect ? &clip_rect->Max : &pos_max; - bool need_clipping = (pos.x + text_size.x >= clip_max->x) || (pos.y + text_size.y >= clip_max->y); - if (clip_rect) // If we had no explicit clipping rectangle then pos==clip_min - need_clipping |= (pos.x < clip_min->x) || (pos.y < clip_min->y); - - // Align whole block. We should defer that to the better rendering function when we'll have support for individual line alignment. - if (align.x > 0.0f) pos.x = ImMax(pos.x, pos.x + (pos_max.x - pos.x - text_size.x) * align.x); - if (align.y > 0.0f) pos.y = ImMax(pos.y, pos.y + (pos_max.y - pos.y - text_size.y) * align.y); - - // Render - if (need_clipping) - { - ImVec4 fine_clip_rect(clip_min->x, clip_min->y, clip_max->x, clip_max->y); - window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, &fine_clip_rect); - } - else - { - window->DrawList->AddText(g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end, 0.0f, NULL); - } - if (g.LogEnabled) - LogRenderedText(&pos, text, text_display_end); -} - -// Render a rectangle shaped with optional rounding and borders -void ImGui::RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border, float rounding) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - window->DrawList->AddRectFilled(p_min, p_max, fill_col, rounding); - const float border_size = g.Style.FrameBorderSize; - if (border && border_size > 0.0f) - { - window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size); - window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size); - } -} - -void ImGui::RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - const float border_size = g.Style.FrameBorderSize; - if (border_size > 0.0f) - { - window->DrawList->AddRect(p_min+ImVec2(1,1), p_max+ImVec2(1,1), GetColorU32(ImGuiCol_BorderShadow), rounding, ImDrawCornerFlags_All, border_size); - window->DrawList->AddRect(p_min, p_max, GetColorU32(ImGuiCol_Border), rounding, ImDrawCornerFlags_All, border_size); - } -} - -// Render a triangle to denote expanded/collapsed state -void ImGui::RenderArrow(ImVec2 p_min, ImGuiDir dir, float scale) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - const float h = g.FontSize * 1.00f; - float r = h * 0.40f * scale; - ImVec2 center = p_min + ImVec2(h * 0.50f, h * 0.50f * scale); - - ImVec2 a, b, c; - switch (dir) - { - case ImGuiDir_Up: - case ImGuiDir_Down: - if (dir == ImGuiDir_Up) r = -r; - center.y -= r * 0.25f; - a = ImVec2(0,1) * r; - b = ImVec2(-0.866f,-0.5f) * r; - c = ImVec2(+0.866f,-0.5f) * r; - break; - case ImGuiDir_Left: - case ImGuiDir_Right: - if (dir == ImGuiDir_Left) r = -r; - center.x -= r * 0.25f; - a = ImVec2(1,0) * r; - b = ImVec2(-0.500f,+0.866f) * r; - c = ImVec2(-0.500f,-0.866f) * r; - break; - case ImGuiDir_None: - case ImGuiDir_COUNT: - IM_ASSERT(0); - break; - } - - window->DrawList->AddTriangleFilled(center + a, center + b, center + c, GetColorU32(ImGuiCol_Text)); -} - -void ImGui::RenderBullet(ImVec2 pos) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - window->DrawList->AddCircleFilled(pos, GImGui->FontSize*0.20f, GetColorU32(ImGuiCol_Text), 8); -} - -void ImGui::RenderCheckMark(ImVec2 pos, ImU32 col, float sz) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - float thickness = ImMax(sz / 5.0f, 1.0f); - sz -= thickness*0.5f; - pos += ImVec2(thickness*0.25f, thickness*0.25f); - - float third = sz / 3.0f; - float bx = pos.x + third; - float by = pos.y + sz - third*0.5f; - window->DrawList->PathLineTo(ImVec2(bx - third, by - third)); - window->DrawList->PathLineTo(ImVec2(bx, by)); - window->DrawList->PathLineTo(ImVec2(bx + third*2, by - third*2)); - window->DrawList->PathStroke(col, false, thickness); -} - -void ImGui::RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags) -{ - ImGuiContext& g = *GImGui; - if (id != g.NavId) - return; - if (g.NavDisableHighlight && !(flags & ImGuiNavHighlightFlags_AlwaysDraw)) - return; - ImGuiWindow* window = ImGui::GetCurrentWindow(); - if (window->DC.NavHideHighlightOneFrame) - return; - - float rounding = (flags & ImGuiNavHighlightFlags_NoRounding) ? 0.0f : g.Style.FrameRounding; - ImRect display_rect = bb; - display_rect.ClipWith(window->ClipRect); - if (flags & ImGuiNavHighlightFlags_TypeDefault) - { - const float THICKNESS = 2.0f; - const float DISTANCE = 3.0f + THICKNESS * 0.5f; - display_rect.Expand(ImVec2(DISTANCE,DISTANCE)); - bool fully_visible = window->ClipRect.Contains(display_rect); - if (!fully_visible) - window->DrawList->PushClipRect(display_rect.Min, display_rect.Max); - window->DrawList->AddRect(display_rect.Min + ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), display_rect.Max - ImVec2(THICKNESS*0.5f,THICKNESS*0.5f), GetColorU32(ImGuiCol_NavHighlight), rounding, ImDrawCornerFlags_All, THICKNESS); - if (!fully_visible) - window->DrawList->PopClipRect(); - } - if (flags & ImGuiNavHighlightFlags_TypeThin) - { - window->DrawList->AddRect(display_rect.Min, display_rect.Max, GetColorU32(ImGuiCol_NavHighlight), rounding, ~0, 1.0f); - } -} - -// Calculate text size. Text can be multi-line. Optionally ignore text after a ## marker. -// CalcTextSize("") should return ImVec2(0.0f, GImGui->FontSize) -ImVec2 ImGui::CalcTextSize(const char* text, const char* text_end, bool hide_text_after_double_hash, float wrap_width) -{ - ImGuiContext& g = *GImGui; - - const char* text_display_end; - if (hide_text_after_double_hash) - text_display_end = FindRenderedTextEnd(text, text_end); // Hide anything after a '##' string - else - text_display_end = text_end; - - ImFont* font = g.Font; - const float font_size = g.FontSize; - if (text == text_display_end) - return ImVec2(0.0f, font_size); - ImVec2 text_size = font->CalcTextSizeA(font_size, FLT_MAX, wrap_width, text, text_display_end, NULL); - - // Cancel out character spacing for the last character of a line (it is baked into glyph->AdvanceX field) - const float font_scale = font_size / font->FontSize; - const float character_spacing_x = 1.0f * font_scale; - if (text_size.x > 0.0f) - text_size.x -= character_spacing_x; - text_size.x = (float)(int)(text_size.x + 0.95f); - - return text_size; -} - -// Helper to calculate coarse clipping of large list of evenly sized items. -// NB: Prefer using the ImGuiListClipper higher-level helper if you can! Read comments and instructions there on how those use this sort of pattern. -// NB: 'items_count' is only used to clamp the result, if you don't know your count you can use INT_MAX -void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (g.LogEnabled) - { - // If logging is active, do not perform any clipping - *out_items_display_start = 0; - *out_items_display_end = items_count; - return; - } - if (window->SkipItems) - { - *out_items_display_start = *out_items_display_end = 0; - return; - } - - // We create the union of the ClipRect and the NavScoringRect which at worst should be 1 page away from ClipRect - ImRect unclipped_rect = window->ClipRect; - if (g.NavMoveRequest) - unclipped_rect.Add(g.NavScoringRectScreen); - - const ImVec2 pos = window->DC.CursorPos; - int start = (int)((unclipped_rect.Min.y - pos.y) / items_height); - int end = (int)((unclipped_rect.Max.y - pos.y) / items_height); - - // When performing a navigation request, ensure we have one item extra in the direction we are moving to - if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Up) - start--; - if (g.NavMoveRequest && g.NavMoveClipDir == ImGuiDir_Down) - end++; - - start = ImClamp(start, 0, items_count); - end = ImClamp(end + 1, start, items_count); - *out_items_display_start = start; - *out_items_display_end = end; -} - -// Find window given position, search front-to-back -// FIXME: Note that we have a lag here because WindowRectClipped is updated in Begin() so windows moved by user via SetWindowPos() and not SetNextWindowPos() will have that rectangle lagging by a frame at the time FindHoveredWindow() is called, aka before the next Begin(). Moving window thankfully isn't affected. -static ImGuiWindow* FindHoveredWindow() -{ - ImGuiContext& g = *GImGui; - for (int i = g.Windows.Size - 1; i >= 0; i--) - { - ImGuiWindow* window = g.Windows[i]; - if (!window->Active) - continue; - if (window->Flags & ImGuiWindowFlags_NoInputs) - continue; - - // Using the clipped AABB, a child window will typically be clipped by its parent (not always) - ImRect bb(window->OuterRectClipped.Min - g.Style.TouchExtraPadding, window->OuterRectClipped.Max + g.Style.TouchExtraPadding); - if (bb.Contains(g.IO.MousePos)) - return window; - } - return NULL; -} - -// Test if mouse cursor is hovering given rectangle -// NB- Rectangle is clipped by our current clip setting -// NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding) -bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip) -{ - ImGuiContext& g = *GImGui; - - // Clip - ImRect rect_clipped(r_min, r_max); - if (clip) - rect_clipped.ClipWith(g.CurrentWindow->ClipRect); - - // Expand for touch input - const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding); - return rect_for_touch.Contains(g.IO.MousePos); -} - -static bool IsKeyPressedMap(ImGuiKey key, bool repeat) -{ - const int key_index = GImGui->IO.KeyMap[key]; - return (key_index >= 0) ? ImGui::IsKeyPressed(key_index, repeat) : false; -} - -int ImGui::GetKeyIndex(ImGuiKey imgui_key) -{ - IM_ASSERT(imgui_key >= 0 && imgui_key < ImGuiKey_COUNT); - return GImGui->IO.KeyMap[imgui_key]; -} - -// Note that imgui doesn't know the semantic of each entry of io.KeysDown[]. Use your own indices/enums according to how your back-end/engine stored them into io.KeysDown[]! -bool ImGui::IsKeyDown(int user_key_index) -{ - if (user_key_index < 0) return false; - IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(GImGui->IO.KeysDown)); - return GImGui->IO.KeysDown[user_key_index]; -} - -int ImGui::CalcTypematicPressedRepeatAmount(float t, float t_prev, float repeat_delay, float repeat_rate) -{ - if (t == 0.0f) - return 1; - if (t <= repeat_delay || repeat_rate <= 0.0f) - return 0; - const int count = (int)((t - repeat_delay) / repeat_rate) - (int)((t_prev - repeat_delay) / repeat_rate); - return (count > 0) ? count : 0; -} - -int ImGui::GetKeyPressedAmount(int key_index, float repeat_delay, float repeat_rate) -{ - ImGuiContext& g = *GImGui; - if (key_index < 0) return false; - IM_ASSERT(key_index >= 0 && key_index < IM_ARRAYSIZE(g.IO.KeysDown)); - const float t = g.IO.KeysDownDuration[key_index]; - return CalcTypematicPressedRepeatAmount(t, t - g.IO.DeltaTime, repeat_delay, repeat_rate); -} - -bool ImGui::IsKeyPressed(int user_key_index, bool repeat) -{ - ImGuiContext& g = *GImGui; - if (user_key_index < 0) return false; - IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown)); - const float t = g.IO.KeysDownDuration[user_key_index]; - if (t == 0.0f) - return true; - if (repeat && t > g.IO.KeyRepeatDelay) - return GetKeyPressedAmount(user_key_index, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0; - return false; -} - -bool ImGui::IsKeyReleased(int user_key_index) -{ - ImGuiContext& g = *GImGui; - if (user_key_index < 0) return false; - IM_ASSERT(user_key_index >= 0 && user_key_index < IM_ARRAYSIZE(g.IO.KeysDown)); - return g.IO.KeysDownDurationPrev[user_key_index] >= 0.0f && !g.IO.KeysDown[user_key_index]; -} - -bool ImGui::IsMouseDown(int button) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - return g.IO.MouseDown[button]; -} - -bool ImGui::IsAnyMouseDown() -{ - ImGuiContext& g = *GImGui; - for (int n = 0; n < IM_ARRAYSIZE(g.IO.MouseDown); n++) - if (g.IO.MouseDown[n]) - return true; - return false; -} - -bool ImGui::IsMouseClicked(int button, bool repeat) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - const float t = g.IO.MouseDownDuration[button]; - if (t == 0.0f) - return true; - - if (repeat && t > g.IO.KeyRepeatDelay) - { - float delay = g.IO.KeyRepeatDelay, rate = g.IO.KeyRepeatRate; - if ((ImFmod(t - delay, rate) > rate*0.5f) != (ImFmod(t - delay - g.IO.DeltaTime, rate) > rate*0.5f)) - return true; - } - - return false; -} - -bool ImGui::IsMouseReleased(int button) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - return g.IO.MouseReleased[button]; -} - -bool ImGui::IsMouseDoubleClicked(int button) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - return g.IO.MouseDoubleClicked[button]; -} - -bool ImGui::IsMouseDragging(int button, float lock_threshold) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - if (!g.IO.MouseDown[button]) - return false; - if (lock_threshold < 0.0f) - lock_threshold = g.IO.MouseDragThreshold; - return g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold; -} - -ImVec2 ImGui::GetMousePos() -{ - return GImGui->IO.MousePos; -} - -// NB: prefer to call right after BeginPopup(). At the time Selectable/MenuItem is activated, the popup is already closed! -ImVec2 ImGui::GetMousePosOnOpeningCurrentPopup() -{ - ImGuiContext& g = *GImGui; - if (g.CurrentPopupStack.Size > 0) - return g.OpenPopupStack[g.CurrentPopupStack.Size-1].OpenMousePos; - return g.IO.MousePos; -} - -// We typically use ImVec2(-FLT_MAX,-FLT_MAX) to denote an invalid mouse position -bool ImGui::IsMousePosValid(const ImVec2* mouse_pos) -{ - if (mouse_pos == NULL) - mouse_pos = &GImGui->IO.MousePos; - const float MOUSE_INVALID = -256000.0f; - return mouse_pos->x >= MOUSE_INVALID && mouse_pos->y >= MOUSE_INVALID; -} - -// NB: This is only valid if IsMousePosValid(). Back-ends in theory should always keep mouse position valid when dragging even outside the client window. -ImVec2 ImGui::GetMouseDragDelta(int button, float lock_threshold) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - if (lock_threshold < 0.0f) - lock_threshold = g.IO.MouseDragThreshold; - if (g.IO.MouseDown[button]) - if (g.IO.MouseDragMaxDistanceSqr[button] >= lock_threshold * lock_threshold) - return g.IO.MousePos - g.IO.MouseClickedPos[button]; // Assume we can only get active with left-mouse button (at the moment). - return ImVec2(0.0f, 0.0f); -} - -void ImGui::ResetMouseDragDelta(int button) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - // NB: We don't need to reset g.IO.MouseDragMaxDistanceSqr - g.IO.MouseClickedPos[button] = g.IO.MousePos; -} - -ImGuiMouseCursor ImGui::GetMouseCursor() -{ - return GImGui->MouseCursor; -} - -void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type) -{ - GImGui->MouseCursor = cursor_type; -} - -void ImGui::CaptureKeyboardFromApp(bool capture) -{ - GImGui->WantCaptureKeyboardNextFrame = capture ? 1 : 0; -} - -void ImGui::CaptureMouseFromApp(bool capture) -{ - GImGui->WantCaptureMouseNextFrame = capture ? 1 : 0; -} - -bool ImGui::IsItemActive() -{ - ImGuiContext& g = *GImGui; - if (g.ActiveId) - { - ImGuiWindow* window = g.CurrentWindow; - return g.ActiveId == window->DC.LastItemId; - } - return false; -} - -bool ImGui::IsItemDeactivated() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - return (g.ActiveIdPreviousFrame == window->DC.LastItemId && g.ActiveIdPreviousFrame != 0 && g.ActiveId != window->DC.LastItemId); -} - -bool ImGui::IsItemDeactivatedAfterChange() -{ - ImGuiContext& g = *GImGui; - return IsItemDeactivated() && (g.ActiveIdPreviousFrameValueChanged || (g.ActiveId == 0 && g.ActiveIdValueChanged)); -} - -bool ImGui::IsItemFocused() -{ - ImGuiContext& g = *GImGui; - return g.NavId && !g.NavDisableHighlight && g.NavId == g.CurrentWindow->DC.LastItemId; -} - -bool ImGui::IsItemClicked(int mouse_button) -{ - return IsMouseClicked(mouse_button) && IsItemHovered(ImGuiHoveredFlags_None); -} - -bool ImGui::IsAnyItemHovered() -{ - ImGuiContext& g = *GImGui; - return g.HoveredId != 0 || g.HoveredIdPreviousFrame != 0; -} - -bool ImGui::IsAnyItemActive() -{ - ImGuiContext& g = *GImGui; - return g.ActiveId != 0; -} - -bool ImGui::IsAnyItemFocused() -{ - ImGuiContext& g = *GImGui; - return g.NavId != 0 && !g.NavDisableHighlight; -} - -bool ImGui::IsItemVisible() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->ClipRect.Overlaps(window->DC.LastItemRect); -} - -// Allow last item to be overlapped by a subsequent item. Both may be activated during the same frame before the later one takes priority. -void ImGui::SetItemAllowOverlap() -{ - ImGuiContext& g = *GImGui; - if (g.HoveredId == g.CurrentWindow->DC.LastItemId) - g.HoveredIdAllowOverlap = true; - if (g.ActiveId == g.CurrentWindow->DC.LastItemId) - g.ActiveIdAllowOverlap = true; -} - -ImVec2 ImGui::GetItemRectMin() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->DC.LastItemRect.Min; -} - -ImVec2 ImGui::GetItemRectMax() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->DC.LastItemRect.Max; -} - -ImVec2 ImGui::GetItemRectSize() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->DC.LastItemRect.GetSize(); -} - -static ImRect GetViewportRect() -{ - ImGuiContext& g = *GImGui; - if (g.IO.DisplayVisibleMin.x != g.IO.DisplayVisibleMax.x && g.IO.DisplayVisibleMin.y != g.IO.DisplayVisibleMax.y) - return ImRect(g.IO.DisplayVisibleMin, g.IO.DisplayVisibleMax); - return ImRect(0.0f, 0.0f, g.IO.DisplaySize.x, g.IO.DisplaySize.y); -} - -// Not exposed publicly as BeginTooltip() because bool parameters are evil. Let's see if other needs arise first. -void ImGui::BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip) -{ - ImGuiContext& g = *GImGui; - char window_name[16]; - ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount); - if (override_previous_tooltip) - if (ImGuiWindow* window = FindWindowByName(window_name)) - if (window->Active) - { - // Hide previous tooltip from being displayed. We can't easily "reset" the content of a window so we create a new one. - window->HiddenFrames = 1; - ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", ++g.TooltipOverrideCount); - } - ImGuiWindowFlags flags = ImGuiWindowFlags_Tooltip|ImGuiWindowFlags_NoInputs|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoNav; - Begin(window_name, NULL, flags | extra_flags); -} - -void ImGui::SetTooltipV(const char* fmt, va_list args) -{ - BeginTooltipEx(0, true); - TextV(fmt, args); - EndTooltip(); -} - -void ImGui::SetTooltip(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - SetTooltipV(fmt, args); - va_end(args); -} - -void ImGui::BeginTooltip() -{ - BeginTooltipEx(0, false); -} - -void ImGui::EndTooltip() -{ - IM_ASSERT(GetCurrentWindowRead()->Flags & ImGuiWindowFlags_Tooltip); // Mismatched BeginTooltip()/EndTooltip() calls - End(); -} - -// Mark popup as open (toggle toward open state). -// Popups are closed when user click outside, or activate a pressable item, or CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. -// Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). -// One open popup per level of the popup hierarchy (NB: when assigning we reset the Window member of ImGuiPopupRef to NULL) -void ImGui::OpenPopupEx(ImGuiID id) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* parent_window = g.CurrentWindow; - int current_stack_size = g.CurrentPopupStack.Size; - ImGuiPopupRef popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack. - popup_ref.PopupId = id; - popup_ref.Window = NULL; - popup_ref.ParentWindow = parent_window; - popup_ref.OpenFrameCount = g.FrameCount; - popup_ref.OpenParentId = parent_window->IDStack.back(); - popup_ref.OpenMousePos = g.IO.MousePos; - popup_ref.OpenPopupPos = NavCalcPreferredRefPos(); - - //printf("[%05d] OpenPopupEx(0x%08X)\n", g.FrameCount, id); - if (g.OpenPopupStack.Size < current_stack_size + 1) - { - g.OpenPopupStack.push_back(popup_ref); - } - else - { - // Gently handle the user mistakenly calling OpenPopup() every frame. It is a programming mistake! However, if we were to run the regular code path, the ui - // would become completely unusable because the popup will always be in hidden-while-calculating-size state _while_ claiming focus. Which would be a very confusing - // situation for the programmer. Instead, we silently allow the popup to proceed, it will keep reappearing and the programming error will be more obvious to understand. - if (g.OpenPopupStack[current_stack_size].PopupId == id && g.OpenPopupStack[current_stack_size].OpenFrameCount == g.FrameCount - 1) - { - g.OpenPopupStack[current_stack_size].OpenFrameCount = popup_ref.OpenFrameCount; - } - else - { - // Close child popups if any, then flag popup for open/reopen - g.OpenPopupStack.resize(current_stack_size + 1); - g.OpenPopupStack[current_stack_size] = popup_ref; - } - - // When reopening a popup we first refocus its parent, otherwise if its parent is itself a popup it would get closed by ClosePopupsOverWindow(). - // This is equivalent to what ClosePopupToLevel() does. - //if (g.OpenPopupStack[current_stack_size].PopupId == id) - // FocusWindow(parent_window); - } -} - -void ImGui::OpenPopup(const char* str_id) -{ - ImGuiContext& g = *GImGui; - OpenPopupEx(g.CurrentWindow->GetID(str_id)); -} - -void ImGui::ClosePopupsOverWindow(ImGuiWindow* ref_window) -{ - ImGuiContext& g = *GImGui; - if (g.OpenPopupStack.empty()) - return; - - // When popups are stacked, clicking on a lower level popups puts focus back to it and close popups above it. - // Don't close our own child popup windows. - int n = 0; - if (ref_window) - { - for (n = 0; n < g.OpenPopupStack.Size; n++) - { - ImGuiPopupRef& popup = g.OpenPopupStack[n]; - if (!popup.Window) - continue; - IM_ASSERT((popup.Window->Flags & ImGuiWindowFlags_Popup) != 0); - if (popup.Window->Flags & ImGuiWindowFlags_ChildWindow) - continue; - - // Trim the stack if popups are not direct descendant of the reference window (which is often the NavWindow) - bool has_focus = false; - for (int m = n; m < g.OpenPopupStack.Size && !has_focus; m++) - has_focus = (g.OpenPopupStack[m].Window && g.OpenPopupStack[m].Window->RootWindow == ref_window->RootWindow); - if (!has_focus) - break; - } - } - if (n < g.OpenPopupStack.Size) // This test is not required but it allows to set a convenient breakpoint on the block below - ClosePopupToLevel(n); -} - -ImGuiWindow* ImGui::GetFrontMostPopupModal() -{ - ImGuiContext& g = *GImGui; - for (int n = g.OpenPopupStack.Size-1; n >= 0; n--) - if (ImGuiWindow* popup = g.OpenPopupStack.Data[n].Window) - if (popup->Flags & ImGuiWindowFlags_Modal) - return popup; - return NULL; -} - -static void ClosePopupToLevel(int remaining) -{ - IM_ASSERT(remaining >= 0); - ImGuiContext& g = *GImGui; - ImGuiWindow* focus_window = (remaining > 0) ? g.OpenPopupStack[remaining-1].Window : g.OpenPopupStack[0].ParentWindow; - if (g.NavLayer == 0) - focus_window = NavRestoreLastChildNavWindow(focus_window); - ImGui::FocusWindow(focus_window); - focus_window->DC.NavHideHighlightOneFrame = true; - g.OpenPopupStack.resize(remaining); -} - -void ImGui::ClosePopup(ImGuiID id) -{ - if (!IsPopupOpen(id)) - return; - ImGuiContext& g = *GImGui; - ClosePopupToLevel(g.OpenPopupStack.Size - 1); -} - -// Close the popup we have begin-ed into. -void ImGui::CloseCurrentPopup() -{ - ImGuiContext& g = *GImGui; - int popup_idx = g.CurrentPopupStack.Size - 1; - if (popup_idx < 0 || popup_idx >= g.OpenPopupStack.Size || g.CurrentPopupStack[popup_idx].PopupId != g.OpenPopupStack[popup_idx].PopupId) - return; - while (popup_idx > 0 && g.OpenPopupStack[popup_idx].Window && (g.OpenPopupStack[popup_idx].Window->Flags & ImGuiWindowFlags_ChildMenu)) - popup_idx--; - ClosePopupToLevel(popup_idx); -} - -bool ImGui::BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags) -{ - ImGuiContext& g = *GImGui; - if (!IsPopupOpen(id)) - { - g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values - return false; - } - - char name[20]; - if (extra_flags & ImGuiWindowFlags_ChildMenu) - ImFormatString(name, IM_ARRAYSIZE(name), "##Menu_%02d", g.CurrentPopupStack.Size); // Recycle windows based on depth - else - ImFormatString(name, IM_ARRAYSIZE(name), "##Popup_%08x", id); // Not recycling, so we can close/open during the same frame - - bool is_open = Begin(name, NULL, extra_flags | ImGuiWindowFlags_Popup); - if (!is_open) // NB: Begin can return false when the popup is completely clipped (e.g. zero size display) - EndPopup(); - - return is_open; -} - -bool ImGui::BeginPopup(const char* str_id, ImGuiWindowFlags flags) -{ - ImGuiContext& g = *GImGui; - if (g.OpenPopupStack.Size <= g.CurrentPopupStack.Size) // Early out for performance - { - g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values - return false; - } - return BeginPopupEx(g.CurrentWindow->GetID(str_id), flags|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings); -} - -bool ImGui::IsPopupOpen(ImGuiID id) -{ - ImGuiContext& g = *GImGui; - return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == id; -} - -bool ImGui::IsPopupOpen(const char* str_id) -{ - ImGuiContext& g = *GImGui; - return g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].PopupId == g.CurrentWindow->GetID(str_id); -} - -bool ImGui::BeginPopupModal(const char* name, bool* p_open, ImGuiWindowFlags flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - const ImGuiID id = window->GetID(name); - if (!IsPopupOpen(id)) - { - g.NextWindowData.Clear(); // We behave like Begin() and need to consume those values - return false; - } - - // Center modal windows by default - // FIXME: Should test for (PosCond & window->SetWindowPosAllowFlags) with the upcoming window. - if (g.NextWindowData.PosCond == 0) - SetNextWindowPos(g.IO.DisplaySize * 0.5f, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f)); - - bool is_open = Begin(name, p_open, flags | ImGuiWindowFlags_Popup | ImGuiWindowFlags_Modal | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoSavedSettings); - if (!is_open || (p_open && !*p_open)) // NB: is_open can be 'false' when the popup is completely clipped (e.g. zero size display) - { - EndPopup(); - if (is_open) - ClosePopup(id); - return false; - } - - return is_open; -} - -void ImGui::NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.NavMoveRequestForward == ImGuiNavForward_None); - ImGui::NavMoveRequestCancel(); - g.NavMoveDir = move_dir; - g.NavMoveClipDir = clip_dir; - g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued; - g.NavMoveRequestFlags = move_flags; - g.NavWindow->NavRectRel[g.NavLayer] = bb_rel; -} - -void ImGui::NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags) -{ - ImGuiContext& g = *GImGui; - if (g.NavWindow != window || !NavMoveRequestButNoResultYet() || g.NavMoveRequestForward != ImGuiNavForward_None || g.NavLayer != 0) - return; - IM_ASSERT(move_flags != 0); // No points calling this with no wrapping - ImRect bb_rel = window->NavRectRel[0]; - - ImGuiDir clip_dir = g.NavMoveDir; - if (g.NavMoveDir == ImGuiDir_Left && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) - { - bb_rel.Min.x = bb_rel.Max.x = ImMax(window->SizeFull.x, window->SizeContents.x) - window->Scroll.x; - if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(-bb_rel.GetHeight()); clip_dir = ImGuiDir_Up; } - NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); - } - if (g.NavMoveDir == ImGuiDir_Right && (move_flags & (ImGuiNavMoveFlags_WrapX | ImGuiNavMoveFlags_LoopX))) - { - bb_rel.Min.x = bb_rel.Max.x = -window->Scroll.x; - if (move_flags & ImGuiNavMoveFlags_WrapX) { bb_rel.TranslateY(+bb_rel.GetHeight()); clip_dir = ImGuiDir_Down; } - NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); - } - if (g.NavMoveDir == ImGuiDir_Up && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) - { - bb_rel.Min.y = bb_rel.Max.y = ImMax(window->SizeFull.y, window->SizeContents.y) - window->Scroll.y; - if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(-bb_rel.GetWidth()); clip_dir = ImGuiDir_Left; } - NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); - } - if (g.NavMoveDir == ImGuiDir_Down && (move_flags & (ImGuiNavMoveFlags_WrapY | ImGuiNavMoveFlags_LoopY))) - { - bb_rel.Min.y = bb_rel.Max.y = -window->Scroll.y; - if (move_flags & ImGuiNavMoveFlags_WrapY) { bb_rel.TranslateX(+bb_rel.GetWidth()); clip_dir = ImGuiDir_Right; } - NavMoveRequestForward(g.NavMoveDir, clip_dir, bb_rel, move_flags); - } -} - -void ImGui::EndPopup() -{ - ImGuiContext& g = *GImGui; (void)g; - IM_ASSERT(g.CurrentWindow->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginPopup()/EndPopup() calls - IM_ASSERT(g.CurrentPopupStack.Size > 0); - - // Make all menus and popups wrap around for now, may need to expose that policy. - NavMoveRequestTryWrapping(g.CurrentWindow, ImGuiNavMoveFlags_LoopY); - - End(); -} - -bool ImGui::OpenPopupOnItemClick(const char* str_id, int mouse_button) -{ - ImGuiWindow* window = GImGui->CurrentWindow; - if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) - { - ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! - IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) - OpenPopupEx(id); - return true; - } - return false; -} - -// This is a helper to handle the simplest case of associating one named popup to one given widget. -// You may want to handle this on user side if you have specific needs (e.g. tweaking IsItemHovered() parameters). -// You can pass a NULL str_id to use the identifier of the last item. -bool ImGui::BeginPopupContextItem(const char* str_id, int mouse_button) -{ - ImGuiWindow* window = GImGui->CurrentWindow; - ImGuiID id = str_id ? window->GetID(str_id) : window->DC.LastItemId; // If user hasn't passed an ID, we can use the LastItemID. Using LastItemID as a Popup ID won't conflict! - IM_ASSERT(id != 0); // You cannot pass a NULL str_id if the last item has no identifier (e.g. a Text() item) - if (IsMouseReleased(mouse_button) && IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) - OpenPopupEx(id); - return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings); -} - -bool ImGui::BeginPopupContextWindow(const char* str_id, int mouse_button, bool also_over_items) -{ - if (!str_id) - str_id = "window_context"; - ImGuiID id = GImGui->CurrentWindow->GetID(str_id); - if (IsMouseReleased(mouse_button) && IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup)) - if (also_over_items || !IsAnyItemHovered()) - OpenPopupEx(id); - return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings); -} - -bool ImGui::BeginPopupContextVoid(const char* str_id, int mouse_button) -{ - if (!str_id) - str_id = "void_context"; - ImGuiID id = GImGui->CurrentWindow->GetID(str_id); - if (IsMouseReleased(mouse_button) && !IsWindowHovered(ImGuiHoveredFlags_AnyWindow)) - OpenPopupEx(id); - return BeginPopupEx(id, ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoSavedSettings); -} - -static bool BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* parent_window = ImGui::GetCurrentWindow(); - ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_ChildWindow; - flags |= (parent_window->Flags & ImGuiWindowFlags_NoMove); // Inherit the NoMove flag - - const ImVec2 content_avail = ImGui::GetContentRegionAvail(); - ImVec2 size = ImFloor(size_arg); - const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00); - if (size.x <= 0.0f) - size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues) - if (size.y <= 0.0f) - size.y = ImMax(content_avail.y + size.y, 4.0f); - - const float backup_border_size = g.Style.ChildBorderSize; - if (!border) - g.Style.ChildBorderSize = 0.0f; - flags |= extra_flags; - - char title[256]; - if (name) - ImFormatString(title, IM_ARRAYSIZE(title), "%s/%s", parent_window->Name, name); - else - ImFormatString(title, IM_ARRAYSIZE(title), "%s/%08X", parent_window->Name, id); - - ImGui::SetNextWindowSize(size); - bool ret = ImGui::Begin(title, NULL, flags); - ImGuiWindow* child_window = ImGui::GetCurrentWindow(); - child_window->ChildId = id; - child_window->AutoFitChildAxises = auto_fit_axises; - g.Style.ChildBorderSize = backup_border_size; - - // Process navigation-in immediately so NavInit can run on first frame - if (!(flags & ImGuiWindowFlags_NavFlattened) && (child_window->DC.NavLayerActiveMask != 0 || child_window->DC.NavHasScroll) && g.NavActivateId == id) - { - ImGui::FocusWindow(child_window); - ImGui::NavInitWindow(child_window, false); - ImGui::SetActiveID(id+1, child_window); // Steal ActiveId with a dummy id so that key-press won't activate child item - g.ActiveIdSource = ImGuiInputSource_Nav; - } - - return ret; -} - -bool ImGui::BeginChild(const char* str_id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - return BeginChildEx(str_id, window->GetID(str_id), size_arg, border, extra_flags); -} - -bool ImGui::BeginChild(ImGuiID id, const ImVec2& size_arg, bool border, ImGuiWindowFlags extra_flags) -{ - IM_ASSERT(id != 0); - return BeginChildEx(NULL, id, size_arg, border, extra_flags); -} - -void ImGui::EndChild() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - IM_ASSERT(window->Flags & ImGuiWindowFlags_ChildWindow); // Mismatched BeginChild()/EndChild() callss - if (window->BeginCount > 1) - { - End(); - } - else - { - ImVec2 sz = window->Size; - if (window->AutoFitChildAxises & (1 << ImGuiAxis_X)) // Arbitrary minimum zero-ish child size of 4.0f causes less trouble than a 0.0f - sz.x = ImMax(4.0f, sz.x); - if (window->AutoFitChildAxises & (1 << ImGuiAxis_Y)) - sz.y = ImMax(4.0f, sz.y); - End(); - - ImGuiWindow* parent_window = g.CurrentWindow; - ImRect bb(parent_window->DC.CursorPos, parent_window->DC.CursorPos + sz); - ItemSize(sz); - if ((window->DC.NavLayerActiveMask != 0 || window->DC.NavHasScroll) && !(window->Flags & ImGuiWindowFlags_NavFlattened)) - { - ItemAdd(bb, window->ChildId); - RenderNavHighlight(bb, window->ChildId); - - // When browsing a window that has no activable items (scroll only) we keep a highlight on the child - if (window->DC.NavLayerActiveMask == 0 && window == g.NavWindow) - RenderNavHighlight(ImRect(bb.Min - ImVec2(2,2), bb.Max + ImVec2(2,2)), g.NavId, ImGuiNavHighlightFlags_TypeThin); - } - else - { - // Not navigable into - ItemAdd(bb, 0); - } - } -} - -// Helper to create a child window / scrolling region that looks like a normal widget frame. -bool ImGui::BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags extra_flags) -{ - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - PushStyleColor(ImGuiCol_ChildBg, style.Colors[ImGuiCol_FrameBg]); - PushStyleVar(ImGuiStyleVar_ChildRounding, style.FrameRounding); - PushStyleVar(ImGuiStyleVar_ChildBorderSize, style.FrameBorderSize); - PushStyleVar(ImGuiStyleVar_WindowPadding, style.FramePadding); - bool ret = BeginChild(id, size, true, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysUseWindowPadding | extra_flags); - PopStyleVar(3); - PopStyleColor(); - return ret; -} - -void ImGui::EndChildFrame() -{ - EndChild(); -} - -// Save and compare stack sizes on Begin()/End() to detect usage errors -static void CheckStacksSize(ImGuiWindow* window, bool write) -{ - // NOT checking: DC.ItemWidth, DC.AllowKeyboardFocus, DC.ButtonRepeat, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin) - ImGuiContext& g = *GImGui; - int* p_backup = &window->DC.StackSizesBackup[0]; - { int current = window->IDStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "PushID/PopID or TreeNode/TreePop Mismatch!"); p_backup++; } // Too few or too many PopID()/TreePop() - { int current = window->DC.GroupStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "BeginGroup/EndGroup Mismatch!"); p_backup++; } // Too few or too many EndGroup() - { int current = g.CurrentPopupStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup == current && "BeginMenu/EndMenu or BeginPopup/EndPopup Mismatch"); p_backup++;}// Too few or too many EndMenu()/EndPopup() - // For color, style and font stacks there is an incentive to use Push/Begin/Pop/.../End patterns, so we relax our checks a little to allow them. - { int current = g.ColorModifiers.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup >= current && "PushStyleColor/PopStyleColor Mismatch!"); p_backup++; } // Too few or too many PopStyleColor() - { int current = g.StyleModifiers.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup >= current && "PushStyleVar/PopStyleVar Mismatch!"); p_backup++; } // Too few or too many PopStyleVar() - { int current = g.FontStack.Size; if (write) *p_backup = current; else IM_ASSERT(*p_backup >= current && "PushFont/PopFont Mismatch!"); p_backup++; } // Too few or too many PopFont() - IM_ASSERT(p_backup == window->DC.StackSizesBackup + IM_ARRAYSIZE(window->DC.StackSizesBackup)); -} - -enum ImGuiPopupPositionPolicy -{ - ImGuiPopupPositionPolicy_Default, - ImGuiPopupPositionPolicy_ComboBox -}; - -static ImRect FindAllowedExtentRectForWindow(ImGuiWindow*) -{ - ImVec2 padding = GImGui->Style.DisplaySafeAreaPadding; - ImRect r_screen = GetViewportRect(); - r_screen.Expand(ImVec2((r_screen.GetWidth() > padding.x * 2) ? -padding.x : 0.0f, (r_screen.GetHeight() > padding.y * 2) ? -padding.y : 0.0f)); - return r_screen; -} - -// r_avoid = the rectangle to avoid (e.g. for tooltip it is a rectangle around the mouse cursor which we want to avoid. for popups it's a small point around the cursor.) -// r_outer = the visible area rectangle, minus safe area padding. If our popup size won't fit because of safe area padding we ignore it. -static ImVec2 FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& size, ImGuiDir* last_dir, const ImRect& r_outer, const ImRect& r_avoid, ImGuiPopupPositionPolicy policy = ImGuiPopupPositionPolicy_Default) -{ - ImVec2 base_pos_clamped = ImClamp(ref_pos, r_outer.Min, r_outer.Max - size); - //GImGui->OverlayDrawList.AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255,0,0,255)); - //GImGui->OverlayDrawList.AddRect(r_outer.Min, r_outer.Max, IM_COL32(0,255,0,255)); - - // Combo Box policy (we want a connecting edge) - if (policy == ImGuiPopupPositionPolicy_ComboBox) - { - const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Down, ImGuiDir_Right, ImGuiDir_Left, ImGuiDir_Up }; - for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++) - { - const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n]; - if (n != -1 && dir == *last_dir) // Already tried this direction? - continue; - ImVec2 pos; - if (dir == ImGuiDir_Down) pos = ImVec2(r_avoid.Min.x, r_avoid.Max.y); // Below, Toward Right (default) - if (dir == ImGuiDir_Right) pos = ImVec2(r_avoid.Min.x, r_avoid.Min.y - size.y); // Above, Toward Right - if (dir == ImGuiDir_Left) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Max.y); // Below, Toward Left - if (dir == ImGuiDir_Up) pos = ImVec2(r_avoid.Max.x - size.x, r_avoid.Min.y - size.y); // Above, Toward Left - if (!r_outer.Contains(ImRect(pos, pos + size))) - continue; - *last_dir = dir; - return pos; - } - } - - // Default popup policy - const ImGuiDir dir_prefered_order[ImGuiDir_COUNT] = { ImGuiDir_Right, ImGuiDir_Down, ImGuiDir_Up, ImGuiDir_Left }; - for (int n = (*last_dir != ImGuiDir_None) ? -1 : 0; n < ImGuiDir_COUNT; n++) - { - const ImGuiDir dir = (n == -1) ? *last_dir : dir_prefered_order[n]; - if (n != -1 && dir == *last_dir) // Already tried this direction? - continue; - float avail_w = (dir == ImGuiDir_Left ? r_avoid.Min.x : r_outer.Max.x) - (dir == ImGuiDir_Right ? r_avoid.Max.x : r_outer.Min.x); - float avail_h = (dir == ImGuiDir_Up ? r_avoid.Min.y : r_outer.Max.y) - (dir == ImGuiDir_Down ? r_avoid.Max.y : r_outer.Min.y); - if (avail_w < size.x || avail_h < size.y) - continue; - ImVec2 pos; - pos.x = (dir == ImGuiDir_Left) ? r_avoid.Min.x - size.x : (dir == ImGuiDir_Right) ? r_avoid.Max.x : base_pos_clamped.x; - pos.y = (dir == ImGuiDir_Up) ? r_avoid.Min.y - size.y : (dir == ImGuiDir_Down) ? r_avoid.Max.y : base_pos_clamped.y; - *last_dir = dir; - return pos; - } - - // Fallback, try to keep within display - *last_dir = ImGuiDir_None; - ImVec2 pos = ref_pos; - pos.x = ImMax(ImMin(pos.x + size.x, r_outer.Max.x) - size.x, r_outer.Min.x); - pos.y = ImMax(ImMin(pos.y + size.y, r_outer.Max.y) - size.y, r_outer.Min.y); - return pos; -} - -static ImVec2 FindBestWindowPosForPopup(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - - ImRect r_outer = FindAllowedExtentRectForWindow(window); - if (window->Flags & ImGuiWindowFlags_ChildMenu) - { - // Child menus typically request _any_ position within the parent menu item, and then our FindBestWindowPosForPopup() function will move the new menu outside the parent bounds. - // This is how we end up with child menus appearing (most-commonly) on the right of the parent menu. - IM_ASSERT(g.CurrentWindow == window); - ImGuiWindow* parent_window = g.CurrentWindowStack[g.CurrentWindowStack.Size - 2]; - float horizontal_overlap = g.Style.ItemSpacing.x; // We want some overlap to convey the relative depth of each menu (currently the amount of overlap is hard-coded to style.ItemSpacing.x). - ImRect r_avoid; - if (parent_window->DC.MenuBarAppending) - r_avoid = ImRect(-FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight(), FLT_MAX, parent_window->Pos.y + parent_window->TitleBarHeight() + parent_window->MenuBarHeight()); - else - r_avoid = ImRect(parent_window->Pos.x + horizontal_overlap, -FLT_MAX, parent_window->Pos.x + parent_window->Size.x - horizontal_overlap - parent_window->ScrollbarSizes.x, FLT_MAX); - return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid); - } - if (window->Flags & ImGuiWindowFlags_Popup) - { - ImRect r_avoid = ImRect(window->Pos.x - 1, window->Pos.y - 1, window->Pos.x + 1, window->Pos.y + 1); - return FindBestWindowPosForPopupEx(window->Pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid); - } - if (window->Flags & ImGuiWindowFlags_Tooltip) - { - // Position tooltip (always follows mouse) - float sc = g.Style.MouseCursorScale; - ImVec2 ref_pos = NavCalcPreferredRefPos(); - ImRect r_avoid; - if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos)) - r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8); - else - r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * sc, ref_pos.y + 24 * sc); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important. - ImVec2 pos = FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid); - if (window->AutoPosLastDirection == ImGuiDir_None) - pos = ref_pos + ImVec2(2, 2); // If there's not enough room, for tooltip we prefer avoiding the cursor at all cost even if it means that part of the tooltip won't be visible. - return pos; - } - IM_ASSERT(0); - return window->Pos; -} - -static void SetWindowConditionAllowFlags(ImGuiWindow* window, ImGuiCond flags, bool enabled) -{ - window->SetWindowPosAllowFlags = enabled ? (window->SetWindowPosAllowFlags | flags) : (window->SetWindowPosAllowFlags & ~flags); - window->SetWindowSizeAllowFlags = enabled ? (window->SetWindowSizeAllowFlags | flags) : (window->SetWindowSizeAllowFlags & ~flags); - window->SetWindowCollapsedAllowFlags = enabled ? (window->SetWindowCollapsedAllowFlags | flags) : (window->SetWindowCollapsedAllowFlags & ~flags); -} - -ImGuiWindow* ImGui::FindWindowByName(const char* name) -{ - ImGuiContext& g = *GImGui; - ImGuiID id = ImHash(name, 0); - return (ImGuiWindow*)g.WindowsById.GetVoidPtr(id); -} - -static ImGuiWindow* CreateNewWindow(const char* name, ImVec2 size, ImGuiWindowFlags flags) -{ - ImGuiContext& g = *GImGui; - - // Create window the first time - ImGuiWindow* window = IM_NEW(ImGuiWindow)(&g, name); - window->Flags = flags; - g.WindowsById.SetVoidPtr(window->ID, window); - - // Default/arbitrary window position. Use SetNextWindowPos() with the appropriate condition flag to change the initial position of a window. - window->Pos = ImVec2(60, 60); - - // User can disable loading and saving of settings. Tooltip and child windows also don't store settings. - if (!(flags & ImGuiWindowFlags_NoSavedSettings)) - { - // Retrieve settings from .ini file - if (ImGuiWindowSettings* settings = ImGui::FindWindowSettings(window->ID)) - { - SetWindowConditionAllowFlags(window, ImGuiCond_FirstUseEver, false); - window->Pos = ImFloor(settings->Pos); - window->Collapsed = settings->Collapsed; - if (ImLengthSqr(settings->Size) > 0.00001f) - size = ImFloor(settings->Size); - } - } - window->Size = window->SizeFull = window->SizeFullAtLastBegin = size; - - if ((flags & ImGuiWindowFlags_AlwaysAutoResize) != 0) - { - window->AutoFitFramesX = window->AutoFitFramesY = 2; - window->AutoFitOnlyGrows = false; - } - else - { - if (window->Size.x <= 0.0f) - window->AutoFitFramesX = 2; - if (window->Size.y <= 0.0f) - window->AutoFitFramesY = 2; - window->AutoFitOnlyGrows = (window->AutoFitFramesX > 0) || (window->AutoFitFramesY > 0); - } - - if (flags & ImGuiWindowFlags_NoBringToFrontOnFocus) - g.Windows.insert(g.Windows.begin(), window); // Quite slow but rare and only once - else - g.Windows.push_back(window); - return window; -} - -static ImVec2 CalcSizeAfterConstraint(ImGuiWindow* window, ImVec2 new_size) -{ - ImGuiContext& g = *GImGui; - if (g.NextWindowData.SizeConstraintCond != 0) - { - // Using -1,-1 on either X/Y axis to preserve the current size. - ImRect cr = g.NextWindowData.SizeConstraintRect; - new_size.x = (cr.Min.x >= 0 && cr.Max.x >= 0) ? ImClamp(new_size.x, cr.Min.x, cr.Max.x) : window->SizeFull.x; - new_size.y = (cr.Min.y >= 0 && cr.Max.y >= 0) ? ImClamp(new_size.y, cr.Min.y, cr.Max.y) : window->SizeFull.y; - if (g.NextWindowData.SizeCallback) - { - ImGuiSizeCallbackData data; - data.UserData = g.NextWindowData.SizeCallbackUserData; - data.Pos = window->Pos; - data.CurrentSize = window->SizeFull; - data.DesiredSize = new_size; - g.NextWindowData.SizeCallback(&data); - new_size = data.DesiredSize; - } - } - - // Minimum size - if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize))) - { - new_size = ImMax(new_size, g.Style.WindowMinSize); - new_size.y = ImMax(new_size.y, window->TitleBarHeight() + window->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows - } - return new_size; -} - -static ImVec2 CalcSizeContents(ImGuiWindow* window) -{ - ImVec2 sz; - sz.x = (float)(int)((window->SizeContentsExplicit.x != 0.0f) ? window->SizeContentsExplicit.x : (window->DC.CursorMaxPos.x - window->Pos.x + window->Scroll.x)); - sz.y = (float)(int)((window->SizeContentsExplicit.y != 0.0f) ? window->SizeContentsExplicit.y : (window->DC.CursorMaxPos.y - window->Pos.y + window->Scroll.y)); - return sz + window->WindowPadding; -} - -static ImVec2 CalcSizeAutoFit(ImGuiWindow* window, const ImVec2& size_contents) -{ - ImGuiContext& g = *GImGui; - ImGuiStyle& style = g.Style; - if (window->Flags & ImGuiWindowFlags_Tooltip) - { - // Tooltip always resize - return size_contents; - } - else - { - // When the window cannot fit all contents (either because of constraints, either because screen is too small): we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than DisplaySize-WindowPadding. - ImVec2 size_auto_fit = ImClamp(size_contents, style.WindowMinSize, ImMax(style.WindowMinSize, g.IO.DisplaySize - g.Style.DisplaySafeAreaPadding * 2.0f)); - ImVec2 size_auto_fit_after_constraint = CalcSizeAfterConstraint(window, size_auto_fit); - if (size_auto_fit_after_constraint.x < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) - size_auto_fit.y += style.ScrollbarSize; - if (size_auto_fit_after_constraint.y < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) - size_auto_fit.x += style.ScrollbarSize; - return size_auto_fit; - } -} - -static float GetScrollMaxX(ImGuiWindow* window) -{ - return ImMax(0.0f, window->SizeContents.x - (window->SizeFull.x - window->ScrollbarSizes.x)); -} - -static float GetScrollMaxY(ImGuiWindow* window) -{ - return ImMax(0.0f, window->SizeContents.y - (window->SizeFull.y - window->ScrollbarSizes.y)); -} - -static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window, bool snap_on_edges) -{ - ImGuiContext& g = *GImGui; - ImVec2 scroll = window->Scroll; - if (window->ScrollTarget.x < FLT_MAX) - { - float cr_x = window->ScrollTargetCenterRatio.x; - scroll.x = window->ScrollTarget.x - cr_x * (window->SizeFull.x - window->ScrollbarSizes.x); - } - if (window->ScrollTarget.y < FLT_MAX) - { - // 'snap_on_edges' allows for a discontinuity at the edge of scrolling limits to take account of WindowPadding so that scrolling to make the last item visible scroll far enough to see the padding. - float cr_y = window->ScrollTargetCenterRatio.y; - float target_y = window->ScrollTarget.y; - if (snap_on_edges && cr_y <= 0.0f && target_y <= window->WindowPadding.y) - target_y = 0.0f; - if (snap_on_edges && cr_y >= 1.0f && target_y >= window->SizeContents.y - window->WindowPadding.y + g.Style.ItemSpacing.y) - target_y = window->SizeContents.y; - scroll.y = target_y - (1.0f - cr_y) * (window->TitleBarHeight() + window->MenuBarHeight()) - cr_y * (window->SizeFull.y - window->ScrollbarSizes.y); - } - scroll = ImMax(scroll, ImVec2(0.0f, 0.0f)); - if (!window->Collapsed && !window->SkipItems) - { - scroll.x = ImMin(scroll.x, GetScrollMaxX(window)); - scroll.y = ImMin(scroll.y, GetScrollMaxY(window)); - } - return scroll; -} - -static ImGuiCol GetWindowBgColorIdxFromFlags(ImGuiWindowFlags flags) -{ - if (flags & (ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_Popup)) - return ImGuiCol_PopupBg; - if (flags & ImGuiWindowFlags_ChildWindow) - return ImGuiCol_ChildBg; - return ImGuiCol_WindowBg; -} - -static void CalcResizePosSizeFromAnyCorner(ImGuiWindow* window, const ImVec2& corner_target, const ImVec2& corner_norm, ImVec2* out_pos, ImVec2* out_size) -{ - ImVec2 pos_min = ImLerp(corner_target, window->Pos, corner_norm); // Expected window upper-left - ImVec2 pos_max = ImLerp(window->Pos + window->Size, corner_target, corner_norm); // Expected window lower-right - ImVec2 size_expected = pos_max - pos_min; - ImVec2 size_constrained = CalcSizeAfterConstraint(window, size_expected); - *out_pos = pos_min; - if (corner_norm.x == 0.0f) - out_pos->x -= (size_constrained.x - size_expected.x); - if (corner_norm.y == 0.0f) - out_pos->y -= (size_constrained.y - size_expected.y); - *out_size = size_constrained; -} - -struct ImGuiResizeGripDef -{ - ImVec2 CornerPos; - ImVec2 InnerDir; - int AngleMin12, AngleMax12; -}; - -const ImGuiResizeGripDef resize_grip_def[4] = -{ - { ImVec2(1,1), ImVec2(-1,-1), 0, 3 }, // Lower right - { ImVec2(0,1), ImVec2(+1,-1), 3, 6 }, // Lower left - { ImVec2(0,0), ImVec2(+1,+1), 6, 9 }, // Upper left - { ImVec2(1,0), ImVec2(-1,+1), 9,12 }, // Upper right -}; - -static ImRect GetBorderRect(ImGuiWindow* window, int border_n, float perp_padding, float thickness) -{ - ImRect rect = window->Rect(); - if (thickness == 0.0f) rect.Max -= ImVec2(1,1); - if (border_n == 0) return ImRect(rect.Min.x + perp_padding, rect.Min.y, rect.Max.x - perp_padding, rect.Min.y + thickness); - if (border_n == 1) return ImRect(rect.Max.x - thickness, rect.Min.y + perp_padding, rect.Max.x, rect.Max.y - perp_padding); - if (border_n == 2) return ImRect(rect.Min.x + perp_padding, rect.Max.y - thickness, rect.Max.x - perp_padding, rect.Max.y); - if (border_n == 3) return ImRect(rect.Min.x, rect.Min.y + perp_padding, rect.Min.x + thickness, rect.Max.y - perp_padding); - IM_ASSERT(0); - return ImRect(); -} - -// Handle resize for: Resize Grips, Borders, Gamepad -static void ImGui::UpdateManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4]) -{ - ImGuiContext& g = *GImGui; - ImGuiWindowFlags flags = window->Flags; - if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0) - return; - - const int resize_border_count = (flags & ImGuiWindowFlags_ResizeFromAnySide) ? 4 : 0; - const float grip_draw_size = (float)(int)ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f); - const float grip_hover_size = (float)(int)(grip_draw_size * 0.75f); - - ImVec2 pos_target(FLT_MAX, FLT_MAX); - ImVec2 size_target(FLT_MAX, FLT_MAX); - - // Manual resize grips - PushID("#RESIZE"); - for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++) - { - const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n]; - const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPos); - - // Using the FlattenChilds button flag we make the resize button accessible even if we are hovering over a child window - ImRect resize_rect(corner, corner + grip.InnerDir * grip_hover_size); - if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x); - if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y); - bool hovered, held; - ButtonBehavior(resize_rect, window->GetID((void*)(intptr_t)resize_grip_n), &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus); - if (hovered || held) - g.MouseCursor = (resize_grip_n & 1) ? ImGuiMouseCursor_ResizeNESW : ImGuiMouseCursor_ResizeNWSE; - - if (g.HoveredWindow == window && held && g.IO.MouseDoubleClicked[0] && resize_grip_n == 0) - { - // Manual auto-fit when double-clicking - size_target = CalcSizeAfterConstraint(window, size_auto_fit); - ClearActiveID(); - } - else if (held) - { - // Resize from any of the four corners - // We don't use an incremental MouseDelta but rather compute an absolute target size based on mouse position - ImVec2 corner_target = g.IO.MousePos - g.ActiveIdClickOffset + resize_rect.GetSize() * grip.CornerPos; // Corner of the window corresponding to our corner grip - CalcResizePosSizeFromAnyCorner(window, corner_target, grip.CornerPos, &pos_target, &size_target); - } - if (resize_grip_n == 0 || held || hovered) - resize_grip_col[resize_grip_n] = GetColorU32(held ? ImGuiCol_ResizeGripActive : hovered ? ImGuiCol_ResizeGripHovered : ImGuiCol_ResizeGrip); - } - for (int border_n = 0; border_n < resize_border_count; border_n++) - { - const float BORDER_SIZE = 5.0f; // FIXME: Only works _inside_ window because of HoveredWindow check. - const float BORDER_APPEAR_TIMER = 0.05f; // Reduce visual noise - bool hovered, held; - ImRect border_rect = GetBorderRect(window, border_n, grip_hover_size, BORDER_SIZE); - ButtonBehavior(border_rect, window->GetID((void*)(intptr_t)(border_n + 4)), &hovered, &held, ImGuiButtonFlags_FlattenChildren); - if ((hovered && g.HoveredIdTimer > BORDER_APPEAR_TIMER) || held) - { - g.MouseCursor = (border_n & 1) ? ImGuiMouseCursor_ResizeEW : ImGuiMouseCursor_ResizeNS; - if (held) *border_held = border_n; - } - if (held) - { - ImVec2 border_target = window->Pos; - ImVec2 border_posn; - if (border_n == 0) { border_posn = ImVec2(0, 0); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y); } - if (border_n == 1) { border_posn = ImVec2(1, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x + BORDER_SIZE); } - if (border_n == 2) { border_posn = ImVec2(0, 1); border_target.y = (g.IO.MousePos.y - g.ActiveIdClickOffset.y + BORDER_SIZE); } - if (border_n == 3) { border_posn = ImVec2(0, 0); border_target.x = (g.IO.MousePos.x - g.ActiveIdClickOffset.x); } - CalcResizePosSizeFromAnyCorner(window, border_target, border_posn, &pos_target, &size_target); - } - } - PopID(); - - // Navigation resize (keyboard/gamepad) - if (g.NavWindowingTarget == window) - { - ImVec2 nav_resize_delta; - if (g.NavInputSource == ImGuiInputSource_NavKeyboard && g.IO.KeyShift) - nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard, ImGuiInputReadMode_Down); - if (g.NavInputSource == ImGuiInputSource_NavGamepad) - nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_Down); - if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f) - { - const float NAV_RESIZE_SPEED = 600.0f; - nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); - g.NavWindowingToggleLayer = false; - g.NavDisableMouseHover = true; - resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive); - // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck. - size_target = CalcSizeAfterConstraint(window, window->SizeFull + nav_resize_delta); - } - } - - // Apply back modified position/size to window - if (size_target.x != FLT_MAX) - { - window->SizeFull = size_target; - MarkIniSettingsDirty(window); - } - if (pos_target.x != FLT_MAX) - { - window->Pos = ImFloor(pos_target); - MarkIniSettingsDirty(window); - } - - window->Size = window->SizeFull; -} - -// Push a new ImGui window to add widgets to. -// - A default window called "Debug" is automatically stacked at the beginning of every frame so you can use widgets without explicitly calling a Begin/End pair. -// - Begin/End can be called multiple times during the frame with the same window name to append content. -// - The window name is used as a unique identifier to preserve window information across frames (and save rudimentary information to the .ini file). -// You can use the "##" or "###" markers to use the same label with different id, or same id with different label. See documentation at the top of this file. -// - Return false when window is collapsed, so you can early out in your code. You always need to call ImGui::End() even if false is returned. -// - Passing 'bool* p_open' displays a Close button on the upper-right corner of the window, the pointed value will be set to false when the button is pressed. -bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) -{ - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - IM_ASSERT(name != NULL); // Window name required - IM_ASSERT(g.Initialized); // Forgot to call ImGui::NewFrame() - IM_ASSERT(g.FrameCountEnded != g.FrameCount); // Called ImGui::Render() or ImGui::EndFrame() and haven't called ImGui::NewFrame() again yet - - // Find or create - ImGuiWindow* window = FindWindowByName(name); - const bool window_just_created = (window == NULL); - if (window_just_created) - { - ImVec2 size_on_first_use = (g.NextWindowData.SizeCond != 0) ? g.NextWindowData.SizeVal : ImVec2(0.0f, 0.0f); // Any condition flag will do since we are creating a new window here. - window = CreateNewWindow(name, size_on_first_use, flags); - } - - // Automatically disable manual moving/resizing when NoInputs is set - if (flags & ImGuiWindowFlags_NoInputs) - flags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize; - - if (flags & ImGuiWindowFlags_NavFlattened) - IM_ASSERT(flags & ImGuiWindowFlags_ChildWindow); - - const int current_frame = g.FrameCount; - const bool first_begin_of_the_frame = (window->LastFrameActive != current_frame); - if (first_begin_of_the_frame) - window->Flags = (ImGuiWindowFlags)flags; - else - flags = window->Flags; - - // Update the Appearing flag - bool window_just_activated_by_user = (window->LastFrameActive < current_frame - 1); // Not using !WasActive because the implicit "Debug" window would always toggle off->on - const bool window_just_appearing_after_hidden_for_resize = (window->HiddenFrames > 0); - if (flags & ImGuiWindowFlags_Popup) - { - ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.CurrentPopupStack.Size]; - window_just_activated_by_user |= (window->PopupId != popup_ref.PopupId); // We recycle popups so treat window as activated if popup id changed - window_just_activated_by_user |= (window != popup_ref.Window); - } - window->Appearing = (window_just_activated_by_user || window_just_appearing_after_hidden_for_resize); - window->CloseButton = (p_open != NULL); - if (window->Appearing) - SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, true); - - // Parent window is latched only on the first call to Begin() of the frame, so further append-calls can be done from a different window stack - ImGuiWindow* parent_window_in_stack = g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back(); - ImGuiWindow* parent_window = first_begin_of_the_frame ? ((flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup)) ? parent_window_in_stack : NULL) : window->ParentWindow; - IM_ASSERT(parent_window != NULL || !(flags & ImGuiWindowFlags_ChildWindow)); - - // Add to stack - g.CurrentWindowStack.push_back(window); - SetCurrentWindow(window); - CheckStacksSize(window, true); - if (flags & ImGuiWindowFlags_Popup) - { - ImGuiPopupRef& popup_ref = g.OpenPopupStack[g.CurrentPopupStack.Size]; - popup_ref.Window = window; - g.CurrentPopupStack.push_back(popup_ref); - window->PopupId = popup_ref.PopupId; - } - - if (window_just_appearing_after_hidden_for_resize && !(flags & ImGuiWindowFlags_ChildWindow)) - window->NavLastIds[0] = 0; - - // Process SetNextWindow***() calls - bool window_pos_set_by_api = false; - bool window_size_x_set_by_api = false, window_size_y_set_by_api = false; - if (g.NextWindowData.PosCond) - { - window_pos_set_by_api = (window->SetWindowPosAllowFlags & g.NextWindowData.PosCond) != 0; - if (window_pos_set_by_api && ImLengthSqr(g.NextWindowData.PosPivotVal) > 0.00001f) - { - // May be processed on the next frame if this is our first frame and we are measuring size - // FIXME: Look into removing the branch so everything can go through this same code path for consistency. - window->SetWindowPosVal = g.NextWindowData.PosVal; - window->SetWindowPosPivot = g.NextWindowData.PosPivotVal; - window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); - } - else - { - SetWindowPos(window, g.NextWindowData.PosVal, g.NextWindowData.PosCond); - } - } - if (g.NextWindowData.SizeCond) - { - window_size_x_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.x > 0.0f); - window_size_y_set_by_api = (window->SetWindowSizeAllowFlags & g.NextWindowData.SizeCond) != 0 && (g.NextWindowData.SizeVal.y > 0.0f); - SetWindowSize(window, g.NextWindowData.SizeVal, g.NextWindowData.SizeCond); - } - if (g.NextWindowData.ContentSizeCond) - { - // Adjust passed "client size" to become a "window size" - window->SizeContentsExplicit = g.NextWindowData.ContentSizeVal; - if (window->SizeContentsExplicit.y != 0.0f) - window->SizeContentsExplicit.y += window->TitleBarHeight() + window->MenuBarHeight(); - } - else if (first_begin_of_the_frame) - { - window->SizeContentsExplicit = ImVec2(0.0f, 0.0f); - } - if (g.NextWindowData.CollapsedCond) - SetWindowCollapsed(window, g.NextWindowData.CollapsedVal, g.NextWindowData.CollapsedCond); - if (g.NextWindowData.FocusCond) - FocusWindow(window); - if (window->Appearing) - SetWindowConditionAllowFlags(window, ImGuiCond_Appearing, false); - - // When reusing window again multiple times a frame, just append content (don't need to setup again) - if (first_begin_of_the_frame) - { - const bool window_is_child_tooltip = (flags & ImGuiWindowFlags_ChildWindow) && (flags & ImGuiWindowFlags_Tooltip); // FIXME-WIP: Undocumented behavior of Child+Tooltip for pinned tooltip (#1345) - - // Initialize - window->ParentWindow = parent_window; - window->RootWindow = window->RootWindowForTitleBarHighlight = window->RootWindowForTabbing = window->RootWindowForNav = window; - if (parent_window && (flags & ImGuiWindowFlags_ChildWindow) && !window_is_child_tooltip) - window->RootWindow = parent_window->RootWindow; - if (parent_window && !(flags & ImGuiWindowFlags_Modal) && (flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Popup))) - window->RootWindowForTitleBarHighlight = window->RootWindowForTabbing = parent_window->RootWindowForTitleBarHighlight; // Same value in master branch, will differ for docking - while (window->RootWindowForNav->Flags & ImGuiWindowFlags_NavFlattened) - window->RootWindowForNav = window->RootWindowForNav->ParentWindow; - - window->Active = true; - window->BeginOrderWithinParent = 0; - window->BeginOrderWithinContext = g.WindowsActiveCount++; - window->BeginCount = 0; - window->ClipRect = ImVec4(-FLT_MAX,-FLT_MAX,+FLT_MAX,+FLT_MAX); - window->LastFrameActive = current_frame; - window->IDStack.resize(1); - - // UPDATE CONTENTS SIZE, UPDATE HIDDEN STATUS - - // Update contents size from last frame for auto-fitting (or use explicit size) - window->SizeContents = CalcSizeContents(window); - if (window->HiddenFrames > 0) - window->HiddenFrames--; - - // Hide new windows for one frame until they calculate their size - if (window_just_created && (!window_size_x_set_by_api || !window_size_y_set_by_api)) - window->HiddenFrames = 1; - - // Hide popup/tooltip window when re-opening while we measure size (because we recycle the windows) - // We reset Size/SizeContents for reappearing popups/tooltips early in this function, so further code won't be tempted to use the old size. - if (window_just_activated_by_user && (flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) != 0) - { - window->HiddenFrames = 1; - if (flags & ImGuiWindowFlags_AlwaysAutoResize) - { - if (!window_size_x_set_by_api) - window->Size.x = window->SizeFull.x = 0.f; - if (!window_size_y_set_by_api) - window->Size.y = window->SizeFull.y = 0.f; - window->SizeContents = ImVec2(0.f, 0.f); - } - } - - SetCurrentWindow(window); - - // Lock border size and padding for the frame (so that altering them doesn't cause inconsistencies) - window->WindowBorderSize = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildBorderSize : ((flags & (ImGuiWindowFlags_Popup | ImGuiWindowFlags_Tooltip)) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupBorderSize : style.WindowBorderSize; - window->WindowPadding = style.WindowPadding; - if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & (ImGuiWindowFlags_AlwaysUseWindowPadding | ImGuiWindowFlags_Popup)) && window->WindowBorderSize == 0.0f) - window->WindowPadding = ImVec2(0.0f, (flags & ImGuiWindowFlags_MenuBar) ? style.WindowPadding.y : 0.0f); - window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x); - window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y; - - // Collapse window by double-clicking on title bar - // At this point we don't have a clipping rectangle setup yet, so we can use the title bar area for hit detection and drawing - if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse)) - { - ImRect title_bar_rect = window->TitleBarRect(); - if (window->CollapseToggleWanted || (g.HoveredWindow == window && IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max) && g.IO.MouseDoubleClicked[0])) - { - window->Collapsed = !window->Collapsed; - MarkIniSettingsDirty(window); - FocusWindow(window); - } - } - else - { - window->Collapsed = false; - } - window->CollapseToggleWanted = false; - - // SIZE - - // Calculate auto-fit size, handle automatic resize - const ImVec2 size_auto_fit = CalcSizeAutoFit(window, window->SizeContents); - ImVec2 size_full_modified(FLT_MAX, FLT_MAX); - if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed) - { - // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc. - if (!window_size_x_set_by_api) - window->SizeFull.x = size_full_modified.x = size_auto_fit.x; - if (!window_size_y_set_by_api) - window->SizeFull.y = size_full_modified.y = size_auto_fit.y; - } - else if (window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0) - { - // Auto-fit may only grow window during the first few frames - // We still process initial auto-fit on collapsed windows to get a window width, but otherwise don't honor ImGuiWindowFlags_AlwaysAutoResize when collapsed. - if (!window_size_x_set_by_api && window->AutoFitFramesX > 0) - window->SizeFull.x = size_full_modified.x = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.x, size_auto_fit.x) : size_auto_fit.x; - if (!window_size_y_set_by_api && window->AutoFitFramesY > 0) - window->SizeFull.y = size_full_modified.y = window->AutoFitOnlyGrows ? ImMax(window->SizeFull.y, size_auto_fit.y) : size_auto_fit.y; - if (!window->Collapsed) - MarkIniSettingsDirty(window); - } - - // Apply minimum/maximum window size constraints and final size - window->SizeFull = CalcSizeAfterConstraint(window, window->SizeFull); - window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull; - - // SCROLLBAR STATUS - - // Update scrollbar status (based on the Size that was effective during last frame or the auto-resized Size). - if (!window->Collapsed) - { - // When reading the current size we need to read it after size constraints have been applied - float size_x_for_scrollbars = size_full_modified.x != FLT_MAX ? window->SizeFull.x : window->SizeFullAtLastBegin.x; - float size_y_for_scrollbars = size_full_modified.y != FLT_MAX ? window->SizeFull.y : window->SizeFullAtLastBegin.y; - window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((window->SizeContents.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar)); - window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((window->SizeContents.x > size_x_for_scrollbars - (window->ScrollbarY ? style.ScrollbarSize : 0.0f)) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar)); - if (window->ScrollbarX && !window->ScrollbarY) - window->ScrollbarY = (window->SizeContents.y > size_y_for_scrollbars - style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar); - window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f); - } - - // POSITION - - // Popup latch its initial position, will position itself when it appears next frame - if (window_just_activated_by_user) - { - window->AutoPosLastDirection = ImGuiDir_None; - if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api) - window->Pos = g.CurrentPopupStack.back().OpenPopupPos; - } - - // Position child window - if (flags & ImGuiWindowFlags_ChildWindow) - { - window->BeginOrderWithinParent = parent_window->DC.ChildWindows.Size; - parent_window->DC.ChildWindows.push_back(window); - if (!(flags & ImGuiWindowFlags_Popup) && !window_pos_set_by_api && !window_is_child_tooltip) - window->Pos = parent_window->DC.CursorPos; - } - - const bool window_pos_with_pivot = (window->SetWindowPosVal.x != FLT_MAX && window->HiddenFrames == 0); - if (window_pos_with_pivot) - SetWindowPos(window, ImMax(style.DisplaySafeAreaPadding, window->SetWindowPosVal - window->SizeFull * window->SetWindowPosPivot), 0); // Position given a pivot (e.g. for centering) - else if ((flags & ImGuiWindowFlags_ChildMenu) != 0) - window->Pos = FindBestWindowPosForPopup(window); - else if ((flags & ImGuiWindowFlags_Popup) != 0 && !window_pos_set_by_api && window_just_appearing_after_hidden_for_resize) - window->Pos = FindBestWindowPosForPopup(window); - else if ((flags & ImGuiWindowFlags_Tooltip) != 0 && !window_pos_set_by_api && !window_is_child_tooltip) - window->Pos = FindBestWindowPosForPopup(window); - - // Clamp position so it stays visible - if (!(flags & ImGuiWindowFlags_ChildWindow)) - { - if (!window_pos_set_by_api && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0 && g.IO.DisplaySize.x > 0.0f && g.IO.DisplaySize.y > 0.0f) // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. - { - ImVec2 padding = ImMax(style.DisplayWindowPadding, style.DisplaySafeAreaPadding); - window->Pos = ImMax(window->Pos + window->Size, padding) - window->Size; - window->Pos = ImMin(window->Pos, g.IO.DisplaySize - padding); - } - } - window->Pos = ImFloor(window->Pos); - - // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies) - window->WindowRounding = (flags & ImGuiWindowFlags_ChildWindow) ? style.ChildRounding : ((flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiWindowFlags_Modal)) ? style.PopupRounding : style.WindowRounding; - - // Prepare for focus requests - window->FocusIdxAllRequestCurrent = (window->FocusIdxAllRequestNext == INT_MAX || window->FocusIdxAllCounter == -1) ? INT_MAX : (window->FocusIdxAllRequestNext + (window->FocusIdxAllCounter+1)) % (window->FocusIdxAllCounter+1); - window->FocusIdxTabRequestCurrent = (window->FocusIdxTabRequestNext == INT_MAX || window->FocusIdxTabCounter == -1) ? INT_MAX : (window->FocusIdxTabRequestNext + (window->FocusIdxTabCounter+1)) % (window->FocusIdxTabCounter+1); - window->FocusIdxAllCounter = window->FocusIdxTabCounter = -1; - window->FocusIdxAllRequestNext = window->FocusIdxTabRequestNext = INT_MAX; - - // Apply scrolling - window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window, true); - window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); - - // Apply focus, new windows appears in front - bool want_focus = false; - if (window_just_activated_by_user && !(flags & ImGuiWindowFlags_NoFocusOnAppearing)) - if (!(flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_Tooltip)) || (flags & ImGuiWindowFlags_Popup)) - want_focus = true; - - // Handle manual resize: Resize Grips, Borders, Gamepad - int border_held = -1; - ImU32 resize_grip_col[4] = { 0 }; - const int resize_grip_count = (flags & ImGuiWindowFlags_ResizeFromAnySide) ? 2 : 1; // 4 - const float grip_draw_size = (float)(int)ImMax(g.FontSize * 1.35f, window->WindowRounding + 1.0f + g.FontSize * 0.2f); - if (!window->Collapsed) - UpdateManualResize(window, size_auto_fit, &border_held, resize_grip_count, &resize_grip_col[0]); - - // Default item width. Make it proportional to window size if window manually resizes - if (window->Size.x > 0.0f && !(flags & ImGuiWindowFlags_Tooltip) && !(flags & ImGuiWindowFlags_AlwaysAutoResize)) - window->ItemWidthDefault = (float)(int)(window->Size.x * 0.65f); - else - window->ItemWidthDefault = (float)(int)(g.FontSize * 16.0f); - - // DRAWING - - // Setup draw list and outer clipping rectangle - window->DrawList->Clear(); - window->DrawList->Flags = (g.Style.AntiAliasedLines ? ImDrawListFlags_AntiAliasedLines : 0) | (g.Style.AntiAliasedFill ? ImDrawListFlags_AntiAliasedFill : 0); - window->DrawList->PushTextureID(g.Font->ContainerAtlas->TexID); - ImRect viewport_rect(GetViewportRect()); - if ((flags & ImGuiWindowFlags_ChildWindow) && !(flags & ImGuiWindowFlags_Popup) && !window_is_child_tooltip) - PushClipRect(parent_window->ClipRect.Min, parent_window->ClipRect.Max, true); - else - PushClipRect(viewport_rect.Min, viewport_rect.Max, true); - - // Draw modal window background (darkens what is behind them) - if ((flags & ImGuiWindowFlags_Modal) != 0 && window == GetFrontMostPopupModal()) - window->DrawList->AddRectFilled(viewport_rect.Min, viewport_rect.Max, GetColorU32(ImGuiCol_ModalWindowDarkening, g.ModalWindowDarkeningRatio)); - - // Draw navigation selection/windowing rectangle background - if (g.NavWindowingTarget == window) - { - ImRect bb = window->Rect(); - bb.Expand(g.FontSize); - if (!bb.Contains(viewport_rect)) // Avoid drawing if the window covers all the viewport anyway - window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha * 0.25f), g.Style.WindowRounding); - } - - // Draw window + handle manual resize - const float window_rounding = window->WindowRounding; - const float window_border_size = window->WindowBorderSize; - const bool title_bar_is_highlight = want_focus || (g.NavWindow && window->RootWindowForTitleBarHighlight == g.NavWindow->RootWindowForTitleBarHighlight); - const ImRect title_bar_rect = window->TitleBarRect(); - if (window->Collapsed) - { - // Title bar only - float backup_border_size = style.FrameBorderSize; - g.Style.FrameBorderSize = window->WindowBorderSize; - ImU32 title_bar_col = GetColorU32((title_bar_is_highlight && !g.NavDisableHighlight) ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBgCollapsed); - RenderFrame(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, true, window_rounding); - g.Style.FrameBorderSize = backup_border_size; - } - else - { - // Window background - ImU32 bg_col = GetColorU32(GetWindowBgColorIdxFromFlags(flags)); - if (g.NextWindowData.BgAlphaCond != 0) - { - bg_col = (bg_col & ~IM_COL32_A_MASK) | (IM_F32_TO_INT8_SAT(g.NextWindowData.BgAlphaVal) << IM_COL32_A_SHIFT); - g.NextWindowData.BgAlphaCond = 0; - } - window->DrawList->AddRectFilled(window->Pos+ImVec2(0,window->TitleBarHeight()), window->Pos+window->Size, bg_col, window_rounding, (flags & ImGuiWindowFlags_NoTitleBar) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Bot); - - // Title bar - ImU32 title_bar_col = GetColorU32(window->Collapsed ? ImGuiCol_TitleBgCollapsed : title_bar_is_highlight ? ImGuiCol_TitleBgActive : ImGuiCol_TitleBg); - if (!(flags & ImGuiWindowFlags_NoTitleBar)) - window->DrawList->AddRectFilled(title_bar_rect.Min, title_bar_rect.Max, title_bar_col, window_rounding, ImDrawCornerFlags_Top); - - // Menu bar - if (flags & ImGuiWindowFlags_MenuBar) - { - ImRect menu_bar_rect = window->MenuBarRect(); - menu_bar_rect.ClipWith(window->Rect()); // Soft clipping, in particular child window don't have minimum size covering the menu bar so this is useful for them. - window->DrawList->AddRectFilled(menu_bar_rect.Min, menu_bar_rect.Max, GetColorU32(ImGuiCol_MenuBarBg), (flags & ImGuiWindowFlags_NoTitleBar) ? window_rounding : 0.0f, ImDrawCornerFlags_Top); - if (style.FrameBorderSize > 0.0f && menu_bar_rect.Max.y < window->Pos.y + window->Size.y) - window->DrawList->AddLine(menu_bar_rect.GetBL(), menu_bar_rect.GetBR(), GetColorU32(ImGuiCol_Border), style.FrameBorderSize); - } - - // Scrollbars - if (window->ScrollbarX) - Scrollbar(ImGuiLayoutType_Horizontal); - if (window->ScrollbarY) - Scrollbar(ImGuiLayoutType_Vertical); - - // Render resize grips (after their input handling so we don't have a frame of latency) - if (!(flags & ImGuiWindowFlags_NoResize)) - { - for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++) - { - const ImGuiResizeGripDef& grip = resize_grip_def[resize_grip_n]; - const ImVec2 corner = ImLerp(window->Pos, window->Pos + window->Size, grip.CornerPos); - window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(window_border_size, grip_draw_size) : ImVec2(grip_draw_size, window_border_size))); - window->DrawList->PathLineTo(corner + grip.InnerDir * ((resize_grip_n & 1) ? ImVec2(grip_draw_size, window_border_size) : ImVec2(window_border_size, grip_draw_size))); - window->DrawList->PathArcToFast(ImVec2(corner.x + grip.InnerDir.x * (window_rounding + window_border_size), corner.y + grip.InnerDir.y * (window_rounding + window_border_size)), window_rounding, grip.AngleMin12, grip.AngleMax12); - window->DrawList->PathFillConvex(resize_grip_col[resize_grip_n]); - } - } - - // Borders - if (window_border_size > 0.0f) - window->DrawList->AddRect(window->Pos, window->Pos + window->Size, GetColorU32(ImGuiCol_Border), window_rounding, ImDrawCornerFlags_All, window_border_size); - if (border_held != -1) - { - ImRect border = GetBorderRect(window, border_held, grip_draw_size, 0.0f); - window->DrawList->AddLine(border.Min, border.Max, GetColorU32(ImGuiCol_SeparatorActive), ImMax(1.0f, window_border_size)); - } - if (style.FrameBorderSize > 0 && !(flags & ImGuiWindowFlags_NoTitleBar)) - window->DrawList->AddLine(title_bar_rect.GetBL() + ImVec2(style.WindowBorderSize, -1), title_bar_rect.GetBR() + ImVec2(-style.WindowBorderSize,-1), GetColorU32(ImGuiCol_Border), style.FrameBorderSize); - } - - // Draw navigation selection/windowing rectangle border - if (g.NavWindowingTarget == window) - { - float rounding = ImMax(window->WindowRounding, g.Style.WindowRounding); - ImRect bb = window->Rect(); - bb.Expand(g.FontSize); - if (bb.Contains(viewport_rect)) // If a window fits the entire viewport, adjust its highlight inward - { - bb.Expand(-g.FontSize - 1.0f); - rounding = window->WindowRounding; - } - window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_NavWindowingHighlight, g.NavWindowingHighlightAlpha), rounding, ~0, 3.0f); - } - - // Store a backup of SizeFull which we will use next frame to decide if we need scrollbars. - window->SizeFullAtLastBegin = window->SizeFull; - - // Update various regions. Variables they depends on are set above in this function. - // FIXME: window->ContentsRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it. - window->ContentsRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x; - window->ContentsRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + window->TitleBarHeight() + window->MenuBarHeight(); - window->ContentsRegionRect.Max.x = window->Pos.x - window->Scroll.x - window->WindowPadding.x + (window->SizeContentsExplicit.x != 0.0f ? window->SizeContentsExplicit.x : (window->Size.x - window->ScrollbarSizes.x)); - window->ContentsRegionRect.Max.y = window->Pos.y - window->Scroll.y - window->WindowPadding.y + (window->SizeContentsExplicit.y != 0.0f ? window->SizeContentsExplicit.y : (window->Size.y - window->ScrollbarSizes.y)); - - // Setup drawing context - // (NB: That term "drawing context / DC" lost its meaning a long time ago. Initially was meant to hold transient data only. Nowadays difference between window-> and window->DC-> is dubious.) - window->DC.IndentX = 0.0f + window->WindowPadding.x - window->Scroll.x; - window->DC.GroupOffsetX = 0.0f; - window->DC.ColumnsOffsetX = 0.0f; - window->DC.CursorStartPos = window->Pos + ImVec2(window->DC.IndentX + window->DC.ColumnsOffsetX, window->TitleBarHeight() + window->MenuBarHeight() + window->WindowPadding.y - window->Scroll.y); - window->DC.CursorPos = window->DC.CursorStartPos; - window->DC.CursorPosPrevLine = window->DC.CursorPos; - window->DC.CursorMaxPos = window->DC.CursorStartPos; - window->DC.CurrentLineHeight = window->DC.PrevLineHeight = 0.0f; - window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f; - window->DC.NavHideHighlightOneFrame = false; - window->DC.NavHasScroll = (GetScrollMaxY() > 0.0f); - window->DC.NavLayerActiveMask = window->DC.NavLayerActiveMaskNext; - window->DC.NavLayerActiveMaskNext = 0x00; - window->DC.MenuBarAppending = false; - window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; - window->DC.ChildWindows.resize(0); - window->DC.LayoutType = ImGuiLayoutType_Vertical; - window->DC.ParentLayoutType = parent_window ? parent_window->DC.LayoutType : ImGuiLayoutType_Vertical; - window->DC.ItemFlags = parent_window ? parent_window->DC.ItemFlags : ImGuiItemFlags_Default_; - window->DC.ItemWidth = window->ItemWidthDefault; - window->DC.TextWrapPos = -1.0f; // disabled - window->DC.ItemFlagsStack.resize(0); - window->DC.ItemWidthStack.resize(0); - window->DC.TextWrapPosStack.resize(0); - window->DC.ColumnsSet = NULL; - window->DC.TreeDepth = 0; - window->DC.TreeDepthMayJumpToParentOnPop = 0x00; - window->DC.StateStorage = &window->StateStorage; - window->DC.GroupStack.resize(0); - window->MenuColumns.Update(3, style.ItemSpacing.x, window_just_activated_by_user); - - if ((flags & ImGuiWindowFlags_ChildWindow) && (window->DC.ItemFlags != parent_window->DC.ItemFlags)) - { - window->DC.ItemFlags = parent_window->DC.ItemFlags; - window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags); - } - - if (window->AutoFitFramesX > 0) - window->AutoFitFramesX--; - if (window->AutoFitFramesY > 0) - window->AutoFitFramesY--; - - // Apply focus (we need to call FocusWindow() AFTER setting DC.CursorStartPos so our initial navigation reference rectangle can start around there) - if (want_focus) - { - FocusWindow(window); - NavInitWindow(window, false); - } - - // Title bar - if (!(flags & ImGuiWindowFlags_NoTitleBar)) - { - // Close & collapse button are on layer 1 (same as menus) and don't default focus - const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags; - window->DC.ItemFlags |= ImGuiItemFlags_NoNavDefaultFocus; - window->DC.NavLayerCurrent++; - window->DC.NavLayerCurrentMask <<= 1; - - // Collapse button - if (!(flags & ImGuiWindowFlags_NoCollapse)) - { - ImGuiID id = window->GetID("#COLLAPSE"); - ImRect bb(window->Pos + style.FramePadding + ImVec2(1,1), window->Pos + style.FramePadding + ImVec2(g.FontSize,g.FontSize) - ImVec2(1,1)); - ItemAdd(bb, id); - if (ButtonBehavior(bb, id, NULL, NULL)) - window->CollapseToggleWanted = true; // Defer collapsing to next frame as we are too far in the Begin() function - RenderNavHighlight(bb, id); - RenderArrow(window->Pos + style.FramePadding, window->Collapsed ? ImGuiDir_Right : ImGuiDir_Down, 1.0f); - } - - // Close button - if (p_open != NULL) - { - const float pad = style.FramePadding.y; - const float rad = g.FontSize * 0.5f; - if (CloseButton(window->GetID("#CLOSE"), window->Rect().GetTR() + ImVec2(-pad - rad, pad + rad), rad + 1)) - *p_open = false; - } - - window->DC.NavLayerCurrent--; - window->DC.NavLayerCurrentMask >>= 1; - window->DC.ItemFlags = item_flags_backup; - - // Title text (FIXME: refactor text alignment facilities along with RenderText helpers, this is too much code for what it does.) - ImVec2 text_size = CalcTextSize(name, NULL, true); - ImRect text_r = title_bar_rect; - float pad_left = (flags & ImGuiWindowFlags_NoCollapse) ? style.FramePadding.x : (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x); - float pad_right = (p_open == NULL) ? style.FramePadding.x : (style.FramePadding.x + g.FontSize + style.ItemInnerSpacing.x); - if (style.WindowTitleAlign.x > 0.0f) - pad_right = ImLerp(pad_right, pad_left, style.WindowTitleAlign.x); - text_r.Min.x += pad_left; - text_r.Max.x -= pad_right; - ImRect clip_rect = text_r; - clip_rect.Max.x = window->Pos.x + window->Size.x - (p_open ? title_bar_rect.GetHeight() - 3 : style.FramePadding.x); // Match the size of CloseButton() - RenderTextClipped(text_r.Min, text_r.Max, name, NULL, &text_size, style.WindowTitleAlign, &clip_rect); - } - - // Save clipped aabb so we can access it in constant-time in FindHoveredWindow() - window->OuterRectClipped = window->Rect(); - window->OuterRectClipped.ClipWith(window->ClipRect); - - // Pressing CTRL+C while holding on a window copy its content to the clipboard - // This works but 1. doesn't handle multiple Begin/End pairs, 2. recursing into another Begin/End pair - so we need to work that out and add better logging scope. - // Maybe we can support CTRL+C on every element? - /* - if (g.ActiveId == move_id) - if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C)) - ImGui::LogToClipboard(); - */ - - // Inner rectangle - // We set this up after processing the resize grip so that our clip rectangle doesn't lag by a frame - // Note that if our window is collapsed we will end up with an inverted (~null) clipping rectangle which is the correct behavior. - window->InnerMainRect.Min.x = title_bar_rect.Min.x + window->WindowBorderSize; - window->InnerMainRect.Min.y = title_bar_rect.Max.y + window->MenuBarHeight() + (((flags & ImGuiWindowFlags_MenuBar) || !(flags & ImGuiWindowFlags_NoTitleBar)) ? style.FrameBorderSize : window->WindowBorderSize); - window->InnerMainRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x - window->WindowBorderSize; - window->InnerMainRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y - window->WindowBorderSize; - //window->DrawList->AddRect(window->InnerRect.Min, window->InnerRect.Max, IM_COL32_WHITE); - - // Inner clipping rectangle - // Force round operator last to ensure that e.g. (int)(max.x-min.x) in user's render code produce correct result. - window->InnerClipRect.Min.x = ImFloor(0.5f + window->InnerMainRect.Min.x + ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize))); - window->InnerClipRect.Min.y = ImFloor(0.5f + window->InnerMainRect.Min.y); - window->InnerClipRect.Max.x = ImFloor(0.5f + window->InnerMainRect.Max.x - ImMax(0.0f, ImFloor(window->WindowPadding.x*0.5f - window->WindowBorderSize))); - window->InnerClipRect.Max.y = ImFloor(0.5f + window->InnerMainRect.Max.y); - - // After Begin() we fill the last item / hovered data based on title bar data. It is a standard behavior (to allow creation of context menus on title bar only, etc.). - window->DC.LastItemId = window->MoveId; - window->DC.LastItemStatusFlags = IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0; - window->DC.LastItemRect = title_bar_rect; - } - - PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true); - - // Clear 'accessed' flag last thing (After PushClipRect which will set the flag. We want the flag to stay false when the default "Debug" window is unused) - if (first_begin_of_the_frame) - window->WriteAccessed = false; - - window->BeginCount++; - g.NextWindowData.Clear(); - - // Child window can be out of sight and have "negative" clip windows. - // Mark them as collapsed so commands are skipped earlier (we can't manually collapse them because they have no title bar). - if (flags & ImGuiWindowFlags_ChildWindow) - { - IM_ASSERT((flags & ImGuiWindowFlags_NoTitleBar) != 0); - window->Collapsed = parent_window && parent_window->Collapsed; - - if (!(flags & ImGuiWindowFlags_AlwaysAutoResize) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) - window->Collapsed |= (window->OuterRectClipped.Min.x >= window->OuterRectClipped.Max.x || window->OuterRectClipped.Min.y >= window->OuterRectClipped.Max.y); - - // We also hide the window from rendering because we've already added its border to the command list. - // (we could perform the check earlier in the function but it is simpler at this point) - if (window->Collapsed) - window->Active = false; - } - if (style.Alpha <= 0.0f) - window->Active = false; - - // Return false if we don't intend to display anything to allow user to perform an early out optimization - window->SkipItems = (window->Collapsed || !window->Active) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0; - return !window->SkipItems; -} - -// Old Begin() API with 5 parameters, avoid calling this version directly! Use SetNextWindowSize()/SetNextWindowBgAlpha() + Begin() instead. -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS -bool ImGui::Begin(const char* name, bool* p_open, const ImVec2& size_first_use, float bg_alpha_override, ImGuiWindowFlags flags) -{ - // Old API feature: we could pass the initial window size as a parameter. This was misleading because it only had an effect if the window didn't have data in the .ini file. - if (size_first_use.x != 0.0f || size_first_use.y != 0.0f) - ImGui::SetNextWindowSize(size_first_use, ImGuiCond_FirstUseEver); - - // Old API feature: override the window background alpha with a parameter. - if (bg_alpha_override >= 0.0f) - ImGui::SetNextWindowBgAlpha(bg_alpha_override); - - return ImGui::Begin(name, p_open, flags); -} -#endif // IMGUI_DISABLE_OBSOLETE_FUNCTIONS - -void ImGui::End() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - if (window->DC.ColumnsSet != NULL) - EndColumns(); - PopClipRect(); // Inner window clip rectangle - - // Stop logging - if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging - LogFinish(); - - // Pop from window stack - g.CurrentWindowStack.pop_back(); - if (window->Flags & ImGuiWindowFlags_Popup) - g.CurrentPopupStack.pop_back(); - CheckStacksSize(window, false); - SetCurrentWindow(g.CurrentWindowStack.empty() ? NULL : g.CurrentWindowStack.back()); -} - -// Vertical scrollbar -// The entire piece of code below is rather confusing because: -// - We handle absolute seeking (when first clicking outside the grab) and relative manipulation (afterward or when clicking inside the grab) -// - We store values as normalized ratio and in a form that allows the window content to change while we are holding on a scrollbar -// - We handle both horizontal and vertical scrollbars, which makes the terminology not ideal. -void ImGui::Scrollbar(ImGuiLayoutType direction) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - const bool horizontal = (direction == ImGuiLayoutType_Horizontal); - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(horizontal ? "#SCROLLX" : "#SCROLLY"); - - // Render background - bool other_scrollbar = (horizontal ? window->ScrollbarY : window->ScrollbarX); - float other_scrollbar_size_w = other_scrollbar ? style.ScrollbarSize : 0.0f; - const ImRect window_rect = window->Rect(); - const float border_size = window->WindowBorderSize; - ImRect bb = horizontal - ? ImRect(window->Pos.x + border_size, window_rect.Max.y - style.ScrollbarSize, window_rect.Max.x - other_scrollbar_size_w - border_size, window_rect.Max.y - border_size) - : ImRect(window_rect.Max.x - style.ScrollbarSize, window->Pos.y + border_size, window_rect.Max.x - border_size, window_rect.Max.y - other_scrollbar_size_w - border_size); - if (!horizontal) - bb.Min.y += window->TitleBarHeight() + ((window->Flags & ImGuiWindowFlags_MenuBar) ? window->MenuBarHeight() : 0.0f); - if (bb.GetWidth() <= 0.0f || bb.GetHeight() <= 0.0f) - return; - - int window_rounding_corners; - if (horizontal) - window_rounding_corners = ImDrawCornerFlags_BotLeft | (other_scrollbar ? 0 : ImDrawCornerFlags_BotRight); - else - window_rounding_corners = (((window->Flags & ImGuiWindowFlags_NoTitleBar) && !(window->Flags & ImGuiWindowFlags_MenuBar)) ? ImDrawCornerFlags_TopRight : 0) | (other_scrollbar ? 0 : ImDrawCornerFlags_BotRight); - window->DrawList->AddRectFilled(bb.Min, bb.Max, GetColorU32(ImGuiCol_ScrollbarBg), window->WindowRounding, window_rounding_corners); - bb.Expand(ImVec2(-ImClamp((float)(int)((bb.Max.x - bb.Min.x - 2.0f) * 0.5f), 0.0f, 3.0f), -ImClamp((float)(int)((bb.Max.y - bb.Min.y - 2.0f) * 0.5f), 0.0f, 3.0f))); - - // V denote the main, longer axis of the scrollbar (= height for a vertical scrollbar) - float scrollbar_size_v = horizontal ? bb.GetWidth() : bb.GetHeight(); - float scroll_v = horizontal ? window->Scroll.x : window->Scroll.y; - float win_size_avail_v = (horizontal ? window->SizeFull.x : window->SizeFull.y) - other_scrollbar_size_w; - float win_size_contents_v = horizontal ? window->SizeContents.x : window->SizeContents.y; - - // Calculate the height of our grabbable box. It generally represent the amount visible (vs the total scrollable amount) - // But we maintain a minimum size in pixel to allow for the user to still aim inside. - IM_ASSERT(ImMax(win_size_contents_v, win_size_avail_v) > 0.0f); // Adding this assert to check if the ImMax(XXX,1.0f) is still needed. PLEASE CONTACT ME if this triggers. - const float win_size_v = ImMax(ImMax(win_size_contents_v, win_size_avail_v), 1.0f); - const float grab_h_pixels = ImClamp(scrollbar_size_v * (win_size_avail_v / win_size_v), style.GrabMinSize, scrollbar_size_v); - const float grab_h_norm = grab_h_pixels / scrollbar_size_v; - - // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar(). - bool held = false; - bool hovered = false; - const bool previously_held = (g.ActiveId == id); - ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_NoNavFocus); - - float scroll_max = ImMax(1.0f, win_size_contents_v - win_size_avail_v); - float scroll_ratio = ImSaturate(scroll_v / scroll_max); - float grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; - if (held && grab_h_norm < 1.0f) - { - float scrollbar_pos_v = horizontal ? bb.Min.x : bb.Min.y; - float mouse_pos_v = horizontal ? g.IO.MousePos.x : g.IO.MousePos.y; - float* click_delta_to_grab_center_v = horizontal ? &g.ScrollbarClickDeltaToGrabCenter.x : &g.ScrollbarClickDeltaToGrabCenter.y; - - // Click position in scrollbar normalized space (0.0f->1.0f) - const float clicked_v_norm = ImSaturate((mouse_pos_v - scrollbar_pos_v) / scrollbar_size_v); - SetHoveredID(id); - - bool seek_absolute = false; - if (!previously_held) - { - // On initial click calculate the distance between mouse and the center of the grab - if (clicked_v_norm >= grab_v_norm && clicked_v_norm <= grab_v_norm + grab_h_norm) - { - *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f; - } - else - { - seek_absolute = true; - *click_delta_to_grab_center_v = 0.0f; - } - } - - // Apply scroll - // It is ok to modify Scroll here because we are being called in Begin() after the calculation of SizeContents and before setting up our starting position - const float scroll_v_norm = ImSaturate((clicked_v_norm - *click_delta_to_grab_center_v - grab_h_norm*0.5f) / (1.0f - grab_h_norm)); - scroll_v = (float)(int)(0.5f + scroll_v_norm * scroll_max);//(win_size_contents_v - win_size_v)); - if (horizontal) - window->Scroll.x = scroll_v; - else - window->Scroll.y = scroll_v; - - // Update values for rendering - scroll_ratio = ImSaturate(scroll_v / scroll_max); - grab_v_norm = scroll_ratio * (scrollbar_size_v - grab_h_pixels) / scrollbar_size_v; - - // Update distance to grab now that we have seeked and saturated - if (seek_absolute) - *click_delta_to_grab_center_v = clicked_v_norm - grab_v_norm - grab_h_norm*0.5f; - } - - // Render - const ImU32 grab_col = GetColorU32(held ? ImGuiCol_ScrollbarGrabActive : hovered ? ImGuiCol_ScrollbarGrabHovered : ImGuiCol_ScrollbarGrab); - ImRect grab_rect; - if (horizontal) - grab_rect = ImRect(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm), bb.Min.y, ImMin(ImLerp(bb.Min.x, bb.Max.x, grab_v_norm) + grab_h_pixels, window_rect.Max.x), bb.Max.y); - else - grab_rect = ImRect(bb.Min.x, ImLerp(bb.Min.y, bb.Max.y, grab_v_norm), bb.Max.x, ImMin(ImLerp(bb.Min.y, bb.Max.y, grab_v_norm) + grab_h_pixels, window_rect.Max.y)); - window->DrawList->AddRectFilled(grab_rect.Min, grab_rect.Max, grab_col, style.ScrollbarRounding); -} - -void ImGui::BringWindowToFront(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* current_front_window = g.Windows.back(); - if (current_front_window == window || current_front_window->RootWindow == window) - return; - for (int i = g.Windows.Size - 2; i >= 0; i--) // We can ignore the front most window - if (g.Windows[i] == window) - { - g.Windows.erase(g.Windows.Data + i); - g.Windows.push_back(window); - break; - } -} - -void ImGui::BringWindowToBack(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - if (g.Windows[0] == window) - return; - for (int i = 0; i < g.Windows.Size; i++) - if (g.Windows[i] == window) - { - memmove(&g.Windows[1], &g.Windows[0], (size_t)i * sizeof(ImGuiWindow*)); - g.Windows[0] = window; - break; - } -} - -// Moving window to front of display and set focus (which happens to be back of our sorted list) -void ImGui::FocusWindow(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - - if (g.NavWindow != window) - { - g.NavWindow = window; - if (window && g.NavDisableMouseHover) - g.NavMousePosDirty = true; - g.NavInitRequest = false; - g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId - g.NavIdIsAlive = false; - g.NavLayer = 0; - //printf("[%05d] FocusWindow(\"%s\")\n", g.FrameCount, window ? window->Name : NULL); - } - - // Passing NULL allow to disable keyboard focus - if (!window) - return; - - // Move the root window to the top of the pile - if (window->RootWindow) - window = window->RootWindow; - - // Steal focus on active widgets - if (window->Flags & ImGuiWindowFlags_Popup) // FIXME: This statement should be unnecessary. Need further testing before removing it.. - if (g.ActiveId != 0 && g.ActiveIdWindow && g.ActiveIdWindow->RootWindow != window) - ClearActiveID(); - - // Bring to front - if (!(window->Flags & ImGuiWindowFlags_NoBringToFrontOnFocus)) - BringWindowToFront(window); -} - -void ImGui::FocusFrontMostActiveWindow(ImGuiWindow* ignore_window) -{ - ImGuiContext& g = *GImGui; - for (int i = g.Windows.Size - 1; i >= 0; i--) - if (g.Windows[i] != ignore_window && g.Windows[i]->WasActive && !(g.Windows[i]->Flags & ImGuiWindowFlags_ChildWindow)) - { - ImGuiWindow* focus_window = NavRestoreLastChildNavWindow(g.Windows[i]); - FocusWindow(focus_window); - return; - } -} - -void ImGui::PushItemWidth(float item_width) -{ - ImGuiWindow* window = GetCurrentWindow(); - window->DC.ItemWidth = (item_width == 0.0f ? window->ItemWidthDefault : item_width); - window->DC.ItemWidthStack.push_back(window->DC.ItemWidth); -} - -void ImGui::PushMultiItemsWidths(int components, float w_full) -{ - ImGuiWindow* window = GetCurrentWindow(); - const ImGuiStyle& style = GImGui->Style; - if (w_full <= 0.0f) - w_full = CalcItemWidth(); - const float w_item_one = ImMax(1.0f, (float)(int)((w_full - (style.ItemInnerSpacing.x) * (components-1)) / (float)components)); - const float w_item_last = ImMax(1.0f, (float)(int)(w_full - (w_item_one + style.ItemInnerSpacing.x) * (components-1))); - window->DC.ItemWidthStack.push_back(w_item_last); - for (int i = 0; i < components-1; i++) - window->DC.ItemWidthStack.push_back(w_item_one); - window->DC.ItemWidth = window->DC.ItemWidthStack.back(); -} - -void ImGui::PopItemWidth() -{ - ImGuiWindow* window = GetCurrentWindow(); - window->DC.ItemWidthStack.pop_back(); - window->DC.ItemWidth = window->DC.ItemWidthStack.empty() ? window->ItemWidthDefault : window->DC.ItemWidthStack.back(); -} - -float ImGui::CalcItemWidth() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - float w = window->DC.ItemWidth; - if (w < 0.0f) - { - // Align to a right-side limit. We include 1 frame padding in the calculation because this is how the width is always used (we add 2 frame padding to it), but we could move that responsibility to the widget as well. - float width_to_right_edge = GetContentRegionAvail().x; - w = ImMax(1.0f, width_to_right_edge + w); - } - w = (float)(int)w; - return w; -} - -static ImFont* GetDefaultFont() -{ - ImGuiContext& g = *GImGui; - return g.IO.FontDefault ? g.IO.FontDefault : g.IO.Fonts->Fonts[0]; -} - -void ImGui::SetCurrentFont(ImFont* font) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(font && font->IsLoaded()); // Font Atlas not created. Did you call io.Fonts->GetTexDataAsRGBA32 / GetTexDataAsAlpha8 ? - IM_ASSERT(font->Scale > 0.0f); - g.Font = font; - g.FontBaseSize = g.IO.FontGlobalScale * g.Font->FontSize * g.Font->Scale; - g.FontSize = g.CurrentWindow ? g.CurrentWindow->CalcFontSize() : 0.0f; - - ImFontAtlas* atlas = g.Font->ContainerAtlas; - g.DrawListSharedData.TexUvWhitePixel = atlas->TexUvWhitePixel; - g.DrawListSharedData.Font = g.Font; - g.DrawListSharedData.FontSize = g.FontSize; -} - -void ImGui::PushFont(ImFont* font) -{ - ImGuiContext& g = *GImGui; - if (!font) - font = GetDefaultFont(); - SetCurrentFont(font); - g.FontStack.push_back(font); - g.CurrentWindow->DrawList->PushTextureID(font->ContainerAtlas->TexID); -} - -void ImGui::PopFont() -{ - ImGuiContext& g = *GImGui; - g.CurrentWindow->DrawList->PopTextureID(); - g.FontStack.pop_back(); - SetCurrentFont(g.FontStack.empty() ? GetDefaultFont() : g.FontStack.back()); -} - -void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (enabled) - window->DC.ItemFlags |= option; - else - window->DC.ItemFlags &= ~option; - window->DC.ItemFlagsStack.push_back(window->DC.ItemFlags); -} - -void ImGui::PopItemFlag() -{ - ImGuiWindow* window = GetCurrentWindow(); - window->DC.ItemFlagsStack.pop_back(); - window->DC.ItemFlags = window->DC.ItemFlagsStack.empty() ? ImGuiItemFlags_Default_ : window->DC.ItemFlagsStack.back(); -} - -void ImGui::PushAllowKeyboardFocus(bool allow_keyboard_focus) -{ - PushItemFlag(ImGuiItemFlags_AllowKeyboardFocus, allow_keyboard_focus); -} - -void ImGui::PopAllowKeyboardFocus() -{ - PopItemFlag(); -} - -void ImGui::PushButtonRepeat(bool repeat) -{ - PushItemFlag(ImGuiItemFlags_ButtonRepeat, repeat); -} - -void ImGui::PopButtonRepeat() -{ - PopItemFlag(); -} - -void ImGui::PushTextWrapPos(float wrap_pos_x) -{ - ImGuiWindow* window = GetCurrentWindow(); - window->DC.TextWrapPos = wrap_pos_x; - window->DC.TextWrapPosStack.push_back(wrap_pos_x); -} - -void ImGui::PopTextWrapPos() -{ - ImGuiWindow* window = GetCurrentWindow(); - window->DC.TextWrapPosStack.pop_back(); - window->DC.TextWrapPos = window->DC.TextWrapPosStack.empty() ? -1.0f : window->DC.TextWrapPosStack.back(); -} - -// FIXME: This may incur a round-trip (if the end user got their data from a float4) but eventually we aim to store the in-flight colors as ImU32 -void ImGui::PushStyleColor(ImGuiCol idx, ImU32 col) -{ - ImGuiContext& g = *GImGui; - ImGuiColMod backup; - backup.Col = idx; - backup.BackupValue = g.Style.Colors[idx]; - g.ColorModifiers.push_back(backup); - g.Style.Colors[idx] = ColorConvertU32ToFloat4(col); -} - -void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col) -{ - ImGuiContext& g = *GImGui; - ImGuiColMod backup; - backup.Col = idx; - backup.BackupValue = g.Style.Colors[idx]; - g.ColorModifiers.push_back(backup); - g.Style.Colors[idx] = col; -} - -void ImGui::PopStyleColor(int count) -{ - ImGuiContext& g = *GImGui; - while (count > 0) - { - ImGuiColMod& backup = g.ColorModifiers.back(); - g.Style.Colors[backup.Col] = backup.BackupValue; - g.ColorModifiers.pop_back(); - count--; - } -} - -struct ImGuiStyleVarInfo -{ - ImGuiDataType Type; - ImU32 Count; - ImU32 Offset; - void* GetVarPtr(ImGuiStyle* style) const { return (void*)((unsigned char*)style + Offset); } -}; - -static const ImGuiStyleVarInfo GStyleVarInfo[] = -{ - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, Alpha) }, // ImGuiStyleVar_Alpha - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowPadding) }, // ImGuiStyleVar_WindowPadding - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowRounding) }, // ImGuiStyleVar_WindowRounding - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowBorderSize) }, // ImGuiStyleVar_WindowBorderSize - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowMinSize) }, // ImGuiStyleVar_WindowMinSize - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, WindowTitleAlign) }, // ImGuiStyleVar_WindowTitleAlign - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildRounding) }, // ImGuiStyleVar_ChildRounding - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ChildBorderSize) }, // ImGuiStyleVar_ChildBorderSize - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupRounding) }, // ImGuiStyleVar_PopupRounding - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, PopupBorderSize) }, // ImGuiStyleVar_PopupBorderSize - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, FramePadding) }, // ImGuiStyleVar_FramePadding - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameRounding) }, // ImGuiStyleVar_FrameRounding - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, FrameBorderSize) }, // ImGuiStyleVar_FrameBorderSize - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemSpacing) }, // ImGuiStyleVar_ItemSpacing - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ItemInnerSpacing) }, // ImGuiStyleVar_ItemInnerSpacing - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, IndentSpacing) }, // ImGuiStyleVar_IndentSpacing - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarSize) }, // ImGuiStyleVar_ScrollbarSize - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, ScrollbarRounding) }, // ImGuiStyleVar_ScrollbarRounding - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabMinSize) }, // ImGuiStyleVar_GrabMinSize - { ImGuiDataType_Float, 1, (ImU32)IM_OFFSETOF(ImGuiStyle, GrabRounding) }, // ImGuiStyleVar_GrabRounding - { ImGuiDataType_Float, 2, (ImU32)IM_OFFSETOF(ImGuiStyle, ButtonTextAlign) }, // ImGuiStyleVar_ButtonTextAlign -}; - -static const ImGuiStyleVarInfo* GetStyleVarInfo(ImGuiStyleVar idx) -{ - IM_ASSERT(idx >= 0 && idx < ImGuiStyleVar_COUNT); - IM_ASSERT(IM_ARRAYSIZE(GStyleVarInfo) == ImGuiStyleVar_COUNT); - return &GStyleVarInfo[idx]; -} - -void ImGui::PushStyleVar(ImGuiStyleVar idx, float val) -{ - const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx); - if (var_info->Type == ImGuiDataType_Float && var_info->Count == 1) - { - ImGuiContext& g = *GImGui; - float* pvar = (float*)var_info->GetVarPtr(&g.Style); - g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar)); - *pvar = val; - return; - } - IM_ASSERT(0); // Called function with wrong-type? Variable is not a float. -} - -void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val) -{ - const ImGuiStyleVarInfo* var_info = GetStyleVarInfo(idx); - if (var_info->Type == ImGuiDataType_Float && var_info->Count == 2) - { - ImGuiContext& g = *GImGui; - ImVec2* pvar = (ImVec2*)var_info->GetVarPtr(&g.Style); - g.StyleModifiers.push_back(ImGuiStyleMod(idx, *pvar)); - *pvar = val; - return; - } - IM_ASSERT(0); // Called function with wrong-type? Variable is not a ImVec2. -} - -void ImGui::PopStyleVar(int count) -{ - ImGuiContext& g = *GImGui; - while (count > 0) - { - // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it. - ImGuiStyleMod& backup = g.StyleModifiers.back(); - const ImGuiStyleVarInfo* info = GetStyleVarInfo(backup.VarIdx); - void* data = info->GetVarPtr(&g.Style); - if (info->Type == ImGuiDataType_Float && info->Count == 1) { ((float*)data)[0] = backup.BackupFloat[0]; } - else if (info->Type == ImGuiDataType_Float && info->Count == 2) { ((float*)data)[0] = backup.BackupFloat[0]; ((float*)data)[1] = backup.BackupFloat[1]; } - g.StyleModifiers.pop_back(); - count--; - } -} - -const char* ImGui::GetStyleColorName(ImGuiCol idx) -{ - // Create switch-case from enum with regexp: ImGuiCol_{.*}, --> case ImGuiCol_\1: return "\1"; - switch (idx) - { - case ImGuiCol_Text: return "Text"; - case ImGuiCol_TextDisabled: return "TextDisabled"; - case ImGuiCol_WindowBg: return "WindowBg"; - case ImGuiCol_ChildBg: return "ChildBg"; - case ImGuiCol_PopupBg: return "PopupBg"; - case ImGuiCol_Border: return "Border"; - case ImGuiCol_BorderShadow: return "BorderShadow"; - case ImGuiCol_FrameBg: return "FrameBg"; - case ImGuiCol_FrameBgHovered: return "FrameBgHovered"; - case ImGuiCol_FrameBgActive: return "FrameBgActive"; - case ImGuiCol_TitleBg: return "TitleBg"; - case ImGuiCol_TitleBgActive: return "TitleBgActive"; - case ImGuiCol_TitleBgCollapsed: return "TitleBgCollapsed"; - case ImGuiCol_MenuBarBg: return "MenuBarBg"; - case ImGuiCol_ScrollbarBg: return "ScrollbarBg"; - case ImGuiCol_ScrollbarGrab: return "ScrollbarGrab"; - case ImGuiCol_ScrollbarGrabHovered: return "ScrollbarGrabHovered"; - case ImGuiCol_ScrollbarGrabActive: return "ScrollbarGrabActive"; - case ImGuiCol_CheckMark: return "CheckMark"; - case ImGuiCol_SliderGrab: return "SliderGrab"; - case ImGuiCol_SliderGrabActive: return "SliderGrabActive"; - case ImGuiCol_Button: return "Button"; - case ImGuiCol_ButtonHovered: return "ButtonHovered"; - case ImGuiCol_ButtonActive: return "ButtonActive"; - case ImGuiCol_Header: return "Header"; - case ImGuiCol_HeaderHovered: return "HeaderHovered"; - case ImGuiCol_HeaderActive: return "HeaderActive"; - case ImGuiCol_Separator: return "Separator"; - case ImGuiCol_SeparatorHovered: return "SeparatorHovered"; - case ImGuiCol_SeparatorActive: return "SeparatorActive"; - case ImGuiCol_ResizeGrip: return "ResizeGrip"; - case ImGuiCol_ResizeGripHovered: return "ResizeGripHovered"; - case ImGuiCol_ResizeGripActive: return "ResizeGripActive"; - case ImGuiCol_PlotLines: return "PlotLines"; - case ImGuiCol_PlotLinesHovered: return "PlotLinesHovered"; - case ImGuiCol_PlotHistogram: return "PlotHistogram"; - case ImGuiCol_PlotHistogramHovered: return "PlotHistogramHovered"; - case ImGuiCol_TextSelectedBg: return "TextSelectedBg"; - case ImGuiCol_ModalWindowDarkening: return "ModalWindowDarkening"; - case ImGuiCol_DragDropTarget: return "DragDropTarget"; - case ImGuiCol_NavHighlight: return "NavHighlight"; - case ImGuiCol_NavWindowingHighlight: return "NavWindowingHighlight"; - } - IM_ASSERT(0); - return "Unknown"; -} - -bool ImGui::IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent) -{ - if (window->RootWindow == potential_parent) - return true; - while (window != NULL) - { - if (window == potential_parent) - return true; - window = window->ParentWindow; - } - return false; -} - -bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) -{ - IM_ASSERT((flags & ImGuiHoveredFlags_AllowWhenOverlapped) == 0); // Flags not supported by this function - ImGuiContext& g = *GImGui; - - if (flags & ImGuiHoveredFlags_AnyWindow) - { - if (g.HoveredWindow == NULL) - return false; - } - else - { - switch (flags & (ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows)) - { - case ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows: - if (g.HoveredRootWindow != g.CurrentWindow->RootWindow) - return false; - break; - case ImGuiHoveredFlags_RootWindow: - if (g.HoveredWindow != g.CurrentWindow->RootWindow) - return false; - break; - case ImGuiHoveredFlags_ChildWindows: - if (g.HoveredWindow == NULL || !IsWindowChildOf(g.HoveredWindow, g.CurrentWindow)) - return false; - break; - default: - if (g.HoveredWindow != g.CurrentWindow) - return false; - break; - } - } - - if (!IsWindowContentHoverable(g.HoveredRootWindow, flags)) - return false; - if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) - if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != g.HoveredWindow->MoveId) - return false; - return true; -} - -bool ImGui::IsWindowFocused(ImGuiFocusedFlags flags) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.CurrentWindow); // Not inside a Begin()/End() - - if (flags & ImGuiFocusedFlags_AnyWindow) - return g.NavWindow != NULL; - - switch (flags & (ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows)) - { - case ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows: - return g.NavWindow && g.NavWindow->RootWindow == g.CurrentWindow->RootWindow; - case ImGuiFocusedFlags_RootWindow: - return g.NavWindow == g.CurrentWindow->RootWindow; - case ImGuiFocusedFlags_ChildWindows: - return g.NavWindow && IsWindowChildOf(g.NavWindow, g.CurrentWindow); - default: - return g.NavWindow == g.CurrentWindow; - } -} - -// Can we focus this window with CTRL+TAB (or PadMenu + PadFocusPrev/PadFocusNext) -bool ImGui::IsWindowNavFocusable(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - return window->Active && window == window->RootWindowForTabbing && (!(window->Flags & ImGuiWindowFlags_NoNavFocus) || window == g.NavWindow); -} - -float ImGui::GetWindowWidth() -{ - ImGuiWindow* window = GImGui->CurrentWindow; - return window->Size.x; -} - -float ImGui::GetWindowHeight() -{ - ImGuiWindow* window = GImGui->CurrentWindow; - return window->Size.y; -} - -ImVec2 ImGui::GetWindowPos() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - return window->Pos; -} - -static void SetWindowScrollX(ImGuiWindow* window, float new_scroll_x) -{ - window->DC.CursorMaxPos.x += window->Scroll.x; // SizeContents is generally computed based on CursorMaxPos which is affected by scroll position, so we need to apply our change to it. - window->Scroll.x = new_scroll_x; - window->DC.CursorMaxPos.x -= window->Scroll.x; -} - -static void SetWindowScrollY(ImGuiWindow* window, float new_scroll_y) -{ - window->DC.CursorMaxPos.y += window->Scroll.y; // SizeContents is generally computed based on CursorMaxPos which is affected by scroll position, so we need to apply our change to it. - window->Scroll.y = new_scroll_y; - window->DC.CursorMaxPos.y -= window->Scroll.y; -} - -static void SetWindowPos(ImGuiWindow* window, const ImVec2& pos, ImGuiCond cond) -{ - // Test condition (NB: bit 0 is always true) and clear flags for next time - if (cond && (window->SetWindowPosAllowFlags & cond) == 0) - return; - - IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. - window->SetWindowPosAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); - window->SetWindowPosVal = ImVec2(FLT_MAX, FLT_MAX); - - // Set - const ImVec2 old_pos = window->Pos; - window->Pos = ImFloor(pos); - window->DC.CursorPos += (window->Pos - old_pos); // As we happen to move the window while it is being appended to (which is a bad idea - will smear) let's at least offset the cursor - window->DC.CursorMaxPos += (window->Pos - old_pos); // And more importantly we need to adjust this so size calculation doesn't get affected. -} - -void ImGui::SetWindowPos(const ImVec2& pos, ImGuiCond cond) -{ - ImGuiWindow* window = GetCurrentWindowRead(); - SetWindowPos(window, pos, cond); -} - -void ImGui::SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond) -{ - if (ImGuiWindow* window = FindWindowByName(name)) - SetWindowPos(window, pos, cond); -} - -ImVec2 ImGui::GetWindowSize() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->Size; -} - -static void SetWindowSize(ImGuiWindow* window, const ImVec2& size, ImGuiCond cond) -{ - // Test condition (NB: bit 0 is always true) and clear flags for next time - if (cond && (window->SetWindowSizeAllowFlags & cond) == 0) - return; - - IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. - window->SetWindowSizeAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); - - // Set - if (size.x > 0.0f) - { - window->AutoFitFramesX = 0; - window->SizeFull.x = size.x; - } - else - { - window->AutoFitFramesX = 2; - window->AutoFitOnlyGrows = false; - } - if (size.y > 0.0f) - { - window->AutoFitFramesY = 0; - window->SizeFull.y = size.y; - } - else - { - window->AutoFitFramesY = 2; - window->AutoFitOnlyGrows = false; - } -} - -void ImGui::SetWindowSize(const ImVec2& size, ImGuiCond cond) -{ - SetWindowSize(GImGui->CurrentWindow, size, cond); -} - -void ImGui::SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond) -{ - if (ImGuiWindow* window = FindWindowByName(name)) - SetWindowSize(window, size, cond); -} - -static void SetWindowCollapsed(ImGuiWindow* window, bool collapsed, ImGuiCond cond) -{ - // Test condition (NB: bit 0 is always true) and clear flags for next time - if (cond && (window->SetWindowCollapsedAllowFlags & cond) == 0) - return; - window->SetWindowCollapsedAllowFlags &= ~(ImGuiCond_Once | ImGuiCond_FirstUseEver | ImGuiCond_Appearing); - - // Set - window->Collapsed = collapsed; -} - -void ImGui::SetWindowCollapsed(bool collapsed, ImGuiCond cond) -{ - SetWindowCollapsed(GImGui->CurrentWindow, collapsed, cond); -} - -bool ImGui::IsWindowCollapsed() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->Collapsed; -} - -bool ImGui::IsWindowAppearing() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->Appearing; -} - -void ImGui::SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond) -{ - if (ImGuiWindow* window = FindWindowByName(name)) - SetWindowCollapsed(window, collapsed, cond); -} - -void ImGui::SetWindowFocus() -{ - FocusWindow(GImGui->CurrentWindow); -} - -void ImGui::SetWindowFocus(const char* name) -{ - if (name) - { - if (ImGuiWindow* window = FindWindowByName(name)) - FocusWindow(window); - } - else - { - FocusWindow(NULL); - } -} - -void ImGui::SetNextWindowPos(const ImVec2& pos, ImGuiCond cond, const ImVec2& pivot) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. - g.NextWindowData.PosVal = pos; - g.NextWindowData.PosPivotVal = pivot; - g.NextWindowData.PosCond = cond ? cond : ImGuiCond_Always; -} - -void ImGui::SetNextWindowSize(const ImVec2& size, ImGuiCond cond) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. - g.NextWindowData.SizeVal = size; - g.NextWindowData.SizeCond = cond ? cond : ImGuiCond_Always; -} - -void ImGui::SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback, void* custom_callback_user_data) -{ - ImGuiContext& g = *GImGui; - g.NextWindowData.SizeConstraintCond = ImGuiCond_Always; - g.NextWindowData.SizeConstraintRect = ImRect(size_min, size_max); - g.NextWindowData.SizeCallback = custom_callback; - g.NextWindowData.SizeCallbackUserData = custom_callback_user_data; -} - -void ImGui::SetNextWindowContentSize(const ImVec2& size) -{ - ImGuiContext& g = *GImGui; - g.NextWindowData.ContentSizeVal = size; // In Begin() we will add the size of window decorations (title bar, menu etc.) to that to form a SizeContents value. - g.NextWindowData.ContentSizeCond = ImGuiCond_Always; -} - -void ImGui::SetNextWindowCollapsed(bool collapsed, ImGuiCond cond) -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(cond == 0 || ImIsPowerOfTwo(cond)); // Make sure the user doesn't attempt to combine multiple condition flags. - g.NextWindowData.CollapsedVal = collapsed; - g.NextWindowData.CollapsedCond = cond ? cond : ImGuiCond_Always; -} - -void ImGui::SetNextWindowFocus() -{ - ImGuiContext& g = *GImGui; - g.NextWindowData.FocusCond = ImGuiCond_Always; // Using a Cond member for consistency (may transition all of them to single flag set for fast Clear() op) -} - -void ImGui::SetNextWindowBgAlpha(float alpha) -{ - ImGuiContext& g = *GImGui; - g.NextWindowData.BgAlphaVal = alpha; - g.NextWindowData.BgAlphaCond = ImGuiCond_Always; // Using a Cond member for consistency (may transition all of them to single flag set for fast Clear() op) -} - -// In window space (not screen space!) -ImVec2 ImGui::GetContentRegionMax() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - ImVec2 mx = window->ContentsRegionRect.Max - window->Pos; - if (window->DC.ColumnsSet) - mx.x = GetColumnOffset(window->DC.ColumnsSet->Current + 1) - window->WindowPadding.x; - return mx; -} - -ImVec2 ImGui::GetContentRegionAvail() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return GetContentRegionMax() - (window->DC.CursorPos - window->Pos); -} - -float ImGui::GetContentRegionAvailWidth() -{ - return GetContentRegionAvail().x; -} - -// In window space (not screen space!) -ImVec2 ImGui::GetWindowContentRegionMin() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->ContentsRegionRect.Min - window->Pos; -} - -ImVec2 ImGui::GetWindowContentRegionMax() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->ContentsRegionRect.Max - window->Pos; -} - -float ImGui::GetWindowContentRegionWidth() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->ContentsRegionRect.GetWidth(); -} - -float ImGui::GetTextLineHeight() -{ - ImGuiContext& g = *GImGui; - return g.FontSize; -} - -float ImGui::GetTextLineHeightWithSpacing() -{ - ImGuiContext& g = *GImGui; - return g.FontSize + g.Style.ItemSpacing.y; -} - -float ImGui::GetFrameHeight() -{ - ImGuiContext& g = *GImGui; - return g.FontSize + g.Style.FramePadding.y * 2.0f; -} - -float ImGui::GetFrameHeightWithSpacing() -{ - ImGuiContext& g = *GImGui; - return g.FontSize + g.Style.FramePadding.y * 2.0f + g.Style.ItemSpacing.y; -} - -ImDrawList* ImGui::GetWindowDrawList() -{ - ImGuiWindow* window = GetCurrentWindow(); - return window->DrawList; -} - -ImFont* ImGui::GetFont() -{ - return GImGui->Font; -} - -float ImGui::GetFontSize() -{ - return GImGui->FontSize; -} - -ImVec2 ImGui::GetFontTexUvWhitePixel() -{ - return GImGui->DrawListSharedData.TexUvWhitePixel; -} - -void ImGui::SetWindowFontScale(float scale) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - window->FontWindowScale = scale; - g.FontSize = g.DrawListSharedData.FontSize = window->CalcFontSize(); -} - -// User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient. -// Conversion happens as we pass the value to user, but it makes our naming convention confusing because GetCursorPos() == (DC.CursorPos - window.Pos). May want to rename 'DC.CursorPos'. -ImVec2 ImGui::GetCursorPos() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->DC.CursorPos - window->Pos + window->Scroll; -} - -float ImGui::GetCursorPosX() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->DC.CursorPos.x - window->Pos.x + window->Scroll.x; -} - -float ImGui::GetCursorPosY() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->DC.CursorPos.y - window->Pos.y + window->Scroll.y; -} - -void ImGui::SetCursorPos(const ImVec2& local_pos) -{ - ImGuiWindow* window = GetCurrentWindow(); - window->DC.CursorPos = window->Pos - window->Scroll + local_pos; - window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos); -} - -void ImGui::SetCursorPosX(float x) -{ - ImGuiWindow* window = GetCurrentWindow(); - window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x; - window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x); -} - -void ImGui::SetCursorPosY(float y) -{ - ImGuiWindow* window = GetCurrentWindow(); - window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y; - window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y); -} - -ImVec2 ImGui::GetCursorStartPos() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->DC.CursorStartPos - window->Pos; -} - -ImVec2 ImGui::GetCursorScreenPos() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->DC.CursorPos; -} - -void ImGui::SetCursorScreenPos(const ImVec2& screen_pos) -{ - ImGuiWindow* window = GetCurrentWindow(); - window->DC.CursorPos = screen_pos; - window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos); -} - -float ImGui::GetScrollX() -{ - return GImGui->CurrentWindow->Scroll.x; -} - -float ImGui::GetScrollY() -{ - return GImGui->CurrentWindow->Scroll.y; -} - -float ImGui::GetScrollMaxX() -{ - return GetScrollMaxX(GImGui->CurrentWindow); -} - -float ImGui::GetScrollMaxY() -{ - return GetScrollMaxY(GImGui->CurrentWindow); -} - -void ImGui::SetScrollX(float scroll_x) -{ - ImGuiWindow* window = GetCurrentWindow(); - window->ScrollTarget.x = scroll_x; - window->ScrollTargetCenterRatio.x = 0.0f; -} - -void ImGui::SetScrollY(float scroll_y) -{ - ImGuiWindow* window = GetCurrentWindow(); - window->ScrollTarget.y = scroll_y + window->TitleBarHeight() + window->MenuBarHeight(); // title bar height canceled out when using ScrollTargetRelY - window->ScrollTargetCenterRatio.y = 0.0f; -} - -void ImGui::SetScrollFromPosY(float pos_y, float center_y_ratio) -{ - // We store a target position so centering can occur on the next frame when we are guaranteed to have a known window size - ImGuiWindow* window = GetCurrentWindow(); - IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f); - window->ScrollTarget.y = (float)(int)(pos_y + window->Scroll.y); - window->ScrollTargetCenterRatio.y = center_y_ratio; -} - -// center_y_ratio: 0.0f top of last item, 0.5f vertical center of last item, 1.0f bottom of last item. -void ImGui::SetScrollHere(float center_y_ratio) -{ - ImGuiWindow* window = GetCurrentWindow(); - float target_y = window->DC.CursorPosPrevLine.y - window->Pos.y; // Top of last item, in window space - target_y += (window->DC.PrevLineHeight * center_y_ratio) + (GImGui->Style.ItemSpacing.y * (center_y_ratio - 0.5f) * 2.0f); // Precisely aim above, in the middle or below the last line. - SetScrollFromPosY(target_y, center_y_ratio); -} - -void ImGui::ActivateItem(ImGuiID id) -{ - ImGuiContext& g = *GImGui; - g.NavNextActivateId = id; -} - -void ImGui::SetKeyboardFocusHere(int offset) -{ - IM_ASSERT(offset >= -1); // -1 is allowed but not below - ImGuiWindow* window = GetCurrentWindow(); - window->FocusIdxAllRequestNext = window->FocusIdxAllCounter + 1 + offset; - window->FocusIdxTabRequestNext = INT_MAX; -} - -void ImGui::SetItemDefaultFocus() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (!window->Appearing) - return; - if (g.NavWindow == window->RootWindowForNav && (g.NavInitRequest || g.NavInitResultId != 0) && g.NavLayer == g.NavWindow->DC.NavLayerCurrent) - { - g.NavInitRequest = false; - g.NavInitResultId = g.NavWindow->DC.LastItemId; - g.NavInitResultRectRel = ImRect(g.NavWindow->DC.LastItemRect.Min - g.NavWindow->Pos, g.NavWindow->DC.LastItemRect.Max - g.NavWindow->Pos); - NavUpdateAnyRequestFlag(); - if (!IsItemVisible()) - SetScrollHere(); - } -} - -void ImGui::SetStateStorage(ImGuiStorage* tree) -{ - ImGuiWindow* window = GetCurrentWindow(); - window->DC.StateStorage = tree ? tree : &window->StateStorage; -} - -ImGuiStorage* ImGui::GetStateStorage() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->DC.StateStorage; -} - -void ImGui::TextV(const char* fmt, va_list args) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - const char* text_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); - TextUnformatted(g.TempBuffer, text_end); -} - -void ImGui::Text(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - TextV(fmt, args); - va_end(args); -} - -void ImGui::TextColoredV(const ImVec4& col, const char* fmt, va_list args) -{ - PushStyleColor(ImGuiCol_Text, col); - TextV(fmt, args); - PopStyleColor(); -} - -void ImGui::TextColored(const ImVec4& col, const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - TextColoredV(col, fmt, args); - va_end(args); -} - -void ImGui::TextDisabledV(const char* fmt, va_list args) -{ - PushStyleColor(ImGuiCol_Text, GImGui->Style.Colors[ImGuiCol_TextDisabled]); - TextV(fmt, args); - PopStyleColor(); -} - -void ImGui::TextDisabled(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - TextDisabledV(fmt, args); - va_end(args); -} - -void ImGui::TextWrappedV(const char* fmt, va_list args) -{ - bool need_wrap = (GImGui->CurrentWindow->DC.TextWrapPos < 0.0f); // Keep existing wrap position is one ia already set - if (need_wrap) PushTextWrapPos(0.0f); - TextV(fmt, args); - if (need_wrap) PopTextWrapPos(); -} - -void ImGui::TextWrapped(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - TextWrappedV(fmt, args); - va_end(args); -} - -void ImGui::TextUnformatted(const char* text, const char* text_end) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - IM_ASSERT(text != NULL); - const char* text_begin = text; - if (text_end == NULL) - text_end = text + strlen(text); // FIXME-OPT - - const ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrentLineTextBaseOffset); - const float wrap_pos_x = window->DC.TextWrapPos; - const bool wrap_enabled = wrap_pos_x >= 0.0f; - if (text_end - text > 2000 && !wrap_enabled) - { - // Long text! - // Perform manual coarse clipping to optimize for long multi-line text - // From this point we will only compute the width of lines that are visible. Optimization only available when word-wrapping is disabled. - // We also don't vertically center the text within the line full height, which is unlikely to matter because we are likely the biggest and only item on the line. - const char* line = text; - const float line_height = GetTextLineHeight(); - const ImRect clip_rect = window->ClipRect; - ImVec2 text_size(0,0); - - if (text_pos.y <= clip_rect.Max.y) - { - ImVec2 pos = text_pos; - - // Lines to skip (can't skip when logging text) - if (!g.LogEnabled) - { - int lines_skippable = (int)((clip_rect.Min.y - text_pos.y) / line_height); - if (lines_skippable > 0) - { - int lines_skipped = 0; - while (line < text_end && lines_skipped < lines_skippable) - { - const char* line_end = strchr(line, '\n'); - if (!line_end) - line_end = text_end; - line = line_end + 1; - lines_skipped++; - } - pos.y += lines_skipped * line_height; - } - } - - // Lines to render - if (line < text_end) - { - ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height)); - while (line < text_end) - { - const char* line_end = strchr(line, '\n'); - if (IsClippedEx(line_rect, 0, false)) - break; - - const ImVec2 line_size = CalcTextSize(line, line_end, false); - text_size.x = ImMax(text_size.x, line_size.x); - RenderText(pos, line, line_end, false); - if (!line_end) - line_end = text_end; - line = line_end + 1; - line_rect.Min.y += line_height; - line_rect.Max.y += line_height; - pos.y += line_height; - } - - // Count remaining lines - int lines_skipped = 0; - while (line < text_end) - { - const char* line_end = strchr(line, '\n'); - if (!line_end) - line_end = text_end; - line = line_end + 1; - lines_skipped++; - } - pos.y += lines_skipped * line_height; - } - - text_size.y += (pos - text_pos).y; - } - - ImRect bb(text_pos, text_pos + text_size); - ItemSize(bb); - ItemAdd(bb, 0); - } - else - { - const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0f; - const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width); - - // Account of baseline offset - ImRect bb(text_pos, text_pos + text_size); - ItemSize(text_size); - if (!ItemAdd(bb, 0)) - return; - - // Render (we don't hide text after ## in this end-user function) - RenderTextWrapped(bb.Min, text_begin, text_end, wrap_width); - } -} - -void ImGui::AlignTextToFramePadding() -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - window->DC.CurrentLineHeight = ImMax(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y * 2); - window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.CurrentLineTextBaseOffset, g.Style.FramePadding.y); -} - -// Add a label+text combo aligned to other label+value widgets -void ImGui::LabelTextV(const char* label, const char* fmt, va_list args) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const float w = CalcItemWidth(); - - const ImVec2 label_size = CalcTextSize(label, NULL, true); - const ImRect value_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2)); - const ImRect total_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w + (label_size.x > 0.0f ? style.ItemInnerSpacing.x : 0.0f), style.FramePadding.y*2) + label_size); - ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, 0)) - return; - - // Render - const char* value_text_begin = &g.TempBuffer[0]; - const char* value_text_end = value_text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); - RenderTextClipped(value_bb.Min, value_bb.Max, value_text_begin, value_text_end, NULL, ImVec2(0.0f,0.5f)); - if (label_size.x > 0.0f) - RenderText(ImVec2(value_bb.Max.x + style.ItemInnerSpacing.x, value_bb.Min.y + style.FramePadding.y), label); -} - -void ImGui::LabelText(const char* label, const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - LabelTextV(label, fmt, args); - va_end(args); -} - -bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - - if (flags & ImGuiButtonFlags_Disabled) - { - if (out_hovered) *out_hovered = false; - if (out_held) *out_held = false; - if (g.ActiveId == id) ClearActiveID(); - return false; - } - - // Default behavior requires click+release on same spot - if ((flags & (ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick)) == 0) - flags |= ImGuiButtonFlags_PressedOnClickRelease; - - ImGuiWindow* backup_hovered_window = g.HoveredWindow; - if ((flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredRootWindow == window) - g.HoveredWindow = window; - - bool pressed = false; - bool hovered = ItemHoverable(bb, id); - - // Drag source doesn't report as hovered - if (hovered && g.DragDropActive && g.DragDropPayload.SourceId == id) - hovered = false; - - // Special mode for Drag and Drop where holding button pressed for a long time while dragging another item triggers the button - if (g.DragDropActive && (flags & ImGuiButtonFlags_PressedOnDragDropHold) && !(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoHoldToOpenOthers)) - if (IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) - { - hovered = true; - SetHoveredID(id); - if (CalcTypematicPressedRepeatAmount(g.HoveredIdTimer + 0.0001f, g.HoveredIdTimer + 0.0001f - g.IO.DeltaTime, 0.01f, 0.70f)) // FIXME: Our formula for CalcTypematicPressedRepeatAmount() is fishy - { - pressed = true; - FocusWindow(window); - } - } - - if ((flags & ImGuiButtonFlags_FlattenChildren) && g.HoveredRootWindow == window) - g.HoveredWindow = backup_hovered_window; - - // AllowOverlap mode (rarely used) requires previous frame HoveredId to be null or to match. This allows using patterns where a later submitted widget overlaps a previous one. - if (hovered && (flags & ImGuiButtonFlags_AllowItemOverlap) && (g.HoveredIdPreviousFrame != id && g.HoveredIdPreviousFrame != 0)) - hovered = false; - - // Mouse - if (hovered) - { - if (!(flags & ImGuiButtonFlags_NoKeyModifiers) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt)) - { - // | CLICKING | HOLDING with ImGuiButtonFlags_Repeat - // PressedOnClickRelease | * | .. (NOT on release) <-- MOST COMMON! (*) only if both click/release were over bounds - // PressedOnClick | | .. - // PressedOnRelease | | .. (NOT on release) - // PressedOnDoubleClick | | .. - // FIXME-NAV: We don't honor those different behaviors. - if ((flags & ImGuiButtonFlags_PressedOnClickRelease) && g.IO.MouseClicked[0]) - { - SetActiveID(id, window); - if (!(flags & ImGuiButtonFlags_NoNavFocus)) - SetFocusID(id, window); - FocusWindow(window); - } - if (((flags & ImGuiButtonFlags_PressedOnClick) && g.IO.MouseClicked[0]) || ((flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseDoubleClicked[0])) - { - pressed = true; - if (flags & ImGuiButtonFlags_NoHoldingActiveID) - ClearActiveID(); - else - SetActiveID(id, window); // Hold on ID - FocusWindow(window); - } - if ((flags & ImGuiButtonFlags_PressedOnRelease) && g.IO.MouseReleased[0]) - { - if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay)) // Repeat mode trumps - pressed = true; - ClearActiveID(); - } - - // 'Repeat' mode acts when held regardless of _PressedOn flags (see table above). - // Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings. - if ((flags & ImGuiButtonFlags_Repeat) && g.ActiveId == id && g.IO.MouseDownDuration[0] > 0.0f && IsMouseClicked(0, true)) - pressed = true; - } - - if (pressed) - g.NavDisableHighlight = true; - } - - // Gamepad/Keyboard navigation - // We report navigated item as hovered but we don't set g.HoveredId to not interfere with mouse. - if (g.NavId == id && !g.NavDisableHighlight && g.NavDisableMouseHover && (g.ActiveId == 0 || g.ActiveId == id || g.ActiveId == window->MoveId)) - hovered = true; - - if (g.NavActivateDownId == id) - { - bool nav_activated_by_code = (g.NavActivateId == id); - bool nav_activated_by_inputs = IsNavInputPressed(ImGuiNavInput_Activate, (flags & ImGuiButtonFlags_Repeat) ? ImGuiInputReadMode_Repeat : ImGuiInputReadMode_Pressed); - if (nav_activated_by_code || nav_activated_by_inputs) - pressed = true; - if (nav_activated_by_code || nav_activated_by_inputs || g.ActiveId == id) - { - // Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button. - g.NavActivateId = id; // This is so SetActiveId assign a Nav source - SetActiveID(id, window); - if (!(flags & ImGuiButtonFlags_NoNavFocus)) - SetFocusID(id, window); - g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right) | (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); - } - } - - bool held = false; - if (g.ActiveId == id) - { - if (g.ActiveIdSource == ImGuiInputSource_Mouse) - { - if (g.ActiveIdIsJustActivated) - g.ActiveIdClickOffset = g.IO.MousePos - bb.Min; - if (g.IO.MouseDown[0]) - { - held = true; - } - else - { - if (hovered && (flags & ImGuiButtonFlags_PressedOnClickRelease)) - if (!((flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[0] >= g.IO.KeyRepeatDelay)) // Repeat mode trumps - if (!g.DragDropActive) - pressed = true; - ClearActiveID(); - } - if (!(flags & ImGuiButtonFlags_NoNavFocus)) - g.NavDisableHighlight = true; - } - else if (g.ActiveIdSource == ImGuiInputSource_Nav) - { - if (g.NavActivateDownId != id) - ClearActiveID(); - } - } - - if (out_hovered) *out_hovered = hovered; - if (out_held) *out_held = held; - - return pressed; -} - -bool ImGui::ButtonEx(const char* label, const ImVec2& size_arg, ImGuiButtonFlags flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - const ImVec2 label_size = CalcTextSize(label, NULL, true); - - ImVec2 pos = window->DC.CursorPos; - if ((flags & ImGuiButtonFlags_AlignTextBaseLine) && style.FramePadding.y < window->DC.CurrentLineTextBaseOffset) // Try to vertically align buttons that are smaller/have no padding so that text baseline matches (bit hacky, since it shouldn't be a flag) - pos.y += window->DC.CurrentLineTextBaseOffset - style.FramePadding.y; - ImVec2 size = CalcItemSize(size_arg, label_size.x + style.FramePadding.x * 2.0f, label_size.y + style.FramePadding.y * 2.0f); - - const ImRect bb(pos, pos + size); - ItemSize(bb, style.FramePadding.y); - if (!ItemAdd(bb, id)) - return false; - - if (window->DC.ItemFlags & ImGuiItemFlags_ButtonRepeat) - flags |= ImGuiButtonFlags_Repeat; - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held, flags); - if (pressed) - MarkItemValueChanged(id); - - // Render - const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); - RenderNavHighlight(bb, id); - RenderFrame(bb.Min, bb.Max, col, true, style.FrameRounding); - RenderTextClipped(bb.Min + style.FramePadding, bb.Max - style.FramePadding, label, NULL, &label_size, style.ButtonTextAlign, &bb); - - // Automatically close popups - //if (pressed && !(flags & ImGuiButtonFlags_DontClosePopups) && (window->Flags & ImGuiWindowFlags_Popup)) - // CloseCurrentPopup(); - - return pressed; -} - -bool ImGui::Button(const char* label, const ImVec2& size_arg) -{ - return ButtonEx(label, size_arg, 0); -} - -// Small buttons fits within text without additional vertical spacing. -bool ImGui::SmallButton(const char* label) -{ - ImGuiContext& g = *GImGui; - float backup_padding_y = g.Style.FramePadding.y; - g.Style.FramePadding.y = 0.0f; - bool pressed = ButtonEx(label, ImVec2(0,0), ImGuiButtonFlags_AlignTextBaseLine); - g.Style.FramePadding.y = backup_padding_y; - return pressed; -} - -bool ImGui::ArrowButton(const char* str_id, ImGuiDir dir) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiID id = window->GetID(str_id); - float sz = ImGui::GetFrameHeight(); - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(sz, sz)); - ItemSize(bb); - if (!ItemAdd(bb, id)) - return false; - - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held); - - // Render - const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); - RenderNavHighlight(bb, id); - RenderFrame(bb.Min, bb.Max, col, true, g.Style.FrameRounding); - RenderArrow(bb.Min + g.Style.FramePadding, dir); - - return pressed; -} - -// Tip: use ImGui::PushID()/PopID() to push indices or pointers in the ID stack. -// Then you can keep 'str_id' empty or the same for all your buttons (instead of creating a string based on a non-string id) -bool ImGui::InvisibleButton(const char* str_id, const ImVec2& size_arg) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - const ImGuiID id = window->GetID(str_id); - ImVec2 size = CalcItemSize(size_arg, 0.0f, 0.0f); - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); - ItemSize(bb); - if (!ItemAdd(bb, id)) - return false; - - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held); - - return pressed; -} - -// Button to close a window -bool ImGui::CloseButton(ImGuiID id, const ImVec2& pos, float radius) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - // We intentionally allow interaction when clipped so that a mechanical Alt,Right,Validate sequence close a window. - // (this isn't the regular behavior of buttons, but it doesn't affect the user much because navigation tends to keep items visible). - const ImRect bb(pos - ImVec2(radius,radius), pos + ImVec2(radius,radius)); - bool is_clipped = !ItemAdd(bb, id); - - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held); - if (is_clipped) - return pressed; - - // Render - ImVec2 center = bb.GetCenter(); - if (hovered) - window->DrawList->AddCircleFilled(center, ImMax(2.0f, radius), GetColorU32((held && hovered) ? ImGuiCol_ButtonActive : ImGuiCol_ButtonHovered), 9); - - float cross_extent = (radius * 0.7071f) - 1.0f; - ImU32 cross_col = GetColorU32(ImGuiCol_Text); - center -= ImVec2(0.5f, 0.5f); - window->DrawList->AddLine(center + ImVec2(+cross_extent,+cross_extent), center + ImVec2(-cross_extent,-cross_extent), cross_col, 1.0f); - window->DrawList->AddLine(center + ImVec2(+cross_extent,-cross_extent), center + ImVec2(-cross_extent,+cross_extent), cross_col, 1.0f); - - return pressed; -} - -void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& tint_col, const ImVec4& border_col) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); - if (border_col.w > 0.0f) - bb.Max += ImVec2(2,2); - ItemSize(bb); - if (!ItemAdd(bb, 0)) - return; - - if (border_col.w > 0.0f) - { - window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(border_col), 0.0f); - window->DrawList->AddImage(user_texture_id, bb.Min+ImVec2(1,1), bb.Max-ImVec2(1,1), uv0, uv1, GetColorU32(tint_col)); - } - else - { - window->DrawList->AddImage(user_texture_id, bb.Min, bb.Max, uv0, uv1, GetColorU32(tint_col)); - } -} - -// frame_padding < 0: uses FramePadding from style (default) -// frame_padding = 0: no framing -// frame_padding > 0: set framing size -// The color used are the button colors. -bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - - // Default to using texture ID as ID. User can still push string/integer prefixes. - // We could hash the size/uv to create a unique ID but that would prevent the user from animating UV. - PushID((void *)user_texture_id); - const ImGuiID id = window->GetID("#image"); - PopID(); - - const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float)frame_padding, (float)frame_padding) : style.FramePadding; - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding*2); - const ImRect image_bb(window->DC.CursorPos + padding, window->DC.CursorPos + padding + size); - ItemSize(bb); - if (!ItemAdd(bb, id)) - return false; - - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held); - - // Render - const ImU32 col = GetColorU32((hovered && held) ? ImGuiCol_ButtonActive : hovered ? ImGuiCol_ButtonHovered : ImGuiCol_Button); - RenderNavHighlight(bb, id); - RenderFrame(bb.Min, bb.Max, col, true, ImClamp((float)ImMin(padding.x, padding.y), 0.0f, style.FrameRounding)); - if (bg_col.w > 0.0f) - window->DrawList->AddRectFilled(image_bb.Min, image_bb.Max, GetColorU32(bg_col)); - window->DrawList->AddImage(user_texture_id, image_bb.Min, image_bb.Max, uv0, uv1, GetColorU32(tint_col)); - - return pressed; -} - -// Start logging ImGui output to TTY -void ImGui::LogToTTY(int max_depth) -{ - ImGuiContext& g = *GImGui; - if (g.LogEnabled) - return; - ImGuiWindow* window = g.CurrentWindow; - - IM_ASSERT(g.LogFile == NULL); - g.LogFile = stdout; - g.LogEnabled = true; - g.LogStartDepth = window->DC.TreeDepth; - if (max_depth >= 0) - g.LogAutoExpandMaxDepth = max_depth; -} - -// Start logging ImGui output to given file -void ImGui::LogToFile(int max_depth, const char* filename) -{ - ImGuiContext& g = *GImGui; - if (g.LogEnabled) - return; - ImGuiWindow* window = g.CurrentWindow; - - if (!filename) - { - filename = g.IO.LogFilename; - if (!filename) - return; - } - - IM_ASSERT(g.LogFile == NULL); - g.LogFile = ImFileOpen(filename, "ab"); - if (!g.LogFile) - { - IM_ASSERT(g.LogFile != NULL); // Consider this an error - return; - } - g.LogEnabled = true; - g.LogStartDepth = window->DC.TreeDepth; - if (max_depth >= 0) - g.LogAutoExpandMaxDepth = max_depth; -} - -// Start logging ImGui output to clipboard -void ImGui::LogToClipboard(int max_depth) -{ - ImGuiContext& g = *GImGui; - if (g.LogEnabled) - return; - ImGuiWindow* window = g.CurrentWindow; - - IM_ASSERT(g.LogFile == NULL); - g.LogFile = NULL; - g.LogEnabled = true; - g.LogStartDepth = window->DC.TreeDepth; - if (max_depth >= 0) - g.LogAutoExpandMaxDepth = max_depth; -} - -void ImGui::LogFinish() -{ - ImGuiContext& g = *GImGui; - if (!g.LogEnabled) - return; - - LogText(IM_NEWLINE); - if (g.LogFile != NULL) - { - if (g.LogFile == stdout) - fflush(g.LogFile); - else - fclose(g.LogFile); - g.LogFile = NULL; - } - if (g.LogClipboard.size() > 1) - { - SetClipboardText(g.LogClipboard.begin()); - g.LogClipboard.clear(); - } - g.LogEnabled = false; -} - -// Helper to display logging buttons -void ImGui::LogButtons() -{ - ImGuiContext& g = *GImGui; - - PushID("LogButtons"); - const bool log_to_tty = Button("Log To TTY"); SameLine(); - const bool log_to_file = Button("Log To File"); SameLine(); - const bool log_to_clipboard = Button("Log To Clipboard"); SameLine(); - PushItemWidth(80.0f); - PushAllowKeyboardFocus(false); - SliderInt("Depth", &g.LogAutoExpandMaxDepth, 0, 9, NULL); - PopAllowKeyboardFocus(); - PopItemWidth(); - PopID(); - - // Start logging at the end of the function so that the buttons don't appear in the log - if (log_to_tty) - LogToTTY(g.LogAutoExpandMaxDepth); - if (log_to_file) - LogToFile(g.LogAutoExpandMaxDepth, g.IO.LogFilename); - if (log_to_clipboard) - LogToClipboard(g.LogAutoExpandMaxDepth); -} - -bool ImGui::TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags) -{ - if (flags & ImGuiTreeNodeFlags_Leaf) - return true; - - // We only write to the tree storage if the user clicks (or explicitly use SetNextTreeNode*** functions) - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - ImGuiStorage* storage = window->DC.StateStorage; - - bool is_open; - if (g.NextTreeNodeOpenCond != 0) - { - if (g.NextTreeNodeOpenCond & ImGuiCond_Always) - { - is_open = g.NextTreeNodeOpenVal; - storage->SetInt(id, is_open); - } - else - { - // We treat ImGuiCond_Once and ImGuiCond_FirstUseEver the same because tree node state are not saved persistently. - const int stored_value = storage->GetInt(id, -1); - if (stored_value == -1) - { - is_open = g.NextTreeNodeOpenVal; - storage->SetInt(id, is_open); - } - else - { - is_open = stored_value != 0; - } - } - g.NextTreeNodeOpenCond = 0; - } - else - { - is_open = storage->GetInt(id, (flags & ImGuiTreeNodeFlags_DefaultOpen) ? 1 : 0) != 0; - } - - // When logging is enabled, we automatically expand tree nodes (but *NOT* collapsing headers.. seems like sensible behavior). - // NB- If we are above max depth we still allow manually opened nodes to be logged. - if (g.LogEnabled && !(flags & ImGuiTreeNodeFlags_NoAutoOpenOnLog) && window->DC.TreeDepth < g.LogAutoExpandMaxDepth) - is_open = true; - - return is_open; -} - -bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const bool display_frame = (flags & ImGuiTreeNodeFlags_Framed) != 0; - const ImVec2 padding = (display_frame || (flags & ImGuiTreeNodeFlags_FramePadding)) ? style.FramePadding : ImVec2(style.FramePadding.x, 0.0f); - - if (!label_end) - label_end = FindRenderedTextEnd(label); - const ImVec2 label_size = CalcTextSize(label, label_end, false); - - // We vertically grow up to current line height up the typical widget height. - const float text_base_offset_y = ImMax(padding.y, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it - const float frame_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + style.FramePadding.y*2), label_size.y + padding.y*2); - ImRect frame_bb = ImRect(window->DC.CursorPos, ImVec2(window->Pos.x + GetContentRegionMax().x, window->DC.CursorPos.y + frame_height)); - if (display_frame) - { - // Framed header expand a little outside the default padding - frame_bb.Min.x -= (float)(int)(window->WindowPadding.x*0.5f) - 1; - frame_bb.Max.x += (float)(int)(window->WindowPadding.x*0.5f) - 1; - } - - const float text_offset_x = (g.FontSize + (display_frame ? padding.x*3 : padding.x*2)); // Collapser arrow width + Spacing - const float text_width = g.FontSize + (label_size.x > 0.0f ? label_size.x + padding.x*2 : 0.0f); // Include collapser - ItemSize(ImVec2(text_width, frame_height), text_base_offset_y); - - // For regular tree nodes, we arbitrary allow to click past 2 worth of ItemSpacing - // (Ideally we'd want to add a flag for the user to specify if we want the hit test to be done up to the right side of the content or not) - const ImRect interact_bb = display_frame ? frame_bb : ImRect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + text_width + style.ItemSpacing.x*2, frame_bb.Max.y); - bool is_open = TreeNodeBehaviorIsOpen(id, flags); - - // Store a flag for the current depth to tell if we will allow closing this node when navigating one of its child. - // For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop(). - // This is currently only support 32 level deep and we are fine with (1 << Depth) overflowing into a zero. - if (is_open && !g.NavIdIsAlive && (flags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) - window->DC.TreeDepthMayJumpToParentOnPop |= (1 << window->DC.TreeDepth); - - bool item_add = ItemAdd(interact_bb, id); - window->DC.LastItemStatusFlags |= ImGuiItemStatusFlags_HasDisplayRect; - window->DC.LastItemDisplayRect = frame_bb; - - if (!item_add) - { - if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) - TreePushRawID(id); - return is_open; - } - - // Flags that affects opening behavior: - // - 0(default) ..................... single-click anywhere to open - // - OpenOnDoubleClick .............. double-click anywhere to open - // - OpenOnArrow .................... single-click on arrow to open - // - OpenOnDoubleClick|OpenOnArrow .. single-click on arrow or double-click anywhere to open - ImGuiButtonFlags button_flags = ImGuiButtonFlags_NoKeyModifiers | ((flags & ImGuiTreeNodeFlags_AllowItemOverlap) ? ImGuiButtonFlags_AllowItemOverlap : 0); - if (!(flags & ImGuiTreeNodeFlags_Leaf)) - button_flags |= ImGuiButtonFlags_PressedOnDragDropHold; - if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) - button_flags |= ImGuiButtonFlags_PressedOnDoubleClick | ((flags & ImGuiTreeNodeFlags_OpenOnArrow) ? ImGuiButtonFlags_PressedOnClickRelease : 0); - - bool hovered, held, pressed = ButtonBehavior(interact_bb, id, &hovered, &held, button_flags); - if (!(flags & ImGuiTreeNodeFlags_Leaf)) - { - bool toggled = false; - if (pressed) - { - toggled = !(flags & (ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick)) || (g.NavActivateId == id); - if (flags & ImGuiTreeNodeFlags_OpenOnArrow) - toggled |= IsMouseHoveringRect(interact_bb.Min, ImVec2(interact_bb.Min.x + text_offset_x, interact_bb.Max.y)) && (!g.NavDisableMouseHover); - if (flags & ImGuiTreeNodeFlags_OpenOnDoubleClick) - toggled |= g.IO.MouseDoubleClicked[0]; - if (g.DragDropActive && is_open) // When using Drag and Drop "hold to open" we keep the node highlighted after opening, but never close it again. - toggled = false; - } - - if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Left && is_open) - { - toggled = true; - NavMoveRequestCancel(); - } - if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right && !is_open) // If there's something upcoming on the line we may want to give it the priority? - { - toggled = true; - NavMoveRequestCancel(); - } - - if (toggled) - { - is_open = !is_open; - window->DC.StateStorage->SetInt(id, is_open); - } - } - if (flags & ImGuiTreeNodeFlags_AllowItemOverlap) - SetItemAllowOverlap(); - - // Render - const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); - const ImVec2 text_pos = frame_bb.Min + ImVec2(text_offset_x, text_base_offset_y); - if (display_frame) - { - // Framed type - RenderFrame(frame_bb.Min, frame_bb.Max, col, true, style.FrameRounding); - RenderNavHighlight(frame_bb, id, ImGuiNavHighlightFlags_TypeThin); - RenderArrow(frame_bb.Min + ImVec2(padding.x, text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 1.0f); - if (g.LogEnabled) - { - // NB: '##' is normally used to hide text (as a library-wide feature), so we need to specify the text range to make sure the ## aren't stripped out here. - const char log_prefix[] = "\n##"; - const char log_suffix[] = "##"; - LogRenderedText(&text_pos, log_prefix, log_prefix+3); - RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); - LogRenderedText(&text_pos, log_suffix+1, log_suffix+3); - } - else - { - RenderTextClipped(text_pos, frame_bb.Max, label, label_end, &label_size); - } - } - else - { - // Unframed typed for tree nodes - if (hovered || (flags & ImGuiTreeNodeFlags_Selected)) - { - RenderFrame(frame_bb.Min, frame_bb.Max, col, false); - RenderNavHighlight(frame_bb, id, ImGuiNavHighlightFlags_TypeThin); - } - - if (flags & ImGuiTreeNodeFlags_Bullet) - RenderBullet(frame_bb.Min + ImVec2(text_offset_x * 0.5f, g.FontSize*0.50f + text_base_offset_y)); - else if (!(flags & ImGuiTreeNodeFlags_Leaf)) - RenderArrow(frame_bb.Min + ImVec2(padding.x, g.FontSize*0.15f + text_base_offset_y), is_open ? ImGuiDir_Down : ImGuiDir_Right, 0.70f); - if (g.LogEnabled) - LogRenderedText(&text_pos, ">"); - RenderText(text_pos, label, label_end, false); - } - - if (is_open && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) - TreePushRawID(id); - return is_open; -} - -// CollapsingHeader returns true when opened but do not indent nor push into the ID stack (because of the ImGuiTreeNodeFlags_NoTreePushOnOpen flag). -// This is basically the same as calling TreeNodeEx(label, ImGuiTreeNodeFlags_CollapsingHeader). You can remove the _NoTreePushOnOpen flag if you want behavior closer to normal TreeNode(). -bool ImGui::CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - return TreeNodeBehavior(window->GetID(label), flags | ImGuiTreeNodeFlags_CollapsingHeader, label); -} - -bool ImGui::CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - if (p_open && !*p_open) - return false; - - ImGuiID id = window->GetID(label); - bool is_open = TreeNodeBehavior(id, flags | ImGuiTreeNodeFlags_CollapsingHeader | (p_open ? ImGuiTreeNodeFlags_AllowItemOverlap : 0), label); - if (p_open) - { - // Create a small overlapping close button // FIXME: We can evolve this into user accessible helpers to add extra buttons on title bars, headers, etc. - ImGuiContext& g = *GImGui; - float button_sz = g.FontSize * 0.5f; - ImGuiItemHoveredDataBackup last_item_backup; - if (CloseButton(window->GetID((void*)(intptr_t)(id+1)), ImVec2(ImMin(window->DC.LastItemRect.Max.x, window->ClipRect.Max.x) - g.Style.FramePadding.x - button_sz, window->DC.LastItemRect.Min.y + g.Style.FramePadding.y + button_sz), button_sz)) - *p_open = false; - last_item_backup.Restore(); - } - - return is_open; -} - -bool ImGui::TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - return TreeNodeBehavior(window->GetID(label), flags, label, NULL); -} - -bool ImGui::TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); - return TreeNodeBehavior(window->GetID(str_id), flags, g.TempBuffer, label_end); -} - -bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const char* label_end = g.TempBuffer + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); - return TreeNodeBehavior(window->GetID(ptr_id), flags, g.TempBuffer, label_end); -} - -bool ImGui::TreeNodeV(const char* str_id, const char* fmt, va_list args) -{ - return TreeNodeExV(str_id, 0, fmt, args); -} - -bool ImGui::TreeNodeV(const void* ptr_id, const char* fmt, va_list args) -{ - return TreeNodeExV(ptr_id, 0, fmt, args); -} - -bool ImGui::TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - bool is_open = TreeNodeExV(str_id, flags, fmt, args); - va_end(args); - return is_open; -} - -bool ImGui::TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - bool is_open = TreeNodeExV(ptr_id, flags, fmt, args); - va_end(args); - return is_open; -} - -bool ImGui::TreeNode(const char* str_id, const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - bool is_open = TreeNodeExV(str_id, 0, fmt, args); - va_end(args); - return is_open; -} - -bool ImGui::TreeNode(const void* ptr_id, const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - bool is_open = TreeNodeExV(ptr_id, 0, fmt, args); - va_end(args); - return is_open; -} - -bool ImGui::TreeNode(const char* label) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - return TreeNodeBehavior(window->GetID(label), 0, label, NULL); -} - -void ImGui::TreeAdvanceToLabelPos() -{ - ImGuiContext& g = *GImGui; - g.CurrentWindow->DC.CursorPos.x += GetTreeNodeToLabelSpacing(); -} - -// Horizontal distance preceding label when using TreeNode() or Bullet() -float ImGui::GetTreeNodeToLabelSpacing() -{ - ImGuiContext& g = *GImGui; - return g.FontSize + (g.Style.FramePadding.x * 2.0f); -} - -void ImGui::SetNextTreeNodeOpen(bool is_open, ImGuiCond cond) -{ - ImGuiContext& g = *GImGui; - if (g.CurrentWindow->SkipItems) - return; - g.NextTreeNodeOpenVal = is_open; - g.NextTreeNodeOpenCond = cond ? cond : ImGuiCond_Always; -} - -void ImGui::PushID(const char* str_id) -{ - ImGuiWindow* window = GetCurrentWindowRead(); - window->IDStack.push_back(window->GetID(str_id)); -} - -void ImGui::PushID(const char* str_id_begin, const char* str_id_end) -{ - ImGuiWindow* window = GetCurrentWindowRead(); - window->IDStack.push_back(window->GetID(str_id_begin, str_id_end)); -} - -void ImGui::PushID(const void* ptr_id) -{ - ImGuiWindow* window = GetCurrentWindowRead(); - window->IDStack.push_back(window->GetID(ptr_id)); -} - -void ImGui::PushID(int int_id) -{ - const void* ptr_id = (void*)(intptr_t)int_id; - ImGuiWindow* window = GetCurrentWindowRead(); - window->IDStack.push_back(window->GetID(ptr_id)); -} - -void ImGui::PopID() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - window->IDStack.pop_back(); -} - -ImGuiID ImGui::GetID(const char* str_id) -{ - return GImGui->CurrentWindow->GetID(str_id); -} - -ImGuiID ImGui::GetID(const char* str_id_begin, const char* str_id_end) -{ - return GImGui->CurrentWindow->GetID(str_id_begin, str_id_end); -} - -ImGuiID ImGui::GetID(const void* ptr_id) -{ - return GImGui->CurrentWindow->GetID(ptr_id); -} - -void ImGui::Bullet() -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), g.FontSize); - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize, line_height)); - ItemSize(bb); - if (!ItemAdd(bb, 0)) - { - SameLine(0, style.FramePadding.x*2); - return; - } - - // Render and stay on same line - RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f)); - SameLine(0, style.FramePadding.x*2); -} - -// Text with a little bullet aligned to the typical tree node. -void ImGui::BulletTextV(const char* fmt, va_list args) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - - const char* text_begin = g.TempBuffer; - const char* text_end = text_begin + ImFormatStringV(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), fmt, args); - const ImVec2 label_size = CalcTextSize(text_begin, text_end, false); - const float text_base_offset_y = ImMax(0.0f, window->DC.CurrentLineTextBaseOffset); // Latch before ItemSize changes it - const float line_height = ImMax(ImMin(window->DC.CurrentLineHeight, g.FontSize + g.Style.FramePadding.y*2), g.FontSize); - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(g.FontSize + (label_size.x > 0.0f ? (label_size.x + style.FramePadding.x*2) : 0.0f), ImMax(line_height, label_size.y))); // Empty text doesn't add padding - ItemSize(bb); - if (!ItemAdd(bb, 0)) - return; - - // Render - RenderBullet(bb.Min + ImVec2(style.FramePadding.x + g.FontSize*0.5f, line_height*0.5f)); - RenderText(bb.Min+ImVec2(g.FontSize + style.FramePadding.x*2, text_base_offset_y), text_begin, text_end, false); -} - -void ImGui::BulletText(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - BulletTextV(fmt, args); - va_end(args); -} - -static inline int DataTypeFormatString(char* buf, int buf_size, ImGuiDataType data_type, const void* data_ptr, const char* format) -{ - if (data_type == ImGuiDataType_S32 || data_type == ImGuiDataType_U32) // Signedness doesn't matter when pushing the argument - return ImFormatString(buf, buf_size, format, *(const ImU32*)data_ptr); - if (data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64) // Signedness doesn't matter when pushing the argument - return ImFormatString(buf, buf_size, format, *(const ImU64*)data_ptr); - if (data_type == ImGuiDataType_Float) - return ImFormatString(buf, buf_size, format, *(const float*)data_ptr); - if (data_type == ImGuiDataType_Double) - return ImFormatString(buf, buf_size, format, *(const double*)data_ptr); - IM_ASSERT(0); - return 0; -} - -// FIXME: Adding support for clamping on boundaries of the data type would be nice. -static void DataTypeApplyOp(ImGuiDataType data_type, int op, void* output, void* arg1, const void* arg2) -{ - IM_ASSERT(op == '+' || op == '-'); - switch (data_type) - { - case ImGuiDataType_S32: - if (op == '+') *(int*)output = *(const int*)arg1 + *(const int*)arg2; - else if (op == '-') *(int*)output = *(const int*)arg1 - *(const int*)arg2; - return; - case ImGuiDataType_U32: - if (op == '+') *(unsigned int*)output = *(const unsigned int*)arg1 + *(const ImU32*)arg2; - else if (op == '-') *(unsigned int*)output = *(const unsigned int*)arg1 - *(const ImU32*)arg2; - return; - case ImGuiDataType_S64: - if (op == '+') *(ImS64*)output = *(const ImS64*)arg1 + *(const ImS64*)arg2; - else if (op == '-') *(ImS64*)output = *(const ImS64*)arg1 - *(const ImS64*)arg2; - return; - case ImGuiDataType_U64: - if (op == '+') *(ImU64*)output = *(const ImU64*)arg1 + *(const ImU64*)arg2; - else if (op == '-') *(ImU64*)output = *(const ImU64*)arg1 - *(const ImU64*)arg2; - return; - case ImGuiDataType_Float: - if (op == '+') *(float*)output = *(const float*)arg1 + *(const float*)arg2; - else if (op == '-') *(float*)output = *(const float*)arg1 - *(const float*)arg2; - return; - case ImGuiDataType_Double: - if (op == '+') *(double*)output = *(const double*)arg1 + *(const double*)arg2; - else if (op == '-') *(double*)output = *(const double*)arg1 - *(const double*)arg2; - return; - case ImGuiDataType_COUNT: break; - } - IM_ASSERT(0); -} - -struct ImGuiDataTypeInfo -{ - size_t Size; - const char* PrintFmt; // Unused - const char* ScanFmt; -}; - -static const ImGuiDataTypeInfo GDataTypeInfo[] = -{ - { sizeof(int), "%d", "%d" }, - { sizeof(unsigned int), "%u", "%u" }, -#ifdef _MSC_VER - { sizeof(ImS64), "%I64d","%I64d" }, - { sizeof(ImU64), "%I64u","%I64u" }, -#else - { sizeof(ImS64), "%lld", "%lld" }, - { sizeof(ImU64), "%llu", "%llu" }, -#endif - { sizeof(float), "%f", "%f" }, // float are promoted to double in va_arg - { sizeof(double), "%f", "%lf" }, -}; -IM_STATIC_ASSERT(IM_ARRAYSIZE(GDataTypeInfo) == ImGuiDataType_COUNT); - -// User can input math operators (e.g. +100) to edit a numerical values. -// NB: This is _not_ a full expression evaluator. We should probably add one and replace this dumb mess.. -static bool DataTypeApplyOpFromText(const char* buf, const char* initial_value_buf, ImGuiDataType data_type, void* data_ptr, const char* format) -{ - while (ImCharIsBlankA(*buf)) - buf++; - - // We don't support '-' op because it would conflict with inputing negative value. - // Instead you can use +-100 to subtract from an existing value - char op = buf[0]; - if (op == '+' || op == '*' || op == '/') - { - buf++; - while (ImCharIsBlankA(*buf)) - buf++; - } - else - { - op = 0; - } - if (!buf[0]) - return false; - - // Copy the value in an opaque buffer so we can compare at the end of the function if it changed at all. - IM_ASSERT(data_type < ImGuiDataType_COUNT); - int data_backup[2]; - IM_ASSERT(GDataTypeInfo[data_type].Size <= sizeof(data_backup)); - memcpy(data_backup, data_ptr, GDataTypeInfo[data_type].Size); - - if (format == NULL) - format = GDataTypeInfo[data_type].ScanFmt; - - int arg1i = 0; - if (data_type == ImGuiDataType_S32) - { - int* v = (int*)data_ptr; - int arg0i = *v; - float arg1f = 0.0f; - if (op && sscanf(initial_value_buf, format, &arg0i) < 1) - return false; - // Store operand in a float so we can use fractional value for multipliers (*1.1), but constant always parsed as integer so we can fit big integers (e.g. 2000000003) past float precision - if (op == '+') { if (sscanf(buf, "%d", &arg1i)) *v = (int)(arg0i + arg1i); } // Add (use "+-" to subtract) - else if (op == '*') { if (sscanf(buf, "%f", &arg1f)) *v = (int)(arg0i * arg1f); } // Multiply - else if (op == '/') { if (sscanf(buf, "%f", &arg1f) && arg1f != 0.0f) *v = (int)(arg0i / arg1f); } // Divide - else { if (sscanf(buf, format, &arg1i) == 1) *v = arg1i; } // Assign constant - } - else if (data_type == ImGuiDataType_U32 || data_type == ImGuiDataType_S64 || data_type == ImGuiDataType_U64) - { - // Assign constant - // FIXME: We don't bother handling support for legacy operators since they are a little too crappy. Instead we may implement a proper expression evaluator in the future. - sscanf(buf, format, data_ptr); - } - else if (data_type == ImGuiDataType_Float) - { - // For floats we have to ignore format with precision (e.g. "%.2f") because sscanf doesn't take them in - format = "%f"; - float* v = (float*)data_ptr; - float arg0f = *v, arg1f = 0.0f; - if (op && sscanf(initial_value_buf, format, &arg0f) < 1) - return false; - if (sscanf(buf, format, &arg1f) < 1) - return false; - if (op == '+') { *v = arg0f + arg1f; } // Add (use "+-" to subtract) - else if (op == '*') { *v = arg0f * arg1f; } // Multiply - else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } // Divide - else { *v = arg1f; } // Assign constant - } - else if (data_type == ImGuiDataType_Double) - { - format = "%lf"; // scanf differentiate float/double unlike printf which forces everything to double because of ellipsis - double* v = (double*)data_ptr; - double arg0f = *v, arg1f = 0.0; - if (op && sscanf(initial_value_buf, format, &arg0f) < 1) - return false; - if (sscanf(buf, format, &arg1f) < 1) - return false; - if (op == '+') { *v = arg0f + arg1f; } // Add (use "+-" to subtract) - else if (op == '*') { *v = arg0f * arg1f; } // Multiply - else if (op == '/') { if (arg1f != 0.0f) *v = arg0f / arg1f; } // Divide - else { *v = arg1f; } // Assign constant - } - return memcmp(data_backup, data_ptr, GDataTypeInfo[data_type].Size) != 0; -} - -// Create text input in place of a slider (when CTRL+Clicking on slider) -// FIXME: Logic is messy and confusing. -bool ImGui::InputScalarAsWidgetReplacement(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* data_ptr, const char* format) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - - // Our replacement widget will override the focus ID (registered previously to allow for a TAB focus to happen) - // On the first frame, g.ScalarAsInputTextId == 0, then on subsequent frames it becomes == id - SetActiveID(g.ScalarAsInputTextId, window); - g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); - SetHoveredID(0); - FocusableItemUnregister(window); - - char fmt_buf[32]; - char data_buf[32]; - format = ImParseFormatTrimDecorations(format, fmt_buf, IM_ARRAYSIZE(fmt_buf)); - DataTypeFormatString(data_buf, IM_ARRAYSIZE(data_buf), data_type, data_ptr, format); - ImStrTrimBlanks(data_buf); - ImGuiInputTextFlags flags = ImGuiInputTextFlags_AutoSelectAll | ((data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) ? ImGuiInputTextFlags_CharsScientific : ImGuiInputTextFlags_CharsDecimal); - bool value_changed = InputTextEx(label, data_buf, IM_ARRAYSIZE(data_buf), bb.GetSize(), flags); - if (g.ScalarAsInputTextId == 0) // First frame we started displaying the InputText widget - { - IM_ASSERT(g.ActiveId == id); // InputText ID expected to match the Slider ID - g.ScalarAsInputTextId = g.ActiveId; - SetHoveredID(id); - } - if (value_changed) - return DataTypeApplyOpFromText(data_buf, g.InputTextState.InitialText.begin(), data_type, data_ptr, NULL); - return false; -} - -// We don't use strchr() because our strings are usually very short and often start with '%' -const char* ImParseFormatFindStart(const char* fmt) -{ - while (char c = fmt[0]) - { - if (c == '%' && fmt[1] != '%') - return fmt; - else if (c == '%') - fmt++; - fmt++; - } - return fmt; -} - -const char* ImParseFormatFindEnd(const char* fmt) -{ - // Printf/scanf types modifiers: I/L/h/j/l/t/w/z. Other uppercase letters qualify as types aka end of the format. - if (fmt[0] != '%') - return fmt; - const unsigned int ignored_uppercase_mask = (1 << ('I'-'A')) | (1 << ('L'-'A')); - const unsigned int ignored_lowercase_mask = (1 << ('h'-'a')) | (1 << ('j'-'a')) | (1 << ('l'-'a')) | (1 << ('t'-'a')) | (1 << ('w'-'a')) | (1 << ('z'-'a')); - for (char c; (c = *fmt) != 0; fmt++) - { - if (c >= 'A' && c <= 'Z' && ((1 << (c - 'A')) & ignored_uppercase_mask) == 0) - return fmt + 1; - if (c >= 'a' && c <= 'z' && ((1 << (c - 'a')) & ignored_lowercase_mask) == 0) - return fmt + 1; - } - return fmt; -} - -// Extract the format out of a format string with leading or trailing decorations -// fmt = "blah blah" -> return fmt -// fmt = "%.3f" -> return fmt -// fmt = "hello %.3f" -> return fmt + 6 -// fmt = "%.3f hello" -> return buf written with "%.3f" -const char* ImParseFormatTrimDecorations(const char* fmt, char* buf, int buf_size) -{ - const char* fmt_start = ImParseFormatFindStart(fmt); - if (fmt_start[0] != '%') - return fmt; - const char* fmt_end = ImParseFormatFindEnd(fmt_start); - if (fmt_end[0] == 0) // If we only have leading decoration, we don't need to copy the data. - return fmt_start; - ImStrncpy(buf, fmt_start, ImMin((int)(fmt_end + 1 - fmt_start), buf_size)); - return buf; -} - -// Parse display precision back from the display format string -// FIXME: This is still used by some navigation code path to infer a minimum tweak step, but we should aim to rework widgets so it isn't needed. -int ImParseFormatPrecision(const char* fmt, int default_precision) -{ - fmt = ImParseFormatFindStart(fmt); - if (fmt[0] != '%') - return default_precision; - fmt++; - while (*fmt >= '0' && *fmt <= '9') - fmt++; - int precision = INT_MAX; - if (*fmt == '.') - { - fmt = ImAtoi(fmt + 1, &precision); - if (precision < 0 || precision > 99) - precision = default_precision; - } - if (*fmt == 'e' || *fmt == 'E') // Maximum precision with scientific notation - precision = -1; - if ((*fmt == 'g' || *fmt == 'G') && precision == INT_MAX) - precision = -1; - return (precision == INT_MAX) ? default_precision : precision; -} - -static float GetMinimumStepAtDecimalPrecision(int decimal_precision) -{ - static const float min_steps[10] = { 1.0f, 0.1f, 0.01f, 0.001f, 0.0001f, 0.00001f, 0.000001f, 0.0000001f, 0.00000001f, 0.000000001f }; - if (decimal_precision < 0) - return FLT_MIN; - return (decimal_precision >= 0 && decimal_precision < 10) ? min_steps[decimal_precision] : ImPow(10.0f, (float)-decimal_precision); -} - -template -static inline TYPE RoundScalarWithFormat(const char* format, ImGuiDataType data_type, TYPE v) -{ - const char* fmt_start = ImParseFormatFindStart(format); - if (fmt_start[0] != '%' || fmt_start[1] == '%') // Don't apply if the value is not visible in the format string - return v; - char v_str[64]; - ImFormatString(v_str, IM_ARRAYSIZE(v_str), fmt_start, v); - const char* p = v_str; - while (*p == ' ') - p++; - if (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) - v = (TYPE)ImAtof(p); - else - ImAtoi(p, (SIGNEDTYPE*)&v); - return v; -} - -template -static inline float SliderBehaviorCalcRatioFromValue(ImGuiDataType data_type, TYPE v, TYPE v_min, TYPE v_max, float power, float linear_zero_pos) -{ - if (v_min == v_max) - return 0.0f; - - const bool is_power = (power != 1.0f) && (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double); - const TYPE v_clamped = (v_min < v_max) ? ImClamp(v, v_min, v_max) : ImClamp(v, v_max, v_min); - if (is_power) - { - if (v_clamped < 0.0f) - { - const float f = 1.0f - (float)((v_clamped - v_min) / (ImMin((TYPE)0, v_max) - v_min)); - return (1.0f - ImPow(f, 1.0f/power)) * linear_zero_pos; - } - else - { - const float f = (float)((v_clamped - ImMax((TYPE)0, v_min)) / (v_max - ImMax((TYPE)0, v_min))); - return linear_zero_pos + ImPow(f, 1.0f/power) * (1.0f - linear_zero_pos); - } - } - - // Linear slider - return (float)((FLOATTYPE)(v_clamped - v_min) / (FLOATTYPE)(v_max - v_min)); -} - -// FIXME: Move some of the code into SliderBehavior(). Current responsability is larger than what the equivalent DragBehaviorT<> does, we also do some rendering, etc. -template -static bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, TYPE* v, const TYPE v_min, const TYPE v_max, const char* format, float power, ImGuiSliderFlags flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - const ImGuiStyle& style = g.Style; - - // Draw frame - const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); - RenderNavHighlight(bb, id); - RenderFrame(bb.Min, bb.Max, frame_col, true, style.FrameRounding); - - const bool is_horizontal = (flags & ImGuiSliderFlags_Vertical) == 0; - const bool is_decimal = (data_type == ImGuiDataType_Float) || (data_type == ImGuiDataType_Double); - const bool is_power = (power != 1.0f) && is_decimal; - - const float grab_padding = 2.0f; - const float slider_sz = is_horizontal ? (bb.GetWidth() - grab_padding * 2.0f) : (bb.GetHeight() - grab_padding * 2.0f); - float grab_sz = style.GrabMinSize; - SIGNEDTYPE v_range = (v_min < v_max ? v_max - v_min : v_min - v_max); - if (!is_decimal && v_range >= 0) // v_range < 0 may happen on integer overflows - grab_sz = ImMax((float)(slider_sz / (v_range + 1)), style.GrabMinSize); // For integer sliders: if possible have the grab size represent 1 unit - grab_sz = ImMin(grab_sz, slider_sz); - const float slider_usable_sz = slider_sz - grab_sz; - const float slider_usable_pos_min = (is_horizontal ? bb.Min.x : bb.Min.y) + grab_padding + grab_sz*0.5f; - const float slider_usable_pos_max = (is_horizontal ? bb.Max.x : bb.Max.y) - grab_padding - grab_sz*0.5f; - - // For power curve sliders that cross over sign boundary we want the curve to be symmetric around 0.0f - float linear_zero_pos; // 0.0->1.0f - if (is_power && v_min * v_max < 0.0f) - { - // Different sign - const FLOATTYPE linear_dist_min_to_0 = ImPow(v_min >= 0 ? (FLOATTYPE)v_min : -(FLOATTYPE)v_min, (FLOATTYPE)1.0f/power); - const FLOATTYPE linear_dist_max_to_0 = ImPow(v_max >= 0 ? (FLOATTYPE)v_max : -(FLOATTYPE)v_max, (FLOATTYPE)1.0f/power); - linear_zero_pos = (float)(linear_dist_min_to_0 / (linear_dist_min_to_0 + linear_dist_max_to_0)); - } - else - { - // Same sign - linear_zero_pos = v_min < 0.0f ? 1.0f : 0.0f; - } - - // Process interacting with the slider - bool value_changed = false; - if (g.ActiveId == id) - { - bool set_new_value = false; - float clicked_t = 0.0f; - if (g.ActiveIdSource == ImGuiInputSource_Mouse) - { - if (!g.IO.MouseDown[0]) - { - ClearActiveID(); - } - else - { - const float mouse_abs_pos = is_horizontal ? g.IO.MousePos.x : g.IO.MousePos.y; - clicked_t = (slider_usable_sz > 0.0f) ? ImClamp((mouse_abs_pos - slider_usable_pos_min) / slider_usable_sz, 0.0f, 1.0f) : 0.0f; - if (!is_horizontal) - clicked_t = 1.0f - clicked_t; - set_new_value = true; - } - } - else if (g.ActiveIdSource == ImGuiInputSource_Nav) - { - const ImVec2 delta2 = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard | ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 0.0f, 0.0f); - float delta = is_horizontal ? delta2.x : -delta2.y; - if (g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) - { - ClearActiveID(); - } - else if (delta != 0.0f) - { - clicked_t = SliderBehaviorCalcRatioFromValue(data_type, *v, v_min, v_max, power, linear_zero_pos); - const int decimal_precision = is_decimal ? ImParseFormatPrecision(format, 3) : 0; - if ((decimal_precision > 0) || is_power) - { - delta /= 100.0f; // Gamepad/keyboard tweak speeds in % of slider bounds - if (IsNavInputDown(ImGuiNavInput_TweakSlow)) - delta /= 10.0f; - } - else - { - if ((v_range >= -100.0f && v_range <= 100.0f) || IsNavInputDown(ImGuiNavInput_TweakSlow)) - delta = ((delta < 0.0f) ? -1.0f : +1.0f) / (float)v_range; // Gamepad/keyboard tweak speeds in integer steps - else - delta /= 100.0f; - } - if (IsNavInputDown(ImGuiNavInput_TweakFast)) - delta *= 10.0f; - set_new_value = true; - if ((clicked_t >= 1.0f && delta > 0.0f) || (clicked_t <= 0.0f && delta < 0.0f)) // This is to avoid applying the saturation when already past the limits - set_new_value = false; - else - clicked_t = ImSaturate(clicked_t + delta); - } - } - - if (set_new_value) - { - TYPE v_new; - if (is_power) - { - // Account for power curve scale on both sides of the zero - if (clicked_t < linear_zero_pos) - { - // Negative: rescale to the negative range before powering - float a = 1.0f - (clicked_t / linear_zero_pos); - a = ImPow(a, power); - v_new = ImLerp(ImMin(v_max, (TYPE)0), v_min, a); - } - else - { - // Positive: rescale to the positive range before powering - float a; - if (ImFabs(linear_zero_pos - 1.0f) > 1.e-6f) - a = (clicked_t - linear_zero_pos) / (1.0f - linear_zero_pos); - else - a = clicked_t; - a = ImPow(a, power); - v_new = ImLerp(ImMax(v_min, (TYPE)0), v_max, a); - } - } - else - { - // Linear slider - if (is_decimal) - { - v_new = ImLerp(v_min, v_max, clicked_t); - } - else - { - // For integer values we want the clicking position to match the grab box so we round above - // This code is carefully tuned to work with large values (e.g. high ranges of U64) while preserving this property.. - FLOATTYPE v_new_off_f = (v_max - v_min) * clicked_t; - TYPE v_new_off_floor = (TYPE)(v_new_off_f); - TYPE v_new_off_round = (TYPE)(v_new_off_f + (FLOATTYPE)0.5); - if (!is_decimal && v_new_off_floor < v_new_off_round) - v_new = v_min + v_new_off_round; - else - v_new = v_min + v_new_off_floor; - } - } - - // Round to user desired precision based on format string - v_new = RoundScalarWithFormat(format, data_type, v_new); - - // Apply result - if (*v != v_new) - { - *v = v_new; - value_changed = true; - } - } - } - - // Draw - float grab_t = SliderBehaviorCalcRatioFromValue(data_type, *v, v_min, v_max, power, linear_zero_pos); - if (!is_horizontal) - grab_t = 1.0f - grab_t; - const float grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t); - ImRect grab_bb; - if (is_horizontal) - grab_bb = ImRect(grab_pos - grab_sz*0.5f, bb.Min.y + grab_padding, grab_pos + grab_sz*0.5f, bb.Max.y - grab_padding); - else - grab_bb = ImRect(bb.Min.x + grab_padding, grab_pos - grab_sz*0.5f, bb.Max.x - grab_padding, grab_pos + grab_sz*0.5f); - window->DrawList->AddRectFilled(grab_bb.Min, grab_bb.Max, GetColorU32(g.ActiveId == id ? ImGuiCol_SliderGrabActive : ImGuiCol_SliderGrab), style.GrabRounding); - - return value_changed; -} - -// For 32-bits and larger types, slider bounds are limited to half the natural type range. -// So e.g. an integer Slider between INT_MAX-10 and INT_MAX will fail, but an integer Slider between INT_MAX/2-10 and INT_MAX/2. -// It would be possible to life that limitation with some work but it doesn't seem to be work it for sliders. -bool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format, float power, ImGuiSliderFlags flags) -{ - switch (data_type) - { - case ImGuiDataType_S32: - IM_ASSERT(*(const ImS32*)v_min >= IM_S32_MIN/2 && *(const ImS32*)v_max <= IM_S32_MAX/2); - return SliderBehaviorT(bb, id, data_type, (ImS32*)v, *(const ImS32*)v_min, *(const ImS32*)v_max, format, power, flags); - case ImGuiDataType_U32: - IM_ASSERT(*(const ImU32*)v_min <= IM_U32_MAX/2); - return SliderBehaviorT(bb, id, data_type, (ImU32*)v, *(const ImU32*)v_min, *(const ImU32*)v_max, format, power, flags); - case ImGuiDataType_S64: - IM_ASSERT(*(const ImS64*)v_min >= IM_S64_MIN/2 && *(const ImS64*)v_max <= IM_S64_MAX/2); - return SliderBehaviorT(bb, id, data_type, (ImS64*)v, *(const ImS64*)v_min, *(const ImS64*)v_max, format, power, flags); - case ImGuiDataType_U64: - IM_ASSERT(*(const ImU64*)v_min <= IM_U64_MAX/2); - return SliderBehaviorT(bb, id, data_type, (ImU64*)v, *(const ImU64*)v_min, *(const ImU64*)v_max, format, power, flags); - case ImGuiDataType_Float: - IM_ASSERT(*(const float*)v_min >= -FLT_MAX/2.0f && *(const float*)v_max <= FLT_MAX/2.0f); - return SliderBehaviorT(bb, id, data_type, (float*)v, *(const float*)v_min, *(const float*)v_max, format, power, flags); - case ImGuiDataType_Double: - IM_ASSERT(*(const double*)v_min >= -DBL_MAX/2.0f && *(const double*)v_max <= DBL_MAX/2.0f); - return SliderBehaviorT(bb, id, data_type, (double*)v, *(const double*)v_min, *(const double*)v_max, format, power, flags); - case ImGuiDataType_COUNT: break; - } - IM_ASSERT(0); - return false; -} - -// FIXME-LEGACY: Prior to 1.61 our DragInt() function internally used floats and because of this the compile-time default value for format was "%.0f". -// Even though we changed the compile-time default, we expect users to have carried %f around, which would break the display of DragInt() calls. -// To honor backward compatibility we are rewriting the format string, unless IMGUI_DISABLE_OBSOLETE_FUNCTIONS is enabled. What could possibly go wrong?! -static const char* PatchFormatStringFloatToInt(const char* fmt) -{ - if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '0' && fmt[3] == 'f' && fmt[4] == 0) // Fast legacy path for "%.0f" which is expected to be the most common case. - return "%d"; - const char* fmt_start = ImParseFormatFindStart(fmt); // Find % (if any, and ignore %%) - const char* fmt_end = ImParseFormatFindEnd(fmt_start); // Find end of format specifier, which itself is an exercise of confidence/recklessness (because snprintf is dependent on libc or user). - if (fmt_end > fmt_start && fmt_end[-1] == 'f') - { -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - if (fmt_start == fmt && fmt_end[0] == 0) - return "%d"; - ImGuiContext& g = *GImGui; - ImFormatString(g.TempBuffer, IM_ARRAYSIZE(g.TempBuffer), "%.*s%%d%s", (int)(fmt_start - fmt), fmt, fmt_end); // Honor leading and trailing decorations, but lose alignment/precision. - return g.TempBuffer; -#else - IM_ASSERT(0 && "DragInt(): Invalid format string!"); // Old versions used a default parameter of "%.0f", please replace with e.g. "%d" -#endif - } - return fmt; -} - -bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format, float power) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - const float w = CalcItemWidth(); - - const ImVec2 label_size = CalcTextSize(label, NULL, true); - const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f)); - const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); - - // NB- we don't call ItemSize() yet because we may turn into a text edit box below - if (!ItemAdd(total_bb, id, &frame_bb)) - { - ItemSize(total_bb, style.FramePadding.y); - return false; - } - - // Default format string when passing NULL - // Patch old "%.0f" format string to use "%d", read function comments for more details. - IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT); - if (format == NULL) - format = GDataTypeInfo[data_type].PrintFmt; - else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) - format = PatchFormatStringFloatToInt(format); - - // Tabbing or CTRL-clicking on Slider turns it into an input box - bool start_text_input = false; - const bool tab_focus_requested = FocusableItemRegister(window, id); - const bool hovered = ItemHoverable(frame_bb, id); - if (tab_focus_requested || (hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id)) - { - SetActiveID(id, window); - SetFocusID(id, window); - FocusWindow(window); - g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); - if (tab_focus_requested || g.IO.KeyCtrl || g.NavInputId == id) - { - start_text_input = true; - g.ScalarAsInputTextId = 0; - } - } - if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id)) - return InputScalarAsWidgetReplacement(frame_bb, id, label, data_type, v, format); - - // Actual slider behavior + render grab - ItemSize(total_bb, style.FramePadding.y); - const bool value_changed = SliderBehavior(frame_bb, id, data_type, v, v_min, v_max, format, power); - if (value_changed) - MarkItemValueChanged(id); - - // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. - char value_buf[64]; - const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, v, format); - RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.5f)); - - if (label_size.x > 0.0f) - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); - - return value_changed; -} - -bool ImGui::SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format, float power) -{ - return SliderScalar(label, ImGuiDataType_Float, v, &v_min, &v_max, format, power); -} - -bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format, float power) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - - const ImVec2 label_size = CalcTextSize(label, NULL, true); - const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size); - const ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); - - ItemSize(bb, style.FramePadding.y); - if (!ItemAdd(frame_bb, id)) - return false; - - // Default format string when passing NULL - // Patch old "%.0f" format string to use "%d", read function comments for more details. - IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT); - if (format == NULL) - format = GDataTypeInfo[data_type].PrintFmt; - else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) - format = PatchFormatStringFloatToInt(format); - - const bool hovered = ItemHoverable(frame_bb, id); - if ((hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavInputId == id) - { - SetActiveID(id, window); - SetFocusID(id, window); - FocusWindow(window); - g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); - } - - // Actual slider behavior + render grab - const bool value_changed = SliderBehavior(frame_bb, id, data_type, v, v_min, v_max, format, power, ImGuiSliderFlags_Vertical); - if (value_changed) - MarkItemValueChanged(id); - - // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. - // For the vertical slider we allow centered text to overlap the frame padding - char value_buf[64]; - const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, v, format); - RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f,0.0f)); - if (label_size.x > 0.0f) - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); - - return value_changed; -} - -bool ImGui::SliderAngle(const char* label, float* v_rad, float v_degrees_min, float v_degrees_max) -{ - float v_deg = (*v_rad) * 360.0f / (2*IM_PI); - bool value_changed = SliderFloat(label, &v_deg, v_degrees_min, v_degrees_max, "%.0f deg", 1.0f); - *v_rad = v_deg * (2*IM_PI) / 360.0f; - return value_changed; -} - -bool ImGui::SliderInt(const char* label, int* v, int v_min, int v_max, const char* format) -{ - return SliderScalar(label, ImGuiDataType_S32, v, &v_min, &v_max, format); -} - -bool ImGui::VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* format, float power) -{ - return VSliderScalar(label, size, ImGuiDataType_Float, v, &v_min, &v_max, format, power); -} - -bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format) -{ - return VSliderScalar(label, size, ImGuiDataType_S32, v, &v_min, &v_max, format); -} - -// Add multiple sliders on 1 line for compact edition of multiple components -bool ImGui::SliderScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* v_min, const void* v_max, const char* format, float power) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - bool value_changed = false; - BeginGroup(); - PushID(label); - PushMultiItemsWidths(components); - size_t type_size = GDataTypeInfo[data_type].Size; - for (int i = 0; i < components; i++) - { - PushID(i); - value_changed |= SliderScalar("##v", data_type, v, v_min, v_max, format, power); - SameLine(0, g.Style.ItemInnerSpacing.x); - PopID(); - PopItemWidth(); - v = (void*)((char*)v + type_size); - } - PopID(); - - TextUnformatted(label, FindRenderedTextEnd(label)); - EndGroup(); - return value_changed; -} - -bool ImGui::SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format, float power) -{ - return SliderScalarN(label, ImGuiDataType_Float, v, 2, &v_min, &v_max, format, power); -} - -bool ImGui::SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format, float power) -{ - return SliderScalarN(label, ImGuiDataType_Float, v, 3, &v_min, &v_max, format, power); -} - -bool ImGui::SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format, float power) -{ - return SliderScalarN(label, ImGuiDataType_Float, v, 4, &v_min, &v_max, format, power); -} - -bool ImGui::SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* format) -{ - return SliderScalarN(label, ImGuiDataType_S32, v, 2, &v_min, &v_max, format); -} - -bool ImGui::SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* format) -{ - return SliderScalarN(label, ImGuiDataType_S32, v, 3, &v_min, &v_max, format); -} - -bool ImGui::SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* format) -{ - return SliderScalarN(label, ImGuiDataType_S32, v, 4, &v_min, &v_max, format); -} - -// This is called by DragBehavior() when the widget is active (held by mouse or being manipulated with Nav controls) -template -static bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const TYPE v_min, const TYPE v_max, const char* format, float power) -{ - ImGuiContext& g = *GImGui; - - // Default tweak speed - bool has_min_max = (v_min != v_max) && (v_max - v_max < FLT_MAX); - if (v_speed == 0.0f && has_min_max) - v_speed = (float)((v_max - v_min) * g.DragSpeedDefaultRatio); - - // Inputs accumulates into g.DragCurrentAccum, which is flushed into the current value as soon as it makes a difference with our precision settings - float adjust_delta = 0.0f; - if (g.ActiveIdSource == ImGuiInputSource_Mouse && IsMousePosValid() && g.IO.MouseDragMaxDistanceSqr[0] > 1.0f*1.0f) - { - adjust_delta = g.IO.MouseDelta.x; - if (g.IO.KeyAlt) - adjust_delta *= 1.0f/100.0f; - if (g.IO.KeyShift) - adjust_delta *= 10.0f; - } - if (g.ActiveIdSource == ImGuiInputSource_Nav) - { - int decimal_precision = (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) ? ImParseFormatPrecision(format, 3) : 0; - adjust_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard|ImGuiNavDirSourceFlags_PadDPad, ImGuiInputReadMode_RepeatFast, 1.0f/10.0f, 10.0f).x; - v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision)); - } - adjust_delta *= v_speed; - - // Clear current value on activation - // Avoid altering values and clamping when we are _already_ past the limits and heading in the same direction, so e.g. if range is 0..255, current value is 300 and we are pushing to the right side, keep the 300. - bool is_just_activated = g.ActiveIdIsJustActivated; - bool is_already_past_limits_and_pushing_outward = has_min_max && ((*v >= v_max && adjust_delta > 0.0f) || (*v <= v_min && adjust_delta < 0.0f)); - if (is_just_activated || is_already_past_limits_and_pushing_outward) - { - g.DragCurrentAccum = 0.0f; - g.DragCurrentAccumDirty = false; - } - else if (adjust_delta != 0.0f) - { - g.DragCurrentAccum += adjust_delta; - g.DragCurrentAccumDirty = true; - } - - if (!g.DragCurrentAccumDirty) - return false; - - TYPE v_cur = *v; - FLOATTYPE v_old_ref_for_accum_remainder = (FLOATTYPE)0.0f; - - const bool is_power = (power != 1.0f && (data_type == ImGuiDataType_Float || data_type == ImGuiDataType_Double) && has_min_max); - if (is_power) - { - // Offset + round to user desired precision, with a curve on the v_min..v_max range to get more precision on one side of the range - FLOATTYPE v_old_norm_curved = ImPow((FLOATTYPE)(v_cur - v_min) / (FLOATTYPE)(v_max - v_min), (FLOATTYPE)1.0f / power); - FLOATTYPE v_new_norm_curved = v_old_norm_curved + (g.DragCurrentAccum / (v_max - v_min)); - v_cur = v_min + (TYPE)ImPow(ImSaturate((float)v_new_norm_curved), power) * (v_max - v_min); - v_old_ref_for_accum_remainder = v_old_norm_curved; - } - else - { - v_cur += (TYPE)g.DragCurrentAccum; - } - - // Round to user desired precision based on format string - v_cur = RoundScalarWithFormat(format, data_type, v_cur); - - // Preserve remainder after rounding has been applied. This also allow slow tweaking of values. - g.DragCurrentAccumDirty = false; - if (is_power) - { - FLOATTYPE v_cur_norm_curved = ImPow((FLOATTYPE)(v_cur - v_min) / (FLOATTYPE)(v_max - v_min), (FLOATTYPE)1.0f / power); - g.DragCurrentAccum -= (float)(v_cur_norm_curved - v_old_ref_for_accum_remainder); - } - else - { - g.DragCurrentAccum -= (float)((SIGNEDTYPE)v_cur - (SIGNEDTYPE)*v); - } - - // Lose zero sign for float/double - if (v_cur == (TYPE)-0) - v_cur = (TYPE)0; - - // Clamp values (handle overflow/wrap-around) - if (*v != v_cur && has_min_max) - { - if (v_cur < v_min || (v_cur > *v && adjust_delta < 0.0f)) - v_cur = v_min; - if (v_cur > v_max || (v_cur < *v && adjust_delta > 0.0f)) - v_cur = v_max; - } - - // Apply result - if (*v == v_cur) - return false; - *v = v_cur; - return true; -} - -bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* v, float v_speed, const void* v_min, const void* v_max, const char* format, float power) -{ - ImGuiContext& g = *GImGui; - if (g.ActiveId == id) - { - if (g.ActiveIdSource == ImGuiInputSource_Mouse && !g.IO.MouseDown[0]) - ClearActiveID(); - else if (g.ActiveIdSource == ImGuiInputSource_Nav && g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) - ClearActiveID(); - } - if (g.ActiveId != id) - return false; - - switch (data_type) - { - case ImGuiDataType_S32: return DragBehaviorT(data_type, (ImS32*)v, v_speed, v_min ? *(const ImS32* )v_min : IM_S32_MIN, v_max ? *(const ImS32* )v_max : IM_S32_MAX, format, power); - case ImGuiDataType_U32: return DragBehaviorT(data_type, (ImU32*)v, v_speed, v_min ? *(const ImU32* )v_min : IM_U32_MIN, v_max ? *(const ImU32* )v_max : IM_U32_MAX, format, power); - case ImGuiDataType_S64: return DragBehaviorT(data_type, (ImS64*)v, v_speed, v_min ? *(const ImS64* )v_min : IM_S64_MIN, v_max ? *(const ImS64* )v_max : IM_S64_MAX, format, power); - case ImGuiDataType_U64: return DragBehaviorT(data_type, (ImU64*)v, v_speed, v_min ? *(const ImU64* )v_min : IM_U64_MIN, v_max ? *(const ImU64* )v_max : IM_U64_MAX, format, power); - case ImGuiDataType_Float: return DragBehaviorT(data_type, (float*)v, v_speed, v_min ? *(const float* )v_min : -FLT_MAX, v_max ? *(const float* )v_max : FLT_MAX, format, power); - case ImGuiDataType_Double: return DragBehaviorT(data_type, (double*)v, v_speed, v_min ? *(const double*)v_min : -DBL_MAX, v_max ? *(const double*)v_max : DBL_MAX, format, power); - case ImGuiDataType_COUNT: break; - } - IM_ASSERT(0); - return false; -} - -bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* v, float v_speed, const void* v_min, const void* v_max, const char* format, float power) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - if (power != 1.0f) - IM_ASSERT(v_min != NULL && v_max != NULL); // When using a power curve the drag needs to have known bounds - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - const float w = CalcItemWidth(); - - const ImVec2 label_size = CalcTextSize(label, NULL, true); - const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f)); - const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding); - const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); - - // NB- we don't call ItemSize() yet because we may turn into a text edit box below - if (!ItemAdd(total_bb, id, &frame_bb)) - { - ItemSize(total_bb, style.FramePadding.y); - return false; - } - const bool hovered = ItemHoverable(frame_bb, id); - - // Default format string when passing NULL - // Patch old "%.0f" format string to use "%d", read function comments for more details. - IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT); - if (format == NULL) - format = GDataTypeInfo[data_type].PrintFmt; - else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) - format = PatchFormatStringFloatToInt(format); - - // Tabbing or CTRL-clicking on Drag turns it into an input box - bool start_text_input = false; - const bool tab_focus_requested = FocusableItemRegister(window, id); - if (tab_focus_requested || (hovered && (g.IO.MouseClicked[0] || g.IO.MouseDoubleClicked[0])) || g.NavActivateId == id || (g.NavInputId == id && g.ScalarAsInputTextId != id)) - { - SetActiveID(id, window); - SetFocusID(id, window); - FocusWindow(window); - g.ActiveIdAllowNavDirFlags = (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); - if (tab_focus_requested || g.IO.KeyCtrl || g.IO.MouseDoubleClicked[0] || g.NavInputId == id) - { - start_text_input = true; - g.ScalarAsInputTextId = 0; - } - } - if (start_text_input || (g.ActiveId == id && g.ScalarAsInputTextId == id)) - return InputScalarAsWidgetReplacement(frame_bb, id, label, data_type, v, format); - - // Actual drag behavior - ItemSize(total_bb, style.FramePadding.y); - const bool value_changed = DragBehavior(id, data_type, v, v_speed, v_min, v_max, format, power); - if (value_changed) - MarkItemValueChanged(id); - - // Draw frame - const ImU32 frame_col = GetColorU32(g.ActiveId == id ? ImGuiCol_FrameBgActive : g.HoveredId == id ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); - RenderNavHighlight(frame_bb, id); - RenderFrame(frame_bb.Min, frame_bb.Max, frame_col, true, style.FrameRounding); - - // Display value using user-provided display format so user can add prefix/suffix/decorations to the value. - char value_buf[64]; - const char* value_buf_end = value_buf + DataTypeFormatString(value_buf, IM_ARRAYSIZE(value_buf), data_type, v, format); - RenderTextClipped(frame_bb.Min, frame_bb.Max, value_buf, value_buf_end, NULL, ImVec2(0.5f, 0.5f)); - - if (label_size.x > 0.0f) - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label); - - return value_changed; -} - -bool ImGui::DragScalarN(const char* label, ImGuiDataType data_type, void* v, int components, float v_speed, const void* v_min, const void* v_max, const char* format, float power) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - bool value_changed = false; - BeginGroup(); - PushID(label); - PushMultiItemsWidths(components); - size_t type_size = GDataTypeInfo[data_type].Size; - for (int i = 0; i < components; i++) - { - PushID(i); - value_changed |= DragScalar("##v", data_type, v, v_speed, v_min, v_max, format, power); - SameLine(0, g.Style.ItemInnerSpacing.x); - PopID(); - PopItemWidth(); - v = (void*)((char*)v + type_size); - } - PopID(); - - TextUnformatted(label, FindRenderedTextEnd(label)); - EndGroup(); - return value_changed; -} - -bool ImGui::DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* format, float power) -{ - return DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, format, power); -} - -bool ImGui::DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* format, float power) -{ - return DragScalarN(label, ImGuiDataType_Float, v, 2, v_speed, &v_min, &v_max, format, power); -} - -bool ImGui::DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* format, float power) -{ - return DragScalarN(label, ImGuiDataType_Float, v, 3, v_speed, &v_min, &v_max, format, power); -} - -bool ImGui::DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* format, float power) -{ - return DragScalarN(label, ImGuiDataType_Float, v, 4, v_speed, &v_min, &v_max, format, power); -} - -bool ImGui::DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed, float v_min, float v_max, const char* format, const char* format_max, float power) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - PushID(label); - BeginGroup(); - PushMultiItemsWidths(2); - - bool value_changed = DragFloat("##min", v_current_min, v_speed, (v_min >= v_max) ? -FLT_MAX : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), format, power); - PopItemWidth(); - SameLine(0, g.Style.ItemInnerSpacing.x); - value_changed |= DragFloat("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? FLT_MAX : v_max, format_max ? format_max : format, power); - PopItemWidth(); - SameLine(0, g.Style.ItemInnerSpacing.x); - - TextUnformatted(label, FindRenderedTextEnd(label)); - EndGroup(); - PopID(); - return value_changed; -} - -// NB: v_speed is float to allow adjusting the drag speed with more precision -bool ImGui::DragInt(const char* label, int* v, float v_speed, int v_min, int v_max, const char* format) -{ - return DragScalar(label, ImGuiDataType_S32, v, v_speed, &v_min, &v_max, format); -} - -bool ImGui::DragInt2(const char* label, int v[2], float v_speed, int v_min, int v_max, const char* format) -{ - return DragScalarN(label, ImGuiDataType_S32, v, 2, v_speed, &v_min, &v_max, format); -} - -bool ImGui::DragInt3(const char* label, int v[3], float v_speed, int v_min, int v_max, const char* format) -{ - return DragScalarN(label, ImGuiDataType_S32, v, 3, v_speed, &v_min, &v_max, format); -} - -bool ImGui::DragInt4(const char* label, int v[4], float v_speed, int v_min, int v_max, const char* format) -{ - return DragScalarN(label, ImGuiDataType_S32, v, 4, v_speed, &v_min, &v_max, format); -} - -bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed, int v_min, int v_max, const char* format, const char* format_max) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - PushID(label); - BeginGroup(); - PushMultiItemsWidths(2); - - bool value_changed = DragInt("##min", v_current_min, v_speed, (v_min >= v_max) ? INT_MIN : v_min, (v_min >= v_max) ? *v_current_max : ImMin(v_max, *v_current_max), format); - PopItemWidth(); - SameLine(0, g.Style.ItemInnerSpacing.x); - value_changed |= DragInt("##max", v_current_max, v_speed, (v_min >= v_max) ? *v_current_min : ImMax(v_min, *v_current_min), (v_min >= v_max) ? INT_MAX : v_max, format_max ? format_max : format); - PopItemWidth(); - SameLine(0, g.Style.ItemInnerSpacing.x); - - TextUnformatted(label, FindRenderedTextEnd(label)); - EndGroup(); - PopID(); - - return value_changed; -} - -void ImGui::PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - - const ImVec2 label_size = CalcTextSize(label, NULL, true); - if (graph_size.x == 0.0f) - graph_size.x = CalcItemWidth(); - if (graph_size.y == 0.0f) - graph_size.y = label_size.y + (style.FramePadding.y * 2); - - const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(graph_size.x, graph_size.y)); - const ImRect inner_bb(frame_bb.Min + style.FramePadding, frame_bb.Max - style.FramePadding); - const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0)); - ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, 0, &frame_bb)) - return; - const bool hovered = ItemHoverable(inner_bb, 0); - - // Determine scale from values if not specified - if (scale_min == FLT_MAX || scale_max == FLT_MAX) - { - float v_min = FLT_MAX; - float v_max = -FLT_MAX; - for (int i = 0; i < values_count; i++) - { - const float v = values_getter(data, i); - v_min = ImMin(v_min, v); - v_max = ImMax(v_max, v); - } - if (scale_min == FLT_MAX) - scale_min = v_min; - if (scale_max == FLT_MAX) - scale_max = v_max; - } - - RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); - - if (values_count > 0) - { - int res_w = ImMin((int)graph_size.x, values_count) + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0); - int item_count = values_count + ((plot_type == ImGuiPlotType_Lines) ? -1 : 0); - - // Tooltip on hover - int v_hovered = -1; - if (hovered) - { - const float t = ImClamp((g.IO.MousePos.x - inner_bb.Min.x) / (inner_bb.Max.x - inner_bb.Min.x), 0.0f, 0.9999f); - const int v_idx = (int)(t * item_count); - IM_ASSERT(v_idx >= 0 && v_idx < values_count); - - const float v0 = values_getter(data, (v_idx + values_offset) % values_count); - const float v1 = values_getter(data, (v_idx + 1 + values_offset) % values_count); - if (plot_type == ImGuiPlotType_Lines) - SetTooltip("%d: %8.4g\n%d: %8.4g", v_idx, v0, v_idx+1, v1); - else if (plot_type == ImGuiPlotType_Histogram) - SetTooltip("%d: %8.4g", v_idx, v0); - v_hovered = v_idx; - } - - const float t_step = 1.0f / (float)res_w; - const float inv_scale = (scale_min == scale_max) ? 0.0f : (1.0f / (scale_max - scale_min)); - - float v0 = values_getter(data, (0 + values_offset) % values_count); - float t0 = 0.0f; - ImVec2 tp0 = ImVec2( t0, 1.0f - ImSaturate((v0 - scale_min) * inv_scale) ); // Point in the normalized space of our target rectangle - float histogram_zero_line_t = (scale_min * scale_max < 0.0f) ? (-scale_min * inv_scale) : (scale_min < 0.0f ? 0.0f : 1.0f); // Where does the zero line stands - - const ImU32 col_base = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLines : ImGuiCol_PlotHistogram); - const ImU32 col_hovered = GetColorU32((plot_type == ImGuiPlotType_Lines) ? ImGuiCol_PlotLinesHovered : ImGuiCol_PlotHistogramHovered); - - for (int n = 0; n < res_w; n++) - { - const float t1 = t0 + t_step; - const int v1_idx = (int)(t0 * item_count + 0.5f); - IM_ASSERT(v1_idx >= 0 && v1_idx < values_count); - const float v1 = values_getter(data, (v1_idx + values_offset + 1) % values_count); - const ImVec2 tp1 = ImVec2( t1, 1.0f - ImSaturate((v1 - scale_min) * inv_scale) ); - - // NB: Draw calls are merged together by the DrawList system. Still, we should render our batch are lower level to save a bit of CPU. - ImVec2 pos0 = ImLerp(inner_bb.Min, inner_bb.Max, tp0); - ImVec2 pos1 = ImLerp(inner_bb.Min, inner_bb.Max, (plot_type == ImGuiPlotType_Lines) ? tp1 : ImVec2(tp1.x, histogram_zero_line_t)); - if (plot_type == ImGuiPlotType_Lines) - { - window->DrawList->AddLine(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base); - } - else if (plot_type == ImGuiPlotType_Histogram) - { - if (pos1.x >= pos0.x + 2.0f) - pos1.x -= 1.0f; - window->DrawList->AddRectFilled(pos0, pos1, v_hovered == v1_idx ? col_hovered : col_base); - } - - t0 = t1; - tp0 = tp1; - } - } - - // Text overlay - if (overlay_text) - RenderTextClipped(ImVec2(frame_bb.Min.x, frame_bb.Min.y + style.FramePadding.y), frame_bb.Max, overlay_text, NULL, NULL, ImVec2(0.5f,0.0f)); - - if (label_size.x > 0.0f) - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, inner_bb.Min.y), label); -} - -struct ImGuiPlotArrayGetterData -{ - const float* Values; - int Stride; - - ImGuiPlotArrayGetterData(const float* values, int stride) { Values = values; Stride = stride; } -}; - -static float Plot_ArrayGetter(void* data, int idx) -{ - ImGuiPlotArrayGetterData* plot_data = (ImGuiPlotArrayGetterData*)data; - const float v = *(const float*)(const void*)((const unsigned char*)plot_data->Values + (size_t)idx * plot_data->Stride); - return v; -} - -void ImGui::PlotLines(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride) -{ - ImGuiPlotArrayGetterData data(values, stride); - PlotEx(ImGuiPlotType_Lines, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); -} - -void ImGui::PlotLines(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size) -{ - PlotEx(ImGuiPlotType_Lines, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); -} - -void ImGui::PlotHistogram(const char* label, const float* values, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size, int stride) -{ - ImGuiPlotArrayGetterData data(values, stride); - PlotEx(ImGuiPlotType_Histogram, label, &Plot_ArrayGetter, (void*)&data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); -} - -void ImGui::PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size) -{ - PlotEx(ImGuiPlotType_Histogram, label, values_getter, data, values_count, values_offset, overlay_text, scale_min, scale_max, graph_size); -} - -// size_arg (for each axis) < 0.0f: align to end, 0.0f: auto, > 0.0f: specified size -void ImGui::ProgressBar(float fraction, const ImVec2& size_arg, const char* overlay) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - - ImVec2 pos = window->DC.CursorPos; - ImRect bb(pos, pos + CalcItemSize(size_arg, CalcItemWidth(), g.FontSize + style.FramePadding.y*2.0f)); - ItemSize(bb, style.FramePadding.y); - if (!ItemAdd(bb, 0)) - return; - - // Render - fraction = ImSaturate(fraction); - RenderFrame(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); - bb.Expand(ImVec2(-style.FrameBorderSize, -style.FrameBorderSize)); - const ImVec2 fill_br = ImVec2(ImLerp(bb.Min.x, bb.Max.x, fraction), bb.Max.y); - RenderRectFilledRangeH(window->DrawList, bb, GetColorU32(ImGuiCol_PlotHistogram), 0.0f, fraction, style.FrameRounding); - - // Default displaying the fraction as percentage string, but user can override it - char overlay_buf[32]; - if (!overlay) - { - ImFormatString(overlay_buf, IM_ARRAYSIZE(overlay_buf), "%.0f%%", fraction*100+0.01f); - overlay = overlay_buf; - } - - ImVec2 overlay_size = CalcTextSize(overlay, NULL); - if (overlay_size.x > 0.0f) - RenderTextClipped(ImVec2(ImClamp(fill_br.x + style.ItemSpacing.x, bb.Min.x, bb.Max.x - overlay_size.x - style.ItemInnerSpacing.x), bb.Min.y), bb.Max, overlay, NULL, &overlay_size, ImVec2(0.0f,0.5f), &bb); -} - -bool ImGui::Checkbox(const char* label, bool* v) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - const ImVec2 label_size = CalcTextSize(label, NULL, true); - - const ImRect check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y*2, label_size.y + style.FramePadding.y*2)); // We want a square shape to we use Y twice - ItemSize(check_bb, style.FramePadding.y); - - ImRect total_bb = check_bb; - if (label_size.x > 0) - SameLine(0, style.ItemInnerSpacing.x); - const ImRect text_bb(window->DC.CursorPos + ImVec2(0,style.FramePadding.y), window->DC.CursorPos + ImVec2(0,style.FramePadding.y) + label_size); - if (label_size.x > 0) - { - ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y); - total_bb = ImRect(ImMin(check_bb.Min, text_bb.Min), ImMax(check_bb.Max, text_bb.Max)); - } - - if (!ItemAdd(total_bb, id)) - return false; - - bool hovered, held; - bool pressed = ButtonBehavior(total_bb, id, &hovered, &held); - if (pressed) - { - *v = !(*v); - MarkItemValueChanged(id); - } - - RenderNavHighlight(total_bb, id); - RenderFrame(check_bb.Min, check_bb.Max, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), true, style.FrameRounding); - if (*v) - { - const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight()); - const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f)); - RenderCheckMark(check_bb.Min + ImVec2(pad,pad), GetColorU32(ImGuiCol_CheckMark), check_bb.GetWidth() - pad*2.0f); - } - - if (g.LogEnabled) - LogRenderedText(&text_bb.Min, *v ? "[x]" : "[ ]"); - if (label_size.x > 0.0f) - RenderText(text_bb.Min, label); - - return pressed; -} - -bool ImGui::CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value) -{ - bool v = ((*flags & flags_value) == flags_value); - bool pressed = Checkbox(label, &v); - if (pressed) - { - if (v) - *flags |= flags_value; - else - *flags &= ~flags_value; - } - - return pressed; -} - -bool ImGui::RadioButton(const char* label, bool active) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - const ImVec2 label_size = CalcTextSize(label, NULL, true); - - const ImRect check_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(label_size.y + style.FramePadding.y*2-1, label_size.y + style.FramePadding.y*2-1)); - ItemSize(check_bb, style.FramePadding.y); - - ImRect total_bb = check_bb; - if (label_size.x > 0) - SameLine(0, style.ItemInnerSpacing.x); - const ImRect text_bb(window->DC.CursorPos + ImVec2(0, style.FramePadding.y), window->DC.CursorPos + ImVec2(0, style.FramePadding.y) + label_size); - if (label_size.x > 0) - { - ItemSize(ImVec2(text_bb.GetWidth(), check_bb.GetHeight()), style.FramePadding.y); - total_bb.Add(text_bb); - } - - if (!ItemAdd(total_bb, id)) - return false; - - ImVec2 center = check_bb.GetCenter(); - center.x = (float)(int)center.x + 0.5f; - center.y = (float)(int)center.y + 0.5f; - const float radius = check_bb.GetHeight() * 0.5f; - - bool hovered, held; - bool pressed = ButtonBehavior(total_bb, id, &hovered, &held); - if (pressed) - MarkItemValueChanged(id); - - RenderNavHighlight(total_bb, id); - window->DrawList->AddCircleFilled(center, radius, GetColorU32((held && hovered) ? ImGuiCol_FrameBgActive : hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg), 16); - if (active) - { - const float check_sz = ImMin(check_bb.GetWidth(), check_bb.GetHeight()); - const float pad = ImMax(1.0f, (float)(int)(check_sz / 6.0f)); - window->DrawList->AddCircleFilled(center, radius-pad, GetColorU32(ImGuiCol_CheckMark), 16); - } - - if (style.FrameBorderSize > 0.0f) - { - window->DrawList->AddCircle(center+ImVec2(1,1), radius, GetColorU32(ImGuiCol_BorderShadow), 16, style.FrameBorderSize); - window->DrawList->AddCircle(center, radius, GetColorU32(ImGuiCol_Border), 16, style.FrameBorderSize); - } - - if (g.LogEnabled) - LogRenderedText(&text_bb.Min, active ? "(x)" : "( )"); - if (label_size.x > 0.0f) - RenderText(text_bb.Min, label); - - return pressed; -} - -bool ImGui::RadioButton(const char* label, int* v, int v_button) -{ - const bool pressed = RadioButton(label, *v == v_button); - if (pressed) - *v = v_button; - return pressed; -} - -static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end) -{ - int line_count = 0; - const char* s = text_begin; - while (char c = *s++) // We are only matching for \n so we can ignore UTF-8 decoding - if (c == '\n') - line_count++; - s--; - if (s[0] != '\n' && s[0] != '\r') - line_count++; - *out_text_end = s; - return line_count; -} - -static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining, ImVec2* out_offset, bool stop_on_new_line) -{ - ImFont* font = GImGui->Font; - const float line_height = GImGui->FontSize; - const float scale = line_height / font->FontSize; - - ImVec2 text_size = ImVec2(0,0); - float line_width = 0.0f; - - const ImWchar* s = text_begin; - while (s < text_end) - { - unsigned int c = (unsigned int)(*s++); - if (c == '\n') - { - text_size.x = ImMax(text_size.x, line_width); - text_size.y += line_height; - line_width = 0.0f; - if (stop_on_new_line) - break; - continue; - } - if (c == '\r') - continue; - - const float char_width = font->GetCharAdvance((unsigned short)c) * scale; - line_width += char_width; - } - - if (text_size.x < line_width) - text_size.x = line_width; - - if (out_offset) - *out_offset = ImVec2(line_width, text_size.y + line_height); // offset allow for the possibility of sitting after a trailing \n - - if (line_width > 0 || text_size.y == 0.0f) // whereas size.y will ignore the trailing \n - text_size.y += line_height; - - if (remaining) - *remaining = s; - - return text_size; -} - -// Wrapper for stb_textedit.h to edit text (our wrapper is for: statically sized buffer, single-line, wchar characters. InputText converts between UTF-8 and wchar) -namespace ImGuiStb -{ - -static int STB_TEXTEDIT_STRINGLEN(const STB_TEXTEDIT_STRING* obj) { return obj->CurLenW; } -static ImWchar STB_TEXTEDIT_GETCHAR(const STB_TEXTEDIT_STRING* obj, int idx) { return obj->Text[idx]; } -static float STB_TEXTEDIT_GETWIDTH(STB_TEXTEDIT_STRING* obj, int line_start_idx, int char_idx) { ImWchar c = obj->Text[line_start_idx+char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; return GImGui->Font->GetCharAdvance(c) * (GImGui->FontSize / GImGui->Font->FontSize); } -static int STB_TEXTEDIT_KEYTOTEXT(int key) { return key >= 0x10000 ? 0 : key; } -static ImWchar STB_TEXTEDIT_NEWLINE = '\n'; -static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, STB_TEXTEDIT_STRING* obj, int line_start_idx) -{ - const ImWchar* text = obj->Text.Data; - const ImWchar* text_remaining = NULL; - const ImVec2 size = InputTextCalcTextSizeW(text + line_start_idx, text + obj->CurLenW, &text_remaining, NULL, true); - r->x0 = 0.0f; - r->x1 = size.x; - r->baseline_y_delta = size.y; - r->ymin = 0.0f; - r->ymax = size.y; - r->num_chars = (int)(text_remaining - (text + line_start_idx)); -} - -static bool is_separator(unsigned int c) { return ImCharIsBlankW(c) || c==',' || c==';' || c=='(' || c==')' || c=='{' || c=='}' || c=='[' || c==']' || c=='|'; } -static int is_word_boundary_from_right(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (is_separator( obj->Text[idx-1] ) && !is_separator( obj->Text[idx] ) ) : 1; } -static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; } -#ifdef __APPLE__ // FIXME: Move setting to IO structure -static int is_word_boundary_from_left(STB_TEXTEDIT_STRING* obj, int idx) { return idx > 0 ? (!is_separator( obj->Text[idx-1] ) && is_separator( obj->Text[idx] ) ) : 1; } -static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; } -#else -static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(STB_TEXTEDIT_STRING* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; } -#endif -#define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h -#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL - -static void STB_TEXTEDIT_DELETECHARS(STB_TEXTEDIT_STRING* obj, int pos, int n) -{ - ImWchar* dst = obj->Text.Data + pos; - - // We maintain our buffer length in both UTF-8 and wchar formats - obj->CurLenA -= ImTextCountUtf8BytesFromStr(dst, dst + n); - obj->CurLenW -= n; - - // Offset remaining text - const ImWchar* src = obj->Text.Data + pos + n; - while (ImWchar c = *src++) - *dst++ = c; - *dst = '\0'; -} - -static bool STB_TEXTEDIT_INSERTCHARS(STB_TEXTEDIT_STRING* obj, int pos, const ImWchar* new_text, int new_text_len) -{ - const int text_len = obj->CurLenW; - IM_ASSERT(pos <= text_len); - if (new_text_len + text_len + 1 > obj->Text.Size) - return false; - - const int new_text_len_utf8 = ImTextCountUtf8BytesFromStr(new_text, new_text + new_text_len); - if (new_text_len_utf8 + obj->CurLenA + 1 > obj->BufSizeA) - return false; - - ImWchar* text = obj->Text.Data; - if (pos != text_len) - memmove(text + pos + new_text_len, text + pos, (size_t)(text_len - pos) * sizeof(ImWchar)); - memcpy(text + pos, new_text, (size_t)new_text_len * sizeof(ImWchar)); - - obj->CurLenW += new_text_len; - obj->CurLenA += new_text_len_utf8; - obj->Text[obj->CurLenW] = '\0'; - - return true; -} - -// We don't use an enum so we can build even with conflicting symbols (if another user of stb_textedit.h leak their STB_TEXTEDIT_K_* symbols) -#define STB_TEXTEDIT_K_LEFT 0x10000 // keyboard input to move cursor left -#define STB_TEXTEDIT_K_RIGHT 0x10001 // keyboard input to move cursor right -#define STB_TEXTEDIT_K_UP 0x10002 // keyboard input to move cursor up -#define STB_TEXTEDIT_K_DOWN 0x10003 // keyboard input to move cursor down -#define STB_TEXTEDIT_K_LINESTART 0x10004 // keyboard input to move cursor to start of line -#define STB_TEXTEDIT_K_LINEEND 0x10005 // keyboard input to move cursor to end of line -#define STB_TEXTEDIT_K_TEXTSTART 0x10006 // keyboard input to move cursor to start of text -#define STB_TEXTEDIT_K_TEXTEND 0x10007 // keyboard input to move cursor to end of text -#define STB_TEXTEDIT_K_DELETE 0x10008 // keyboard input to delete selection or character under cursor -#define STB_TEXTEDIT_K_BACKSPACE 0x10009 // keyboard input to delete selection or character left of cursor -#define STB_TEXTEDIT_K_UNDO 0x1000A // keyboard input to perform undo -#define STB_TEXTEDIT_K_REDO 0x1000B // keyboard input to perform redo -#define STB_TEXTEDIT_K_WORDLEFT 0x1000C // keyboard input to move cursor left one word -#define STB_TEXTEDIT_K_WORDRIGHT 0x1000D // keyboard input to move cursor right one word -#define STB_TEXTEDIT_K_SHIFT 0x20000 - -#define STB_TEXTEDIT_IMPLEMENTATION -#include "stb_textedit.h" - -} - -void ImGuiTextEditState::OnKeyPressed(int key) -{ - stb_textedit_key(this, &StbState, key); - CursorFollow = true; - CursorAnimReset(); -} - -// Public API to manipulate UTF-8 text -// We expose UTF-8 to the user (unlike the STB_TEXTEDIT_* functions which are manipulating wchar) -// FIXME: The existence of this rarely exercised code path is a bit of a nuisance. -void ImGuiTextEditCallbackData::DeleteChars(int pos, int bytes_count) -{ - IM_ASSERT(pos + bytes_count <= BufTextLen); - char* dst = Buf + pos; - const char* src = Buf + pos + bytes_count; - while (char c = *src++) - *dst++ = c; - *dst = '\0'; - - if (CursorPos + bytes_count >= pos) - CursorPos -= bytes_count; - else if (CursorPos >= pos) - CursorPos = pos; - SelectionStart = SelectionEnd = CursorPos; - BufDirty = true; - BufTextLen -= bytes_count; -} - -void ImGuiTextEditCallbackData::InsertChars(int pos, const char* new_text, const char* new_text_end) -{ - const int new_text_len = new_text_end ? (int)(new_text_end - new_text) : (int)strlen(new_text); - if (new_text_len + BufTextLen + 1 >= BufSize) - return; - - if (BufTextLen != pos) - memmove(Buf + pos + new_text_len, Buf + pos, (size_t)(BufTextLen - pos)); - memcpy(Buf + pos, new_text, (size_t)new_text_len * sizeof(char)); - Buf[BufTextLen + new_text_len] = '\0'; - - if (CursorPos >= pos) - CursorPos += new_text_len; - SelectionStart = SelectionEnd = CursorPos; - BufDirty = true; - BufTextLen += new_text_len; -} - -// Return false to discard a character. -static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data) -{ - unsigned int c = *p_char; - - if (c < 128 && c != ' ' && !isprint((int)(c & 0xFF))) - { - bool pass = false; - pass |= (c == '\n' && (flags & ImGuiInputTextFlags_Multiline)); - pass |= (c == '\t' && (flags & ImGuiInputTextFlags_AllowTabInput)); - if (!pass) - return false; - } - - if (c >= 0xE000 && c <= 0xF8FF) // Filter private Unicode range. I don't imagine anybody would want to input them. GLFW on OSX seems to send private characters for special keys like arrow keys. - return false; - - if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_CharsScientific)) - { - if (flags & ImGuiInputTextFlags_CharsDecimal) - if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/')) - return false; - - if (flags & ImGuiInputTextFlags_CharsScientific) - if (!(c >= '0' && c <= '9') && (c != '.') && (c != '-') && (c != '+') && (c != '*') && (c != '/') && (c != 'e') && (c != 'E')) - return false; - - if (flags & ImGuiInputTextFlags_CharsHexadecimal) - if (!(c >= '0' && c <= '9') && !(c >= 'a' && c <= 'f') && !(c >= 'A' && c <= 'F')) - return false; - - if (flags & ImGuiInputTextFlags_CharsUppercase) - if (c >= 'a' && c <= 'z') - *p_char = (c += (unsigned int)('A'-'a')); - - if (flags & ImGuiInputTextFlags_CharsNoBlank) - if (ImCharIsBlankW(c)) - return false; - } - - if (flags & ImGuiInputTextFlags_CallbackCharFilter) - { - ImGuiTextEditCallbackData callback_data; - memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData)); - callback_data.EventFlag = ImGuiInputTextFlags_CallbackCharFilter; - callback_data.EventChar = (ImWchar)c; - callback_data.Flags = flags; - callback_data.UserData = user_data; - if (callback(&callback_data) != 0) - return false; - *p_char = callback_data.EventChar; - if (!callback_data.EventChar) - return false; - } - - return true; -} - -// Edit a string of text -// NB: when active, hold on a privately held copy of the text (and apply back to 'buf'). So changing 'buf' while active has no effect. -// FIXME: Rather messy function partly because we are doing UTF8 > u16 > UTF8 conversions on the go to more easily handle stb_textedit calls. Ideally we should stay in UTF-8 all the time. See https://github.com/nothings/stb/issues/188 -bool ImGui::InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackHistory) && (flags & ImGuiInputTextFlags_Multiline))); // Can't use both together (they both use up/down keys) - IM_ASSERT(!((flags & ImGuiInputTextFlags_CallbackCompletion) && (flags & ImGuiInputTextFlags_AllowTabInput))); // Can't use both together (they both use tab key) - - ImGuiContext& g = *GImGui; - const ImGuiIO& io = g.IO; - const ImGuiStyle& style = g.Style; - - const bool is_multiline = (flags & ImGuiInputTextFlags_Multiline) != 0; - const bool is_editable = (flags & ImGuiInputTextFlags_ReadOnly) == 0; - const bool is_password = (flags & ImGuiInputTextFlags_Password) != 0; - const bool is_undoable = (flags & ImGuiInputTextFlags_NoUndoRedo) == 0; - - if (is_multiline) // Open group before calling GetID() because groups tracks id created within their scope, - BeginGroup(); - const ImGuiID id = window->GetID(label); - const ImVec2 label_size = CalcTextSize(label, NULL, true); - ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), (is_multiline ? GetTextLineHeight() * 8.0f : label_size.y) + style.FramePadding.y*2.0f); // Arbitrary default of 8 lines high for multi-line - const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + size); - const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? (style.ItemInnerSpacing.x + label_size.x) : 0.0f, 0.0f)); - - ImGuiWindow* draw_window = window; - if (is_multiline) - { - ItemAdd(total_bb, id, &frame_bb); - if (!BeginChildFrame(id, frame_bb.GetSize())) - { - EndChildFrame(); - EndGroup(); - return false; - } - draw_window = GetCurrentWindow(); - draw_window->DC.NavLayerActiveMaskNext |= draw_window->DC.NavLayerCurrentMask; // This is to ensure that EndChild() will display a navigation highlight - size.x -= draw_window->ScrollbarSizes.x; - } - else - { - ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, id, &frame_bb)) - return false; - } - const bool hovered = ItemHoverable(frame_bb, id); - if (hovered) - g.MouseCursor = ImGuiMouseCursor_TextInput; - - // Password pushes a temporary font with only a fallback glyph - if (is_password) - { - const ImFontGlyph* glyph = g.Font->FindGlyph('*'); - ImFont* password_font = &g.InputTextPasswordFont; - password_font->FontSize = g.Font->FontSize; - password_font->Scale = g.Font->Scale; - password_font->DisplayOffset = g.Font->DisplayOffset; - password_font->Ascent = g.Font->Ascent; - password_font->Descent = g.Font->Descent; - password_font->ContainerAtlas = g.Font->ContainerAtlas; - password_font->FallbackGlyph = glyph; - password_font->FallbackAdvanceX = glyph->AdvanceX; - IM_ASSERT(password_font->Glyphs.empty() && password_font->IndexAdvanceX.empty() && password_font->IndexLookup.empty()); - PushFont(password_font); - } - - // NB: we are only allowed to access 'edit_state' if we are the active widget. - ImGuiTextEditState& edit_state = g.InputTextState; - - const bool focus_requested = FocusableItemRegister(window, id, (flags & (ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_AllowTabInput)) == 0); // Using completion callback disable keyboard tabbing - const bool focus_requested_by_code = focus_requested && (window->FocusIdxAllCounter == window->FocusIdxAllRequestCurrent); - const bool focus_requested_by_tab = focus_requested && !focus_requested_by_code; - - const bool user_clicked = hovered && io.MouseClicked[0]; - const bool user_scrolled = is_multiline && g.ActiveId == 0 && edit_state.Id == id && g.ActiveIdPreviousFrame == draw_window->GetIDNoKeepAlive("#SCROLLY"); - const bool user_nav_input_start = (g.ActiveId != id) && ((g.NavInputId == id) || (g.NavActivateId == id && g.NavInputSource == ImGuiInputSource_NavKeyboard)); - - bool clear_active_id = false; - - bool select_all = (g.ActiveId != id) && ((flags & ImGuiInputTextFlags_AutoSelectAll) != 0 || user_nav_input_start) && (!is_multiline); - if (focus_requested || user_clicked || user_scrolled || user_nav_input_start) - { - if (g.ActiveId != id) - { - // Start edition - // Take a copy of the initial buffer value (both in original UTF-8 format and converted to wchar) - // From the moment we focused we are ignoring the content of 'buf' (unless we are in read-only mode) - const int prev_len_w = edit_state.CurLenW; - edit_state.Text.resize(buf_size+1); // wchar count <= UTF-8 count. we use +1 to make sure that .Data isn't NULL so it doesn't crash. - edit_state.InitialText.resize(buf_size+1); // UTF-8. we use +1 to make sure that .Data isn't NULL so it doesn't crash. - ImStrncpy(edit_state.InitialText.Data, buf, edit_state.InitialText.Size); - const char* buf_end = NULL; - edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, buf, NULL, &buf_end); - edit_state.CurLenA = (int)(buf_end - buf); // We can't get the result from ImFormatString() above because it is not UTF-8 aware. Here we'll cut off malformed UTF-8. - edit_state.CursorAnimReset(); - - // Preserve cursor position and undo/redo stack if we come back to same widget - // FIXME: We should probably compare the whole buffer to be on the safety side. Comparing buf (utf8) and edit_state.Text (wchar). - const bool recycle_state = (edit_state.Id == id) && (prev_len_w == edit_state.CurLenW); - if (recycle_state) - { - // Recycle existing cursor/selection/undo stack but clamp position - // Note a single mouse click will override the cursor/position immediately by calling stb_textedit_click handler. - edit_state.CursorClamp(); - } - else - { - edit_state.Id = id; - edit_state.ScrollX = 0.0f; - stb_textedit_initialize_state(&edit_state.StbState, !is_multiline); - if (!is_multiline && focus_requested_by_code) - select_all = true; - } - if (flags & ImGuiInputTextFlags_AlwaysInsertMode) - edit_state.StbState.insert_mode = true; - if (!is_multiline && (focus_requested_by_tab || (user_clicked && io.KeyCtrl))) - select_all = true; - } - SetActiveID(id, window); - SetFocusID(id, window); - FocusWindow(window); - if (!is_multiline && !(flags & ImGuiInputTextFlags_CallbackHistory)) - g.ActiveIdAllowNavDirFlags |= ((1 << ImGuiDir_Up) | (1 << ImGuiDir_Down)); - } - else if (io.MouseClicked[0]) - { - // Release focus when we click outside - clear_active_id = true; - } - - bool value_changed = false; - bool enter_pressed = false; - - if (g.ActiveId == id) - { - if (!is_editable && !g.ActiveIdIsJustActivated) - { - // When read-only we always use the live data passed to the function - edit_state.Text.resize(buf_size+1); - const char* buf_end = NULL; - edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, buf, NULL, &buf_end); - edit_state.CurLenA = (int)(buf_end - buf); - edit_state.CursorClamp(); - } - - edit_state.BufSizeA = buf_size; - - // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget. - // Down the line we should have a cleaner library-wide concept of Selected vs Active. - g.ActiveIdAllowOverlap = !io.MouseDown[0]; - g.WantTextInputNextFrame = 1; - - // Edit in progress - const float mouse_x = (io.MousePos.x - frame_bb.Min.x - style.FramePadding.x) + edit_state.ScrollX; - const float mouse_y = (is_multiline ? (io.MousePos.y - draw_window->DC.CursorPos.y - style.FramePadding.y) : (g.FontSize*0.5f)); - - const bool is_osx = io.OptMacOSXBehaviors; - if (select_all || (hovered && !is_osx && io.MouseDoubleClicked[0])) - { - edit_state.SelectAll(); - edit_state.SelectedAllMouseLock = true; - } - else if (hovered && is_osx && io.MouseDoubleClicked[0]) - { - // Double-click select a word only, OS X style (by simulating keystrokes) - edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT); - edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT); - } - else if (io.MouseClicked[0] && !edit_state.SelectedAllMouseLock) - { - if (hovered) - { - stb_textedit_click(&edit_state, &edit_state.StbState, mouse_x, mouse_y); - edit_state.CursorAnimReset(); - } - } - else if (io.MouseDown[0] && !edit_state.SelectedAllMouseLock && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)) - { - stb_textedit_drag(&edit_state, &edit_state.StbState, mouse_x, mouse_y); - edit_state.CursorAnimReset(); - edit_state.CursorFollow = true; - } - if (edit_state.SelectedAllMouseLock && !io.MouseDown[0]) - edit_state.SelectedAllMouseLock = false; - - if (io.InputCharacters[0]) - { - // Process text input (before we check for Return because using some IME will effectively send a Return?) - // We ignore CTRL inputs, but need to allow ALT+CTRL as some keyboards (e.g. German) use AltGR (which _is_ Alt+Ctrl) to input certain characters. - bool ignore_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeySuper); - if (!ignore_inputs && is_editable && !user_nav_input_start) - for (int n = 0; n < IM_ARRAYSIZE(io.InputCharacters) && io.InputCharacters[n]; n++) - { - // Insert character if they pass filtering - unsigned int c = (unsigned int)io.InputCharacters[n]; - if (InputTextFilterCharacter(&c, flags, callback, user_data)) - edit_state.OnKeyPressed((int)c); - } - - // Consume characters - memset(g.IO.InputCharacters, 0, sizeof(g.IO.InputCharacters)); - } - } - - bool cancel_edit = false; - if (g.ActiveId == id && !g.ActiveIdIsJustActivated && !clear_active_id) - { - // Handle key-presses - const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0); - const bool is_osx = io.OptMacOSXBehaviors; - const bool is_shortcut_key = (is_osx ? (io.KeySuper && !io.KeyCtrl) : (io.KeyCtrl && !io.KeySuper)) && !io.KeyAlt && !io.KeyShift; // OS X style: Shortcuts using Cmd/Super instead of Ctrl - const bool is_osx_shift_shortcut = is_osx && io.KeySuper && io.KeyShift && !io.KeyCtrl && !io.KeyAlt; - const bool is_wordmove_key_down = is_osx ? io.KeyAlt : io.KeyCtrl; // OS X style: Text editing cursor movement using Alt instead of Ctrl - const bool is_startend_key_down = is_osx && io.KeySuper && !io.KeyCtrl && !io.KeyAlt; // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End - const bool is_ctrl_key_only = io.KeyCtrl && !io.KeyShift && !io.KeyAlt && !io.KeySuper; - const bool is_shift_key_only = io.KeyShift && !io.KeyCtrl && !io.KeyAlt && !io.KeySuper; - - const bool is_cut = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_X)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Delete))) && is_editable && !is_password && (!is_multiline || edit_state.HasSelection()); - const bool is_copy = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_C)) || (is_ctrl_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && !is_password && (!is_multiline || edit_state.HasSelection()); - const bool is_paste = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_V)) || (is_shift_key_only && IsKeyPressedMap(ImGuiKey_Insert))) && is_editable; - const bool is_undo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Z)) && is_editable && is_undoable); - const bool is_redo = ((is_shortcut_key && IsKeyPressedMap(ImGuiKey_Y)) || (is_osx_shift_shortcut && IsKeyPressedMap(ImGuiKey_Z))) && is_editable && is_undoable; - - if (IsKeyPressedMap(ImGuiKey_LeftArrow)) { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_RightArrow)) { edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_DownArrow) && is_multiline) { if (io.KeyCtrl) SetWindowScrollY(draw_window, ImMin(draw_window->Scroll.y + g.FontSize, GetScrollMaxY())); else edit_state.OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTEND : STB_TEXTEDIT_K_DOWN) | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_Home)) { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTSTART | k_mask : STB_TEXTEDIT_K_LINESTART | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_End)) { edit_state.OnKeyPressed(io.KeyCtrl ? STB_TEXTEDIT_K_TEXTEND | k_mask : STB_TEXTEDIT_K_LINEEND | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_Delete) && is_editable) { edit_state.OnKeyPressed(STB_TEXTEDIT_K_DELETE | k_mask); } - else if (IsKeyPressedMap(ImGuiKey_Backspace) && is_editable) - { - if (!edit_state.HasSelection()) - { - if (is_wordmove_key_down) edit_state.OnKeyPressed(STB_TEXTEDIT_K_WORDLEFT|STB_TEXTEDIT_K_SHIFT); - else if (is_osx && io.KeySuper && !io.KeyAlt && !io.KeyCtrl) edit_state.OnKeyPressed(STB_TEXTEDIT_K_LINESTART|STB_TEXTEDIT_K_SHIFT); - } - edit_state.OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask); - } - else if (IsKeyPressedMap(ImGuiKey_Enter)) - { - bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0; - if (!is_multiline || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl)) - { - enter_pressed = clear_active_id = true; - } - else if (is_editable) - { - unsigned int c = '\n'; // Insert new line - if (InputTextFilterCharacter(&c, flags, callback, user_data)) - edit_state.OnKeyPressed((int)c); - } - } - else if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressedMap(ImGuiKey_Tab) && !io.KeyCtrl && !io.KeyShift && !io.KeyAlt && is_editable) - { - unsigned int c = '\t'; // Insert TAB - if (InputTextFilterCharacter(&c, flags, callback, user_data)) - edit_state.OnKeyPressed((int)c); - } - else if (IsKeyPressedMap(ImGuiKey_Escape)) - { - clear_active_id = cancel_edit = true; - } - else if (is_undo || is_redo) - { - edit_state.OnKeyPressed(is_undo ? STB_TEXTEDIT_K_UNDO : STB_TEXTEDIT_K_REDO); - edit_state.ClearSelection(); - } - else if (is_shortcut_key && IsKeyPressedMap(ImGuiKey_A)) - { - edit_state.SelectAll(); - edit_state.CursorFollow = true; - } - else if (is_cut || is_copy) - { - // Cut, Copy - if (io.SetClipboardTextFn) - { - const int ib = edit_state.HasSelection() ? ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end) : 0; - const int ie = edit_state.HasSelection() ? ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end) : edit_state.CurLenW; - edit_state.TempTextBuffer.resize((ie-ib) * 4 + 1); - ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data+ib, edit_state.Text.Data+ie); - SetClipboardText(edit_state.TempTextBuffer.Data); - } - if (is_cut) - { - if (!edit_state.HasSelection()) - edit_state.SelectAll(); - edit_state.CursorFollow = true; - stb_textedit_cut(&edit_state, &edit_state.StbState); - } - } - else if (is_paste) - { - if (const char* clipboard = GetClipboardText()) - { - // Filter pasted buffer - const int clipboard_len = (int)strlen(clipboard); - ImWchar* clipboard_filtered = (ImWchar*)ImGui::MemAlloc((clipboard_len+1) * sizeof(ImWchar)); - int clipboard_filtered_len = 0; - for (const char* s = clipboard; *s; ) - { - unsigned int c; - s += ImTextCharFromUtf8(&c, s, NULL); - if (c == 0) - break; - if (c >= 0x10000 || !InputTextFilterCharacter(&c, flags, callback, user_data)) - continue; - clipboard_filtered[clipboard_filtered_len++] = (ImWchar)c; - } - clipboard_filtered[clipboard_filtered_len] = 0; - if (clipboard_filtered_len > 0) // If everything was filtered, ignore the pasting operation - { - stb_textedit_paste(&edit_state, &edit_state.StbState, clipboard_filtered, clipboard_filtered_len); - edit_state.CursorFollow = true; - } - ImGui::MemFree(clipboard_filtered); - } - } - } - - if (g.ActiveId == id) - { - if (cancel_edit) - { - // Restore initial value. Only return true if restoring to the initial value changes the current buffer contents. - if (is_editable && strncmp(buf, edit_state.InitialText.Data, buf_size) != 0) - { - ImStrncpy(buf, edit_state.InitialText.Data, buf_size); - value_changed = true; - } - } - - // When using 'ImGuiInputTextFlags_EnterReturnsTrue' as a special case we reapply the live buffer back to the input buffer before clearing ActiveId, even though strictly speaking it wasn't modified on this frame. - // If we didn't do that, code like InputInt() with ImGuiInputTextFlags_EnterReturnsTrue would fail. Also this allows the user to use InputText() with ImGuiInputTextFlags_EnterReturnsTrue without maintaining any user-side storage. - bool apply_edit_back_to_user_buffer = !cancel_edit || (enter_pressed && (flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0); - if (apply_edit_back_to_user_buffer) - { - // Apply new value immediately - copy modified buffer back - // Note that as soon as the input box is active, the in-widget value gets priority over any underlying modification of the input buffer - // FIXME: We actually always render 'buf' when calling DrawList->AddText, making the comment above incorrect. - // FIXME-OPT: CPU waste to do this every time the widget is active, should mark dirty state from the stb_textedit callbacks. - if (is_editable) - { - edit_state.TempTextBuffer.resize(edit_state.Text.Size * 4); - ImTextStrToUtf8(edit_state.TempTextBuffer.Data, edit_state.TempTextBuffer.Size, edit_state.Text.Data, NULL); - } - - // User callback - if ((flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_CallbackHistory | ImGuiInputTextFlags_CallbackAlways)) != 0) - { - IM_ASSERT(callback != NULL); - - // The reason we specify the usage semantic (Completion/History) is that Completion needs to disable keyboard TABBING at the moment. - ImGuiInputTextFlags event_flag = 0; - ImGuiKey event_key = ImGuiKey_COUNT; - if ((flags & ImGuiInputTextFlags_CallbackCompletion) != 0 && IsKeyPressedMap(ImGuiKey_Tab)) - { - event_flag = ImGuiInputTextFlags_CallbackCompletion; - event_key = ImGuiKey_Tab; - } - else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_UpArrow)) - { - event_flag = ImGuiInputTextFlags_CallbackHistory; - event_key = ImGuiKey_UpArrow; - } - else if ((flags & ImGuiInputTextFlags_CallbackHistory) != 0 && IsKeyPressedMap(ImGuiKey_DownArrow)) - { - event_flag = ImGuiInputTextFlags_CallbackHistory; - event_key = ImGuiKey_DownArrow; - } - else if (flags & ImGuiInputTextFlags_CallbackAlways) - event_flag = ImGuiInputTextFlags_CallbackAlways; - - if (event_flag) - { - ImGuiTextEditCallbackData callback_data; - memset(&callback_data, 0, sizeof(ImGuiTextEditCallbackData)); - callback_data.EventFlag = event_flag; - callback_data.Flags = flags; - callback_data.UserData = user_data; - callback_data.ReadOnly = !is_editable; - - callback_data.EventKey = event_key; - callback_data.Buf = edit_state.TempTextBuffer.Data; - callback_data.BufTextLen = edit_state.CurLenA; - callback_data.BufSize = edit_state.BufSizeA; - callback_data.BufDirty = false; - - // We have to convert from wchar-positions to UTF-8-positions, which can be pretty slow (an incentive to ditch the ImWchar buffer, see https://github.com/nothings/stb/issues/188) - ImWchar* text = edit_state.Text.Data; - const int utf8_cursor_pos = callback_data.CursorPos = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.cursor); - const int utf8_selection_start = callback_data.SelectionStart = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_start); - const int utf8_selection_end = callback_data.SelectionEnd = ImTextCountUtf8BytesFromStr(text, text + edit_state.StbState.select_end); - - // Call user code - callback(&callback_data); - - // Read back what user may have modified - IM_ASSERT(callback_data.Buf == edit_state.TempTextBuffer.Data); // Invalid to modify those fields - IM_ASSERT(callback_data.BufSize == edit_state.BufSizeA); - IM_ASSERT(callback_data.Flags == flags); - if (callback_data.CursorPos != utf8_cursor_pos) edit_state.StbState.cursor = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.CursorPos); - if (callback_data.SelectionStart != utf8_selection_start) edit_state.StbState.select_start = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionStart); - if (callback_data.SelectionEnd != utf8_selection_end) edit_state.StbState.select_end = ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd); - if (callback_data.BufDirty) - { - IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text! - edit_state.CurLenW = ImTextStrFromUtf8(edit_state.Text.Data, edit_state.Text.Size, callback_data.Buf, NULL); - edit_state.CurLenA = callback_data.BufTextLen; // Assume correct length and valid UTF-8 from user, saves us an extra strlen() - edit_state.CursorAnimReset(); - } - } - } - - // Copy back to user buffer - if (is_editable && strcmp(edit_state.TempTextBuffer.Data, buf) != 0) - { - ImStrncpy(buf, edit_state.TempTextBuffer.Data, buf_size); - value_changed = true; - } - } - } - - // Release active ID at the end of the function (so e.g. pressing Return still does a final application of the value) - if (clear_active_id && g.ActiveId == id) - ClearActiveID(); - - // Render - // Select which buffer we are going to display. When ImGuiInputTextFlags_NoLiveEdit is set 'buf' might still be the old value. We set buf to NULL to prevent accidental usage from now on. - const char* buf_display = (g.ActiveId == id && is_editable) ? edit_state.TempTextBuffer.Data : buf; buf = NULL; - - if (!is_multiline) - { - RenderNavHighlight(frame_bb, id); - RenderFrame(frame_bb.Min, frame_bb.Max, GetColorU32(ImGuiCol_FrameBg), true, style.FrameRounding); - } - - const ImVec4 clip_rect(frame_bb.Min.x, frame_bb.Min.y, frame_bb.Min.x + size.x, frame_bb.Min.y + size.y); // Not using frame_bb.Max because we have adjusted size - ImVec2 render_pos = is_multiline ? draw_window->DC.CursorPos : frame_bb.Min + style.FramePadding; - ImVec2 text_size(0.f, 0.f); - const bool is_currently_scrolling = (edit_state.Id == id && is_multiline && g.ActiveId == draw_window->GetIDNoKeepAlive("#SCROLLY")); - if (g.ActiveId == id || is_currently_scrolling) - { - edit_state.CursorAnim += io.DeltaTime; - - // This is going to be messy. We need to: - // - Display the text (this alone can be more easily clipped) - // - Handle scrolling, highlight selection, display cursor (those all requires some form of 1d->2d cursor position calculation) - // - Measure text height (for scrollbar) - // We are attempting to do most of that in **one main pass** to minimize the computation cost (non-negligible for large amount of text) + 2nd pass for selection rendering (we could merge them by an extra refactoring effort) - // FIXME: This should occur on buf_display but we'd need to maintain cursor/select_start/select_end for UTF-8. - const ImWchar* text_begin = edit_state.Text.Data; - ImVec2 cursor_offset, select_start_offset; - - { - // Count lines + find lines numbers straddling 'cursor' and 'select_start' position. - const ImWchar* searches_input_ptr[2]; - searches_input_ptr[0] = text_begin + edit_state.StbState.cursor; - searches_input_ptr[1] = NULL; - int searches_remaining = 1; - int searches_result_line_number[2] = { -1, -999 }; - if (edit_state.StbState.select_start != edit_state.StbState.select_end) - { - searches_input_ptr[1] = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end); - searches_result_line_number[1] = -1; - searches_remaining++; - } - - // Iterate all lines to find our line numbers - // In multi-line mode, we never exit the loop until all lines are counted, so add one extra to the searches_remaining counter. - searches_remaining += is_multiline ? 1 : 0; - int line_count = 0; - for (const ImWchar* s = text_begin; *s != 0; s++) - if (*s == '\n') - { - line_count++; - if (searches_result_line_number[0] == -1 && s >= searches_input_ptr[0]) { searches_result_line_number[0] = line_count; if (--searches_remaining <= 0) break; } - if (searches_result_line_number[1] == -1 && s >= searches_input_ptr[1]) { searches_result_line_number[1] = line_count; if (--searches_remaining <= 0) break; } - } - line_count++; - if (searches_result_line_number[0] == -1) searches_result_line_number[0] = line_count; - if (searches_result_line_number[1] == -1) searches_result_line_number[1] = line_count; - - // Calculate 2d position by finding the beginning of the line and measuring distance - cursor_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x; - cursor_offset.y = searches_result_line_number[0] * g.FontSize; - if (searches_result_line_number[1] >= 0) - { - select_start_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x; - select_start_offset.y = searches_result_line_number[1] * g.FontSize; - } - - // Store text height (note that we haven't calculated text width at all, see GitHub issues #383, #1224) - if (is_multiline) - text_size = ImVec2(size.x, line_count * g.FontSize); - } - - // Scroll - if (edit_state.CursorFollow) - { - // Horizontal scroll in chunks of quarter width - if (!(flags & ImGuiInputTextFlags_NoHorizontalScroll)) - { - const float scroll_increment_x = size.x * 0.25f; - if (cursor_offset.x < edit_state.ScrollX) - edit_state.ScrollX = (float)(int)ImMax(0.0f, cursor_offset.x - scroll_increment_x); - else if (cursor_offset.x - size.x >= edit_state.ScrollX) - edit_state.ScrollX = (float)(int)(cursor_offset.x - size.x + scroll_increment_x); - } - else - { - edit_state.ScrollX = 0.0f; - } - - // Vertical scroll - if (is_multiline) - { - float scroll_y = draw_window->Scroll.y; - if (cursor_offset.y - g.FontSize < scroll_y) - scroll_y = ImMax(0.0f, cursor_offset.y - g.FontSize); - else if (cursor_offset.y - size.y >= scroll_y) - scroll_y = cursor_offset.y - size.y; - draw_window->DC.CursorPos.y += (draw_window->Scroll.y - scroll_y); // To avoid a frame of lag - draw_window->Scroll.y = scroll_y; - render_pos.y = draw_window->DC.CursorPos.y; - } - } - edit_state.CursorFollow = false; - const ImVec2 render_scroll = ImVec2(edit_state.ScrollX, 0.0f); - - // Draw selection - if (edit_state.StbState.select_start != edit_state.StbState.select_end) - { - const ImWchar* text_selected_begin = text_begin + ImMin(edit_state.StbState.select_start, edit_state.StbState.select_end); - const ImWchar* text_selected_end = text_begin + ImMax(edit_state.StbState.select_start, edit_state.StbState.select_end); - - float bg_offy_up = is_multiline ? 0.0f : -1.0f; // FIXME: those offsets should be part of the style? they don't play so well with multi-line selection. - float bg_offy_dn = is_multiline ? 0.0f : 2.0f; - ImU32 bg_color = GetColorU32(ImGuiCol_TextSelectedBg); - ImVec2 rect_pos = render_pos + select_start_offset - render_scroll; - for (const ImWchar* p = text_selected_begin; p < text_selected_end; ) - { - if (rect_pos.y > clip_rect.w + g.FontSize) - break; - if (rect_pos.y < clip_rect.y) - { - while (p < text_selected_end) - if (*p++ == '\n') - break; - } - else - { - ImVec2 rect_size = InputTextCalcTextSizeW(p, text_selected_end, &p, NULL, true); - if (rect_size.x <= 0.0f) rect_size.x = (float)(int)(g.Font->GetCharAdvance((unsigned short)' ') * 0.50f); // So we can see selected empty lines - ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos +ImVec2(rect_size.x, bg_offy_dn)); - rect.ClipWith(clip_rect); - if (rect.Overlaps(clip_rect)) - draw_window->DrawList->AddRectFilled(rect.Min, rect.Max, bg_color); - } - rect_pos.x = render_pos.x - render_scroll.x; - rect_pos.y += g.FontSize; - } - } - - draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos - render_scroll, GetColorU32(ImGuiCol_Text), buf_display, buf_display + edit_state.CurLenA, 0.0f, is_multiline ? NULL : &clip_rect); - - // Draw blinking cursor - bool cursor_is_visible = (!g.IO.OptCursorBlink) || (g.InputTextState.CursorAnim <= 0.0f) || ImFmod(g.InputTextState.CursorAnim, 1.20f) <= 0.80f; - ImVec2 cursor_screen_pos = render_pos + cursor_offset - render_scroll; - ImRect cursor_screen_rect(cursor_screen_pos.x, cursor_screen_pos.y-g.FontSize+0.5f, cursor_screen_pos.x+1.0f, cursor_screen_pos.y-1.5f); - if (cursor_is_visible && cursor_screen_rect.Overlaps(clip_rect)) - draw_window->DrawList->AddLine(cursor_screen_rect.Min, cursor_screen_rect.GetBL(), GetColorU32(ImGuiCol_Text)); - - // Notify OS of text input position for advanced IME (-1 x offset so that Windows IME can cover our cursor. Bit of an extra nicety.) - if (is_editable) - g.PlatformImePos = ImVec2(cursor_screen_pos.x - 1, cursor_screen_pos.y - g.FontSize); - } - else - { - // Render text only - const char* buf_end = NULL; - if (is_multiline) - text_size = ImVec2(size.x, InputTextCalcTextLenAndLineCount(buf_display, &buf_end) * g.FontSize); // We don't need width - draw_window->DrawList->AddText(g.Font, g.FontSize, render_pos, GetColorU32(ImGuiCol_Text), buf_display, buf_end, 0.0f, is_multiline ? NULL : &clip_rect); - } - - if (is_multiline) - { - Dummy(text_size + ImVec2(0.0f, g.FontSize)); // Always add room to scroll an extra line - EndChildFrame(); - EndGroup(); - } - - if (is_password) - PopFont(); - - // Log as text - if (g.LogEnabled && !is_password) - LogRenderedText(&render_pos, buf_display, NULL); - - if (label_size.x > 0) - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); - - if (value_changed) - MarkItemValueChanged(id); - - if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0) - return enter_pressed; - else - return value_changed; -} - -bool ImGui::InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data) -{ - IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline() - return InputTextEx(label, buf, (int)buf_size, ImVec2(0,0), flags, callback, user_data); -} - -bool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback, void* user_data) -{ - return InputTextEx(label, buf, (int)buf_size, size, flags | ImGuiInputTextFlags_Multiline, callback, user_data); -} - -// NB: format here must be a simple "%xx" format string with no prefix/suffix (unlike the Drag/Slider functions "format" argument) -bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* data_ptr, const void* step, const void* step_fast, const char* format, ImGuiInputTextFlags extra_flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - - IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT); - if (format == NULL) - format = GDataTypeInfo[data_type].PrintFmt; - - char buf[64]; - DataTypeFormatString(buf, IM_ARRAYSIZE(buf), data_type, data_ptr, format); - - bool value_changed = false; - if ((extra_flags & (ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsScientific)) == 0) - extra_flags |= ImGuiInputTextFlags_CharsDecimal; - extra_flags |= ImGuiInputTextFlags_AutoSelectAll; - - if (step != NULL) - { - const float button_size = GetFrameHeight(); - - BeginGroup(); // The only purpose of the group here is to allow the caller to query item data e.g. IsItemActive() - PushID(label); - PushItemWidth(ImMax(1.0f, CalcItemWidth() - (button_size + style.ItemInnerSpacing.x) * 2)); - if (InputText("", buf, IM_ARRAYSIZE(buf), extra_flags)) // PushId(label) + "" gives us the expected ID from outside point of view - value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialText.Data, data_type, data_ptr, format); - PopItemWidth(); - - // Step buttons - SameLine(0, style.ItemInnerSpacing.x); - if (ButtonEx("-", ImVec2(button_size, button_size), ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups)) - { - DataTypeApplyOp(data_type, '-', data_ptr, data_ptr, g.IO.KeyCtrl && step_fast ? step_fast : step); - value_changed = true; - } - SameLine(0, style.ItemInnerSpacing.x); - if (ButtonEx("+", ImVec2(button_size, button_size), ImGuiButtonFlags_Repeat | ImGuiButtonFlags_DontClosePopups)) - { - DataTypeApplyOp(data_type, '+', data_ptr, data_ptr, g.IO.KeyCtrl && step_fast ? step_fast : step); - value_changed = true; - } - SameLine(0, style.ItemInnerSpacing.x); - TextUnformatted(label, FindRenderedTextEnd(label)); - - PopID(); - EndGroup(); - } - else - { - if (InputText(label, buf, IM_ARRAYSIZE(buf), extra_flags)) - value_changed = DataTypeApplyOpFromText(buf, g.InputTextState.InitialText.Data, data_type, data_ptr, format); - } - - return value_changed; -} - -bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, const char* format, ImGuiInputTextFlags extra_flags) -{ - extra_flags |= ImGuiInputTextFlags_CharsScientific; - return InputScalar(label, ImGuiDataType_Float, (void*)v, (void*)(step>0.0f ? &step : NULL), (void*)(step_fast>0.0f ? &step_fast : NULL), format, extra_flags); -} - -bool ImGui::InputDouble(const char* label, double* v, double step, double step_fast, const char* format, ImGuiInputTextFlags extra_flags) -{ - extra_flags |= ImGuiInputTextFlags_CharsScientific; - return InputScalar(label, ImGuiDataType_Double, (void*)v, (void*)(step>0.0 ? &step : NULL), (void*)(step_fast>0.0 ? &step_fast : NULL), format, extra_flags); -} - -bool ImGui::InputInt(const char* label, int* v, int step, int step_fast, ImGuiInputTextFlags extra_flags) -{ - // Hexadecimal input provided as a convenience but the flag name is awkward. Typically you'd use InputText() to parse your own data, if you want to handle prefixes. - const char* format = (extra_flags & ImGuiInputTextFlags_CharsHexadecimal) ? "%08X" : "%d"; - return InputScalar(label, ImGuiDataType_S32, (void*)v, (void*)(step>0 ? &step : NULL), (void*)(step_fast>0 ? &step_fast : NULL), format, extra_flags); -} - -bool ImGui::InputScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* step, const void* step_fast, const char* format, ImGuiInputTextFlags extra_flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - bool value_changed = false; - BeginGroup(); - PushID(label); - PushMultiItemsWidths(components); - size_t type_size = GDataTypeInfo[data_type].Size; - for (int i = 0; i < components; i++) - { - PushID(i); - value_changed |= InputScalar("##v", data_type, v, step, step_fast, format, extra_flags); - SameLine(0, g.Style.ItemInnerSpacing.x); - PopID(); - PopItemWidth(); - v = (void*)((char*)v + type_size); - } - PopID(); - - TextUnformatted(label, FindRenderedTextEnd(label)); - EndGroup(); - return value_changed; -} - -bool ImGui::InputFloat2(const char* label, float v[2], const char* format, ImGuiInputTextFlags extra_flags) -{ - return InputScalarN(label, ImGuiDataType_Float, v, 2, NULL, NULL, format, extra_flags); -} - -bool ImGui::InputFloat3(const char* label, float v[3], const char* format, ImGuiInputTextFlags extra_flags) -{ - return InputScalarN(label, ImGuiDataType_Float, v, 3, NULL, NULL, format, extra_flags); -} - -bool ImGui::InputFloat4(const char* label, float v[4], const char* format, ImGuiInputTextFlags extra_flags) -{ - return InputScalarN(label, ImGuiDataType_Float, v, 4, NULL, NULL, format, extra_flags); -} - -// Prefer using "const char* format" directly, which is more flexible and consistent with other API. -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS -bool ImGui::InputFloat(const char* label, float* v, float step, float step_fast, int decimal_precision, ImGuiInputTextFlags extra_flags) -{ - char format[16] = "%f"; - if (decimal_precision >= 0) - ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision); - return InputFloat(label, v, step, step_fast, format, extra_flags); -} - -bool ImGui::InputFloat2(const char* label, float v[2], int decimal_precision, ImGuiInputTextFlags extra_flags) -{ - char format[16] = "%f"; - if (decimal_precision >= 0) - ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision); - return InputScalarN(label, ImGuiDataType_Float, v, 2, NULL, NULL, format, extra_flags); -} - -bool ImGui::InputFloat3(const char* label, float v[3], int decimal_precision, ImGuiInputTextFlags extra_flags) -{ - char format[16] = "%f"; - if (decimal_precision >= 0) - ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision); - return InputScalarN(label, ImGuiDataType_Float, v, 3, NULL, NULL, format, extra_flags); -} - -bool ImGui::InputFloat4(const char* label, float v[4], int decimal_precision, ImGuiInputTextFlags extra_flags) -{ - char format[16] = "%f"; - if (decimal_precision >= 0) - ImFormatString(format, IM_ARRAYSIZE(format), "%%.%df", decimal_precision); - return InputScalarN(label, ImGuiDataType_Float, v, 4, NULL, NULL, format, extra_flags); -} -#endif // IMGUI_DISABLE_OBSOLETE_FUNCTIONS - -bool ImGui::InputInt2(const char* label, int v[2], ImGuiInputTextFlags extra_flags) -{ - return InputScalarN(label, ImGuiDataType_S32, v, 2, NULL, NULL, "%d", extra_flags); -} - -bool ImGui::InputInt3(const char* label, int v[3], ImGuiInputTextFlags extra_flags) -{ - return InputScalarN(label, ImGuiDataType_S32, v, 3, NULL, NULL, "%d", extra_flags); -} - -bool ImGui::InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_flags) -{ - return InputScalarN(label, ImGuiDataType_S32, v, 4, NULL, NULL, "%d", extra_flags); -} - -static float CalcMaxPopupHeightFromItemCount(int items_count) -{ - ImGuiContext& g = *GImGui; - if (items_count <= 0) - return FLT_MAX; - return (g.FontSize + g.Style.ItemSpacing.y) * items_count - g.Style.ItemSpacing.y + (g.Style.WindowPadding.y * 2); -} - -bool ImGui::BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags) -{ - // Always consume the SetNextWindowSizeConstraint() call in our early return paths - ImGuiContext& g = *GImGui; - ImGuiCond backup_next_window_size_constraint = g.NextWindowData.SizeConstraintCond; - g.NextWindowData.SizeConstraintCond = 0; - - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - IM_ASSERT((flags & (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)) != (ImGuiComboFlags_NoArrowButton | ImGuiComboFlags_NoPreview)); // Can't use both flags together - - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - - const float arrow_size = (flags & ImGuiComboFlags_NoArrowButton) ? 0.0f : GetFrameHeight(); - const ImVec2 label_size = CalcTextSize(label, NULL, true); - const float w = (flags & ImGuiComboFlags_NoPreview) ? arrow_size : CalcItemWidth(); - const ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + ImVec2(w, label_size.y + style.FramePadding.y*2.0f)); - const ImRect total_bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); - ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, id, &frame_bb)) - return false; - - bool hovered, held; - bool pressed = ButtonBehavior(frame_bb, id, &hovered, &held); - bool popup_open = IsPopupOpen(id); - - const ImRect value_bb(frame_bb.Min, frame_bb.Max - ImVec2(arrow_size, 0.0f)); - const ImU32 frame_col = GetColorU32(hovered ? ImGuiCol_FrameBgHovered : ImGuiCol_FrameBg); - RenderNavHighlight(frame_bb, id); - if (!(flags & ImGuiComboFlags_NoPreview)) - window->DrawList->AddRectFilled(frame_bb.Min, ImVec2(frame_bb.Max.x - arrow_size, frame_bb.Max.y), frame_col, style.FrameRounding, ImDrawCornerFlags_Left); - if (!(flags & ImGuiComboFlags_NoArrowButton)) - { - window->DrawList->AddRectFilled(ImVec2(frame_bb.Max.x - arrow_size, frame_bb.Min.y), frame_bb.Max, GetColorU32((popup_open || hovered) ? ImGuiCol_ButtonHovered : ImGuiCol_Button), style.FrameRounding, (w <= arrow_size) ? ImDrawCornerFlags_All : ImDrawCornerFlags_Right); - RenderArrow(ImVec2(frame_bb.Max.x - arrow_size + style.FramePadding.y, frame_bb.Min.y + style.FramePadding.y), ImGuiDir_Down); - } - RenderFrameBorder(frame_bb.Min, frame_bb.Max, style.FrameRounding); - if (preview_value != NULL && !(flags & ImGuiComboFlags_NoPreview)) - RenderTextClipped(frame_bb.Min + style.FramePadding, value_bb.Max, preview_value, NULL, NULL, ImVec2(0.0f,0.0f)); - if (label_size.x > 0) - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); - - if ((pressed || g.NavActivateId == id) && !popup_open) - { - if (window->DC.NavLayerCurrent == 0) - window->NavLastIds[0] = id; - OpenPopupEx(id); - popup_open = true; - } - - if (!popup_open) - return false; - - if (backup_next_window_size_constraint) - { - g.NextWindowData.SizeConstraintCond = backup_next_window_size_constraint; - g.NextWindowData.SizeConstraintRect.Min.x = ImMax(g.NextWindowData.SizeConstraintRect.Min.x, w); - } - else - { - if ((flags & ImGuiComboFlags_HeightMask_) == 0) - flags |= ImGuiComboFlags_HeightRegular; - IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiComboFlags_HeightMask_)); // Only one - int popup_max_height_in_items = -1; - if (flags & ImGuiComboFlags_HeightRegular) popup_max_height_in_items = 8; - else if (flags & ImGuiComboFlags_HeightSmall) popup_max_height_in_items = 4; - else if (flags & ImGuiComboFlags_HeightLarge) popup_max_height_in_items = 20; - SetNextWindowSizeConstraints(ImVec2(w, 0.0f), ImVec2(FLT_MAX, CalcMaxPopupHeightFromItemCount(popup_max_height_in_items))); - } - - char name[16]; - ImFormatString(name, IM_ARRAYSIZE(name), "##Combo_%02d", g.CurrentPopupStack.Size); // Recycle windows based on depth - - // Peak into expected window size so we can position it - if (ImGuiWindow* popup_window = FindWindowByName(name)) - if (popup_window->WasActive) - { - ImVec2 size_contents = CalcSizeContents(popup_window); - ImVec2 size_expected = CalcSizeAfterConstraint(popup_window, CalcSizeAutoFit(popup_window, size_contents)); - if (flags & ImGuiComboFlags_PopupAlignLeft) - popup_window->AutoPosLastDirection = ImGuiDir_Left; - ImRect r_outer = FindAllowedExtentRectForWindow(popup_window); - ImVec2 pos = FindBestWindowPosForPopupEx(frame_bb.GetBL(), size_expected, &popup_window->AutoPosLastDirection, r_outer, frame_bb, ImGuiPopupPositionPolicy_ComboBox); - SetNextWindowPos(pos); - } - - // Horizontally align ourselves with the framed text - ImGuiWindowFlags window_flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_Popup | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings; - PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(style.FramePadding.x, style.WindowPadding.y)); - bool ret = Begin(name, NULL, window_flags); - PopStyleVar(); - if (!ret) - { - EndPopup(); - IM_ASSERT(0); // This should never happen as we tested for IsPopupOpen() above - return false; - } - return true; -} - -void ImGui::EndCombo() -{ - EndPopup(); -} - -// Old API, prefer using BeginCombo() nowadays if you can. -bool ImGui::Combo(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int popup_max_height_in_items) -{ - ImGuiContext& g = *GImGui; - - const char* preview_text = NULL; - if (*current_item >= 0 && *current_item < items_count) - items_getter(data, *current_item, &preview_text); - - // The old Combo() API exposed "popup_max_height_in_items". The new more general BeginCombo() API doesn't, so we emulate it here. - if (popup_max_height_in_items != -1 && !g.NextWindowData.SizeConstraintCond) - { - float popup_max_height = CalcMaxPopupHeightFromItemCount(popup_max_height_in_items); - SetNextWindowSizeConstraints(ImVec2(0,0), ImVec2(FLT_MAX, popup_max_height)); - } - - if (!BeginCombo(label, preview_text, 0)) - return false; - - // Display items - // FIXME-OPT: Use clipper (but we need to disable it on the appearing frame to make sure our call to SetItemDefaultFocus() is processed) - bool value_changed = false; - for (int i = 0; i < items_count; i++) - { - PushID((void*)(intptr_t)i); - const bool item_selected = (i == *current_item); - const char* item_text; - if (!items_getter(data, i, &item_text)) - item_text = "*Unknown item*"; - if (Selectable(item_text, item_selected)) - { - value_changed = true; - *current_item = i; - } - if (item_selected) - SetItemDefaultFocus(); - PopID(); - } - - EndCombo(); - return value_changed; -} - -static bool Items_ArrayGetter(void* data, int idx, const char** out_text) -{ - const char* const* items = (const char* const*)data; - if (out_text) - *out_text = items[idx]; - return true; -} - -static bool Items_SingleStringGetter(void* data, int idx, const char** out_text) -{ - // FIXME-OPT: we could pre-compute the indices to fasten this. But only 1 active combo means the waste is limited. - const char* items_separated_by_zeros = (const char*)data; - int items_count = 0; - const char* p = items_separated_by_zeros; - while (*p) - { - if (idx == items_count) - break; - p += strlen(p) + 1; - items_count++; - } - if (!*p) - return false; - if (out_text) - *out_text = p; - return true; -} - -// Combo box helper allowing to pass an array of strings. -bool ImGui::Combo(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items) -{ - const bool value_changed = Combo(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_in_items); - return value_changed; -} - -// Combo box helper allowing to pass all items in a single string. -bool ImGui::Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int height_in_items) -{ - int items_count = 0; - const char* p = items_separated_by_zeros; // FIXME-OPT: Avoid computing this, or at least only when combo is open - while (*p) - { - p += strlen(p) + 1; - items_count++; - } - bool value_changed = Combo(label, current_item, Items_SingleStringGetter, (void*)items_separated_by_zeros, items_count, height_in_items); - return value_changed; -} - -// Tip: pass an empty label (e.g. "##dummy") then you can use the space to draw other text or image. -// But you need to make sure the ID is unique, e.g. enclose calls in PushID/PopID. -bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags flags, const ImVec2& size_arg) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - - if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet) // FIXME-OPT: Avoid if vertically clipped. - PopClipRect(); - - ImGuiID id = window->GetID(label); - ImVec2 label_size = CalcTextSize(label, NULL, true); - ImVec2 size(size_arg.x != 0.0f ? size_arg.x : label_size.x, size_arg.y != 0.0f ? size_arg.y : label_size.y); - ImVec2 pos = window->DC.CursorPos; - pos.y += window->DC.CurrentLineTextBaseOffset; - ImRect bb_inner(pos, pos + size); - ItemSize(bb_inner); - - // Fill horizontal space. - ImVec2 window_padding = window->WindowPadding; - float max_x = (flags & ImGuiSelectableFlags_SpanAllColumns) ? GetWindowContentRegionMax().x : GetContentRegionMax().x; - float w_draw = ImMax(label_size.x, window->Pos.x + max_x - window_padding.x - window->DC.CursorPos.x); - ImVec2 size_draw((size_arg.x != 0 && !(flags & ImGuiSelectableFlags_DrawFillAvailWidth)) ? size_arg.x : w_draw, size_arg.y != 0.0f ? size_arg.y : size.y); - ImRect bb(pos, pos + size_draw); - if (size_arg.x == 0.0f || (flags & ImGuiSelectableFlags_DrawFillAvailWidth)) - bb.Max.x += window_padding.x; - - // Selectables are tightly packed together, we extend the box to cover spacing between selectable. - float spacing_L = (float)(int)(style.ItemSpacing.x * 0.5f); - float spacing_U = (float)(int)(style.ItemSpacing.y * 0.5f); - float spacing_R = style.ItemSpacing.x - spacing_L; - float spacing_D = style.ItemSpacing.y - spacing_U; - bb.Min.x -= spacing_L; - bb.Min.y -= spacing_U; - bb.Max.x += spacing_R; - bb.Max.y += spacing_D; - if (!ItemAdd(bb, (flags & ImGuiSelectableFlags_Disabled) ? 0 : id)) - { - if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet) - PushColumnClipRect(); - return false; - } - - // We use NoHoldingActiveID on menus so user can click and _hold_ on a menu then drag to browse child entries - ImGuiButtonFlags button_flags = 0; - if (flags & ImGuiSelectableFlags_NoHoldingActiveID) button_flags |= ImGuiButtonFlags_NoHoldingActiveID; - if (flags & ImGuiSelectableFlags_PressedOnClick) button_flags |= ImGuiButtonFlags_PressedOnClick; - if (flags & ImGuiSelectableFlags_PressedOnRelease) button_flags |= ImGuiButtonFlags_PressedOnRelease; - if (flags & ImGuiSelectableFlags_Disabled) button_flags |= ImGuiButtonFlags_Disabled; - if (flags & ImGuiSelectableFlags_AllowDoubleClick) button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held, button_flags); - if (flags & ImGuiSelectableFlags_Disabled) - selected = false; - - // Hovering selectable with mouse updates NavId accordingly so navigation can be resumed with gamepad/keyboard (this doesn't happen on most widgets) - if (pressed || hovered) - if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent) - { - g.NavDisableHighlight = true; - SetNavID(id, window->DC.NavLayerCurrent); - } - if (pressed) - MarkItemValueChanged(id); - - // Render - if (hovered || selected) - { - const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); - RenderFrame(bb.Min, bb.Max, col, false, 0.0f); - RenderNavHighlight(bb, id, ImGuiNavHighlightFlags_TypeThin | ImGuiNavHighlightFlags_NoRounding); - } - - if ((flags & ImGuiSelectableFlags_SpanAllColumns) && window->DC.ColumnsSet) - { - PushColumnClipRect(); - bb.Max.x -= (GetContentRegionMax().x - max_x); - } - - if (flags & ImGuiSelectableFlags_Disabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); - RenderTextClipped(bb_inner.Min, bb.Max, label, NULL, &label_size, ImVec2(0.0f,0.0f)); - if (flags & ImGuiSelectableFlags_Disabled) PopStyleColor(); - - // Automatically close popups - if (pressed && (window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiSelectableFlags_DontClosePopups) && !(window->DC.ItemFlags & ImGuiItemFlags_SelectableDontClosePopup)) - CloseCurrentPopup(); - return pressed; -} - -bool ImGui::Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags, const ImVec2& size_arg) -{ - if (Selectable(label, *p_selected, flags, size_arg)) - { - *p_selected = !*p_selected; - return true; - } - return false; -} - -// FIXME: Rename to BeginListBox() -// Helper to calculate the size of a listbox and display a label on the right. -// Tip: To have a list filling the entire window width, PushItemWidth(-1) and pass an empty label "##empty" -bool ImGui::ListBoxHeader(const char* label, const ImVec2& size_arg) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - const ImGuiStyle& style = GetStyle(); - const ImGuiID id = GetID(label); - const ImVec2 label_size = CalcTextSize(label, NULL, true); - - // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar. - ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), GetTextLineHeightWithSpacing() * 7.4f + style.ItemSpacing.y); - ImVec2 frame_size = ImVec2(size.x, ImMax(size.y, label_size.y)); - ImRect frame_bb(window->DC.CursorPos, window->DC.CursorPos + frame_size); - ImRect bb(frame_bb.Min, frame_bb.Max + ImVec2(label_size.x > 0.0f ? style.ItemInnerSpacing.x + label_size.x : 0.0f, 0.0f)); - window->DC.LastItemRect = bb; // Forward storage for ListBoxFooter.. dodgy. - - BeginGroup(); - if (label_size.x > 0) - RenderText(ImVec2(frame_bb.Max.x + style.ItemInnerSpacing.x, frame_bb.Min.y + style.FramePadding.y), label); - - BeginChildFrame(id, frame_bb.GetSize()); - return true; -} - -// FIXME: Rename to BeginListBox() -bool ImGui::ListBoxHeader(const char* label, int items_count, int height_in_items) -{ - // Size default to hold ~7 items. Fractional number of items helps seeing that we can scroll down/up without looking at scrollbar. - // We don't add +0.40f if items_count <= height_in_items. It is slightly dodgy, because it means a dynamic list of items will make the widget resize occasionally when it crosses that size. - // I am expecting that someone will come and complain about this behavior in a remote future, then we can advise on a better solution. - if (height_in_items < 0) - height_in_items = ImMin(items_count, 7); - float height_in_items_f = height_in_items < items_count ? (height_in_items + 0.40f) : (height_in_items + 0.00f); - - // We include ItemSpacing.y so that a list sized for the exact number of items doesn't make a scrollbar appears. We could also enforce that by passing a flag to BeginChild(). - ImVec2 size; - size.x = 0.0f; - size.y = GetTextLineHeightWithSpacing() * height_in_items_f + GetStyle().ItemSpacing.y; - return ListBoxHeader(label, size); -} - -// FIXME: Rename to EndListBox() -void ImGui::ListBoxFooter() -{ - ImGuiWindow* parent_window = GetCurrentWindow()->ParentWindow; - const ImRect bb = parent_window->DC.LastItemRect; - const ImGuiStyle& style = GetStyle(); - - EndChildFrame(); - - // Redeclare item size so that it includes the label (we have stored the full size in LastItemRect) - // We call SameLine() to restore DC.CurrentLine* data - SameLine(); - parent_window->DC.CursorPos = bb.Min; - ItemSize(bb, style.FramePadding.y); - EndGroup(); -} - -bool ImGui::ListBox(const char* label, int* current_item, const char* const items[], int items_count, int height_items) -{ - const bool value_changed = ListBox(label, current_item, Items_ArrayGetter, (void*)items, items_count, height_items); - return value_changed; -} - -bool ImGui::ListBox(const char* label, int* current_item, bool (*items_getter)(void*, int, const char**), void* data, int items_count, int height_in_items) -{ - if (!ListBoxHeader(label, items_count, height_in_items)) - return false; - - // Assume all items have even height (= 1 line of text). If you need items of different or variable sizes you can create a custom version of ListBox() in your code without using the clipper. - bool value_changed = false; - ImGuiListClipper clipper(items_count, GetTextLineHeightWithSpacing()); // We know exactly our line height here so we pass it as a minor optimization, but generally you don't need to. - while (clipper.Step()) - for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) - { - const bool item_selected = (i == *current_item); - const char* item_text; - if (!items_getter(data, i, &item_text)) - item_text = "*Unknown item*"; - - PushID(i); - if (Selectable(item_text, item_selected)) - { - *current_item = i; - value_changed = true; - } - if (item_selected) - SetItemDefaultFocus(); - PopID(); - } - ListBoxFooter(); - return value_changed; -} - -bool ImGui::MenuItem(const char* label, const char* shortcut, bool selected, bool enabled) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - ImGuiStyle& style = g.Style; - ImVec2 pos = window->DC.CursorPos; - ImVec2 label_size = CalcTextSize(label, NULL, true); - - ImGuiSelectableFlags flags = ImGuiSelectableFlags_PressedOnRelease | (enabled ? 0 : ImGuiSelectableFlags_Disabled); - bool pressed; - if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) - { - // Mimic the exact layout spacing of BeginMenu() to allow MenuItem() inside a menu bar, which is a little misleading but may be useful - // Note that in this situation we render neither the shortcut neither the selected tick mark - float w = label_size.x; - window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f); - PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f); - pressed = Selectable(label, false, flags, ImVec2(w, 0.0f)); - PopStyleVar(); - window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). - } - else - { - ImVec2 shortcut_size = shortcut ? CalcTextSize(shortcut, NULL) : ImVec2(0.0f, 0.0f); - float w = window->MenuColumns.DeclColumns(label_size.x, shortcut_size.x, (float)(int)(g.FontSize * 1.20f)); // Feedback for next frame - float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w); - pressed = Selectable(label, false, flags | ImGuiSelectableFlags_DrawFillAvailWidth, ImVec2(w, 0.0f)); - if (shortcut_size.x > 0.0f) - { - PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); - RenderText(pos + ImVec2(window->MenuColumns.Pos[1] + extra_w, 0.0f), shortcut, NULL, false); - PopStyleColor(); - } - if (selected) - RenderCheckMark(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled), g.FontSize * 0.866f); - } - return pressed; -} - -bool ImGui::MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled) -{ - if (MenuItem(label, shortcut, p_selected ? *p_selected : false, enabled)) - { - if (p_selected) - *p_selected = !*p_selected; - return true; - } - return false; -} - -// For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set. -bool ImGui::BeginMainMenuBar() -{ - ImGuiContext& g = *GImGui; - g.NextWindowData.MenuBarOffsetMinVal = ImVec2(g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0f)); - SetNextWindowPos(ImVec2(0.0f, 0.0f)); - SetNextWindowSize(ImVec2(g.IO.DisplaySize.x, g.NextWindowData.MenuBarOffsetMinVal.y + g.FontBaseSize + g.Style.FramePadding.y)); - PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); - PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0,0)); - ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar; - bool is_open = Begin("##MainMenuBar", NULL, window_flags) && BeginMenuBar(); - PopStyleVar(2); - g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f); - if (!is_open) - { - End(); - return false; - } - return true; -} - -void ImGui::EndMainMenuBar() -{ - EndMenuBar(); - - // When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window - ImGuiContext& g = *GImGui; - if (g.CurrentWindow == g.NavWindow && g.NavLayer == 0) - FocusFrontMostActiveWindow(g.NavWindow); - - End(); -} - -bool ImGui::BeginMenuBar() -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - if (!(window->Flags & ImGuiWindowFlags_MenuBar)) - return false; - - IM_ASSERT(!window->DC.MenuBarAppending); - BeginGroup(); // Backup position on layer 0 - PushID("##menubar"); - - // We don't clip with current window clipping rectangle as it is already set to the area below. However we clip with window full rect. - // We remove 1 worth of rounding to Max.x to that text in long menus and small windows don't tend to display over the lower-right rounded area, which looks particularly glitchy. - ImRect bar_rect = window->MenuBarRect(); - ImRect clip_rect(ImFloor(bar_rect.Min.x + 0.5f), ImFloor(bar_rect.Min.y + window->WindowBorderSize + 0.5f), ImFloor(ImMax(bar_rect.Min.x, bar_rect.Max.x - window->WindowRounding) + 0.5f), ImFloor(bar_rect.Max.y + 0.5f)); - clip_rect.ClipWith(window->OuterRectClipped); - PushClipRect(clip_rect.Min, clip_rect.Max, false); - - window->DC.CursorPos = ImVec2(bar_rect.Min.x + window->DC.MenuBarOffset.x, bar_rect.Min.y + window->DC.MenuBarOffset.y); - window->DC.LayoutType = ImGuiLayoutType_Horizontal; - window->DC.NavLayerCurrent++; - window->DC.NavLayerCurrentMask <<= 1; - window->DC.MenuBarAppending = true; - AlignTextToFramePadding(); - return true; -} - -void ImGui::EndMenuBar() -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - ImGuiContext& g = *GImGui; - - // Nav: When a move request within one of our child menu failed, capture the request to navigate among our siblings. - if (NavMoveRequestButNoResultYet() && (g.NavMoveDir == ImGuiDir_Left || g.NavMoveDir == ImGuiDir_Right) && (g.NavWindow->Flags & ImGuiWindowFlags_ChildMenu)) - { - ImGuiWindow* nav_earliest_child = g.NavWindow; - while (nav_earliest_child->ParentWindow && (nav_earliest_child->ParentWindow->Flags & ImGuiWindowFlags_ChildMenu)) - nav_earliest_child = nav_earliest_child->ParentWindow; - if (nav_earliest_child->ParentWindow == window && nav_earliest_child->DC.ParentLayoutType == ImGuiLayoutType_Horizontal && g.NavMoveRequestForward == ImGuiNavForward_None) - { - // To do so we claim focus back, restore NavId and then process the movement request for yet another frame. - // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth the hassle/cost) - IM_ASSERT(window->DC.NavLayerActiveMaskNext & 0x02); // Sanity check - FocusWindow(window); - SetNavIDWithRectRel(window->NavLastIds[1], 1, window->NavRectRel[1]); - g.NavLayer = 1; - g.NavDisableHighlight = true; // Hide highlight for the current frame so we don't see the intermediary selection. - g.NavMoveRequestForward = ImGuiNavForward_ForwardQueued; - NavMoveRequestCancel(); - } - } - - IM_ASSERT(window->Flags & ImGuiWindowFlags_MenuBar); - IM_ASSERT(window->DC.MenuBarAppending); - PopClipRect(); - PopID(); - window->DC.MenuBarOffset.x = window->DC.CursorPos.x - window->MenuBarRect().Min.x; // Save horizontal position so next append can reuse it. This is kinda equivalent to a per-layer CursorPos. - window->DC.GroupStack.back().AdvanceCursor = false; - EndGroup(); // Restore position on layer 0 - window->DC.LayoutType = ImGuiLayoutType_Vertical; - window->DC.NavLayerCurrent--; - window->DC.NavLayerCurrentMask >>= 1; - window->DC.MenuBarAppending = false; -} - -bool ImGui::BeginMenu(const char* label, bool enabled) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const ImGuiID id = window->GetID(label); - - ImVec2 label_size = CalcTextSize(label, NULL, true); - - bool pressed; - bool menu_is_open = IsPopupOpen(id); - bool menuset_is_open = !(window->Flags & ImGuiWindowFlags_Popup) && (g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].OpenParentId == window->IDStack.back()); - ImGuiWindow* backed_nav_window = g.NavWindow; - if (menuset_is_open) - g.NavWindow = window; // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent) - - // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu (using FindBestWindowPosForPopup). - ImVec2 popup_pos, pos = window->DC.CursorPos; - if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) - { - // Menu inside an horizontal menu bar - // Selectable extend their highlight by half ItemSpacing in each direction. - // For ChildMenu, the popup position will be overwritten by the call to FindBestWindowPosForPopup() in Begin() - popup_pos = ImVec2(pos.x - window->WindowPadding.x, pos.y - style.FramePadding.y + window->MenuBarHeight()); - window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * 0.5f); - PushStyleVar(ImGuiStyleVar_ItemSpacing, style.ItemSpacing * 2.0f); - float w = label_size.x; - pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_PressedOnClick | ImGuiSelectableFlags_DontClosePopups | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f)); - PopStyleVar(); - window->DC.CursorPos.x += (float)(int)(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). - } - else - { - // Menu inside a menu - popup_pos = ImVec2(pos.x, pos.y - style.WindowPadding.y); - float w = window->MenuColumns.DeclColumns(label_size.x, 0.0f, (float)(int)(g.FontSize * 1.20f)); // Feedback to next frame - float extra_w = ImMax(0.0f, GetContentRegionAvail().x - w); - pressed = Selectable(label, menu_is_open, ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_PressedOnClick | ImGuiSelectableFlags_DontClosePopups | ImGuiSelectableFlags_DrawFillAvailWidth | (!enabled ? ImGuiSelectableFlags_Disabled : 0), ImVec2(w, 0.0f)); - if (!enabled) PushStyleColor(ImGuiCol_Text, g.Style.Colors[ImGuiCol_TextDisabled]); - RenderArrow(pos + ImVec2(window->MenuColumns.Pos[2] + extra_w + g.FontSize * 0.30f, 0.0f), ImGuiDir_Right); - if (!enabled) PopStyleColor(); - } - - const bool hovered = enabled && ItemHoverable(window->DC.LastItemRect, id); - if (menuset_is_open) - g.NavWindow = backed_nav_window; - - bool want_open = false, want_close = false; - if (window->DC.LayoutType == ImGuiLayoutType_Vertical) // (window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) - { - // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive. - bool moving_within_opened_triangle = false; - if (g.HoveredWindow == window && g.OpenPopupStack.Size > g.CurrentPopupStack.Size && g.OpenPopupStack[g.CurrentPopupStack.Size].ParentWindow == window && !(window->Flags & ImGuiWindowFlags_MenuBar)) - { - if (ImGuiWindow* next_window = g.OpenPopupStack[g.CurrentPopupStack.Size].Window) - { - ImRect next_window_rect = next_window->Rect(); - ImVec2 ta = g.IO.MousePos - g.IO.MouseDelta; - ImVec2 tb = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR(); - ImVec2 tc = (window->Pos.x < next_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR(); - float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, 5.0f, 30.0f); // add a bit of extra slack. - ta.x += (window->Pos.x < next_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues - tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -100.0f); // triangle is maximum 200 high to limit the slope and the bias toward large sub-menus // FIXME: Multiply by fb_scale? - tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +100.0f); - moving_within_opened_triangle = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos); - //window->DrawList->PushClipRectFullScreen(); window->DrawList->AddTriangleFilled(ta, tb, tc, moving_within_opened_triangle ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); window->DrawList->PopClipRect(); // Debug - } - } - - want_close = (menu_is_open && !hovered && g.HoveredWindow == window && g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrame != id && !moving_within_opened_triangle); - want_open = (!menu_is_open && hovered && !moving_within_opened_triangle) || (!menu_is_open && hovered && pressed); - - if (g.NavActivateId == id) - { - want_close = menu_is_open; - want_open = !menu_is_open; - } - if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Right) // Nav-Right to open - { - want_open = true; - NavMoveRequestCancel(); - } - } - else - { - // Menu bar - if (menu_is_open && pressed && menuset_is_open) // Click an open menu again to close it - { - want_close = true; - want_open = menu_is_open = false; - } - else if (pressed || (hovered && menuset_is_open && !menu_is_open)) // First click to open, then hover to open others - { - want_open = true; - } - else if (g.NavId == id && g.NavMoveRequest && g.NavMoveDir == ImGuiDir_Down) // Nav-Down to open - { - want_open = true; - NavMoveRequestCancel(); - } - } - - if (!enabled) // explicitly close if an open menu becomes disabled, facilitate users code a lot in pattern such as 'if (BeginMenu("options", has_object)) { ..use object.. }' - want_close = true; - if (want_close && IsPopupOpen(id)) - ClosePopupToLevel(g.CurrentPopupStack.Size); - - if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.CurrentPopupStack.Size) - { - // Don't recycle same menu level in the same frame, first close the other menu and yield for a frame. - OpenPopup(label); - return false; - } - - menu_is_open |= want_open; - if (want_open) - OpenPopup(label); - - if (menu_is_open) - { - // Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu) - SetNextWindowPos(popup_pos, ImGuiCond_Always); - ImGuiWindowFlags flags = ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ((window->Flags & (ImGuiWindowFlags_Popup|ImGuiWindowFlags_ChildMenu)) ? ImGuiWindowFlags_ChildMenu|ImGuiWindowFlags_ChildWindow : ImGuiWindowFlags_ChildMenu); - menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) - } - - return menu_is_open; -} - -void ImGui::EndMenu() -{ - // Nav: When a left move request _within our child menu_ failed, close the menu. - // A menu doesn't close itself because EndMenuBar() wants the catch the last Left<>Right inputs. - // However, it means that with the current code, a BeginMenu() from outside another menu or a menu-bar won't be closable with the Left direction. - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - if (g.NavWindow && g.NavWindow->ParentWindow == window && g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet() && window->DC.LayoutType == ImGuiLayoutType_Vertical) - { - ClosePopupToLevel(g.OpenPopupStack.Size - 1); - NavMoveRequestCancel(); - } - - EndPopup(); -} - -// Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. -void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags) -{ - ImGuiContext& g = *GImGui; - - int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]); - BeginTooltipEx(0, true); - - const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text; - if (text_end > text) - { - TextUnformatted(text, text_end); - Separator(); - } - - ImVec2 sz(g.FontSize * 3 + g.Style.FramePadding.y * 2, g.FontSize * 3 + g.Style.FramePadding.y * 2); - ColorButton("##preview", ImVec4(col[0], col[1], col[2], col[3]), (flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)) | ImGuiColorEditFlags_NoTooltip, sz); - SameLine(); - if (flags & ImGuiColorEditFlags_NoAlpha) - Text("#%02X%02X%02X\nR: %d, G: %d, B: %d\n(%.3f, %.3f, %.3f)", cr, cg, cb, cr, cg, cb, col[0], col[1], col[2]); - else - Text("#%02X%02X%02X%02X\nR:%d, G:%d, B:%d, A:%d\n(%.3f, %.3f, %.3f, %.3f)", cr, cg, cb, ca, cr, cg, cb, ca, col[0], col[1], col[2], col[3]); - EndTooltip(); -} - -static inline ImU32 ImAlphaBlendColor(ImU32 col_a, ImU32 col_b) -{ - float t = ((col_b >> IM_COL32_A_SHIFT) & 0xFF) / 255.f; - int r = ImLerp((int)(col_a >> IM_COL32_R_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_R_SHIFT) & 0xFF, t); - int g = ImLerp((int)(col_a >> IM_COL32_G_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_G_SHIFT) & 0xFF, t); - int b = ImLerp((int)(col_a >> IM_COL32_B_SHIFT) & 0xFF, (int)(col_b >> IM_COL32_B_SHIFT) & 0xFF, t); - return IM_COL32(r, g, b, 0xFF); -} - -// NB: This is rather brittle and will show artifact when rounding this enabled if rounded corners overlap multiple cells. Caller currently responsible for avoiding that. -// I spent a non reasonable amount of time trying to getting this right for ColorButton with rounding+anti-aliasing+ImGuiColorEditFlags_HalfAlphaPreview flag + various grid sizes and offsets, and eventually gave up... probably more reasonable to disable rounding alltogether. -void ImGui::RenderColorRectWithAlphaCheckerboard(ImVec2 p_min, ImVec2 p_max, ImU32 col, float grid_step, ImVec2 grid_off, float rounding, int rounding_corners_flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (((col & IM_COL32_A_MASK) >> IM_COL32_A_SHIFT) < 0xFF) - { - ImU32 col_bg1 = GetColorU32(ImAlphaBlendColor(IM_COL32(204,204,204,255), col)); - ImU32 col_bg2 = GetColorU32(ImAlphaBlendColor(IM_COL32(128,128,128,255), col)); - window->DrawList->AddRectFilled(p_min, p_max, col_bg1, rounding, rounding_corners_flags); - - int yi = 0; - for (float y = p_min.y + grid_off.y; y < p_max.y; y += grid_step, yi++) - { - float y1 = ImClamp(y, p_min.y, p_max.y), y2 = ImMin(y + grid_step, p_max.y); - if (y2 <= y1) - continue; - for (float x = p_min.x + grid_off.x + (yi & 1) * grid_step; x < p_max.x; x += grid_step * 2.0f) - { - float x1 = ImClamp(x, p_min.x, p_max.x), x2 = ImMin(x + grid_step, p_max.x); - if (x2 <= x1) - continue; - int rounding_corners_flags_cell = 0; - if (y1 <= p_min.y) { if (x1 <= p_min.x) rounding_corners_flags_cell |= ImDrawCornerFlags_TopLeft; if (x2 >= p_max.x) rounding_corners_flags_cell |= ImDrawCornerFlags_TopRight; } - if (y2 >= p_max.y) { if (x1 <= p_min.x) rounding_corners_flags_cell |= ImDrawCornerFlags_BotLeft; if (x2 >= p_max.x) rounding_corners_flags_cell |= ImDrawCornerFlags_BotRight; } - rounding_corners_flags_cell &= rounding_corners_flags; - window->DrawList->AddRectFilled(ImVec2(x1,y1), ImVec2(x2,y2), col_bg2, rounding_corners_flags_cell ? rounding : 0.0f, rounding_corners_flags_cell); - } - } - } - else - { - window->DrawList->AddRectFilled(p_min, p_max, col, rounding, rounding_corners_flags); - } -} - -void ImGui::SetColorEditOptions(ImGuiColorEditFlags flags) -{ - ImGuiContext& g = *GImGui; - if ((flags & ImGuiColorEditFlags__InputsMask) == 0) - flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__InputsMask; - if ((flags & ImGuiColorEditFlags__DataTypeMask) == 0) - flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__DataTypeMask; - if ((flags & ImGuiColorEditFlags__PickerMask) == 0) - flags |= ImGuiColorEditFlags__OptionsDefault & ImGuiColorEditFlags__PickerMask; - IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__InputsMask))); // Check only 1 option is selected - IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__DataTypeMask))); // Check only 1 option is selected - IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__PickerMask))); // Check only 1 option is selected - g.ColorEditOptions = flags; -} - -// A little colored square. Return true when clicked. -// FIXME: May want to display/ignore the alpha component in the color display? Yet show it in the tooltip. -// 'desc_id' is not called 'label' because we don't display it next to the button, but only in the tooltip. -bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags, ImVec2 size) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiID id = window->GetID(desc_id); - float default_size = GetFrameHeight(); - if (size.x == 0.0f) - size.x = default_size; - if (size.y == 0.0f) - size.y = default_size; - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); - ItemSize(bb, (size.y >= default_size) ? g.Style.FramePadding.y : 0.0f); - if (!ItemAdd(bb, id)) - return false; - - bool hovered, held; - bool pressed = ButtonBehavior(bb, id, &hovered, &held); - - if (flags & ImGuiColorEditFlags_NoAlpha) - flags &= ~(ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf); - - ImVec4 col_without_alpha(col.x, col.y, col.z, 1.0f); - float grid_step = ImMin(size.x, size.y) / 2.99f; - float rounding = ImMin(g.Style.FrameRounding, grid_step * 0.5f); - ImRect bb_inner = bb; - float off = -0.75f; // The border (using Col_FrameBg) tends to look off when color is near-opaque and rounding is enabled. This offset seemed like a good middle ground to reduce those artifacts. - bb_inner.Expand(off); - if ((flags & ImGuiColorEditFlags_AlphaPreviewHalf) && col.w < 1.0f) - { - float mid_x = (float)(int)((bb_inner.Min.x + bb_inner.Max.x) * 0.5f + 0.5f); - RenderColorRectWithAlphaCheckerboard(ImVec2(bb_inner.Min.x + grid_step, bb_inner.Min.y), bb_inner.Max, GetColorU32(col), grid_step, ImVec2(-grid_step + off, off), rounding, ImDrawCornerFlags_TopRight| ImDrawCornerFlags_BotRight); - window->DrawList->AddRectFilled(bb_inner.Min, ImVec2(mid_x, bb_inner.Max.y), GetColorU32(col_without_alpha), rounding, ImDrawCornerFlags_TopLeft|ImDrawCornerFlags_BotLeft); - } - else - { - // Because GetColorU32() multiplies by the global style Alpha and we don't want to display a checkerboard if the source code had no alpha - ImVec4 col_source = (flags & ImGuiColorEditFlags_AlphaPreview) ? col : col_without_alpha; - if (col_source.w < 1.0f) - RenderColorRectWithAlphaCheckerboard(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), grid_step, ImVec2(off, off), rounding); - else - window->DrawList->AddRectFilled(bb_inner.Min, bb_inner.Max, GetColorU32(col_source), rounding, ImDrawCornerFlags_All); - } - RenderNavHighlight(bb, id); - if (g.Style.FrameBorderSize > 0.0f) - RenderFrameBorder(bb.Min, bb.Max, rounding); - else - window->DrawList->AddRect(bb.Min, bb.Max, GetColorU32(ImGuiCol_FrameBg), rounding); // Color button are often in need of some sort of border - - // Drag and Drop Source - // NB: The ActiveId test is merely an optional micro-optimization, BeginDragDropSource() does the same test. - if (g.ActiveId == id && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropSource()) - { - if (flags & ImGuiColorEditFlags_NoAlpha) - SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F, &col, sizeof(float) * 3, ImGuiCond_Once); - else - SetDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F, &col, sizeof(float) * 4, ImGuiCond_Once); - ColorButton(desc_id, col, flags); - SameLine(); - TextUnformatted("Color"); - EndDragDropSource(); - } - - // Tooltip - if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered) - ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)); - - if (pressed) - MarkItemValueChanged(id); - - return pressed; -} - -bool ImGui::ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags) -{ - return ColorEdit4(label, col, flags | ImGuiColorEditFlags_NoAlpha); -} - -void ImGui::ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags) -{ - bool allow_opt_inputs = !(flags & ImGuiColorEditFlags__InputsMask); - bool allow_opt_datatype = !(flags & ImGuiColorEditFlags__DataTypeMask); - if ((!allow_opt_inputs && !allow_opt_datatype) || !BeginPopup("context")) - return; - ImGuiContext& g = *GImGui; - ImGuiColorEditFlags opts = g.ColorEditOptions; - if (allow_opt_inputs) - { - if (RadioButton("RGB", (opts & ImGuiColorEditFlags_RGB) != 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_RGB; - if (RadioButton("HSV", (opts & ImGuiColorEditFlags_HSV) != 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HSV; - if (RadioButton("HEX", (opts & ImGuiColorEditFlags_HEX) != 0)) opts = (opts & ~ImGuiColorEditFlags__InputsMask) | ImGuiColorEditFlags_HEX; - } - if (allow_opt_datatype) - { - if (allow_opt_inputs) Separator(); - if (RadioButton("0..255", (opts & ImGuiColorEditFlags_Uint8) != 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Uint8; - if (RadioButton("0.00..1.00", (opts & ImGuiColorEditFlags_Float) != 0)) opts = (opts & ~ImGuiColorEditFlags__DataTypeMask) | ImGuiColorEditFlags_Float; - } - - if (allow_opt_inputs || allow_opt_datatype) - Separator(); - if (Button("Copy as..", ImVec2(-1,0))) - OpenPopup("Copy"); - if (BeginPopup("Copy")) - { - int cr = IM_F32_TO_INT8_SAT(col[0]), cg = IM_F32_TO_INT8_SAT(col[1]), cb = IM_F32_TO_INT8_SAT(col[2]), ca = (flags & ImGuiColorEditFlags_NoAlpha) ? 255 : IM_F32_TO_INT8_SAT(col[3]); - char buf[64]; - ImFormatString(buf, IM_ARRAYSIZE(buf), "(%.3ff, %.3ff, %.3ff, %.3ff)", col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); - if (Selectable(buf)) - SetClipboardText(buf); - ImFormatString(buf, IM_ARRAYSIZE(buf), "(%d,%d,%d,%d)", cr, cg, cb, ca); - if (Selectable(buf)) - SetClipboardText(buf); - if (flags & ImGuiColorEditFlags_NoAlpha) - ImFormatString(buf, IM_ARRAYSIZE(buf), "0x%02X%02X%02X", cr, cg, cb); - else - ImFormatString(buf, IM_ARRAYSIZE(buf), "0x%02X%02X%02X%02X", cr, cg, cb, ca); - if (Selectable(buf)) - SetClipboardText(buf); - EndPopup(); - } - - g.ColorEditOptions = opts; - EndPopup(); -} - -static void ColorPickerOptionsPopup(ImGuiColorEditFlags flags, const float* ref_col) -{ - bool allow_opt_picker = !(flags & ImGuiColorEditFlags__PickerMask); - bool allow_opt_alpha_bar = !(flags & ImGuiColorEditFlags_NoAlpha) && !(flags & ImGuiColorEditFlags_AlphaBar); - if ((!allow_opt_picker && !allow_opt_alpha_bar) || !ImGui::BeginPopup("context")) - return; - ImGuiContext& g = *GImGui; - if (allow_opt_picker) - { - ImVec2 picker_size(g.FontSize * 8, ImMax(g.FontSize * 8 - (ImGui::GetFrameHeight() + g.Style.ItemInnerSpacing.x), 1.0f)); // FIXME: Picker size copied from main picker function - ImGui::PushItemWidth(picker_size.x); - for (int picker_type = 0; picker_type < 2; picker_type++) - { - // Draw small/thumbnail version of each picker type (over an invisible button for selection) - if (picker_type > 0) ImGui::Separator(); - ImGui::PushID(picker_type); - ImGuiColorEditFlags picker_flags = ImGuiColorEditFlags_NoInputs|ImGuiColorEditFlags_NoOptions|ImGuiColorEditFlags_NoLabel|ImGuiColorEditFlags_NoSidePreview|(flags & ImGuiColorEditFlags_NoAlpha); - if (picker_type == 0) picker_flags |= ImGuiColorEditFlags_PickerHueBar; - if (picker_type == 1) picker_flags |= ImGuiColorEditFlags_PickerHueWheel; - ImVec2 backup_pos = ImGui::GetCursorScreenPos(); - if (ImGui::Selectable("##selectable", false, 0, picker_size)) // By default, Selectable() is closing popup - g.ColorEditOptions = (g.ColorEditOptions & ~ImGuiColorEditFlags__PickerMask) | (picker_flags & ImGuiColorEditFlags__PickerMask); - ImGui::SetCursorScreenPos(backup_pos); - ImVec4 dummy_ref_col; - memcpy(&dummy_ref_col.x, ref_col, sizeof(float) * (picker_flags & ImGuiColorEditFlags_NoAlpha ? 3 : 4)); - ImGui::ColorPicker4("##dummypicker", &dummy_ref_col.x, picker_flags); - ImGui::PopID(); - } - ImGui::PopItemWidth(); - } - if (allow_opt_alpha_bar) - { - if (allow_opt_picker) ImGui::Separator(); - ImGui::CheckboxFlags("Alpha Bar", (unsigned int*)&g.ColorEditOptions, ImGuiColorEditFlags_AlphaBar); - } - ImGui::EndPopup(); -} - -// Edit colors components (each component in 0.0f..1.0f range). -// See enum ImGuiColorEditFlags_ for available options. e.g. Only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. -// With typical options: Left-click on colored square to open color picker. Right-click to open option menu. CTRL-Click over input fields to edit them and TAB to go to next item. -bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return false; - - ImGuiContext& g = *GImGui; - const ImGuiStyle& style = g.Style; - const float square_sz = GetFrameHeight(); - const float w_extra = (flags & ImGuiColorEditFlags_NoSmallPreview) ? 0.0f : (square_sz + style.ItemInnerSpacing.x); - const float w_items_all = CalcItemWidth() - w_extra; - const char* label_display_end = FindRenderedTextEnd(label); - - BeginGroup(); - PushID(label); - - // If we're not showing any slider there's no point in doing any HSV conversions - const ImGuiColorEditFlags flags_untouched = flags; - if (flags & ImGuiColorEditFlags_NoInputs) - flags = (flags & (~ImGuiColorEditFlags__InputsMask)) | ImGuiColorEditFlags_RGB | ImGuiColorEditFlags_NoOptions; - - // Context menu: display and modify options (before defaults are applied) - if (!(flags & ImGuiColorEditFlags_NoOptions)) - ColorEditOptionsPopup(col, flags); - - // Read stored options - if (!(flags & ImGuiColorEditFlags__InputsMask)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags__InputsMask); - if (!(flags & ImGuiColorEditFlags__DataTypeMask)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags__DataTypeMask); - if (!(flags & ImGuiColorEditFlags__PickerMask)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags__PickerMask); - flags |= (g.ColorEditOptions & ~(ImGuiColorEditFlags__InputsMask | ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask)); - - const bool alpha = (flags & ImGuiColorEditFlags_NoAlpha) == 0; - const bool hdr = (flags & ImGuiColorEditFlags_HDR) != 0; - const int components = alpha ? 4 : 3; - - // Convert to the formats we need - float f[4] = { col[0], col[1], col[2], alpha ? col[3] : 1.0f }; - if (flags & ImGuiColorEditFlags_HSV) - ColorConvertRGBtoHSV(f[0], f[1], f[2], f[0], f[1], f[2]); - int i[4] = { IM_F32_TO_INT8_UNBOUND(f[0]), IM_F32_TO_INT8_UNBOUND(f[1]), IM_F32_TO_INT8_UNBOUND(f[2]), IM_F32_TO_INT8_UNBOUND(f[3]) }; - - bool value_changed = false; - bool value_changed_as_float = false; - - if ((flags & (ImGuiColorEditFlags_RGB | ImGuiColorEditFlags_HSV)) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0) - { - // RGB/HSV 0..255 Sliders - const float w_item_one = ImMax(1.0f, (float)(int)((w_items_all - (style.ItemInnerSpacing.x) * (components-1)) / (float)components)); - const float w_item_last = ImMax(1.0f, (float)(int)(w_items_all - (w_item_one + style.ItemInnerSpacing.x) * (components-1))); - - const bool hide_prefix = (w_item_one <= CalcTextSize((flags & ImGuiColorEditFlags_Float) ? "M:0.000" : "M:000").x); - const char* ids[4] = { "##X", "##Y", "##Z", "##W" }; - const char* fmt_table_int[3][4] = - { - { "%3d", "%3d", "%3d", "%3d" }, // Short display - { "R:%3d", "G:%3d", "B:%3d", "A:%3d" }, // Long display for RGBA - { "H:%3d", "S:%3d", "V:%3d", "A:%3d" } // Long display for HSVA - }; - const char* fmt_table_float[3][4] = - { - { "%0.3f", "%0.3f", "%0.3f", "%0.3f" }, // Short display - { "R:%0.3f", "G:%0.3f", "B:%0.3f", "A:%0.3f" }, // Long display for RGBA - { "H:%0.3f", "S:%0.3f", "V:%0.3f", "A:%0.3f" } // Long display for HSVA - }; - const int fmt_idx = hide_prefix ? 0 : (flags & ImGuiColorEditFlags_HSV) ? 2 : 1; - - PushItemWidth(w_item_one); - for (int n = 0; n < components; n++) - { - if (n > 0) - SameLine(0, style.ItemInnerSpacing.x); - if (n + 1 == components) - PushItemWidth(w_item_last); - if (flags & ImGuiColorEditFlags_Float) - value_changed = value_changed_as_float = value_changed | DragFloat(ids[n], &f[n], 1.0f/255.0f, 0.0f, hdr ? 0.0f : 1.0f, fmt_table_float[fmt_idx][n]); - else - value_changed |= DragInt(ids[n], &i[n], 1.0f, 0, hdr ? 0 : 255, fmt_table_int[fmt_idx][n]); - if (!(flags & ImGuiColorEditFlags_NoOptions)) - OpenPopupOnItemClick("context"); - } - PopItemWidth(); - PopItemWidth(); - } - else if ((flags & ImGuiColorEditFlags_HEX) != 0 && (flags & ImGuiColorEditFlags_NoInputs) == 0) - { - // RGB Hexadecimal Input - char buf[64]; - if (alpha) - ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X%02X", ImClamp(i[0],0,255), ImClamp(i[1],0,255), ImClamp(i[2],0,255), ImClamp(i[3],0,255)); - else - ImFormatString(buf, IM_ARRAYSIZE(buf), "#%02X%02X%02X", ImClamp(i[0],0,255), ImClamp(i[1],0,255), ImClamp(i[2],0,255)); - PushItemWidth(w_items_all); - if (InputText("##Text", buf, IM_ARRAYSIZE(buf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase)) - { - value_changed = true; - char* p = buf; - while (*p == '#' || ImCharIsBlankA(*p)) - p++; - i[0] = i[1] = i[2] = i[3] = 0; - if (alpha) - sscanf(p, "%02X%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2], (unsigned int*)&i[3]); // Treat at unsigned (%X is unsigned) - else - sscanf(p, "%02X%02X%02X", (unsigned int*)&i[0], (unsigned int*)&i[1], (unsigned int*)&i[2]); - } - if (!(flags & ImGuiColorEditFlags_NoOptions)) - OpenPopupOnItemClick("context"); - PopItemWidth(); - } - - ImGuiWindow* picker_active_window = NULL; - if (!(flags & ImGuiColorEditFlags_NoSmallPreview)) - { - if (!(flags & ImGuiColorEditFlags_NoInputs)) - SameLine(0, style.ItemInnerSpacing.x); - - const ImVec4 col_v4(col[0], col[1], col[2], alpha ? col[3] : 1.0f); - if (ColorButton("##ColorButton", col_v4, flags)) - { - if (!(flags & ImGuiColorEditFlags_NoPicker)) - { - // Store current color and open a picker - g.ColorPickerRef = col_v4; - OpenPopup("picker"); - SetNextWindowPos(window->DC.LastItemRect.GetBL() + ImVec2(-1,style.ItemSpacing.y)); - } - } - if (!(flags & ImGuiColorEditFlags_NoOptions)) - OpenPopupOnItemClick("context"); - - if (BeginPopup("picker")) - { - picker_active_window = g.CurrentWindow; - if (label != label_display_end) - { - TextUnformatted(label, label_display_end); - Separator(); - } - ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags__PickerMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar; - ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags__InputsMask | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf; - PushItemWidth(square_sz * 12.0f); // Use 256 + bar sizes? - value_changed |= ColorPicker4("##picker", col, picker_flags, &g.ColorPickerRef.x); - PopItemWidth(); - EndPopup(); - } - } - - if (label != label_display_end && !(flags & ImGuiColorEditFlags_NoLabel)) - { - SameLine(0, style.ItemInnerSpacing.x); - TextUnformatted(label, label_display_end); - } - - // Convert back - if (picker_active_window == NULL) - { - if (!value_changed_as_float) - for (int n = 0; n < 4; n++) - f[n] = i[n] / 255.0f; - if (flags & ImGuiColorEditFlags_HSV) - ColorConvertHSVtoRGB(f[0], f[1], f[2], f[0], f[1], f[2]); - if (value_changed) - { - col[0] = f[0]; - col[1] = f[1]; - col[2] = f[2]; - if (alpha) - col[3] = f[3]; - } - } - - PopID(); - EndGroup(); - - // Drag and Drop Target - // NB: The flag test is merely an optional micro-optimization, BeginDragDropTarget() does the same test. - if ((window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) && !(flags & ImGuiColorEditFlags_NoDragDrop) && BeginDragDropTarget()) - { - if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F)) - { - memcpy((float*)col, payload->Data, sizeof(float) * 3); - value_changed = true; - } - if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F)) - { - memcpy((float*)col, payload->Data, sizeof(float) * components); - value_changed = true; - } - EndDragDropTarget(); - } - - // When picker is being actively used, use its active id so IsItemActive() will function on ColorEdit4(). - if (picker_active_window && g.ActiveId != 0 && g.ActiveIdWindow == picker_active_window) - window->DC.LastItemId = g.ActiveId; - - if (value_changed) - MarkItemValueChanged(window->DC.LastItemId); - - return value_changed; -} - -bool ImGui::ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags) -{ - float col4[4] = { col[0], col[1], col[2], 1.0f }; - if (!ColorPicker4(label, col4, flags | ImGuiColorEditFlags_NoAlpha)) - return false; - col[0] = col4[0]; col[1] = col4[1]; col[2] = col4[2]; - return true; -} - -// 'pos' is position of the arrow tip. half_sz.x is length from base to tip. half_sz.y is length on each side. -static void RenderArrow(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col) -{ - switch (direction) - { - case ImGuiDir_Left: draw_list->AddTriangleFilled(ImVec2(pos.x + half_sz.x, pos.y - half_sz.y), ImVec2(pos.x + half_sz.x, pos.y + half_sz.y), pos, col); return; - case ImGuiDir_Right: draw_list->AddTriangleFilled(ImVec2(pos.x - half_sz.x, pos.y + half_sz.y), ImVec2(pos.x - half_sz.x, pos.y - half_sz.y), pos, col); return; - case ImGuiDir_Up: draw_list->AddTriangleFilled(ImVec2(pos.x + half_sz.x, pos.y + half_sz.y), ImVec2(pos.x - half_sz.x, pos.y + half_sz.y), pos, col); return; - case ImGuiDir_Down: draw_list->AddTriangleFilled(ImVec2(pos.x - half_sz.x, pos.y - half_sz.y), ImVec2(pos.x + half_sz.x, pos.y - half_sz.y), pos, col); return; - case ImGuiDir_None: case ImGuiDir_COUNT: break; // Fix warnings - } -} - -static void RenderArrowsForVerticalBar(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, float bar_w) -{ - RenderArrow(draw_list, ImVec2(pos.x + half_sz.x + 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Right, IM_COL32_BLACK); - RenderArrow(draw_list, ImVec2(pos.x + half_sz.x, pos.y), half_sz, ImGuiDir_Right, IM_COL32_WHITE); - RenderArrow(draw_list, ImVec2(pos.x + bar_w - half_sz.x - 1, pos.y), ImVec2(half_sz.x + 2, half_sz.y + 1), ImGuiDir_Left, IM_COL32_BLACK); - RenderArrow(draw_list, ImVec2(pos.x + bar_w - half_sz.x, pos.y), half_sz, ImGuiDir_Left, IM_COL32_WHITE); -} - -// ColorPicker -// Note: only access 3 floats if ImGuiColorEditFlags_NoAlpha flag is set. -// FIXME: we adjust the big color square height based on item width, which may cause a flickering feedback loop (if automatic height makes a vertical scrollbar appears, affecting automatic width..) -bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags, const float* ref_col) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - ImDrawList* draw_list = window->DrawList; - - ImGuiStyle& style = g.Style; - ImGuiIO& io = g.IO; - - PushID(label); - BeginGroup(); - - if (!(flags & ImGuiColorEditFlags_NoSidePreview)) - flags |= ImGuiColorEditFlags_NoSmallPreview; - - // Context menu: display and store options. - if (!(flags & ImGuiColorEditFlags_NoOptions)) - ColorPickerOptionsPopup(flags, col); - - // Read stored options - if (!(flags & ImGuiColorEditFlags__PickerMask)) - flags |= ((g.ColorEditOptions & ImGuiColorEditFlags__PickerMask) ? g.ColorEditOptions : ImGuiColorEditFlags__OptionsDefault) & ImGuiColorEditFlags__PickerMask; - IM_ASSERT(ImIsPowerOfTwo((int)(flags & ImGuiColorEditFlags__PickerMask))); // Check that only 1 is selected - if (!(flags & ImGuiColorEditFlags_NoOptions)) - flags |= (g.ColorEditOptions & ImGuiColorEditFlags_AlphaBar); - - // Setup - int components = (flags & ImGuiColorEditFlags_NoAlpha) ? 3 : 4; - bool alpha_bar = (flags & ImGuiColorEditFlags_AlphaBar) && !(flags & ImGuiColorEditFlags_NoAlpha); - ImVec2 picker_pos = window->DC.CursorPos; - float square_sz = GetFrameHeight(); - float bars_width = square_sz; // Arbitrary smallish width of Hue/Alpha picking bars - float sv_picker_size = ImMax(bars_width * 1, CalcItemWidth() - (alpha_bar ? 2 : 1) * (bars_width + style.ItemInnerSpacing.x)); // Saturation/Value picking box - float bar0_pos_x = picker_pos.x + sv_picker_size + style.ItemInnerSpacing.x; - float bar1_pos_x = bar0_pos_x + bars_width + style.ItemInnerSpacing.x; - float bars_triangles_half_sz = (float)(int)(bars_width * 0.20f); - - float backup_initial_col[4]; - memcpy(backup_initial_col, col, components * sizeof(float)); - - float wheel_thickness = sv_picker_size * 0.08f; - float wheel_r_outer = sv_picker_size * 0.50f; - float wheel_r_inner = wheel_r_outer - wheel_thickness; - ImVec2 wheel_center(picker_pos.x + (sv_picker_size + bars_width)*0.5f, picker_pos.y + sv_picker_size*0.5f); - - // Note: the triangle is displayed rotated with triangle_pa pointing to Hue, but most coordinates stays unrotated for logic. - float triangle_r = wheel_r_inner - (int)(sv_picker_size * 0.027f); - ImVec2 triangle_pa = ImVec2(triangle_r, 0.0f); // Hue point. - ImVec2 triangle_pb = ImVec2(triangle_r * -0.5f, triangle_r * -0.866025f); // Black point. - ImVec2 triangle_pc = ImVec2(triangle_r * -0.5f, triangle_r * +0.866025f); // White point. - - float H,S,V; - ColorConvertRGBtoHSV(col[0], col[1], col[2], H, S, V); - - bool value_changed = false, value_changed_h = false, value_changed_sv = false; - - PushItemFlag(ImGuiItemFlags_NoNav, true); - if (flags & ImGuiColorEditFlags_PickerHueWheel) - { - // Hue wheel + SV triangle logic - InvisibleButton("hsv", ImVec2(sv_picker_size + style.ItemInnerSpacing.x + bars_width, sv_picker_size)); - if (IsItemActive()) - { - ImVec2 initial_off = g.IO.MouseClickedPos[0] - wheel_center; - ImVec2 current_off = g.IO.MousePos - wheel_center; - float initial_dist2 = ImLengthSqr(initial_off); - if (initial_dist2 >= (wheel_r_inner-1)*(wheel_r_inner-1) && initial_dist2 <= (wheel_r_outer+1)*(wheel_r_outer+1)) - { - // Interactive with Hue wheel - H = ImAtan2(current_off.y, current_off.x) / IM_PI*0.5f; - if (H < 0.0f) - H += 1.0f; - value_changed = value_changed_h = true; - } - float cos_hue_angle = ImCos(-H * 2.0f * IM_PI); - float sin_hue_angle = ImSin(-H * 2.0f * IM_PI); - if (ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, ImRotate(initial_off, cos_hue_angle, sin_hue_angle))) - { - // Interacting with SV triangle - ImVec2 current_off_unrotated = ImRotate(current_off, cos_hue_angle, sin_hue_angle); - if (!ImTriangleContainsPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated)) - current_off_unrotated = ImTriangleClosestPoint(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated); - float uu, vv, ww; - ImTriangleBarycentricCoords(triangle_pa, triangle_pb, triangle_pc, current_off_unrotated, uu, vv, ww); - V = ImClamp(1.0f - vv, 0.0001f, 1.0f); - S = ImClamp(uu / V, 0.0001f, 1.0f); - value_changed = value_changed_sv = true; - } - } - if (!(flags & ImGuiColorEditFlags_NoOptions)) - OpenPopupOnItemClick("context"); - } - else if (flags & ImGuiColorEditFlags_PickerHueBar) - { - // SV rectangle logic - InvisibleButton("sv", ImVec2(sv_picker_size, sv_picker_size)); - if (IsItemActive()) - { - S = ImSaturate((io.MousePos.x - picker_pos.x) / (sv_picker_size-1)); - V = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1)); - value_changed = value_changed_sv = true; - } - if (!(flags & ImGuiColorEditFlags_NoOptions)) - OpenPopupOnItemClick("context"); - - // Hue bar logic - SetCursorScreenPos(ImVec2(bar0_pos_x, picker_pos.y)); - InvisibleButton("hue", ImVec2(bars_width, sv_picker_size)); - if (IsItemActive()) - { - H = ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1)); - value_changed = value_changed_h = true; - } - } - - // Alpha bar logic - if (alpha_bar) - { - SetCursorScreenPos(ImVec2(bar1_pos_x, picker_pos.y)); - InvisibleButton("alpha", ImVec2(bars_width, sv_picker_size)); - if (IsItemActive()) - { - col[3] = 1.0f - ImSaturate((io.MousePos.y - picker_pos.y) / (sv_picker_size-1)); - value_changed = true; - } - } - PopItemFlag(); // ImGuiItemFlags_NoNav - - if (!(flags & ImGuiColorEditFlags_NoSidePreview)) - { - SameLine(0, style.ItemInnerSpacing.x); - BeginGroup(); - } - - if (!(flags & ImGuiColorEditFlags_NoLabel)) - { - const char* label_display_end = FindRenderedTextEnd(label); - if (label != label_display_end) - { - if ((flags & ImGuiColorEditFlags_NoSidePreview)) - SameLine(0, style.ItemInnerSpacing.x); - TextUnformatted(label, label_display_end); - } - } - - if (!(flags & ImGuiColorEditFlags_NoSidePreview)) - { - PushItemFlag(ImGuiItemFlags_NoNavDefaultFocus, true); - ImVec4 col_v4(col[0], col[1], col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : col[3]); - if ((flags & ImGuiColorEditFlags_NoLabel)) - Text("Current"); - ColorButton("##current", col_v4, (flags & (ImGuiColorEditFlags_HDR|ImGuiColorEditFlags_AlphaPreview|ImGuiColorEditFlags_AlphaPreviewHalf|ImGuiColorEditFlags_NoTooltip)), ImVec2(square_sz * 3, square_sz * 2)); - if (ref_col != NULL) - { - Text("Original"); - ImVec4 ref_col_v4(ref_col[0], ref_col[1], ref_col[2], (flags & ImGuiColorEditFlags_NoAlpha) ? 1.0f : ref_col[3]); - if (ColorButton("##original", ref_col_v4, (flags & (ImGuiColorEditFlags_HDR|ImGuiColorEditFlags_AlphaPreview|ImGuiColorEditFlags_AlphaPreviewHalf|ImGuiColorEditFlags_NoTooltip)), ImVec2(square_sz * 3, square_sz * 2))) - { - memcpy(col, ref_col, components * sizeof(float)); - value_changed = true; - } - } - PopItemFlag(); - EndGroup(); - } - - // Convert back color to RGB - if (value_changed_h || value_changed_sv) - ColorConvertHSVtoRGB(H >= 1.0f ? H - 10 * 1e-6f : H, S > 0.0f ? S : 10*1e-6f, V > 0.0f ? V : 1e-6f, col[0], col[1], col[2]); - - // R,G,B and H,S,V slider color editor - if ((flags & ImGuiColorEditFlags_NoInputs) == 0) - { - PushItemWidth((alpha_bar ? bar1_pos_x : bar0_pos_x) + bars_width - picker_pos.x); - ImGuiColorEditFlags sub_flags_to_forward = ImGuiColorEditFlags__DataTypeMask | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoOptions | ImGuiColorEditFlags_NoSmallPreview | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf; - ImGuiColorEditFlags sub_flags = (flags & sub_flags_to_forward) | ImGuiColorEditFlags_NoPicker; - if (flags & ImGuiColorEditFlags_RGB || (flags & ImGuiColorEditFlags__InputsMask) == 0) - value_changed |= ColorEdit4("##rgb", col, sub_flags | ImGuiColorEditFlags_RGB); - if (flags & ImGuiColorEditFlags_HSV || (flags & ImGuiColorEditFlags__InputsMask) == 0) - value_changed |= ColorEdit4("##hsv", col, sub_flags | ImGuiColorEditFlags_HSV); - if (flags & ImGuiColorEditFlags_HEX || (flags & ImGuiColorEditFlags__InputsMask) == 0) - value_changed |= ColorEdit4("##hex", col, sub_flags | ImGuiColorEditFlags_HEX); - PopItemWidth(); - } - - // Try to cancel hue wrap (after ColorEdit), if any - if (value_changed) - { - float new_H, new_S, new_V; - ColorConvertRGBtoHSV(col[0], col[1], col[2], new_H, new_S, new_V); - if (new_H <= 0 && H > 0) - { - if (new_V <= 0 && V != new_V) - ColorConvertHSVtoRGB(H, S, new_V <= 0 ? V * 0.5f : new_V, col[0], col[1], col[2]); - else if (new_S <= 0) - ColorConvertHSVtoRGB(H, new_S <= 0 ? S * 0.5f : new_S, new_V, col[0], col[1], col[2]); - } - } - - ImVec4 hue_color_f(1, 1, 1, 1); ColorConvertHSVtoRGB(H, 1, 1, hue_color_f.x, hue_color_f.y, hue_color_f.z); - ImU32 hue_color32 = ColorConvertFloat4ToU32(hue_color_f); - ImU32 col32_no_alpha = ColorConvertFloat4ToU32(ImVec4(col[0], col[1], col[2], 1.0f)); - - const ImU32 hue_colors[6+1] = { IM_COL32(255,0,0,255), IM_COL32(255,255,0,255), IM_COL32(0,255,0,255), IM_COL32(0,255,255,255), IM_COL32(0,0,255,255), IM_COL32(255,0,255,255), IM_COL32(255,0,0,255) }; - ImVec2 sv_cursor_pos; - - if (flags & ImGuiColorEditFlags_PickerHueWheel) - { - // Render Hue Wheel - const float aeps = 1.5f / wheel_r_outer; // Half a pixel arc length in radians (2pi cancels out). - const int segment_per_arc = ImMax(4, (int)wheel_r_outer / 12); - for (int n = 0; n < 6; n++) - { - const float a0 = (n) /6.0f * 2.0f * IM_PI - aeps; - const float a1 = (n+1.0f)/6.0f * 2.0f * IM_PI + aeps; - const int vert_start_idx = draw_list->VtxBuffer.Size; - draw_list->PathArcTo(wheel_center, (wheel_r_inner + wheel_r_outer)*0.5f, a0, a1, segment_per_arc); - draw_list->PathStroke(IM_COL32_WHITE, false, wheel_thickness); - const int vert_end_idx = draw_list->VtxBuffer.Size; - - // Paint colors over existing vertices - ImVec2 gradient_p0(wheel_center.x + ImCos(a0) * wheel_r_inner, wheel_center.y + ImSin(a0) * wheel_r_inner); - ImVec2 gradient_p1(wheel_center.x + ImCos(a1) * wheel_r_inner, wheel_center.y + ImSin(a1) * wheel_r_inner); - ShadeVertsLinearColorGradientKeepAlpha(draw_list->VtxBuffer.Data + vert_start_idx, draw_list->VtxBuffer.Data + vert_end_idx, gradient_p0, gradient_p1, hue_colors[n], hue_colors[n+1]); - } - - // Render Cursor + preview on Hue Wheel - float cos_hue_angle = ImCos(H * 2.0f * IM_PI); - float sin_hue_angle = ImSin(H * 2.0f * IM_PI); - ImVec2 hue_cursor_pos(wheel_center.x + cos_hue_angle * (wheel_r_inner+wheel_r_outer)*0.5f, wheel_center.y + sin_hue_angle * (wheel_r_inner+wheel_r_outer)*0.5f); - float hue_cursor_rad = value_changed_h ? wheel_thickness * 0.65f : wheel_thickness * 0.55f; - int hue_cursor_segments = ImClamp((int)(hue_cursor_rad / 1.4f), 9, 32); - draw_list->AddCircleFilled(hue_cursor_pos, hue_cursor_rad, hue_color32, hue_cursor_segments); - draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad+1, IM_COL32(128,128,128,255), hue_cursor_segments); - draw_list->AddCircle(hue_cursor_pos, hue_cursor_rad, IM_COL32_WHITE, hue_cursor_segments); - - // Render SV triangle (rotated according to hue) - ImVec2 tra = wheel_center + ImRotate(triangle_pa, cos_hue_angle, sin_hue_angle); - ImVec2 trb = wheel_center + ImRotate(triangle_pb, cos_hue_angle, sin_hue_angle); - ImVec2 trc = wheel_center + ImRotate(triangle_pc, cos_hue_angle, sin_hue_angle); - ImVec2 uv_white = GetFontTexUvWhitePixel(); - draw_list->PrimReserve(6, 6); - draw_list->PrimVtx(tra, uv_white, hue_color32); - draw_list->PrimVtx(trb, uv_white, hue_color32); - draw_list->PrimVtx(trc, uv_white, IM_COL32_WHITE); - draw_list->PrimVtx(tra, uv_white, IM_COL32_BLACK_TRANS); - draw_list->PrimVtx(trb, uv_white, IM_COL32_BLACK); - draw_list->PrimVtx(trc, uv_white, IM_COL32_BLACK_TRANS); - draw_list->AddTriangle(tra, trb, trc, IM_COL32(128,128,128,255), 1.5f); - sv_cursor_pos = ImLerp(ImLerp(trc, tra, ImSaturate(S)), trb, ImSaturate(1 - V)); - } - else if (flags & ImGuiColorEditFlags_PickerHueBar) - { - // Render SV Square - draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), IM_COL32_WHITE, hue_color32, hue_color32, IM_COL32_WHITE); - draw_list->AddRectFilledMultiColor(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), IM_COL32_BLACK_TRANS, IM_COL32_BLACK_TRANS, IM_COL32_BLACK, IM_COL32_BLACK); - RenderFrameBorder(picker_pos, picker_pos + ImVec2(sv_picker_size,sv_picker_size), 0.0f); - sv_cursor_pos.x = ImClamp((float)(int)(picker_pos.x + ImSaturate(S) * sv_picker_size + 0.5f), picker_pos.x + 2, picker_pos.x + sv_picker_size - 2); // Sneakily prevent the circle to stick out too much - sv_cursor_pos.y = ImClamp((float)(int)(picker_pos.y + ImSaturate(1 - V) * sv_picker_size + 0.5f), picker_pos.y + 2, picker_pos.y + sv_picker_size - 2); - - // Render Hue Bar - for (int i = 0; i < 6; ++i) - draw_list->AddRectFilledMultiColor(ImVec2(bar0_pos_x, picker_pos.y + i * (sv_picker_size / 6)), ImVec2(bar0_pos_x + bars_width, picker_pos.y + (i + 1) * (sv_picker_size / 6)), hue_colors[i], hue_colors[i], hue_colors[i + 1], hue_colors[i + 1]); - float bar0_line_y = (float)(int)(picker_pos.y + H * sv_picker_size + 0.5f); - RenderFrameBorder(ImVec2(bar0_pos_x, picker_pos.y), ImVec2(bar0_pos_x + bars_width, picker_pos.y + sv_picker_size), 0.0f); - RenderArrowsForVerticalBar(draw_list, ImVec2(bar0_pos_x - 1, bar0_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f); - } - - // Render cursor/preview circle (clamp S/V within 0..1 range because floating points colors may lead HSV values to be out of range) - float sv_cursor_rad = value_changed_sv ? 10.0f : 6.0f; - draw_list->AddCircleFilled(sv_cursor_pos, sv_cursor_rad, col32_no_alpha, 12); - draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad+1, IM_COL32(128,128,128,255), 12); - draw_list->AddCircle(sv_cursor_pos, sv_cursor_rad, IM_COL32_WHITE, 12); - - // Render alpha bar - if (alpha_bar) - { - float alpha = ImSaturate(col[3]); - ImRect bar1_bb(bar1_pos_x, picker_pos.y, bar1_pos_x + bars_width, picker_pos.y + sv_picker_size); - RenderColorRectWithAlphaCheckerboard(bar1_bb.Min, bar1_bb.Max, IM_COL32(0,0,0,0), bar1_bb.GetWidth() / 2.0f, ImVec2(0.0f, 0.0f)); - draw_list->AddRectFilledMultiColor(bar1_bb.Min, bar1_bb.Max, col32_no_alpha, col32_no_alpha, col32_no_alpha & ~IM_COL32_A_MASK, col32_no_alpha & ~IM_COL32_A_MASK); - float bar1_line_y = (float)(int)(picker_pos.y + (1.0f - alpha) * sv_picker_size + 0.5f); - RenderFrameBorder(bar1_bb.Min, bar1_bb.Max, 0.0f); - RenderArrowsForVerticalBar(draw_list, ImVec2(bar1_pos_x - 1, bar1_line_y), ImVec2(bars_triangles_half_sz + 1, bars_triangles_half_sz), bars_width + 2.0f); - } - - EndGroup(); - - if (value_changed && memcmp(backup_initial_col, col, components * sizeof(float)) == 0) - value_changed = false; - if (value_changed) - MarkItemValueChanged(window->DC.LastItemId); - - PopID(); - - return value_changed; -} - -// Horizontal/vertical separating line -void ImGui::Separator() -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - ImGuiContext& g = *GImGui; - - // Those flags should eventually be overridable by the user - ImGuiSeparatorFlags flags = (window->DC.LayoutType == ImGuiLayoutType_Horizontal) ? ImGuiSeparatorFlags_Vertical : ImGuiSeparatorFlags_Horizontal; - IM_ASSERT(ImIsPowerOfTwo((int)(flags & (ImGuiSeparatorFlags_Horizontal | ImGuiSeparatorFlags_Vertical)))); // Check that only 1 option is selected - if (flags & ImGuiSeparatorFlags_Vertical) - { - VerticalSeparator(); - return; - } - - // Horizontal Separator - if (window->DC.ColumnsSet) - PopClipRect(); - - float x1 = window->Pos.x; - float x2 = window->Pos.x + window->Size.x; - if (!window->DC.GroupStack.empty()) - x1 += window->DC.IndentX; - - const ImRect bb(ImVec2(x1, window->DC.CursorPos.y), ImVec2(x2, window->DC.CursorPos.y+1.0f)); - ItemSize(ImVec2(0.0f, 0.0f)); // NB: we don't provide our width so that it doesn't get feed back into AutoFit, we don't provide height to not alter layout. - if (!ItemAdd(bb, 0)) - { - if (window->DC.ColumnsSet) - PushColumnClipRect(); - return; - } - - window->DrawList->AddLine(bb.Min, ImVec2(bb.Max.x,bb.Min.y), GetColorU32(ImGuiCol_Separator)); - - if (g.LogEnabled) - LogRenderedText(NULL, IM_NEWLINE "--------------------------------"); - - if (window->DC.ColumnsSet) - { - PushColumnClipRect(); - window->DC.ColumnsSet->LineMinY = window->DC.CursorPos.y; - } -} - -void ImGui::VerticalSeparator() -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - ImGuiContext& g = *GImGui; - - float y1 = window->DC.CursorPos.y; - float y2 = window->DC.CursorPos.y + window->DC.CurrentLineHeight; - const ImRect bb(ImVec2(window->DC.CursorPos.x, y1), ImVec2(window->DC.CursorPos.x + 1.0f, y2)); - ItemSize(ImVec2(bb.GetWidth(), 0.0f)); - if (!ItemAdd(bb, 0)) - return; - - window->DrawList->AddLine(ImVec2(bb.Min.x, bb.Min.y), ImVec2(bb.Min.x, bb.Max.y), GetColorU32(ImGuiCol_Separator)); - if (g.LogEnabled) - LogText(" |"); -} - -bool ImGui::SplitterBehavior(ImGuiID id, const ImRect& bb, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - const ImGuiItemFlags item_flags_backup = window->DC.ItemFlags; - window->DC.ItemFlags |= ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus; - bool item_add = ItemAdd(bb, id); - window->DC.ItemFlags = item_flags_backup; - if (!item_add) - return false; - - bool hovered, held; - ImRect bb_interact = bb; - bb_interact.Expand(axis == ImGuiAxis_Y ? ImVec2(0.0f, hover_extend) : ImVec2(hover_extend, 0.0f)); - ButtonBehavior(bb_interact, id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap); - if (g.ActiveId != id) - SetItemAllowOverlap(); - - if (held || (g.HoveredId == id && g.HoveredIdPreviousFrame == id)) - SetMouseCursor(axis == ImGuiAxis_Y ? ImGuiMouseCursor_ResizeNS : ImGuiMouseCursor_ResizeEW); - - ImRect bb_render = bb; - if (held) - { - ImVec2 mouse_delta_2d = g.IO.MousePos - g.ActiveIdClickOffset - bb_interact.Min; - float mouse_delta = (axis == ImGuiAxis_Y) ? mouse_delta_2d.y : mouse_delta_2d.x; - - // Minimum pane size - if (mouse_delta < min_size1 - *size1) - mouse_delta = min_size1 - *size1; - if (mouse_delta > *size2 - min_size2) - mouse_delta = *size2 - min_size2; - - // Apply resize - *size1 += mouse_delta; - *size2 -= mouse_delta; - bb_render.Translate((axis == ImGuiAxis_X) ? ImVec2(mouse_delta, 0.0f) : ImVec2(0.0f, mouse_delta)); - - MarkItemValueChanged(id); - } - - // Render - const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator); - window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, col, g.Style.FrameRounding); - - return held; -} - -void ImGui::Spacing() -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - ItemSize(ImVec2(0,0)); -} - -void ImGui::Dummy(const ImVec2& size) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size); - ItemSize(bb); - ItemAdd(bb, 0); -} - -bool ImGui::IsRectVisible(const ImVec2& size) -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->ClipRect.Overlaps(ImRect(window->DC.CursorPos, window->DC.CursorPos + size)); -} - -bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max) -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->ClipRect.Overlaps(ImRect(rect_min, rect_max)); -} - -// Lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.) -void ImGui::BeginGroup() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - - window->DC.GroupStack.resize(window->DC.GroupStack.Size + 1); - ImGuiGroupData& group_data = window->DC.GroupStack.back(); - group_data.BackupCursorPos = window->DC.CursorPos; - group_data.BackupCursorMaxPos = window->DC.CursorMaxPos; - group_data.BackupIndentX = window->DC.IndentX; - group_data.BackupGroupOffsetX = window->DC.GroupOffsetX; - group_data.BackupCurrentLineHeight = window->DC.CurrentLineHeight; - group_data.BackupCurrentLineTextBaseOffset = window->DC.CurrentLineTextBaseOffset; - group_data.BackupLogLinePosY = window->DC.LogLinePosY; - group_data.BackupActiveIdIsAlive = g.ActiveIdIsAlive; - group_data.BackupActiveIdPreviousFrameIsAlive = g.ActiveIdPreviousFrameIsAlive; - group_data.AdvanceCursor = true; - - window->DC.GroupOffsetX = window->DC.CursorPos.x - window->Pos.x - window->DC.ColumnsOffsetX; - window->DC.IndentX = window->DC.GroupOffsetX; - window->DC.CursorMaxPos = window->DC.CursorPos; - window->DC.CurrentLineHeight = 0.0f; - window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; // To enforce Log carriage return -} - -void ImGui::EndGroup() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - IM_ASSERT(!window->DC.GroupStack.empty()); // Mismatched BeginGroup()/EndGroup() calls - - ImGuiGroupData& group_data = window->DC.GroupStack.back(); - - ImRect group_bb(group_data.BackupCursorPos, window->DC.CursorMaxPos); - group_bb.Max = ImMax(group_bb.Min, group_bb.Max); - - window->DC.CursorPos = group_data.BackupCursorPos; - window->DC.CursorMaxPos = ImMax(group_data.BackupCursorMaxPos, window->DC.CursorMaxPos); - window->DC.IndentX = group_data.BackupIndentX; - window->DC.GroupOffsetX = group_data.BackupGroupOffsetX; - window->DC.CurrentLineHeight = group_data.BackupCurrentLineHeight; - window->DC.CurrentLineTextBaseOffset = group_data.BackupCurrentLineTextBaseOffset; - window->DC.LogLinePosY = window->DC.CursorPos.y - 9999.0f; // To enforce Log carriage return - - if (group_data.AdvanceCursor) - { - window->DC.CurrentLineTextBaseOffset = ImMax(window->DC.PrevLineTextBaseOffset, group_data.BackupCurrentLineTextBaseOffset); // FIXME: Incorrect, we should grab the base offset from the *first line* of the group but it is hard to obtain now. - ItemSize(group_bb.GetSize(), group_data.BackupCurrentLineTextBaseOffset); - ItemAdd(group_bb, 0); - } - - // If the current ActiveId was declared within the boundary of our group, we copy it to LastItemId so IsItemActive(), IsItemDeactivated() etc. will be functional on the entire group. - // It would be be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but put a little more burden on individual widgets. - // (and if you grep for LastItemId you'll notice it is only used in that context. - if (!group_data.BackupActiveIdIsAlive && g.ActiveIdIsAlive && g.ActiveId) // && g.ActiveIdWindow->RootWindow == window->RootWindow) - window->DC.LastItemId = g.ActiveId; - else if (!group_data.BackupActiveIdPreviousFrameIsAlive && g.ActiveIdPreviousFrameIsAlive) // && g.ActiveIdPreviousFrameWindow->RootWindow == window->RootWindow) - window->DC.LastItemId = g.ActiveIdPreviousFrame; - window->DC.LastItemRect = group_bb; - - window->DC.GroupStack.pop_back(); - - //window->DrawList->AddRect(group_bb.Min, group_bb.Max, IM_COL32(255,0,255,255)); // [Debug] -} - -// Gets back to previous line and continue with horizontal layout -// pos_x == 0 : follow right after previous item -// pos_x != 0 : align to specified x position (relative to window/group left) -// spacing_w < 0 : use default spacing if pos_x == 0, no spacing if pos_x != 0 -// spacing_w >= 0 : enforce spacing amount -void ImGui::SameLine(float pos_x, float spacing_w) -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - if (pos_x != 0.0f) - { - if (spacing_w < 0.0f) spacing_w = 0.0f; - window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + pos_x + spacing_w + window->DC.GroupOffsetX + window->DC.ColumnsOffsetX; - window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y; - } - else - { - if (spacing_w < 0.0f) spacing_w = g.Style.ItemSpacing.x; - window->DC.CursorPos.x = window->DC.CursorPosPrevLine.x + spacing_w; - window->DC.CursorPos.y = window->DC.CursorPosPrevLine.y; - } - window->DC.CurrentLineHeight = window->DC.PrevLineHeight; - window->DC.CurrentLineTextBaseOffset = window->DC.PrevLineTextBaseOffset; -} - -void ImGui::NewLine() -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems) - return; - - ImGuiContext& g = *GImGui; - const ImGuiLayoutType backup_layout_type = window->DC.LayoutType; - window->DC.LayoutType = ImGuiLayoutType_Vertical; - if (window->DC.CurrentLineHeight > 0.0f) // In the event that we are on a line with items that is smaller that FontSize high, we will preserve its height. - ItemSize(ImVec2(0,0)); - else - ItemSize(ImVec2(0.0f, g.FontSize)); - window->DC.LayoutType = backup_layout_type; -} - -void ImGui::NextColumn() -{ - ImGuiWindow* window = GetCurrentWindow(); - if (window->SkipItems || window->DC.ColumnsSet == NULL) - return; - - ImGuiContext& g = *GImGui; - PopItemWidth(); - PopClipRect(); - - ImGuiColumnsSet* columns = window->DC.ColumnsSet; - columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y); - if (++columns->Current < columns->Count) - { - // Columns 1+ cancel out IndentX - window->DC.ColumnsOffsetX = GetColumnOffset(columns->Current) - window->DC.IndentX + g.Style.ItemSpacing.x; - window->DrawList->ChannelsSetCurrent(columns->Current); - } - else - { - window->DC.ColumnsOffsetX = 0.0f; - window->DrawList->ChannelsSetCurrent(0); - columns->Current = 0; - columns->LineMinY = columns->LineMaxY; - } - window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX); - window->DC.CursorPos.y = columns->LineMinY; - window->DC.CurrentLineHeight = 0.0f; - window->DC.CurrentLineTextBaseOffset = 0.0f; - - PushColumnClipRect(); - PushItemWidth(GetColumnWidth() * 0.65f); // FIXME: Move on columns setup -} - -int ImGui::GetColumnIndex() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->DC.ColumnsSet ? window->DC.ColumnsSet->Current : 0; -} - -int ImGui::GetColumnsCount() -{ - ImGuiWindow* window = GetCurrentWindowRead(); - return window->DC.ColumnsSet ? window->DC.ColumnsSet->Count : 1; -} - -static float OffsetNormToPixels(const ImGuiColumnsSet* columns, float offset_norm) -{ - return offset_norm * (columns->MaxX - columns->MinX); -} - -static float PixelsToOffsetNorm(const ImGuiColumnsSet* columns, float offset) -{ - return offset / (columns->MaxX - columns->MinX); -} - -static inline float GetColumnsRectHalfWidth() { return 4.0f; } - -static float GetDraggedColumnOffset(ImGuiColumnsSet* columns, int column_index) -{ - // Active (dragged) column always follow mouse. The reason we need this is that dragging a column to the right edge of an auto-resizing - // window creates a feedback loop because we store normalized positions. So while dragging we enforce absolute positioning. - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - IM_ASSERT(column_index > 0); // We are not supposed to drag column 0. - IM_ASSERT(g.ActiveId == columns->ID + ImGuiID(column_index)); - - float x = g.IO.MousePos.x - g.ActiveIdClickOffset.x + GetColumnsRectHalfWidth() - window->Pos.x; - x = ImMax(x, ImGui::GetColumnOffset(column_index - 1) + g.Style.ColumnsMinSpacing); - if ((columns->Flags & ImGuiColumnsFlags_NoPreserveWidths)) - x = ImMin(x, ImGui::GetColumnOffset(column_index + 1) - g.Style.ColumnsMinSpacing); - - return x; -} - -float ImGui::GetColumnOffset(int column_index) -{ - ImGuiWindow* window = GetCurrentWindowRead(); - ImGuiColumnsSet* columns = window->DC.ColumnsSet; - IM_ASSERT(columns != NULL); - - if (column_index < 0) - column_index = columns->Current; - IM_ASSERT(column_index < columns->Columns.Size); - - const float t = columns->Columns[column_index].OffsetNorm; - const float x_offset = ImLerp(columns->MinX, columns->MaxX, t); - return x_offset; -} - -static float GetColumnWidthEx(ImGuiColumnsSet* columns, int column_index, bool before_resize = false) -{ - if (column_index < 0) - column_index = columns->Current; - - float offset_norm; - if (before_resize) - offset_norm = columns->Columns[column_index + 1].OffsetNormBeforeResize - columns->Columns[column_index].OffsetNormBeforeResize; - else - offset_norm = columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm; - return OffsetNormToPixels(columns, offset_norm); -} - -float ImGui::GetColumnWidth(int column_index) -{ - ImGuiWindow* window = GetCurrentWindowRead(); - ImGuiColumnsSet* columns = window->DC.ColumnsSet; - IM_ASSERT(columns != NULL); - - if (column_index < 0) - column_index = columns->Current; - return OffsetNormToPixels(columns, columns->Columns[column_index + 1].OffsetNorm - columns->Columns[column_index].OffsetNorm); -} - -void ImGui::SetColumnOffset(int column_index, float offset) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - ImGuiColumnsSet* columns = window->DC.ColumnsSet; - IM_ASSERT(columns != NULL); - - if (column_index < 0) - column_index = columns->Current; - IM_ASSERT(column_index < columns->Columns.Size); - - const bool preserve_width = !(columns->Flags & ImGuiColumnsFlags_NoPreserveWidths) && (column_index < columns->Count-1); - const float width = preserve_width ? GetColumnWidthEx(columns, column_index, columns->IsBeingResized) : 0.0f; - - if (!(columns->Flags & ImGuiColumnsFlags_NoForceWithinWindow)) - offset = ImMin(offset, columns->MaxX - g.Style.ColumnsMinSpacing * (columns->Count - column_index)); - columns->Columns[column_index].OffsetNorm = PixelsToOffsetNorm(columns, offset - columns->MinX); - - if (preserve_width) - SetColumnOffset(column_index + 1, offset + ImMax(g.Style.ColumnsMinSpacing, width)); -} - -void ImGui::SetColumnWidth(int column_index, float width) -{ - ImGuiWindow* window = GetCurrentWindowRead(); - ImGuiColumnsSet* columns = window->DC.ColumnsSet; - IM_ASSERT(columns != NULL); - - if (column_index < 0) - column_index = columns->Current; - SetColumnOffset(column_index + 1, GetColumnOffset(column_index) + width); -} - -void ImGui::PushColumnClipRect(int column_index) -{ - ImGuiWindow* window = GetCurrentWindowRead(); - ImGuiColumnsSet* columns = window->DC.ColumnsSet; - if (column_index < 0) - column_index = columns->Current; - - PushClipRect(columns->Columns[column_index].ClipRect.Min, columns->Columns[column_index].ClipRect.Max, false); -} - -static ImGuiColumnsSet* FindOrAddColumnsSet(ImGuiWindow* window, ImGuiID id) -{ - for (int n = 0; n < window->ColumnsStorage.Size; n++) - if (window->ColumnsStorage[n].ID == id) - return &window->ColumnsStorage[n]; - - window->ColumnsStorage.push_back(ImGuiColumnsSet()); - ImGuiColumnsSet* columns = &window->ColumnsStorage.back(); - columns->ID = id; - return columns; -} - -void ImGui::BeginColumns(const char* str_id, int columns_count, ImGuiColumnsFlags flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - - IM_ASSERT(columns_count > 1); - IM_ASSERT(window->DC.ColumnsSet == NULL); // Nested columns are currently not supported - - // Differentiate column ID with an arbitrary prefix for cases where users name their columns set the same as another widget. - // In addition, when an identifier isn't explicitly provided we include the number of columns in the hash to make it uniquer. - PushID(0x11223347 + (str_id ? 0 : columns_count)); - ImGuiID id = window->GetID(str_id ? str_id : "columns"); - PopID(); - - // Acquire storage for the columns set - ImGuiColumnsSet* columns = FindOrAddColumnsSet(window, id); - IM_ASSERT(columns->ID == id); - columns->Current = 0; - columns->Count = columns_count; - columns->Flags = flags; - window->DC.ColumnsSet = columns; - - // Set state for first column - const float content_region_width = (window->SizeContentsExplicit.x != 0.0f) ? (window->SizeContentsExplicit.x) : (window->InnerClipRect.Max.x - window->Pos.x); - columns->MinX = window->DC.IndentX - g.Style.ItemSpacing.x; // Lock our horizontal range - columns->MaxX = ImMax(content_region_width - window->Scroll.x, columns->MinX + 1.0f); - columns->StartPosY = window->DC.CursorPos.y; - columns->StartMaxPosX = window->DC.CursorMaxPos.x; - columns->LineMinY = columns->LineMaxY = window->DC.CursorPos.y; - window->DC.ColumnsOffsetX = 0.0f; - window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX); - - // Clear data if columns count changed - if (columns->Columns.Size != 0 && columns->Columns.Size != columns_count + 1) - columns->Columns.resize(0); - - // Initialize defaults - columns->IsFirstFrame = (columns->Columns.Size == 0); - if (columns->Columns.Size == 0) - { - columns->Columns.reserve(columns_count + 1); - for (int n = 0; n < columns_count + 1; n++) - { - ImGuiColumnData column; - column.OffsetNorm = n / (float)columns_count; - columns->Columns.push_back(column); - } - } - - for (int n = 0; n < columns_count; n++) - { - // Compute clipping rectangle - ImGuiColumnData* column = &columns->Columns[n]; - float clip_x1 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(n) - 1.0f); - float clip_x2 = ImFloor(0.5f + window->Pos.x + GetColumnOffset(n + 1) - 1.0f); - column->ClipRect = ImRect(clip_x1, -FLT_MAX, clip_x2, +FLT_MAX); - column->ClipRect.ClipWith(window->ClipRect); - } - - window->DrawList->ChannelsSplit(columns->Count); - PushColumnClipRect(); - PushItemWidth(GetColumnWidth() * 0.65f); -} - -void ImGui::EndColumns() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - ImGuiColumnsSet* columns = window->DC.ColumnsSet; - IM_ASSERT(columns != NULL); - - PopItemWidth(); - PopClipRect(); - window->DrawList->ChannelsMerge(); - - columns->LineMaxY = ImMax(columns->LineMaxY, window->DC.CursorPos.y); - window->DC.CursorPos.y = columns->LineMaxY; - if (!(columns->Flags & ImGuiColumnsFlags_GrowParentContentsSize)) - window->DC.CursorMaxPos.x = columns->StartMaxPosX; // Restore cursor max pos, as columns don't grow parent - - // Draw columns borders and handle resize - bool is_being_resized = false; - if (!(columns->Flags & ImGuiColumnsFlags_NoBorder) && !window->SkipItems) - { - const float y1 = columns->StartPosY; - const float y2 = window->DC.CursorPos.y; - int dragging_column = -1; - for (int n = 1; n < columns->Count; n++) - { - float x = window->Pos.x + GetColumnOffset(n); - const ImGuiID column_id = columns->ID + ImGuiID(n); - const float column_hw = GetColumnsRectHalfWidth(); // Half-width for interaction - const ImRect column_rect(ImVec2(x - column_hw, y1), ImVec2(x + column_hw, y2)); - KeepAliveID(column_id); - if (IsClippedEx(column_rect, column_id, false)) - continue; - - bool hovered = false, held = false; - if (!(columns->Flags & ImGuiColumnsFlags_NoResize)) - { - ButtonBehavior(column_rect, column_id, &hovered, &held); - if (hovered || held) - g.MouseCursor = ImGuiMouseCursor_ResizeEW; - if (held && !(columns->Columns[n].Flags & ImGuiColumnsFlags_NoResize)) - dragging_column = n; - } - - // Draw column (we clip the Y boundaries CPU side because very long triangles are mishandled by some GPU drivers.) - const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : hovered ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator); - const float xi = (float)(int)x; - window->DrawList->AddLine(ImVec2(xi, ImMax(y1 + 1.0f, window->ClipRect.Min.y)), ImVec2(xi, ImMin(y2, window->ClipRect.Max.y)), col); - } - - // Apply dragging after drawing the column lines, so our rendered lines are in sync with how items were displayed during the frame. - if (dragging_column != -1) - { - if (!columns->IsBeingResized) - for (int n = 0; n < columns->Count + 1; n++) - columns->Columns[n].OffsetNormBeforeResize = columns->Columns[n].OffsetNorm; - columns->IsBeingResized = is_being_resized = true; - float x = GetDraggedColumnOffset(columns, dragging_column); - SetColumnOffset(dragging_column, x); - } - } - columns->IsBeingResized = is_being_resized; - - window->DC.ColumnsSet = NULL; - window->DC.ColumnsOffsetX = 0.0f; - window->DC.CursorPos.x = (float)(int)(window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX); -} - -// [2018-03: This is currently the only public API, while we are working on making BeginColumns/EndColumns user-facing] -void ImGui::Columns(int columns_count, const char* id, bool border) -{ - ImGuiWindow* window = GetCurrentWindow(); - IM_ASSERT(columns_count >= 1); - - ImGuiColumnsFlags flags = (border ? 0 : ImGuiColumnsFlags_NoBorder); - //flags |= ImGuiColumnsFlags_NoPreserveWidths; // NB: Legacy behavior - if (window->DC.ColumnsSet != NULL && window->DC.ColumnsSet->Count == columns_count && window->DC.ColumnsSet->Flags == flags) - return; - - if (window->DC.ColumnsSet != NULL) - EndColumns(); - - if (columns_count != 1) - BeginColumns(id, columns_count, flags); -} - -void ImGui::Indent(float indent_w) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - window->DC.IndentX += (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing; - window->DC.CursorPos.x = window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX; -} - -void ImGui::Unindent(float indent_w) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = GetCurrentWindow(); - window->DC.IndentX -= (indent_w != 0.0f) ? indent_w : g.Style.IndentSpacing; - window->DC.CursorPos.x = window->Pos.x + window->DC.IndentX + window->DC.ColumnsOffsetX; -} - -void ImGui::TreePush(const char* str_id) -{ - ImGuiWindow* window = GetCurrentWindow(); - Indent(); - window->DC.TreeDepth++; - PushID(str_id ? str_id : "#TreePush"); -} - -void ImGui::TreePush(const void* ptr_id) -{ - ImGuiWindow* window = GetCurrentWindow(); - Indent(); - window->DC.TreeDepth++; - PushID(ptr_id ? ptr_id : (const void*)"#TreePush"); -} - -void ImGui::TreePushRawID(ImGuiID id) -{ - ImGuiWindow* window = GetCurrentWindow(); - Indent(); - window->DC.TreeDepth++; - window->IDStack.push_back(id); -} - -void ImGui::TreePop() -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - Unindent(); - - window->DC.TreeDepth--; - if (g.NavMoveDir == ImGuiDir_Left && g.NavWindow == window && NavMoveRequestButNoResultYet()) - if (g.NavIdIsAlive && (window->DC.TreeDepthMayJumpToParentOnPop & (1 << window->DC.TreeDepth))) - { - SetNavID(window->IDStack.back(), g.NavLayer); - NavMoveRequestCancel(); - } - window->DC.TreeDepthMayJumpToParentOnPop &= (1 << window->DC.TreeDepth) - 1; - - IM_ASSERT(window->IDStack.Size > 1); // There should always be 1 element in the IDStack (pushed during window creation). If this triggers you called TreePop/PopID too much. - PopID(); -} - -void ImGui::Value(const char* prefix, bool b) -{ - Text("%s: %s", prefix, (b ? "true" : "false")); -} - -void ImGui::Value(const char* prefix, int v) -{ - Text("%s: %d", prefix, v); -} - -void ImGui::Value(const char* prefix, unsigned int v) -{ - Text("%s: %d", prefix, v); -} - -void ImGui::Value(const char* prefix, float v, const char* float_format) -{ - if (float_format) - { - char fmt[64]; - ImFormatString(fmt, IM_ARRAYSIZE(fmt), "%%s: %s", float_format); - Text(fmt, prefix, v); - } - else - { - Text("%s: %.3f", prefix, v); - } -} - -//----------------------------------------------------------------------------- -// DRAG AND DROP -//----------------------------------------------------------------------------- - -void ImGui::ClearDragDrop() -{ - ImGuiContext& g = *GImGui; - g.DragDropActive = false; - g.DragDropPayload.Clear(); - g.DragDropAcceptFlags = 0; - g.DragDropAcceptIdCurr = g.DragDropAcceptIdPrev = 0; - g.DragDropAcceptIdCurrRectSurface = FLT_MAX; - g.DragDropAcceptFrameCount = -1; -} - -// Call when current ID is active. -// When this returns true you need to: a) call SetDragDropPayload() exactly once, b) you may render the payload visual/description, c) call EndDragDropSource() -bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - - bool source_drag_active = false; - ImGuiID source_id = 0; - ImGuiID source_parent_id = 0; - int mouse_button = 0; - if (!(flags & ImGuiDragDropFlags_SourceExtern)) - { - source_id = window->DC.LastItemId; - if (source_id != 0 && g.ActiveId != source_id) // Early out for most common case - return false; - if (g.IO.MouseDown[mouse_button] == false) - return false; - - if (source_id == 0) - { - // If you want to use BeginDragDropSource() on an item with no unique identifier for interaction, such as Text() or Image(), you need to: - // A) Read the explanation below, B) Use the ImGuiDragDropFlags_SourceAllowNullID flag, C) Swallow your programmer pride. - if (!(flags & ImGuiDragDropFlags_SourceAllowNullID)) - { - IM_ASSERT(0); - return false; - } - - // Magic fallback (=somehow reprehensible) to handle items with no assigned ID, e.g. Text(), Image() - // We build a throwaway ID based on current ID stack + relative AABB of items in window. - // THE IDENTIFIER WON'T SURVIVE ANY REPOSITIONING OF THE WIDGET, so if your widget moves your dragging operation will be canceled. - // We don't need to maintain/call ClearActiveID() as releasing the button will early out this function and trigger !ActiveIdIsAlive. - bool is_hovered = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect) != 0; - if (!is_hovered && (g.ActiveId == 0 || g.ActiveIdWindow != window)) - return false; - source_id = window->DC.LastItemId = window->GetIDFromRectangle(window->DC.LastItemRect); - if (is_hovered) - SetHoveredID(source_id); - if (is_hovered && g.IO.MouseClicked[mouse_button]) - { - SetActiveID(source_id, window); - FocusWindow(window); - } - if (g.ActiveId == source_id) // Allow the underlying widget to display/return hovered during the mouse release frame, else we would get a flicker. - g.ActiveIdAllowOverlap = is_hovered; - } - else - { - g.ActiveIdAllowOverlap = false; - } - if (g.ActiveId != source_id) - return false; - source_parent_id = window->IDStack.back(); - source_drag_active = IsMouseDragging(mouse_button); - } - else - { - window = NULL; - source_id = ImHash("#SourceExtern", 0); - source_drag_active = true; - } - - if (source_drag_active) - { - if (!g.DragDropActive) - { - IM_ASSERT(source_id != 0); - ClearDragDrop(); - ImGuiPayload& payload = g.DragDropPayload; - payload.SourceId = source_id; - payload.SourceParentId = source_parent_id; - g.DragDropActive = true; - g.DragDropSourceFlags = flags; - g.DragDropMouseButton = mouse_button; - } - - if (!(flags & ImGuiDragDropFlags_SourceNoPreviewTooltip)) - { - // Target can request the Source to not display its tooltip (we use a dedicated flag to make this request explicit) - // We unfortunately can't just modify the source flags and skip the call to BeginTooltip, as caller may be emitting contents. - BeginDragDropTooltip(); - if (g.DragDropActive && g.DragDropAcceptIdPrev && (g.DragDropAcceptFlags & ImGuiDragDropFlags_AcceptNoPreviewTooltip)) - { - ImGuiWindow* tooltip_window = g.CurrentWindow; - tooltip_window->SkipItems = true; - tooltip_window->HiddenFrames = 1; - } - } - - if (!(flags & ImGuiDragDropFlags_SourceNoDisableHover) && !(flags & ImGuiDragDropFlags_SourceExtern)) - window->DC.LastItemStatusFlags &= ~ImGuiItemStatusFlags_HoveredRect; - - return true; - } - return false; -} - -void ImGui::EndDragDropSource() -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(g.DragDropActive); - - if (!(g.DragDropSourceFlags & ImGuiDragDropFlags_SourceNoPreviewTooltip)) - EndDragDropTooltip(); - - // Discard the drag if have not called SetDragDropPayload() - if (g.DragDropPayload.DataFrameCount == -1) - ClearDragDrop(); -} - -void ImGui::BeginDragDropTooltip() -{ - // The default tooltip position is a little offset to give space to see the context menu (it's also clamped within the current viewport/monitor) - // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor. - // Whatever we do we want to call SetNextWindowPos() to enforce a tooltip position and disable clipping the tooltip without our display area, like regular tooltip do. - ImGuiContext& g = *GImGui; - //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding; - ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale); - SetNextWindowPos(tooltip_pos); - SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f); - //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :( - BeginTooltipEx(0, true); -} - -void ImGui::EndDragDropTooltip() -{ - EndTooltip(); -} - -// Use 'cond' to choose to submit payload on drag start or every frame -bool ImGui::SetDragDropPayload(const char* type, const void* data, size_t data_size, ImGuiCond cond) -{ - ImGuiContext& g = *GImGui; - ImGuiPayload& payload = g.DragDropPayload; - if (cond == 0) - cond = ImGuiCond_Always; - - IM_ASSERT(type != NULL); - IM_ASSERT(strlen(type) < IM_ARRAYSIZE(payload.DataType) && "Payload type can be at most 32 characters long"); - IM_ASSERT((data != NULL && data_size > 0) || (data == NULL && data_size == 0)); - IM_ASSERT(cond == ImGuiCond_Always || cond == ImGuiCond_Once); - IM_ASSERT(payload.SourceId != 0); // Not called between BeginDragDropSource() and EndDragDropSource() - - if (cond == ImGuiCond_Always || payload.DataFrameCount == -1) - { - // Copy payload - ImStrncpy(payload.DataType, type, IM_ARRAYSIZE(payload.DataType)); - g.DragDropPayloadBufHeap.resize(0); - if (data_size > sizeof(g.DragDropPayloadBufLocal)) - { - // Store in heap - g.DragDropPayloadBufHeap.resize((int)data_size); - payload.Data = g.DragDropPayloadBufHeap.Data; - memcpy(payload.Data, data, data_size); - } - else if (data_size > 0) - { - // Store locally - memset(&g.DragDropPayloadBufLocal, 0, sizeof(g.DragDropPayloadBufLocal)); - payload.Data = g.DragDropPayloadBufLocal; - memcpy(payload.Data, data, data_size); - } - else - { - payload.Data = NULL; - } - payload.DataSize = (int)data_size; - } - payload.DataFrameCount = g.FrameCount; - - return (g.DragDropAcceptFrameCount == g.FrameCount) || (g.DragDropAcceptFrameCount == g.FrameCount - 1); -} - -bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id) -{ - ImGuiContext& g = *GImGui; - if (!g.DragDropActive) - return false; - - ImGuiWindow* window = g.CurrentWindow; - if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow) - return false; - IM_ASSERT(id != 0); - if (!IsMouseHoveringRect(bb.Min, bb.Max) || (id == g.DragDropPayload.SourceId)) - return false; - - g.DragDropTargetRect = bb; - g.DragDropTargetId = id; - return true; -} - -// We don't use BeginDragDropTargetCustom() and duplicate its code because: -// 1) we use LastItemRectHoveredRect which handles items that pushes a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them. -// 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can. -// Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case) -bool ImGui::BeginDragDropTarget() -{ - ImGuiContext& g = *GImGui; - if (!g.DragDropActive) - return false; - - ImGuiWindow* window = g.CurrentWindow; - if (!(window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HoveredRect)) - return false; - if (g.HoveredWindow == NULL || window->RootWindow != g.HoveredWindow->RootWindow) - return false; - - const ImRect& display_rect = (window->DC.LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) ? window->DC.LastItemDisplayRect : window->DC.LastItemRect; - ImGuiID id = window->DC.LastItemId; - if (id == 0) - id = window->GetIDFromRectangle(display_rect); - if (g.DragDropPayload.SourceId == id) - return false; - - g.DragDropTargetRect = display_rect; - g.DragDropTargetId = id; - return true; -} - -bool ImGui::IsDragDropPayloadBeingAccepted() -{ - ImGuiContext& g = *GImGui; - return g.DragDropActive && g.DragDropAcceptIdPrev != 0; -} - -const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags) -{ - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - ImGuiPayload& payload = g.DragDropPayload; - IM_ASSERT(g.DragDropActive); // Not called between BeginDragDropTarget() and EndDragDropTarget() ? - IM_ASSERT(payload.DataFrameCount != -1); // Forgot to call EndDragDropTarget() ? - if (type != NULL && !payload.IsDataType(type)) - return NULL; - - // Accept smallest drag target bounding box, this allows us to nest drag targets conveniently without ordering constraints. - // NB: We currently accept NULL id as target. However, overlapping targets requires a unique ID to function! - const bool was_accepted_previously = (g.DragDropAcceptIdPrev == g.DragDropTargetId); - ImRect r = g.DragDropTargetRect; - float r_surface = r.GetWidth() * r.GetHeight(); - if (r_surface < g.DragDropAcceptIdCurrRectSurface) - { - g.DragDropAcceptFlags = flags; - g.DragDropAcceptIdCurr = g.DragDropTargetId; - g.DragDropAcceptIdCurrRectSurface = r_surface; - } - - // Render default drop visuals - payload.Preview = was_accepted_previously; - flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame) - if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview) - { - // FIXME-DRAG: Settle on a proper default visuals for drop target. - r.Expand(3.5f); - bool push_clip_rect = !window->ClipRect.Contains(r); - if (push_clip_rect) window->DrawList->PushClipRectFullScreen(); - window->DrawList->AddRect(r.Min, r.Max, GetColorU32(ImGuiCol_DragDropTarget), 0.0f, ~0, 2.0f); - if (push_clip_rect) window->DrawList->PopClipRect(); - } - - g.DragDropAcceptFrameCount = g.FrameCount; - payload.Delivery = was_accepted_previously && !IsMouseDown(g.DragDropMouseButton); // For extern drag sources affecting os window focus, it's easier to just test !IsMouseDown() instead of IsMouseReleased() - if (!payload.Delivery && !(flags & ImGuiDragDropFlags_AcceptBeforeDelivery)) - return NULL; - - return &payload; -} - -// We don't really use/need this now, but added it for the sake of consistency and because we might need it later. -void ImGui::EndDragDropTarget() -{ - ImGuiContext& g = *GImGui; (void)g; - IM_ASSERT(g.DragDropActive); -} - -//----------------------------------------------------------------------------- -// PLATFORM DEPENDENT HELPERS -//----------------------------------------------------------------------------- - -#if defined(_WIN32) && !defined(_WINDOWS_) && (!defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) || !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS)) -#undef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#ifndef __MINGW32__ -#include -#else -#include -#endif -#endif - -// Win32 API clipboard implementation -#if defined(_WIN32) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_CLIPBOARD_FUNCTIONS) - -#ifdef _MSC_VER -#pragma comment(lib, "user32") -#endif - -static const char* GetClipboardTextFn_DefaultImpl(void*) -{ - static ImVector buf_local; - buf_local.clear(); - if (!::OpenClipboard(NULL)) - return NULL; - HANDLE wbuf_handle = ::GetClipboardData(CF_UNICODETEXT); - if (wbuf_handle == NULL) - { - ::CloseClipboard(); - return NULL; - } - if (ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle)) - { - int buf_len = ImTextCountUtf8BytesFromStr(wbuf_global, NULL) + 1; - buf_local.resize(buf_len); - ImTextStrToUtf8(buf_local.Data, buf_len, wbuf_global, NULL); - } - ::GlobalUnlock(wbuf_handle); - ::CloseClipboard(); - return buf_local.Data; -} - -static void SetClipboardTextFn_DefaultImpl(void*, const char* text) -{ - if (!::OpenClipboard(NULL)) - return; - const int wbuf_length = ImTextCountCharsFromUtf8(text, NULL) + 1; - HGLOBAL wbuf_handle = ::GlobalAlloc(GMEM_MOVEABLE, (SIZE_T)wbuf_length * sizeof(ImWchar)); - if (wbuf_handle == NULL) - { - ::CloseClipboard(); - return; - } - ImWchar* wbuf_global = (ImWchar*)::GlobalLock(wbuf_handle); - ImTextStrFromUtf8(wbuf_global, wbuf_length, text, NULL); - ::GlobalUnlock(wbuf_handle); - ::EmptyClipboard(); - if (::SetClipboardData(CF_UNICODETEXT, wbuf_handle) == NULL) - ::GlobalFree(wbuf_handle); - ::CloseClipboard(); -} - -#else - -// Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers -static const char* GetClipboardTextFn_DefaultImpl(void*) -{ - ImGuiContext& g = *GImGui; - return g.PrivateClipboard.empty() ? NULL : g.PrivateClipboard.begin(); -} - -// Local ImGui-only clipboard implementation, if user hasn't defined better clipboard handlers -static void SetClipboardTextFn_DefaultImpl(void*, const char* text) -{ - ImGuiContext& g = *GImGui; - g.PrivateClipboard.clear(); - const char* text_end = text + strlen(text); - g.PrivateClipboard.resize((int)(text_end - text) + 1); - memcpy(&g.PrivateClipboard[0], text, (size_t)(text_end - text)); - g.PrivateClipboard[(int)(text_end - text)] = 0; -} - -#endif - -// Win32 API IME support (for Asian languages, etc.) -#if defined(_WIN32) && !defined(__GNUC__) && !defined(IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS) - -#include -#ifdef _MSC_VER -#pragma comment(lib, "imm32") -#endif - -static void ImeSetInputScreenPosFn_DefaultImpl(int x, int y) -{ - // Notify OS Input Method Editor of text input position - if (HWND hwnd = (HWND)GImGui->IO.ImeWindowHandle) - if (HIMC himc = ::ImmGetContext(hwnd)) - { - COMPOSITIONFORM cf; - cf.ptCurrentPos.x = x; - cf.ptCurrentPos.y = y; - cf.dwStyle = CFS_FORCE_POSITION; - ::ImmSetCompositionWindow(himc, &cf); - } -} - -#else - -static void ImeSetInputScreenPosFn_DefaultImpl(int, int) {} - -#endif - -//----------------------------------------------------------------------------- -// HELP, METRICS -//----------------------------------------------------------------------------- - -void ImGui::ShowMetricsWindow(bool* p_open) -{ - if (ImGui::Begin("ImGui Metrics", p_open)) - { - static bool show_draw_cmd_clip_rects = true; - ImGui::Text("Dear ImGui %s", ImGui::GetVersion()); - ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / ImGui::GetIO().Framerate, ImGui::GetIO().Framerate); - ImGui::Text("%d vertices, %d indices (%d triangles)", ImGui::GetIO().MetricsRenderVertices, ImGui::GetIO().MetricsRenderIndices, ImGui::GetIO().MetricsRenderIndices / 3); - ImGui::Text("%d allocations", (int)GImAllocatorActiveAllocationsCount); - ImGui::Checkbox("Show clipping rectangles when hovering draw commands", &show_draw_cmd_clip_rects); - ImGui::Separator(); - - struct Funcs - { - static void NodeDrawList(ImGuiWindow* window, ImDrawList* draw_list, const char* label) - { - bool node_open = ImGui::TreeNode(draw_list, "%s: '%s' %d vtx, %d indices, %d cmds", label, draw_list->_OwnerName ? draw_list->_OwnerName : "", draw_list->VtxBuffer.Size, draw_list->IdxBuffer.Size, draw_list->CmdBuffer.Size); - if (draw_list == ImGui::GetWindowDrawList()) - { - ImGui::SameLine(); - ImGui::TextColored(ImColor(255,100,100), "CURRENTLY APPENDING"); // Can't display stats for active draw list! (we don't have the data double-buffered) - if (node_open) ImGui::TreePop(); - return; - } - - ImDrawList* overlay_draw_list = GetOverlayDrawList(); // Render additional visuals into the top-most draw list - if (window && IsItemHovered()) - overlay_draw_list->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 255, 0, 255)); - if (!node_open) - return; - - int elem_offset = 0; - for (const ImDrawCmd* pcmd = draw_list->CmdBuffer.begin(); pcmd < draw_list->CmdBuffer.end(); elem_offset += pcmd->ElemCount, pcmd++) - { - if (pcmd->UserCallback == NULL && pcmd->ElemCount == 0) - continue; - if (pcmd->UserCallback) - { - ImGui::BulletText("Callback %p, user_data %p", pcmd->UserCallback, pcmd->UserCallbackData); - continue; - } - ImDrawIdx* idx_buffer = (draw_list->IdxBuffer.Size > 0) ? draw_list->IdxBuffer.Data : NULL; - bool pcmd_node_open = ImGui::TreeNode((void*)(pcmd - draw_list->CmdBuffer.begin()), "Draw %4d %s vtx, tex 0x%p, clip_rect (%4.0f,%4.0f)-(%4.0f,%4.0f)", pcmd->ElemCount, draw_list->IdxBuffer.Size > 0 ? "indexed" : "non-indexed", pcmd->TextureId, pcmd->ClipRect.x, pcmd->ClipRect.y, pcmd->ClipRect.z, pcmd->ClipRect.w); - if (show_draw_cmd_clip_rects && ImGui::IsItemHovered()) - { - ImRect clip_rect = pcmd->ClipRect; - ImRect vtxs_rect; - for (int i = elem_offset; i < elem_offset + (int)pcmd->ElemCount; i++) - vtxs_rect.Add(draw_list->VtxBuffer[idx_buffer ? idx_buffer[i] : i].pos); - clip_rect.Floor(); overlay_draw_list->AddRect(clip_rect.Min, clip_rect.Max, IM_COL32(255,255,0,255)); - vtxs_rect.Floor(); overlay_draw_list->AddRect(vtxs_rect.Min, vtxs_rect.Max, IM_COL32(255,0,255,255)); - } - if (!pcmd_node_open) - continue; - - // Display individual triangles/vertices. Hover on to get the corresponding triangle highlighted. - ImGuiListClipper clipper(pcmd->ElemCount/3); // Manually coarse clip our print out of individual vertices to save CPU, only items that may be visible. - while (clipper.Step()) - for (int prim = clipper.DisplayStart, vtx_i = elem_offset + clipper.DisplayStart*3; prim < clipper.DisplayEnd; prim++) - { - char buf[300]; - char *buf_p = buf, *buf_end = buf + IM_ARRAYSIZE(buf); - ImVec2 triangles_pos[3]; - for (int n = 0; n < 3; n++, vtx_i++) - { - ImDrawVert& v = draw_list->VtxBuffer[idx_buffer ? idx_buffer[vtx_i] : vtx_i]; - triangles_pos[n] = v.pos; - buf_p += ImFormatString(buf_p, (int)(buf_end - buf_p), "%s %04d: pos (%8.2f,%8.2f), uv (%.6f,%.6f), col %08X\n", (n == 0) ? "vtx" : " ", vtx_i, v.pos.x, v.pos.y, v.uv.x, v.uv.y, v.col); - } - ImGui::Selectable(buf, false); - if (ImGui::IsItemHovered()) - { - ImDrawListFlags backup_flags = overlay_draw_list->Flags; - overlay_draw_list->Flags &= ~ImDrawListFlags_AntiAliasedLines; // Disable AA on triangle outlines at is more readable for very large and thin triangles. - overlay_draw_list->AddPolyline(triangles_pos, 3, IM_COL32(255,255,0,255), true, 1.0f); - overlay_draw_list->Flags = backup_flags; - } - } - ImGui::TreePop(); - } - ImGui::TreePop(); - } - - static void NodeWindows(ImVector& windows, const char* label) - { - if (!ImGui::TreeNode(label, "%s (%d)", label, windows.Size)) - return; - for (int i = 0; i < windows.Size; i++) - Funcs::NodeWindow(windows[i], "Window"); - ImGui::TreePop(); - } - - static void NodeWindow(ImGuiWindow* window, const char* label) - { - if (!ImGui::TreeNode(window, "%s '%s', %d @ 0x%p", label, window->Name, window->Active || window->WasActive, window)) - return; - ImGuiWindowFlags flags = window->Flags; - NodeDrawList(window, window->DrawList, "DrawList"); - ImGui::BulletText("Pos: (%.1f,%.1f), Size: (%.1f,%.1f), SizeContents (%.1f,%.1f)", window->Pos.x, window->Pos.y, window->Size.x, window->Size.y, window->SizeContents.x, window->SizeContents.y); - ImGui::BulletText("Flags: 0x%08X (%s%s%s%s%s%s..)", flags, - (flags & ImGuiWindowFlags_ChildWindow) ? "Child " : "", (flags & ImGuiWindowFlags_Tooltip) ? "Tooltip " : "", (flags & ImGuiWindowFlags_Popup) ? "Popup " : "", - (flags & ImGuiWindowFlags_Modal) ? "Modal " : "", (flags & ImGuiWindowFlags_ChildMenu) ? "ChildMenu " : "", (flags & ImGuiWindowFlags_NoSavedSettings) ? "NoSavedSettings " : ""); - ImGui::BulletText("Scroll: (%.2f/%.2f,%.2f/%.2f)", window->Scroll.x, GetScrollMaxX(window), window->Scroll.y, GetScrollMaxY(window)); - ImGui::BulletText("Active: %d, WriteAccessed: %d", window->Active, window->WriteAccessed); - ImGui::BulletText("NavLastIds: 0x%08X,0x%08X, NavLayerActiveMask: %X", window->NavLastIds[0], window->NavLastIds[1], window->DC.NavLayerActiveMask); - ImGui::BulletText("NavLastChildNavWindow: %s", window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL"); - if (!window->NavRectRel[0].IsInverted()) - ImGui::BulletText("NavRectRel[0]: (%.1f,%.1f)(%.1f,%.1f)", window->NavRectRel[0].Min.x, window->NavRectRel[0].Min.y, window->NavRectRel[0].Max.x, window->NavRectRel[0].Max.y); - else - ImGui::BulletText("NavRectRel[0]: "); - if (window->RootWindow != window) NodeWindow(window->RootWindow, "RootWindow"); - if (window->ParentWindow != NULL) NodeWindow(window->ParentWindow, "ParentWindow"); - if (window->DC.ChildWindows.Size > 0) NodeWindows(window->DC.ChildWindows, "ChildWindows"); - if (window->ColumnsStorage.Size > 0 && ImGui::TreeNode("Columns", "Columns sets (%d)", window->ColumnsStorage.Size)) - { - for (int n = 0; n < window->ColumnsStorage.Size; n++) - { - const ImGuiColumnsSet* columns = &window->ColumnsStorage[n]; - if (ImGui::TreeNode((void*)(uintptr_t)columns->ID, "Columns Id: 0x%08X, Count: %d, Flags: 0x%04X", columns->ID, columns->Count, columns->Flags)) - { - ImGui::BulletText("Width: %.1f (MinX: %.1f, MaxX: %.1f)", columns->MaxX - columns->MinX, columns->MinX, columns->MaxX); - for (int column_n = 0; column_n < columns->Columns.Size; column_n++) - ImGui::BulletText("Column %02d: OffsetNorm %.3f (= %.1f px)", column_n, columns->Columns[column_n].OffsetNorm, OffsetNormToPixels(columns, columns->Columns[column_n].OffsetNorm)); - ImGui::TreePop(); - } - } - ImGui::TreePop(); - } - ImGui::BulletText("Storage: %d bytes", window->StateStorage.Data.Size * (int)sizeof(ImGuiStorage::Pair)); - ImGui::TreePop(); - } - }; - - // Access private state, we are going to display the draw lists from last frame - ImGuiContext& g = *GImGui; - Funcs::NodeWindows(g.Windows, "Windows"); - if (ImGui::TreeNode("DrawList", "Active DrawLists (%d)", g.DrawDataBuilder.Layers[0].Size)) - { - for (int i = 0; i < g.DrawDataBuilder.Layers[0].Size; i++) - Funcs::NodeDrawList(NULL, g.DrawDataBuilder.Layers[0][i], "DrawList"); - ImGui::TreePop(); - } - if (ImGui::TreeNode("Popups", "Open Popups Stack (%d)", g.OpenPopupStack.Size)) - { - for (int i = 0; i < g.OpenPopupStack.Size; i++) - { - ImGuiWindow* window = g.OpenPopupStack[i].Window; - ImGui::BulletText("PopupID: %08x, Window: '%s'%s%s", g.OpenPopupStack[i].PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? " ChildWindow" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? " ChildMenu" : ""); - } - ImGui::TreePop(); - } - if (ImGui::TreeNode("Internal state")) - { - const char* input_source_names[] = { "None", "Mouse", "Nav", "NavKeyboard", "NavGamepad" }; IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT); - ImGui::Text("HoveredWindow: '%s'", g.HoveredWindow ? g.HoveredWindow->Name : "NULL"); - ImGui::Text("HoveredRootWindow: '%s'", g.HoveredRootWindow ? g.HoveredRootWindow->Name : "NULL"); - ImGui::Text("HoveredId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredId, g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Data is "in-flight" so depending on when the Metrics window is called we may see current frame information or not - ImGui::Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, input_source_names[g.ActiveIdSource]); - ImGui::Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); - ImGui::Text("MovingWindow: '%s'", g.MovingWindow ? g.MovingWindow->Name : "NULL"); - ImGui::Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL"); - ImGui::Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer); - ImGui::Text("NavInputSource: %s", input_source_names[g.NavInputSource]); - ImGui::Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible); - ImGui::Text("NavActivateId: 0x%08X, NavInputId: 0x%08X", g.NavActivateId, g.NavInputId); - ImGui::Text("NavDisableHighlight: %d, NavDisableMouseHover: %d", g.NavDisableHighlight, g.NavDisableMouseHover); - ImGui::Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize); - ImGui::TreePop(); - } - } - ImGui::End(); -} - -//----------------------------------------------------------------------------- - -// Include imgui_user.inl at the end of imgui.cpp to access private data/functions that aren't exposed. -// Prefer just including imgui_internal.h from your code rather than using this define. If a declaration is missing from imgui_internal.h add it or request it on the github. -#ifdef IMGUI_INCLUDE_IMGUI_USER_INL -#include "imgui_user.inl" -#endif - -//----------------------------------------------------------------------------- diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui.h b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui.h deleted file mode 100644 index 707a5054..00000000 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui.h +++ /dev/null @@ -1,1943 +0,0 @@ -// dear imgui, v1.62 -// (headers) - -// See imgui.cpp file for documentation. -// Call and read ImGui::ShowDemoWindow() in imgui_demo.cpp for demo code. -// Read 'Programmer guide' in imgui.cpp for notes on how to setup ImGui in your codebase. -// Get latest version at https://github.com/ocornut/imgui - -#pragma once - -// Configuration file (edit imconfig.h or define IMGUI_USER_CONFIG to set your own filename) -#ifdef IMGUI_USER_CONFIG -#include IMGUI_USER_CONFIG -#endif -#if !defined(IMGUI_DISABLE_INCLUDE_IMCONFIG_H) || defined(IMGUI_INCLUDE_IMCONFIG_H) -#include "imconfig.h" -#endif - -#include // FLT_MAX -#include // va_list -#include // ptrdiff_t, NULL -#include // memset, memmove, memcpy, strlen, strchr, strcpy, strcmp - -// Version -#define IMGUI_VERSION "1.62" -#define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert)) - -// Define attributes of all API symbols declarations (e.g. for DLL under Windows) -// IMGUI_API is used for core imgui functions, IMGUI_IMPL_API is used for the default bindings files (imgui_impl_xxx.h) -#ifndef IMGUI_API -#define IMGUI_API -#endif -#ifndef IMGUI_IMPL_API -#define IMGUI_IMPL_API IMGUI_API -#endif - -// Helpers -#ifndef IM_ASSERT -#include -#define IM_ASSERT(_EXPR) assert(_EXPR) // You can override the default assert handler by editing imconfig.h -#endif -#if defined(__clang__) || defined(__GNUC__) -#define IM_FMTARGS(FMT) __attribute__((format(printf, FMT, FMT+1))) // Apply printf-style warnings to user functions. -#define IM_FMTLIST(FMT) __attribute__((format(printf, FMT, 0))) -#else -#define IM_FMTARGS(FMT) -#define IM_FMTLIST(FMT) -#endif -#define IM_ARRAYSIZE(_ARR) ((int)(sizeof(_ARR)/sizeof(*_ARR))) // Size of a static C-style array. Don't use on pointers! -#define IM_OFFSETOF(_TYPE,_MEMBER) ((size_t)&(((_TYPE*)0)->_MEMBER)) // Offset of _MEMBER within _TYPE. Standardized as offsetof() in modern C++. - -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wold-style-cast" -#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" // [Bruno Levy] -#pragma clang diagnostic ignored "-Wc++11-long-long" // [Bruno Levy] -#endif - -#ifdef __GNUC__ -#pragma GCC diagnostic ignored "-Wunknown-pragmas" // [Bruno Levy] -#ifndef __clang__ -#pragma GCC diagnostic ignored "-Wclass-memaccess" // [Bruno Levy] -#endif -#endif - -// Forward declarations -struct ImDrawChannel; // Temporary storage for outputting drawing commands out of order, used by ImDrawList::ChannelsSplit() -struct ImDrawCmd; // A single draw command within a parent ImDrawList (generally maps to 1 GPU draw call) -struct ImDrawData; // All draw command lists required to render the frame -struct ImDrawList; // A single draw command list (generally one per window, conceptually you may see this as a dynamic "mesh" builder) -struct ImDrawListSharedData; // Data shared among multiple draw lists (typically owned by parent ImGui context, but you may create one yourself) -struct ImDrawVert; // A single vertex (20 bytes by default, override layout with IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT) -struct ImFont; // Runtime data for a single font within a parent ImFontAtlas -struct ImFontAtlas; // Runtime data for multiple fonts, bake multiple fonts into a single texture, TTF/OTF font loader -struct ImFontConfig; // Configuration data when adding a font or merging fonts -struct ImColor; // Helper functions to create a color that can be converted to either u32 or float4 (*obsolete* please avoid using) -struct ImGuiIO; // Main configuration and I/O between your application and ImGui -struct ImGuiOnceUponAFrame; // Simple helper for running a block of code not more than once a frame, used by IMGUI_ONCE_UPON_A_FRAME macro -struct ImGuiStorage; // Simple custom key value storage -struct ImGuiStyle; // Runtime data for styling/colors -struct ImGuiTextFilter; // Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]" -struct ImGuiTextBuffer; // Text buffer for logging/accumulating text -struct ImGuiTextEditCallbackData; // Shared state of ImGui::InputText() when using custom ImGuiTextEditCallback (rare/advanced use) -struct ImGuiSizeCallbackData; // Structure used to constraint window size in custom ways when using custom ImGuiSizeCallback (rare/advanced use) -struct ImGuiListClipper; // Helper to manually clip large list of items -struct ImGuiPayload; // User data payload for drag and drop operations -struct ImGuiContext; // ImGui context (opaque) -#ifndef ImTextureID -typedef void* ImTextureID; // User data to identify a texture (this is whatever to you want it to be! read the FAQ about ImTextureID in imgui.cpp) -#endif - -// Typedefs and Enumerations (declared as int for compatibility with old C++ and to not pollute the top of this file) -// Use your programming IDE "Go to definition" facility on the names of the right-most columns to find the actual flags/enum lists. -typedef unsigned int ImGuiID; // Unique ID used by widgets (typically hashed from a stack of string) -typedef unsigned short ImWchar; // Character for keyboard input/display -typedef int ImGuiCol; // enum: a color identifier for styling // enum ImGuiCol_ -typedef int ImGuiDataType; // enum: a primary data type // enum ImGuiDataType_ -typedef int ImGuiDir; // enum: a cardinal direction // enum ImGuiDir_ -typedef int ImGuiCond; // enum: a condition for Set*() // enum ImGuiCond_ -typedef int ImGuiKey; // enum: a key identifier (ImGui-side enum) // enum ImGuiKey_ -typedef int ImGuiNavInput; // enum: an input identifier for navigation // enum ImGuiNavInput_ -typedef int ImGuiMouseCursor; // enum: a mouse cursor identifier // enum ImGuiMouseCursor_ -typedef int ImGuiStyleVar; // enum: a variable identifier for styling // enum ImGuiStyleVar_ -typedef int ImDrawCornerFlags; // flags: for ImDrawList::AddRect*() etc. // enum ImDrawCornerFlags_ -typedef int ImDrawListFlags; // flags: for ImDrawList // enum ImDrawListFlags_ -typedef int ImFontAtlasFlags; // flags: for ImFontAtlas // enum ImFontAtlasFlags_ -typedef int ImGuiBackendFlags; // flags: for io.BackendFlags // enum ImGuiBackendFlags_ -typedef int ImGuiColorEditFlags; // flags: for ColorEdit*(), ColorPicker*() // enum ImGuiColorEditFlags_ -typedef int ImGuiColumnsFlags; // flags: for *Columns*() // enum ImGuiColumnsFlags_ -typedef int ImGuiConfigFlags; // flags: for io.ConfigFlags // enum ImGuiConfigFlags_ -typedef int ImGuiComboFlags; // flags: for BeginCombo() // enum ImGuiComboFlags_ -typedef int ImGuiDragDropFlags; // flags: for *DragDrop*() // enum ImGuiDragDropFlags_ -typedef int ImGuiFocusedFlags; // flags: for IsWindowFocused() // enum ImGuiFocusedFlags_ -typedef int ImGuiHoveredFlags; // flags: for IsItemHovered() etc. // enum ImGuiHoveredFlags_ -typedef int ImGuiInputTextFlags; // flags: for InputText*() // enum ImGuiInputTextFlags_ -typedef int ImGuiSelectableFlags; // flags: for Selectable() // enum ImGuiSelectableFlags_ -typedef int ImGuiTreeNodeFlags; // flags: for TreeNode*(),CollapsingHeader()// enum ImGuiTreeNodeFlags_ -typedef int ImGuiWindowFlags; // flags: for Begin*() // enum ImGuiWindowFlags_ -typedef int (*ImGuiTextEditCallback)(ImGuiTextEditCallbackData *data); -typedef void (*ImGuiSizeCallback)(ImGuiSizeCallbackData* data); - -// Scalar data types -typedef signed int ImS32; // 32-bit signed integer == int -typedef unsigned int ImU32; // 32-bit unsigned integer (often used to store packed colors) -#if defined(_MSC_VER) && !defined(__clang__) -typedef signed __int64 ImS64; // 64-bit signed integer -typedef unsigned __int64 ImU64; // 64-bit unsigned integer -#else -typedef signed long long ImS64; // 64-bit signed integer -typedef unsigned long long ImU64; // 64-bit unsigned integer -#endif - -// 2D vector (often used to store positions, sizes, etc.) -struct ImVec2 -{ - float x, y; - ImVec2() { x = y = 0.0f; } - ImVec2(float _x, float _y) { x = _x; y = _y; } - float operator[] (size_t i) const { IM_ASSERT(i <= 1); return (&x)[i]; } // We very rarely use this [] operator, the assert overhead is fine. -#ifdef IM_VEC2_CLASS_EXTRA - IM_VEC2_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImVec2. -#endif -}; - -// 4D vector (often used to store floating-point colors) -struct ImVec4 -{ - float x, y, z, w; - ImVec4() { x = y = z = w = 0.0f; } - ImVec4(float _x, float _y, float _z, float _w) { x = _x; y = _y; z = _z; w = _w; } -#ifdef IM_VEC4_CLASS_EXTRA - IM_VEC4_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImVec4. -#endif -}; - -// Dear ImGui end-user API -// (In a namespace so you can add extra functions in your own separate file. Please don't modify imgui.cpp/.h!) -namespace ImGui -{ - // Context creation and access - // Each context create its own ImFontAtlas by default. You may instance one yourself and pass it to CreateContext() to share a font atlas between imgui contexts. - // All those functions are not reliant on the current context. - IMGUI_API ImGuiContext* CreateContext(ImFontAtlas* shared_font_atlas = NULL); - IMGUI_API void DestroyContext(ImGuiContext* ctx = NULL); // NULL = destroy current context - IMGUI_API ImGuiContext* GetCurrentContext(); - IMGUI_API void SetCurrentContext(ImGuiContext* ctx); - IMGUI_API bool DebugCheckVersionAndDataLayout(const char* version_str, size_t sz_io, size_t sz_style, size_t sz_vec2, size_t sz_vec4, size_t sz_drawvert); - - // Main - IMGUI_API ImGuiIO& GetIO(); // access the IO structure (mouse/keyboard/gamepad inputs, time, various configuration options/flags) - IMGUI_API ImGuiStyle& GetStyle(); // access the Style structure (colors, sizes). Always use PushStyleCol(), PushStyleVar() to modify style mid-frame. - IMGUI_API void NewFrame(); // start a new ImGui frame, you can submit any command from this point until Render()/EndFrame(). - IMGUI_API void EndFrame(); // ends the ImGui frame. automatically called by Render(), you likely don't need to call that yourself directly. If you don't need to render data (skipping rendering) you may call EndFrame() but you'll have wasted CPU already! If you don't need to render, better to not create any imgui windows and not call NewFrame() at all! - IMGUI_API void Render(); // ends the ImGui frame, finalize the draw data. (Obsolete: optionally call io.RenderDrawListsFn if set. Nowadays, prefer calling your render function yourself.) - IMGUI_API ImDrawData* GetDrawData(); // valid after Render() and until the next call to NewFrame(). this is what you have to render. (Obsolete: this used to be passed to your io.RenderDrawListsFn() function.) - - // Demo, Debug, Information - IMGUI_API void ShowDemoWindow(bool* p_open = NULL); // create demo/test window (previously called ShowTestWindow). demonstrate most ImGui features. call this to learn about the library! try to make it always available in your application! - IMGUI_API void ShowMetricsWindow(bool* p_open = NULL); // create metrics window. display ImGui internals: draw commands (with individual draw calls and vertices), window list, basic internal state, etc. - IMGUI_API void ShowStyleEditor(ImGuiStyle* ref = NULL); // add style editor block (not a window). you can pass in a reference ImGuiStyle structure to compare to, revert to and save to (else it uses the default style) - IMGUI_API bool ShowStyleSelector(const char* label); // add style selector block (not a window), essentially a combo listing the default styles. - IMGUI_API void ShowFontSelector(const char* label); // add font selector block (not a window), essentially a combo listing the loaded fonts. - IMGUI_API void ShowUserGuide(); // add basic help/info block (not a window): how to manipulate ImGui as a end-user (mouse/keyboard controls). - IMGUI_API const char* GetVersion(); // get a version string e.g. "1.23" - - // Styles - IMGUI_API void StyleColorsDark(ImGuiStyle* dst = NULL); // new, recommended style (default) - IMGUI_API void StyleColorsClassic(ImGuiStyle* dst = NULL); // classic imgui style - IMGUI_API void StyleColorsLight(ImGuiStyle* dst = NULL); // best used with borders and a custom, thicker font - - // Windows - // (Begin = push window to the stack and start appending to it. End = pop window from the stack. You may append multiple times to the same window during the same frame) - // Begin()/BeginChild() return false to indicate the window being collapsed or fully clipped, so you may early out and omit submitting anything to the window. - // You need to always call a matching End()/EndChild() for a Begin()/BeginChild() call, regardless of its return value (this is due to legacy reason and is inconsistent with BeginMenu/EndMenu, BeginPopup/EndPopup and other functions where the End call should only be called if the corresponding Begin function returned true.) - // Passing 'bool* p_open != NULL' shows a close widget in the upper-right corner of the window, which when clicking will set the boolean to false. - // Use child windows to introduce independent scrolling/clipping regions within a host window. Child windows can embed their own child. - IMGUI_API bool Begin(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0); - IMGUI_API void End(); - IMGUI_API bool BeginChild(const char* str_id, const ImVec2& size = ImVec2(0,0), bool border = false, ImGuiWindowFlags flags = 0); // Begin a scrolling region. size==0.0f: use remaining window size, size<0.0f: use remaining window size minus abs(size). size>0.0f: fixed size. each axis can use a different mode, e.g. ImVec2(0,400). - IMGUI_API bool BeginChild(ImGuiID id, const ImVec2& size = ImVec2(0,0), bool border = false, ImGuiWindowFlags flags = 0); - IMGUI_API void EndChild(); - - // Windows Utilities - IMGUI_API bool IsWindowAppearing(); - IMGUI_API bool IsWindowCollapsed(); - IMGUI_API bool IsWindowFocused(ImGuiFocusedFlags flags=0); // is current window focused? or its root/child, depending on flags. see flags for options. - IMGUI_API bool IsWindowHovered(ImGuiHoveredFlags flags=0); // is current window hovered (and typically: not blocked by a popup/modal)? see flags for options. NB: If you are trying to check whether your mouse should be dispatched to imgui or to your app, you should use the 'io.WantCaptureMouse' boolean for that! Please read the FAQ! - IMGUI_API ImDrawList* GetWindowDrawList(); // get draw list associated to the window, to append your own drawing primitives - IMGUI_API ImVec2 GetWindowPos(); // get current window position in screen space (useful if you want to do your own drawing via the DrawList API) - IMGUI_API ImVec2 GetWindowSize(); // get current window size - IMGUI_API float GetWindowWidth(); // get current window width (shortcut for GetWindowSize().x) - IMGUI_API float GetWindowHeight(); // get current window height (shortcut for GetWindowSize().y) - IMGUI_API ImVec2 GetContentRegionMax(); // current content boundaries (typically window boundaries including scrolling, or current column boundaries), in windows coordinates - IMGUI_API ImVec2 GetContentRegionAvail(); // == GetContentRegionMax() - GetCursorPos() - IMGUI_API float GetContentRegionAvailWidth(); // - IMGUI_API ImVec2 GetWindowContentRegionMin(); // content boundaries min (roughly (0,0)-Scroll), in window coordinates - IMGUI_API ImVec2 GetWindowContentRegionMax(); // content boundaries max (roughly (0,0)+Size-Scroll) where Size can be override with SetNextWindowContentSize(), in window coordinates - IMGUI_API float GetWindowContentRegionWidth(); // - - IMGUI_API void SetNextWindowPos(const ImVec2& pos, ImGuiCond cond = 0, const ImVec2& pivot = ImVec2(0,0)); // set next window position. call before Begin(). use pivot=(0.5f,0.5f) to center on given point, etc. - IMGUI_API void SetNextWindowSize(const ImVec2& size, ImGuiCond cond = 0); // set next window size. set axis to 0.0f to force an auto-fit on this axis. call before Begin() - IMGUI_API void SetNextWindowSizeConstraints(const ImVec2& size_min, const ImVec2& size_max, ImGuiSizeCallback custom_callback = NULL, void* custom_callback_data = NULL); // set next window size limits. use -1,-1 on either X/Y axis to preserve the current size. Use callback to apply non-trivial programmatic constraints. - IMGUI_API void SetNextWindowContentSize(const ImVec2& size); // set next window content size (~ enforce the range of scrollbars). not including window decorations (title bar, menu bar, etc.). set an axis to 0.0f to leave it automatic. call before Begin() - IMGUI_API void SetNextWindowCollapsed(bool collapsed, ImGuiCond cond = 0); // set next window collapsed state. call before Begin() - IMGUI_API void SetNextWindowFocus(); // set next window to be focused / front-most. call before Begin() - IMGUI_API void SetNextWindowBgAlpha(float alpha); // set next window background color alpha. helper to easily modify ImGuiCol_WindowBg/ChildBg/PopupBg. - IMGUI_API void SetWindowPos(const ImVec2& pos, ImGuiCond cond = 0); // (not recommended) set current window position - call within Begin()/End(). prefer using SetNextWindowPos(), as this may incur tearing and side-effects. - IMGUI_API void SetWindowSize(const ImVec2& size, ImGuiCond cond = 0); // (not recommended) set current window size - call within Begin()/End(). set to ImVec2(0,0) to force an auto-fit. prefer using SetNextWindowSize(), as this may incur tearing and minor side-effects. - IMGUI_API void SetWindowCollapsed(bool collapsed, ImGuiCond cond = 0); // (not recommended) set current window collapsed state. prefer using SetNextWindowCollapsed(). - IMGUI_API void SetWindowFocus(); // (not recommended) set current window to be focused / front-most. prefer using SetNextWindowFocus(). - IMGUI_API void SetWindowFontScale(float scale); // set font scale. Adjust IO.FontGlobalScale if you want to scale all windows - IMGUI_API void SetWindowPos(const char* name, const ImVec2& pos, ImGuiCond cond = 0); // set named window position. - IMGUI_API void SetWindowSize(const char* name, const ImVec2& size, ImGuiCond cond = 0); // set named window size. set axis to 0.0f to force an auto-fit on this axis. - IMGUI_API void SetWindowCollapsed(const char* name, bool collapsed, ImGuiCond cond = 0); // set named window collapsed state - IMGUI_API void SetWindowFocus(const char* name); // set named window to be focused / front-most. use NULL to remove focus. - - // Windows Scrolling - IMGUI_API float GetScrollX(); // get scrolling amount [0..GetScrollMaxX()] - IMGUI_API float GetScrollY(); // get scrolling amount [0..GetScrollMaxY()] - IMGUI_API float GetScrollMaxX(); // get maximum scrolling amount ~~ ContentSize.X - WindowSize.X - IMGUI_API float GetScrollMaxY(); // get maximum scrolling amount ~~ ContentSize.Y - WindowSize.Y - IMGUI_API void SetScrollX(float scroll_x); // set scrolling amount [0..GetScrollMaxX()] - IMGUI_API void SetScrollY(float scroll_y); // set scrolling amount [0..GetScrollMaxY()] - IMGUI_API void SetScrollHere(float center_y_ratio = 0.5f); // adjust scrolling amount to make current cursor position visible. center_y_ratio=0.0: top, 0.5: center, 1.0: bottom. When using to make a "default/current item" visible, consider using SetItemDefaultFocus() instead. - IMGUI_API void SetScrollFromPosY(float pos_y, float center_y_ratio = 0.5f); // adjust scrolling amount to make given position valid. use GetCursorPos() or GetCursorStartPos()+offset to get valid positions. - - // Parameters stacks (shared) - IMGUI_API void PushFont(ImFont* font); // use NULL as a shortcut to push default font - IMGUI_API void PopFont(); - IMGUI_API void PushStyleColor(ImGuiCol idx, ImU32 col); - IMGUI_API void PushStyleColor(ImGuiCol idx, const ImVec4& col); - IMGUI_API void PopStyleColor(int count = 1); - IMGUI_API void PushStyleVar(ImGuiStyleVar idx, float val); - IMGUI_API void PushStyleVar(ImGuiStyleVar idx, const ImVec2& val); - IMGUI_API void PopStyleVar(int count = 1); - IMGUI_API const ImVec4& GetStyleColorVec4(ImGuiCol idx); // retrieve style color as stored in ImGuiStyle structure. use to feed back into PushStyleColor(), otherwise use GetColorU32() to get style color with style alpha baked in. - IMGUI_API ImFont* GetFont(); // get current font - IMGUI_API float GetFontSize(); // get current font size (= height in pixels) of current font with current scale applied - IMGUI_API ImVec2 GetFontTexUvWhitePixel(); // get UV coordinate for a while pixel, useful to draw custom shapes via the ImDrawList API - IMGUI_API ImU32 GetColorU32(ImGuiCol idx, float alpha_mul = 1.0f); // retrieve given style color with style alpha applied and optional extra alpha multiplier - IMGUI_API ImU32 GetColorU32(const ImVec4& col); // retrieve given color with style alpha applied - IMGUI_API ImU32 GetColorU32(ImU32 col); // retrieve given color with style alpha applied - - // Parameters stacks (current window) - IMGUI_API void PushItemWidth(float item_width); // width of items for the common item+label case, pixels. 0.0f = default to ~2/3 of windows width, >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -1.0f always align width to the right side) - IMGUI_API void PopItemWidth(); - IMGUI_API float CalcItemWidth(); // width of item given pushed settings and current cursor position - IMGUI_API void PushTextWrapPos(float wrap_pos_x = 0.0f); // word-wrapping for Text*() commands. < 0.0f: no wrapping; 0.0f: wrap to end of window (or column); > 0.0f: wrap at 'wrap_pos_x' position in window local space - IMGUI_API void PopTextWrapPos(); - IMGUI_API void PushAllowKeyboardFocus(bool allow_keyboard_focus); // allow focusing using TAB/Shift-TAB, enabled by default but you can disable it for certain widgets - IMGUI_API void PopAllowKeyboardFocus(); - IMGUI_API void PushButtonRepeat(bool repeat); // in 'repeat' mode, Button*() functions return repeated true in a typematic manner (using io.KeyRepeatDelay/io.KeyRepeatRate setting). Note that you can call IsItemActive() after any Button() to tell if the button is held in the current frame. - IMGUI_API void PopButtonRepeat(); - - // Cursor / Layout - IMGUI_API void Separator(); // separator, generally horizontal. inside a menu bar or in horizontal layout mode, this becomes a vertical separator. - IMGUI_API void SameLine(float pos_x = 0.0f, float spacing_w = -1.0f); // call between widgets or groups to layout them horizontally - IMGUI_API void NewLine(); // undo a SameLine() - IMGUI_API void Spacing(); // add vertical spacing - IMGUI_API void Dummy(const ImVec2& size); // add a dummy item of given size - IMGUI_API void Indent(float indent_w = 0.0f); // move content position toward the right, by style.IndentSpacing or indent_w if != 0 - IMGUI_API void Unindent(float indent_w = 0.0f); // move content position back to the left, by style.IndentSpacing or indent_w if != 0 - IMGUI_API void BeginGroup(); // lock horizontal starting position + capture group bounding box into one "item" (so you can use IsItemHovered() or layout primitives such as SameLine() on whole group, etc.) - IMGUI_API void EndGroup(); - IMGUI_API ImVec2 GetCursorPos(); // cursor position is relative to window position - IMGUI_API float GetCursorPosX(); // " - IMGUI_API float GetCursorPosY(); // " - IMGUI_API void SetCursorPos(const ImVec2& local_pos); // " - IMGUI_API void SetCursorPosX(float x); // " - IMGUI_API void SetCursorPosY(float y); // " - IMGUI_API ImVec2 GetCursorStartPos(); // initial cursor position - IMGUI_API ImVec2 GetCursorScreenPos(); // cursor position in absolute screen coordinates [0..io.DisplaySize] (useful to work with ImDrawList API) - IMGUI_API void SetCursorScreenPos(const ImVec2& screen_pos); // cursor position in absolute screen coordinates [0..io.DisplaySize] - IMGUI_API void AlignTextToFramePadding(); // vertically align upcoming text baseline to FramePadding.y so that it will align properly to regularly framed items (call if you have text on a line before a framed item) - IMGUI_API float GetTextLineHeight(); // ~ FontSize - IMGUI_API float GetTextLineHeightWithSpacing(); // ~ FontSize + style.ItemSpacing.y (distance in pixels between 2 consecutive lines of text) - IMGUI_API float GetFrameHeight(); // ~ FontSize + style.FramePadding.y * 2 - IMGUI_API float GetFrameHeightWithSpacing(); // ~ FontSize + style.FramePadding.y * 2 + style.ItemSpacing.y (distance in pixels between 2 consecutive lines of framed widgets) - - // ID stack/scopes - // Read the FAQ for more details about how ID are handled in dear imgui. If you are creating widgets in a loop you most - // likely want to push a unique identifier (e.g. object pointer, loop index) to uniquely differentiate them. - // You can also use the "##foobar" syntax within widget label to distinguish them from each others. - // In this header file we use the "label"/"name" terminology to denote a string that will be displayed and used as an ID, - // whereas "str_id" denote a string that is only used as an ID and not aimed to be displayed. - IMGUI_API void PushID(const char* str_id); // push identifier into the ID stack. IDs are hash of the entire stack! - IMGUI_API void PushID(const char* str_id_begin, const char* str_id_end); - IMGUI_API void PushID(const void* ptr_id); - IMGUI_API void PushID(int int_id); - IMGUI_API void PopID(); - IMGUI_API ImGuiID GetID(const char* str_id); // calculate unique ID (hash of whole ID stack + given parameter). e.g. if you want to query into ImGuiStorage yourself - IMGUI_API ImGuiID GetID(const char* str_id_begin, const char* str_id_end); - IMGUI_API ImGuiID GetID(const void* ptr_id); - - // Widgets: Text - IMGUI_API void TextUnformatted(const char* text, const char* text_end = NULL); // raw text without formatting. Roughly equivalent to Text("%s", text) but: A) doesn't require null terminated string if 'text_end' is specified, B) it's faster, no memory copy is done, no buffer size limits, recommended for long chunks of text. - IMGUI_API void Text(const char* fmt, ...) IM_FMTARGS(1); // simple formatted text - IMGUI_API void TextV(const char* fmt, va_list args) IM_FMTLIST(1); - IMGUI_API void TextColored(const ImVec4& col, const char* fmt, ...) IM_FMTARGS(2); // shortcut for PushStyleColor(ImGuiCol_Text, col); Text(fmt, ...); PopStyleColor(); - IMGUI_API void TextColoredV(const ImVec4& col, const char* fmt, va_list args) IM_FMTLIST(2); - IMGUI_API void TextDisabled(const char* fmt, ...) IM_FMTARGS(1); // shortcut for PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]); Text(fmt, ...); PopStyleColor(); - IMGUI_API void TextDisabledV(const char* fmt, va_list args) IM_FMTLIST(1); - IMGUI_API void TextWrapped(const char* fmt, ...) IM_FMTARGS(1); // shortcut for PushTextWrapPos(0.0f); Text(fmt, ...); PopTextWrapPos();. Note that this won't work on an auto-resizing window if there's no other widgets to extend the window width, yoy may need to set a size using SetNextWindowSize(). - IMGUI_API void TextWrappedV(const char* fmt, va_list args) IM_FMTLIST(1); - IMGUI_API void LabelText(const char* label, const char* fmt, ...) IM_FMTARGS(2); // display text+label aligned the same way as value+label widgets - IMGUI_API void LabelTextV(const char* label, const char* fmt, va_list args) IM_FMTLIST(2); - IMGUI_API void BulletText(const char* fmt, ...) IM_FMTARGS(1); // shortcut for Bullet()+Text() - IMGUI_API void BulletTextV(const char* fmt, va_list args) IM_FMTLIST(1); - - // Widgets: Main - // Most widgets return true when the value has been changed or when pressed/selected - IMGUI_API bool Button(const char* label, const ImVec2& size = ImVec2(0,0)); // button - IMGUI_API bool SmallButton(const char* label); // button with FramePadding=(0,0) to easily embed within text - IMGUI_API bool InvisibleButton(const char* str_id, const ImVec2& size); // button behavior without the visuals, useful to build custom behaviors using the public api (along with IsItemActive, IsItemHovered, etc.) - IMGUI_API bool ArrowButton(const char* str_id, ImGuiDir dir); // square button with an arrow shape - IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0,0), const ImVec2& uv1 = ImVec2(1,1), const ImVec4& tint_col = ImVec4(1,1,1,1), const ImVec4& border_col = ImVec4(0,0,0,0)); - IMGUI_API bool ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0,0), const ImVec2& uv1 = ImVec2(1,1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0,0,0,0), const ImVec4& tint_col = ImVec4(1,1,1,1)); // <0 frame_padding uses default frame padding settings. 0 for no padding - IMGUI_API bool Checkbox(const char* label, bool* v); - IMGUI_API bool CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value); - IMGUI_API bool RadioButton(const char* label, bool active); - IMGUI_API bool RadioButton(const char* label, int* v, int v_button); - IMGUI_API void PlotLines(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0), int stride = sizeof(float)); - IMGUI_API void PlotLines(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0)); - IMGUI_API void PlotHistogram(const char* label, const float* values, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0), int stride = sizeof(float)); - IMGUI_API void PlotHistogram(const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset = 0, const char* overlay_text = NULL, float scale_min = FLT_MAX, float scale_max = FLT_MAX, ImVec2 graph_size = ImVec2(0,0)); - IMGUI_API void ProgressBar(float fraction, const ImVec2& size_arg = ImVec2(-1,0), const char* overlay = NULL); - IMGUI_API void Bullet(); // draw a small circle and keep the cursor on the same line. advance cursor x position by GetTreeNodeToLabelSpacing(), same distance that TreeNode() uses - - // Widgets: Combo Box - // The new BeginCombo()/EndCombo() api allows you to manage your contents and selection state however you want it. - // The old Combo() api are helpers over BeginCombo()/EndCombo() which are kept available for convenience purpose. - IMGUI_API bool BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags = 0); - IMGUI_API void EndCombo(); // only call EndCombo() if BeginCombo() returns true! - IMGUI_API bool Combo(const char* label, int* current_item, const char* const items[], int items_count, int popup_max_height_in_items = -1); - IMGUI_API bool Combo(const char* label, int* current_item, const char* items_separated_by_zeros, int popup_max_height_in_items = -1); // Separate items with \0 within a string, end item-list with \0\0. e.g. "One\0Two\0Three\0" - IMGUI_API bool Combo(const char* label, int* current_item, bool(*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int popup_max_height_in_items = -1); - - // Widgets: Drags (tip: ctrl+click on a drag box to input with keyboard. manually input values aren't clamped, can go off-bounds) - // For all the Float2/Float3/Float4/Int2/Int3/Int4 versions of every functions, note that a 'float v[X]' function argument is the same as 'float* v', the array syntax is just a way to document the number of elements that are expected to be accessible. You can pass address of your first element out of a contiguous set, e.g. &myvector.x - // Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc. - // Speed are per-pixel of mouse movement (v_speed=0.2f: mouse needs to move by 5 pixels to increase value by 1). For gamepad/keyboard navigation, minimum speed is Max(v_speed, minimum_step_at_given_precision). - IMGUI_API bool DragFloat(const char* label, float* v, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", float power = 1.0f); // If v_min >= v_max we have no bound - IMGUI_API bool DragFloat2(const char* label, float v[2], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", float power = 1.0f); - IMGUI_API bool DragFloat3(const char* label, float v[3], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", float power = 1.0f); - IMGUI_API bool DragFloat4(const char* label, float v[4], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", float power = 1.0f); - IMGUI_API bool DragFloatRange2(const char* label, float* v_current_min, float* v_current_max, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", const char* format_max = NULL, float power = 1.0f); - IMGUI_API bool DragInt(const char* label, int* v, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d"); // If v_min >= v_max we have no bound - IMGUI_API bool DragInt2(const char* label, int v[2], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d"); - IMGUI_API bool DragInt3(const char* label, int v[3], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d"); - IMGUI_API bool DragInt4(const char* label, int v[4], float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d"); - IMGUI_API bool DragIntRange2(const char* label, int* v_current_min, int* v_current_max, float v_speed = 1.0f, int v_min = 0, int v_max = 0, const char* format = "%d", const char* format_max = NULL); - IMGUI_API bool DragScalar(const char* label, ImGuiDataType data_type, void* v, float v_speed, const void* v_min = NULL, const void* v_max = NULL, const char* format = NULL, float power = 1.0f); - IMGUI_API bool DragScalarN(const char* label, ImGuiDataType data_type, void* v, int components, float v_speed, const void* v_min = NULL, const void* v_max = NULL, const char* format = NULL, float power = 1.0f); - - // Widgets: Input with Keyboard - IMGUI_API bool InputText(const char* label, char* buf, size_t buf_size, ImGuiInputTextFlags flags = 0, ImGuiTextEditCallback callback = NULL, void* user_data = NULL); - IMGUI_API bool InputTextMultiline(const char* label, char* buf, size_t buf_size, const ImVec2& size = ImVec2(0,0), ImGuiInputTextFlags flags = 0, ImGuiTextEditCallback callback = NULL, void* user_data = NULL); - IMGUI_API bool InputFloat(const char* label, float* v, float step = 0.0f, float step_fast = 0.0f, const char* format = "%.3f", ImGuiInputTextFlags extra_flags = 0); - IMGUI_API bool InputFloat2(const char* label, float v[2], const char* format = "%.3f", ImGuiInputTextFlags extra_flags = 0); - IMGUI_API bool InputFloat3(const char* label, float v[3], const char* format = "%.3f", ImGuiInputTextFlags extra_flags = 0); - IMGUI_API bool InputFloat4(const char* label, float v[4], const char* format = "%.3f", ImGuiInputTextFlags extra_flags = 0); - IMGUI_API bool InputInt(const char* label, int* v, int step = 1, int step_fast = 100, ImGuiInputTextFlags extra_flags = 0); - IMGUI_API bool InputInt2(const char* label, int v[2], ImGuiInputTextFlags extra_flags = 0); - IMGUI_API bool InputInt3(const char* label, int v[3], ImGuiInputTextFlags extra_flags = 0); - IMGUI_API bool InputInt4(const char* label, int v[4], ImGuiInputTextFlags extra_flags = 0); - IMGUI_API bool InputDouble(const char* label, double* v, double step = 0.0, double step_fast = 0.0, const char* format = "%.6f", ImGuiInputTextFlags extra_flags = 0); - IMGUI_API bool InputScalar(const char* label, ImGuiDataType data_type, void* v, const void* step = NULL, const void* step_fast = NULL, const char* format = NULL, ImGuiInputTextFlags extra_flags = 0); - IMGUI_API bool InputScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* step = NULL, const void* step_fast = NULL, const char* format = NULL, ImGuiInputTextFlags extra_flags = 0); - - // Widgets: Sliders (tip: ctrl+click on a slider to input with keyboard. manually input values aren't clamped, can go off-bounds) - // Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc. - IMGUI_API bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f); // adjust format to decorate the value with a prefix or a suffix for in-slider labels or unit display. Use power!=1.0 for power curve sliders - IMGUI_API bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format = "%.3f", float power = 1.0f); - IMGUI_API bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format = "%.3f", float power = 1.0f); - IMGUI_API bool SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format = "%.3f", float power = 1.0f); - IMGUI_API bool SliderAngle(const char* label, float* v_rad, float v_degrees_min = -360.0f, float v_degrees_max = +360.0f); - IMGUI_API bool SliderInt(const char* label, int* v, int v_min, int v_max, const char* format = "%d"); - IMGUI_API bool SliderInt2(const char* label, int v[2], int v_min, int v_max, const char* format = "%d"); - IMGUI_API bool SliderInt3(const char* label, int v[3], int v_min, int v_max, const char* format = "%d"); - IMGUI_API bool SliderInt4(const char* label, int v[4], int v_min, int v_max, const char* format = "%d"); - IMGUI_API bool SliderScalar(const char* label, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format = NULL, float power = 1.0f); - IMGUI_API bool SliderScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* v_min, const void* v_max, const char* format = NULL, float power = 1.0f); - IMGUI_API bool VSliderFloat(const char* label, const ImVec2& size, float* v, float v_min, float v_max, const char* format = "%.3f", float power = 1.0f); - IMGUI_API bool VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, int v_max, const char* format = "%d"); - IMGUI_API bool VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format = NULL, float power = 1.0f); - - // Widgets: Color Editor/Picker (tip: the ColorEdit* functions have a little colored preview square that can be left-clicked to open a picker, and right-clicked to open an option menu.) - // Note that a 'float v[X]' function argument is the same as 'float* v', the array syntax is just a way to document the number of elements that are expected to be accessible. You can the pass the address of a first float element out of a contiguous structure, e.g. &myvector.x - IMGUI_API bool ColorEdit3(const char* label, float col[3], ImGuiColorEditFlags flags = 0); - IMGUI_API bool ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flags = 0); - IMGUI_API bool ColorPicker3(const char* label, float col[3], ImGuiColorEditFlags flags = 0); - IMGUI_API bool ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags flags = 0, const float* ref_col = NULL); - IMGUI_API bool ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFlags flags = 0, ImVec2 size = ImVec2(0,0)); // display a colored square/button, hover for details, return true when pressed. - IMGUI_API void SetColorEditOptions(ImGuiColorEditFlags flags); // initialize current options (generally on application startup) if you want to select a default format, picker type, etc. User will be able to change many settings, unless you pass the _NoOptions flag to your calls. - - // Widgets: Trees - // TreeNode functions return true when the node is open, in which case you need to also call TreePop() when you are finished displaying the tree node contents. - IMGUI_API bool TreeNode(const char* label); - IMGUI_API bool TreeNode(const char* str_id, const char* fmt, ...) IM_FMTARGS(2); // helper variation to completely decorelate the id from the displayed string. Read the FAQ about why and how to use ID. to align arbitrary text at the same level as a TreeNode() you can use Bullet(). - IMGUI_API bool TreeNode(const void* ptr_id, const char* fmt, ...) IM_FMTARGS(2); // " - IMGUI_API bool TreeNodeV(const char* str_id, const char* fmt, va_list args) IM_FMTLIST(2); - IMGUI_API bool TreeNodeV(const void* ptr_id, const char* fmt, va_list args) IM_FMTLIST(2); - IMGUI_API bool TreeNodeEx(const char* label, ImGuiTreeNodeFlags flags = 0); - IMGUI_API bool TreeNodeEx(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) IM_FMTARGS(3); - IMGUI_API bool TreeNodeEx(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, ...) IM_FMTARGS(3); - IMGUI_API bool TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) IM_FMTLIST(3); - IMGUI_API bool TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) IM_FMTLIST(3); - IMGUI_API void TreePush(const char* str_id); // ~ Indent()+PushId(). Already called by TreeNode() when returning true, but you can call TreePush/TreePop yourself if desired. - IMGUI_API void TreePush(const void* ptr_id = NULL); // " - IMGUI_API void TreePop(); // ~ Unindent()+PopId() - IMGUI_API void TreeAdvanceToLabelPos(); // advance cursor x position by GetTreeNodeToLabelSpacing() - IMGUI_API float GetTreeNodeToLabelSpacing(); // horizontal distance preceding label when using TreeNode*() or Bullet() == (g.FontSize + style.FramePadding.x*2) for a regular unframed TreeNode - IMGUI_API void SetNextTreeNodeOpen(bool is_open, ImGuiCond cond = 0); // set next TreeNode/CollapsingHeader open state. - IMGUI_API bool CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags = 0); // if returning 'true' the header is open. doesn't indent nor push on ID stack. user doesn't have to call TreePop(). - IMGUI_API bool CollapsingHeader(const char* label, bool* p_open, ImGuiTreeNodeFlags flags = 0); // when 'p_open' isn't NULL, display an additional small close button on upper right of the header - - // Widgets: Selectable / Lists - IMGUI_API bool Selectable(const char* label, bool selected = false, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0,0)); // "bool selected" carry the selection state (read-only). Selectable() is clicked is returns true so you can modify your selection state. size.x==0.0: use remaining width, size.x>0.0: specify width. size.y==0.0: use label height, size.y>0.0: specify height - IMGUI_API bool Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags = 0, const ImVec2& size = ImVec2(0,0)); // "bool* p_selected" point to the selection state (read-write), as a convenient helper. - IMGUI_API bool ListBox(const char* label, int* current_item, const char* const items[], int items_count, int height_in_items = -1); - IMGUI_API bool ListBox(const char* label, int* current_item, bool (*items_getter)(void* data, int idx, const char** out_text), void* data, int items_count, int height_in_items = -1); - IMGUI_API bool ListBoxHeader(const char* label, const ImVec2& size = ImVec2(0,0)); // use if you want to reimplement ListBox() will custom data or interactions. if the function return true, you can output elements then call ListBoxFooter() afterwards. - IMGUI_API bool ListBoxHeader(const char* label, int items_count, int height_in_items = -1); // " - IMGUI_API void ListBoxFooter(); // terminate the scrolling region. only call ListBoxFooter() if ListBoxHeader() returned true! - - // Widgets: Value() Helpers. Output single value in "name: value" format (tip: freely declare more in your code to handle your types. you can add functions to the ImGui namespace) - IMGUI_API void Value(const char* prefix, bool b); - IMGUI_API void Value(const char* prefix, int v); - IMGUI_API void Value(const char* prefix, unsigned int v); - IMGUI_API void Value(const char* prefix, float v, const char* float_format = NULL); - - // Tooltips - IMGUI_API void BeginTooltip(); // begin/append a tooltip window. to create full-featured tooltip (with any kind of items). - IMGUI_API void EndTooltip(); - IMGUI_API void SetTooltip(const char* fmt, ...) IM_FMTARGS(1); // set a text-only tooltip, typically use with ImGui::IsItemHovered(). overidde any previous call to SetTooltip(). - IMGUI_API void SetTooltipV(const char* fmt, va_list args) IM_FMTLIST(1); - - // Menus - IMGUI_API bool BeginMainMenuBar(); // create and append to a full screen menu-bar. - IMGUI_API void EndMainMenuBar(); // only call EndMainMenuBar() if BeginMainMenuBar() returns true! - IMGUI_API bool BeginMenuBar(); // append to menu-bar of current window (requires ImGuiWindowFlags_MenuBar flag set on parent window). - IMGUI_API void EndMenuBar(); // only call EndMenuBar() if BeginMenuBar() returns true! - IMGUI_API bool BeginMenu(const char* label, bool enabled = true); // create a sub-menu entry. only call EndMenu() if this returns true! - IMGUI_API void EndMenu(); // only call EndMenu() if BeginMenu() returns true! - IMGUI_API bool MenuItem(const char* label, const char* shortcut = NULL, bool selected = false, bool enabled = true); // return true when activated. shortcuts are displayed for convenience but not processed by ImGui at the moment - IMGUI_API bool MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled = true); // return true when activated + toggle (*p_selected) if p_selected != NULL - - // Popups - IMGUI_API void OpenPopup(const char* str_id); // call to mark popup as open (don't call every frame!). popups are closed when user click outside, or if CloseCurrentPopup() is called within a BeginPopup()/EndPopup() block. By default, Selectable()/MenuItem() are calling CloseCurrentPopup(). Popup identifiers are relative to the current ID-stack (so OpenPopup and BeginPopup needs to be at the same level). - IMGUI_API bool BeginPopup(const char* str_id, ImGuiWindowFlags flags = 0); // return true if the popup is open, and you can start outputting to it. only call EndPopup() if BeginPopup() returns true! - IMGUI_API bool BeginPopupContextItem(const char* str_id = NULL, int mouse_button = 1); // helper to open and begin popup when clicked on last item. if you can pass a NULL str_id only if the previous item had an id. If you want to use that on a non-interactive item such as Text() you need to pass in an explicit ID here. read comments in .cpp! - IMGUI_API bool BeginPopupContextWindow(const char* str_id = NULL, int mouse_button = 1, bool also_over_items = true); // helper to open and begin popup when clicked on current window. - IMGUI_API bool BeginPopupContextVoid(const char* str_id = NULL, int mouse_button = 1); // helper to open and begin popup when clicked in void (where there are no imgui windows). - IMGUI_API bool BeginPopupModal(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0); // modal dialog (regular window with title bar, block interactions behind the modal window, can't close the modal window by clicking outside) - IMGUI_API void EndPopup(); // only call EndPopup() if BeginPopupXXX() returns true! - IMGUI_API bool OpenPopupOnItemClick(const char* str_id = NULL, int mouse_button = 1); // helper to open popup when clicked on last item. return true when just opened. - IMGUI_API bool IsPopupOpen(const char* str_id); // return true if the popup is open - IMGUI_API void CloseCurrentPopup(); // close the popup we have begin-ed into. clicking on a MenuItem or Selectable automatically close the current popup. - - // Columns - // You can also use SameLine(pos_x) for simplified columns. The columns API is still work-in-progress and rather lacking. - IMGUI_API void Columns(int count = 1, const char* id = NULL, bool border = true); - IMGUI_API void NextColumn(); // next column, defaults to current row or next row if the current row is finished - IMGUI_API int GetColumnIndex(); // get current column index - IMGUI_API float GetColumnWidth(int column_index = -1); // get column width (in pixels). pass -1 to use current column - IMGUI_API void SetColumnWidth(int column_index, float width); // set column width (in pixels). pass -1 to use current column - IMGUI_API float GetColumnOffset(int column_index = -1); // get position of column line (in pixels, from the left side of the contents region). pass -1 to use current column, otherwise 0..GetColumnsCount() inclusive. column 0 is typically 0.0f - IMGUI_API void SetColumnOffset(int column_index, float offset_x); // set position of column line (in pixels, from the left side of the contents region). pass -1 to use current column - IMGUI_API int GetColumnsCount(); - - // Logging/Capture: all text output from interface is captured to tty/file/clipboard. By default, tree nodes are automatically opened during logging. - IMGUI_API void LogToTTY(int max_depth = -1); // start logging to tty - IMGUI_API void LogToFile(int max_depth = -1, const char* filename = NULL); // start logging to file - IMGUI_API void LogToClipboard(int max_depth = -1); // start logging to OS clipboard - IMGUI_API void LogFinish(); // stop logging (close file, etc.) - IMGUI_API void LogButtons(); // helper to display buttons for logging to tty/file/clipboard - IMGUI_API void LogText(const char* fmt, ...) IM_FMTARGS(1); // pass text data straight to log (without being displayed) - - // Drag and Drop - // [BETA API] Missing Demo code. API may evolve. - IMGUI_API bool BeginDragDropSource(ImGuiDragDropFlags flags = 0); // call when the current item is active. If this return true, you can call SetDragDropPayload() + EndDragDropSource() - IMGUI_API bool SetDragDropPayload(const char* type, const void* data, size_t size, ImGuiCond cond = 0);// type is a user defined string of maximum 32 characters. Strings starting with '_' are reserved for dear imgui internal types. Data is copied and held by imgui. - IMGUI_API void EndDragDropSource(); // only call EndDragDropSource() if BeginDragDropSource() returns true! - IMGUI_API bool BeginDragDropTarget(); // call after submitting an item that may receive an item. If this returns true, you can call AcceptDragDropPayload() + EndDragDropTarget() - IMGUI_API const ImGuiPayload* AcceptDragDropPayload(const char* type, ImGuiDragDropFlags flags = 0); // accept contents of a given type. If ImGuiDragDropFlags_AcceptBeforeDelivery is set you can peek into the payload before the mouse button is released. - IMGUI_API void EndDragDropTarget(); // only call EndDragDropTarget() if BeginDragDropTarget() returns true! - - // Clipping - IMGUI_API void PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect); - IMGUI_API void PopClipRect(); - - // Focus, Activation - // (Prefer using "SetItemDefaultFocus()" over "if (IsWindowAppearing()) SetScrollHere()" when applicable, to make your code more forward compatible when navigation branch is merged) - IMGUI_API void SetItemDefaultFocus(); // make last item the default focused item of a window. Please use instead of "if (IsWindowAppearing()) SetScrollHere()" to signify "default item". - IMGUI_API void SetKeyboardFocusHere(int offset = 0); // focus keyboard on the next widget. Use positive 'offset' to access sub components of a multiple component widget. Use -1 to access previous widget. - - // Utilities - IMGUI_API bool IsItemHovered(ImGuiHoveredFlags flags = 0); // is the last item hovered? (and usable, aka not blocked by a popup, etc.). See ImGuiHoveredFlags for more options. - IMGUI_API bool IsItemActive(); // is the last item active? (e.g. button being held, text field being edited. This will continuously return true while holding mouse button on an item. Items that don't interact will always return false) - IMGUI_API bool IsItemFocused(); // is the last item focused for keyboard/gamepad navigation? - IMGUI_API bool IsItemClicked(int mouse_button = 0); // is the last item clicked? (e.g. button/node just clicked on) == IsMouseClicked(mouse_button) && IsItemHovered() - IMGUI_API bool IsItemVisible(); // is the last item visible? (items may be out of sight because of clipping/scrolling) - IMGUI_API bool IsItemDeactivated(); // was the last item just made inactive (item was previously active). Useful for Undo/Redo patterns with widgets that requires continuous editing. - IMGUI_API bool IsItemDeactivatedAfterChange(); // was the last item just made inactive and made a value change when it was active? (e.g. Slider/Drag moved). Useful for Undo/Redo patterns with widgets that requires continuous editing. Note that you may get false positives (some widgets such as Combo()/ListBox()/Selectable() will return true even when clicking an already selected item). - IMGUI_API bool IsAnyItemHovered(); - IMGUI_API bool IsAnyItemActive(); - IMGUI_API bool IsAnyItemFocused(); - IMGUI_API ImVec2 GetItemRectMin(); // get bounding rectangle of last item, in screen space - IMGUI_API ImVec2 GetItemRectMax(); // " - IMGUI_API ImVec2 GetItemRectSize(); // get size of last item, in screen space - IMGUI_API void SetItemAllowOverlap(); // allow last item to be overlapped by a subsequent item. sometimes useful with invisible buttons, selectables, etc. to catch unused area. - IMGUI_API bool IsRectVisible(const ImVec2& size); // test if rectangle (of given size, starting from cursor position) is visible / not clipped. - IMGUI_API bool IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max); // test if rectangle (in screen space) is visible / not clipped. to perform coarse clipping on user's side. - IMGUI_API float GetTime(); - IMGUI_API int GetFrameCount(); - IMGUI_API ImDrawList* GetOverlayDrawList(); // this draw list will be the last rendered one, useful to quickly draw overlays shapes/text - IMGUI_API ImDrawListSharedData* GetDrawListSharedData(); // you may use this when creating your own ImDrawList instances - IMGUI_API const char* GetStyleColorName(ImGuiCol idx); - IMGUI_API void SetStateStorage(ImGuiStorage* storage); // replace current window storage with our own (if you want to manipulate it yourself, typically clear subsection of it) - IMGUI_API ImGuiStorage* GetStateStorage(); - IMGUI_API ImVec2 CalcTextSize(const char* text, const char* text_end = NULL, bool hide_text_after_double_hash = false, float wrap_width = -1.0f); - IMGUI_API void CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end); // calculate coarse clipping for large list of evenly sized items. Prefer using the ImGuiListClipper higher-level helper if you can. - - IMGUI_API bool BeginChildFrame(ImGuiID id, const ImVec2& size, ImGuiWindowFlags flags = 0); // helper to create a child window / scrolling region that looks like a normal widget frame - IMGUI_API void EndChildFrame(); // always call EndChildFrame() regardless of BeginChildFrame() return values (which indicates a collapsed/clipped window) - - IMGUI_API ImVec4 ColorConvertU32ToFloat4(ImU32 in); - IMGUI_API ImU32 ColorConvertFloat4ToU32(const ImVec4& in); - IMGUI_API void ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v); - IMGUI_API void ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b); - - // Inputs - IMGUI_API int GetKeyIndex(ImGuiKey imgui_key); // map ImGuiKey_* values into user's key index. == io.KeyMap[key] - IMGUI_API bool IsKeyDown(int user_key_index); // is key being held. == io.KeysDown[user_key_index]. note that imgui doesn't know the semantic of each entry of io.KeysDown[]. Use your own indices/enums according to how your backend/engine stored them into io.KeysDown[]! - IMGUI_API bool IsKeyPressed(int user_key_index, bool repeat = true); // was key pressed (went from !Down to Down). if repeat=true, uses io.KeyRepeatDelay / KeyRepeatRate - IMGUI_API bool IsKeyReleased(int user_key_index); // was key released (went from Down to !Down).. - IMGUI_API int GetKeyPressedAmount(int key_index, float repeat_delay, float rate); // uses provided repeat rate/delay. return a count, most often 0 or 1 but might be >1 if RepeatRate is small enough that DeltaTime > RepeatRate - IMGUI_API bool IsMouseDown(int button); // is mouse button held - IMGUI_API bool IsAnyMouseDown(); // is any mouse button held - IMGUI_API bool IsMouseClicked(int button, bool repeat = false); // did mouse button clicked (went from !Down to Down) - IMGUI_API bool IsMouseDoubleClicked(int button); // did mouse button double-clicked. a double-click returns false in IsMouseClicked(). uses io.MouseDoubleClickTime. - IMGUI_API bool IsMouseReleased(int button); // did mouse button released (went from Down to !Down) - IMGUI_API bool IsMouseDragging(int button = 0, float lock_threshold = -1.0f); // is mouse dragging. if lock_threshold < -1.0f uses io.MouseDraggingThreshold - IMGUI_API bool IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip = true); // is mouse hovering given bounding rect (in screen space). clipped by current clipping settings. disregarding of consideration of focus/window ordering/blocked by a popup. - IMGUI_API bool IsMousePosValid(const ImVec2* mouse_pos = NULL); // - IMGUI_API ImVec2 GetMousePos(); // shortcut to ImGui::GetIO().MousePos provided by user, to be consistent with other calls - IMGUI_API ImVec2 GetMousePosOnOpeningCurrentPopup(); // retrieve backup of mouse position at the time of opening popup we have BeginPopup() into - IMGUI_API ImVec2 GetMouseDragDelta(int button = 0, float lock_threshold = -1.0f); // dragging amount since clicking. if lock_threshold < -1.0f uses io.MouseDraggingThreshold - IMGUI_API void ResetMouseDragDelta(int button = 0); // - IMGUI_API ImGuiMouseCursor GetMouseCursor(); // get desired cursor type, reset in ImGui::NewFrame(), this is updated during the frame. valid before Render(). If you use software rendering by setting io.MouseDrawCursor ImGui will render those for you - IMGUI_API void SetMouseCursor(ImGuiMouseCursor type); // set desired cursor type - IMGUI_API void CaptureKeyboardFromApp(bool capture = true); // manually override io.WantCaptureKeyboard flag next frame (said flag is entirely left for your application to handle). e.g. force capture keyboard when your widget is being hovered. - IMGUI_API void CaptureMouseFromApp(bool capture = true); // manually override io.WantCaptureMouse flag next frame (said flag is entirely left for your application to handle). - - // Clipboard Utilities (also see the LogToClipboard() function to capture or output text data to the clipboard) - IMGUI_API const char* GetClipboardText(); - IMGUI_API void SetClipboardText(const char* text); - - // Settings/.Ini Utilities - // The disk functions are automatically called if io.IniFilename != NULL (default is "imgui.ini"). - // Set io.IniFilename to NULL to load/save manually. Read io.WantSaveIniSettings description about handling .ini saving manually. - IMGUI_API void LoadIniSettingsFromDisk(const char* ini_filename); // call after CreateContext() and before the first call to NewFrame(). NewFrame() automatically calls LoadIniSettingsFromDisk(io.IniFilename). - IMGUI_API void LoadIniSettingsFromMemory(const char* ini_data, size_t ini_size=0); // call after CreateContext() and before the first call to NewFrame() to provide .ini data from your own data source. - IMGUI_API void SaveIniSettingsToDisk(const char* ini_filename); - IMGUI_API const char* SaveIniSettingsToMemory(size_t* out_ini_size = NULL); // return a zero-terminated string with the .ini data which you can save by your own mean. call when io.WantSaveIniSettings is set, then save data by your own mean and clear io.WantSaveIniSettings. - - // Memory Utilities - // All those functions are not reliant on the current context. - // If you reload the contents of imgui.cpp at runtime, you may need to call SetCurrentContext() + SetAllocatorFunctions() again. - IMGUI_API void SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void(*free_func)(void* ptr, void* user_data), void* user_data = NULL); - IMGUI_API void* MemAlloc(size_t size); - IMGUI_API void MemFree(void* ptr); - -} // namespace ImGui - -// Flags for ImGui::Begin() -enum ImGuiWindowFlags_ -{ - ImGuiWindowFlags_None = 0, - ImGuiWindowFlags_NoTitleBar = 1 << 0, // Disable title-bar - ImGuiWindowFlags_NoResize = 1 << 1, // Disable user resizing with the lower-right grip - ImGuiWindowFlags_NoMove = 1 << 2, // Disable user moving the window - ImGuiWindowFlags_NoScrollbar = 1 << 3, // Disable scrollbars (window can still scroll with mouse or programatically) - ImGuiWindowFlags_NoScrollWithMouse = 1 << 4, // Disable user vertically scrolling with mouse wheel. On child window, mouse wheel will be forwarded to the parent unless NoScrollbar is also set. - ImGuiWindowFlags_NoCollapse = 1 << 5, // Disable user collapsing window by double-clicking on it - ImGuiWindowFlags_AlwaysAutoResize = 1 << 6, // Resize every window to its content every frame - //ImGuiWindowFlags_ShowBorders = 1 << 7, // Show borders around windows and items (OBSOLETE! Use e.g. style.FrameBorderSize=1.0f to enable borders). - ImGuiWindowFlags_NoSavedSettings = 1 << 8, // Never load/save settings in .ini file - ImGuiWindowFlags_NoInputs = 1 << 9, // Disable catching mouse or keyboard inputs, hovering test with pass through. - ImGuiWindowFlags_MenuBar = 1 << 10, // Has a menu-bar - ImGuiWindowFlags_HorizontalScrollbar = 1 << 11, // Allow horizontal scrollbar to appear (off by default). You may use SetNextWindowContentSize(ImVec2(width,0.0f)); prior to calling Begin() to specify width. Read code in imgui_demo in the "Horizontal Scrolling" section. - ImGuiWindowFlags_NoFocusOnAppearing = 1 << 12, // Disable taking focus when transitioning from hidden to visible state - ImGuiWindowFlags_NoBringToFrontOnFocus = 1 << 13, // Disable bringing window to front when taking focus (e.g. clicking on it or programatically giving it focus) - ImGuiWindowFlags_AlwaysVerticalScrollbar= 1 << 14, // Always show vertical scrollbar (even if ContentSize.y < Size.y) - ImGuiWindowFlags_AlwaysHorizontalScrollbar=1<< 15, // Always show horizontal scrollbar (even if ContentSize.x < Size.x) - ImGuiWindowFlags_AlwaysUseWindowPadding = 1 << 16, // Ensure child windows without border uses style.WindowPadding (ignored by default for non-bordered child windows, because more convenient) - ImGuiWindowFlags_ResizeFromAnySide = 1 << 17, // [BETA] Enable resize from any corners and borders. Your back-end needs to honor the different values of io.MouseCursor set by imgui. - ImGuiWindowFlags_NoNavInputs = 1 << 18, // No gamepad/keyboard navigation within the window - ImGuiWindowFlags_NoNavFocus = 1 << 19, // No focusing toward this window with gamepad/keyboard navigation (e.g. skipped by CTRL+TAB) - ImGuiWindowFlags_NoNav = ImGuiWindowFlags_NoNavInputs | ImGuiWindowFlags_NoNavFocus, - - // [Internal] - ImGuiWindowFlags_NavFlattened = 1 << 23, // [BETA] Allow gamepad/keyboard navigation to cross over parent border to this child (only use on child that have no scrolling!) - ImGuiWindowFlags_ChildWindow = 1 << 24, // Don't use! For internal use by BeginChild() - ImGuiWindowFlags_Tooltip = 1 << 25, // Don't use! For internal use by BeginTooltip() - ImGuiWindowFlags_Popup = 1 << 26, // Don't use! For internal use by BeginPopup() - ImGuiWindowFlags_Modal = 1 << 27, // Don't use! For internal use by BeginPopupModal() - ImGuiWindowFlags_ChildMenu = 1 << 28 // Don't use! For internal use by BeginMenu() -}; - -// Flags for ImGui::InputText() -enum ImGuiInputTextFlags_ -{ - ImGuiInputTextFlags_None = 0, - ImGuiInputTextFlags_CharsDecimal = 1 << 0, // Allow 0123456789.+-*/ - ImGuiInputTextFlags_CharsHexadecimal = 1 << 1, // Allow 0123456789ABCDEFabcdef - ImGuiInputTextFlags_CharsUppercase = 1 << 2, // Turn a..z into A..Z - ImGuiInputTextFlags_CharsNoBlank = 1 << 3, // Filter out spaces, tabs - ImGuiInputTextFlags_AutoSelectAll = 1 << 4, // Select entire text when first taking mouse focus - ImGuiInputTextFlags_EnterReturnsTrue = 1 << 5, // Return 'true' when Enter is pressed (as opposed to when the value was modified) - ImGuiInputTextFlags_CallbackCompletion = 1 << 6, // Call user function on pressing TAB (for completion handling) - ImGuiInputTextFlags_CallbackHistory = 1 << 7, // Call user function on pressing Up/Down arrows (for history handling) - ImGuiInputTextFlags_CallbackAlways = 1 << 8, // Call user function every time. User code may query cursor position, modify text buffer. - ImGuiInputTextFlags_CallbackCharFilter = 1 << 9, // Call user function to filter character. Modify data->EventChar to replace/filter input, or return 1 to discard character. - ImGuiInputTextFlags_AllowTabInput = 1 << 10, // Pressing TAB input a '\t' character into the text field - ImGuiInputTextFlags_CtrlEnterForNewLine = 1 << 11, // In multi-line mode, unfocus with Enter, add new line with Ctrl+Enter (default is opposite: unfocus with Ctrl+Enter, add line with Enter). - ImGuiInputTextFlags_NoHorizontalScroll = 1 << 12, // Disable following the cursor horizontally - ImGuiInputTextFlags_AlwaysInsertMode = 1 << 13, // Insert mode - ImGuiInputTextFlags_ReadOnly = 1 << 14, // Read-only mode - ImGuiInputTextFlags_Password = 1 << 15, // Password mode, display all characters as '*' - ImGuiInputTextFlags_NoUndoRedo = 1 << 16, // Disable undo/redo. Note that input text owns the text data while active, if you want to provide your own undo/redo stack you need e.g. to call ClearActiveID(). - ImGuiInputTextFlags_CharsScientific = 1 << 17, // Allow 0123456789.+-*/eE (Scientific notation input) - // [Internal] - ImGuiInputTextFlags_Multiline = 1 << 20 // For internal use by InputTextMultiline() -}; - -// Flags for ImGui::TreeNodeEx(), ImGui::CollapsingHeader*() -enum ImGuiTreeNodeFlags_ -{ - ImGuiTreeNodeFlags_None = 0, - ImGuiTreeNodeFlags_Selected = 1 << 0, // Draw as selected - ImGuiTreeNodeFlags_Framed = 1 << 1, // Full colored frame (e.g. for CollapsingHeader) - ImGuiTreeNodeFlags_AllowItemOverlap = 1 << 2, // Hit testing to allow subsequent widgets to overlap this one - ImGuiTreeNodeFlags_NoTreePushOnOpen = 1 << 3, // Don't do a TreePush() when open (e.g. for CollapsingHeader) = no extra indent nor pushing on ID stack - ImGuiTreeNodeFlags_NoAutoOpenOnLog = 1 << 4, // Don't automatically and temporarily open node when Logging is active (by default logging will automatically open tree nodes) - ImGuiTreeNodeFlags_DefaultOpen = 1 << 5, // Default node to be open - ImGuiTreeNodeFlags_OpenOnDoubleClick = 1 << 6, // Need double-click to open node - ImGuiTreeNodeFlags_OpenOnArrow = 1 << 7, // Only open when clicking on the arrow part. If ImGuiTreeNodeFlags_OpenOnDoubleClick is also set, single-click arrow or double-click all box to open. - ImGuiTreeNodeFlags_Leaf = 1 << 8, // No collapsing, no arrow (use as a convenience for leaf nodes). - ImGuiTreeNodeFlags_Bullet = 1 << 9, // Display a bullet instead of arrow - ImGuiTreeNodeFlags_FramePadding = 1 << 10, // Use FramePadding (even for an unframed text node) to vertically align text baseline to regular widget height. Equivalent to calling AlignTextToFramePadding(). - //ImGuITreeNodeFlags_SpanAllAvailWidth = 1 << 11, // FIXME: TODO: Extend hit box horizontally even if not framed - //ImGuiTreeNodeFlags_NoScrollOnOpen = 1 << 12, // FIXME: TODO: Disable automatic scroll on TreePop() if node got just open and contents is not visible - ImGuiTreeNodeFlags_NavLeftJumpsBackHere = 1 << 13, // (WIP) Nav: left direction may move to this TreeNode() from any of its child (items submitted between TreeNode and TreePop) - ImGuiTreeNodeFlags_CollapsingHeader = ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_NoAutoOpenOnLog - - // Obsolete names (will be removed) -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - , ImGuiTreeNodeFlags_AllowOverlapMode = ImGuiTreeNodeFlags_AllowItemOverlap -#endif -}; - -// Flags for ImGui::Selectable() -enum ImGuiSelectableFlags_ -{ - ImGuiSelectableFlags_None = 0, - ImGuiSelectableFlags_DontClosePopups = 1 << 0, // Clicking this don't close parent popup window - ImGuiSelectableFlags_SpanAllColumns = 1 << 1, // Selectable frame can span all columns (text will still fit in current column) - ImGuiSelectableFlags_AllowDoubleClick = 1 << 2 // Generate press events on double clicks too -}; - -// Flags for ImGui::BeginCombo() -enum ImGuiComboFlags_ -{ - ImGuiComboFlags_None = 0, - ImGuiComboFlags_PopupAlignLeft = 1 << 0, // Align the popup toward the left by default - ImGuiComboFlags_HeightSmall = 1 << 1, // Max ~4 items visible. Tip: If you want your combo popup to be a specific size you can use SetNextWindowSizeConstraints() prior to calling BeginCombo() - ImGuiComboFlags_HeightRegular = 1 << 2, // Max ~8 items visible (default) - ImGuiComboFlags_HeightLarge = 1 << 3, // Max ~20 items visible - ImGuiComboFlags_HeightLargest = 1 << 4, // As many fitting items as possible - ImGuiComboFlags_NoArrowButton = 1 << 5, // Display on the preview box without the square arrow button - ImGuiComboFlags_NoPreview = 1 << 6, // Display only a square arrow button - ImGuiComboFlags_HeightMask_ = ImGuiComboFlags_HeightSmall | ImGuiComboFlags_HeightRegular | ImGuiComboFlags_HeightLarge | ImGuiComboFlags_HeightLargest -}; - -// Flags for ImGui::IsWindowFocused() -enum ImGuiFocusedFlags_ -{ - ImGuiFocusedFlags_None = 0, - ImGuiFocusedFlags_ChildWindows = 1 << 0, // IsWindowFocused(): Return true if any children of the window is focused - ImGuiFocusedFlags_RootWindow = 1 << 1, // IsWindowFocused(): Test from root window (top most parent of the current hierarchy) - ImGuiFocusedFlags_AnyWindow = 1 << 2, // IsWindowFocused(): Return true if any window is focused - ImGuiFocusedFlags_RootAndChildWindows = ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows -}; - -// Flags for ImGui::IsItemHovered(), ImGui::IsWindowHovered() -// Note: If you are trying to check whether your mouse should be dispatched to imgui or to your app, you should use the 'io.WantCaptureMouse' boolean for that. Please read the FAQ! -enum ImGuiHoveredFlags_ -{ - ImGuiHoveredFlags_None = 0, // Return true if directly over the item/window, not obstructed by another window, not obstructed by an active popup or modal blocking inputs under them. - ImGuiHoveredFlags_ChildWindows = 1 << 0, // IsWindowHovered() only: Return true if any children of the window is hovered - ImGuiHoveredFlags_RootWindow = 1 << 1, // IsWindowHovered() only: Test from root window (top most parent of the current hierarchy) - ImGuiHoveredFlags_AnyWindow = 1 << 2, // IsWindowHovered() only: Return true if any window is hovered - ImGuiHoveredFlags_AllowWhenBlockedByPopup = 1 << 3, // Return true even if a popup window is normally blocking access to this item/window - //ImGuiHoveredFlags_AllowWhenBlockedByModal = 1 << 4, // Return true even if a modal popup window is normally blocking access to this item/window. FIXME-TODO: Unavailable yet. - ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 5, // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns. - ImGuiHoveredFlags_AllowWhenOverlapped = 1 << 6, // Return true even if the position is overlapped by another window - ImGuiHoveredFlags_RectOnly = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped, - ImGuiHoveredFlags_RootAndChildWindows = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows -}; - -// Flags for ImGui::BeginDragDropSource(), ImGui::AcceptDragDropPayload() -enum ImGuiDragDropFlags_ -{ - ImGuiDragDropFlags_None = 0, - // BeginDragDropSource() flags - ImGuiDragDropFlags_SourceNoPreviewTooltip = 1 << 0, // By default, a successful call to BeginDragDropSource opens a tooltip so you can display a preview or description of the source contents. This flag disable this behavior. - ImGuiDragDropFlags_SourceNoDisableHover = 1 << 1, // By default, when dragging we clear data so that IsItemHovered() will return true, to avoid subsequent user code submitting tooltips. This flag disable this behavior so you can still call IsItemHovered() on the source item. - ImGuiDragDropFlags_SourceNoHoldToOpenOthers = 1 << 2, // Disable the behavior that allows to open tree nodes and collapsing header by holding over them while dragging a source item. - ImGuiDragDropFlags_SourceAllowNullID = 1 << 3, // Allow items such as Text(), Image() that have no unique identifier to be used as drag source, by manufacturing a temporary identifier based on their window-relative position. This is extremely unusual within the dear imgui ecosystem and so we made it explicit. - ImGuiDragDropFlags_SourceExtern = 1 << 4, // External source (from outside of imgui), won't attempt to read current item/window info. Will always return true. Only one Extern source can be active simultaneously. - // AcceptDragDropPayload() flags - ImGuiDragDropFlags_AcceptBeforeDelivery = 1 << 10, // AcceptDragDropPayload() will returns true even before the mouse button is released. You can then call IsDelivery() to test if the payload needs to be delivered. - ImGuiDragDropFlags_AcceptNoDrawDefaultRect = 1 << 11, // Do not draw the default highlight rectangle when hovering over target. - ImGuiDragDropFlags_AcceptNoPreviewTooltip = 1 << 12, // Request hiding the BeginDragDropSource tooltip from the BeginDragDropTarget site. - ImGuiDragDropFlags_AcceptPeekOnly = ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect // For peeking ahead and inspecting the payload before delivery. -}; - -// Standard Drag and Drop payload types. You can define you own payload types using short strings. Types starting with '_' are defined by Dear ImGui. -#define IMGUI_PAYLOAD_TYPE_COLOR_3F "_COL3F" // float[3]: Standard type for colors, without alpha. User code may use this type. -#define IMGUI_PAYLOAD_TYPE_COLOR_4F "_COL4F" // float[4]: Standard type for colors. User code may use this type. - -// A primary data type -enum ImGuiDataType_ -{ - ImGuiDataType_S32, // int - ImGuiDataType_U32, // unsigned int - ImGuiDataType_S64, // long long, __int64 - ImGuiDataType_U64, // unsigned long long, unsigned __int64 - ImGuiDataType_Float, // float - ImGuiDataType_Double, // double - ImGuiDataType_COUNT -}; - -// A cardinal direction -enum ImGuiDir_ -{ - ImGuiDir_None = -1, - ImGuiDir_Left = 0, - ImGuiDir_Right = 1, - ImGuiDir_Up = 2, - ImGuiDir_Down = 3, - ImGuiDir_COUNT -}; - -// User fill ImGuiIO.KeyMap[] array with indices into the ImGuiIO.KeysDown[512] array -enum ImGuiKey_ -{ - ImGuiKey_Tab, - ImGuiKey_LeftArrow, - ImGuiKey_RightArrow, - ImGuiKey_UpArrow, - ImGuiKey_DownArrow, - ImGuiKey_PageUp, - ImGuiKey_PageDown, - ImGuiKey_Home, - ImGuiKey_End, - ImGuiKey_Insert, - ImGuiKey_Delete, - ImGuiKey_Backspace, - ImGuiKey_Space, - ImGuiKey_Enter, - ImGuiKey_Escape, - ImGuiKey_A, // for text edit CTRL+A: select all - ImGuiKey_C, // for text edit CTRL+C: copy - ImGuiKey_V, // for text edit CTRL+V: paste - ImGuiKey_X, // for text edit CTRL+X: cut - ImGuiKey_Y, // for text edit CTRL+Y: redo - ImGuiKey_Z, // for text edit CTRL+Z: undo - ImGuiKey_COUNT -}; - -// [BETA] Gamepad/Keyboard directional navigation -// Keyboard: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.KeysDown[] + io.KeyMap[] arrays. -// Gamepad: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. Back-end: set ImGuiBackendFlags_HasGamepad and fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame(). -// Read instructions in imgui.cpp for more details. Download PNG/PSD at goo.gl/9LgVZW. -enum ImGuiNavInput_ -{ - // Gamepad Mapping - ImGuiNavInput_Activate, // activate / open / toggle / tweak value // e.g. Cross (PS4), A (Xbox), A (Switch), Space (Keyboard) - ImGuiNavInput_Cancel, // cancel / close / exit // e.g. Circle (PS4), B (Xbox), B (Switch), Escape (Keyboard) - ImGuiNavInput_Input, // text input / on-screen keyboard // e.g. Triang.(PS4), Y (Xbox), X (Switch), Return (Keyboard) - ImGuiNavInput_Menu, // tap: toggle menu / hold: focus, move, resize // e.g. Square (PS4), X (Xbox), Y (Switch), Alt (Keyboard) - ImGuiNavInput_DpadLeft, // move / tweak / resize window (w/ PadMenu) // e.g. D-pad Left/Right/Up/Down (Gamepads), Arrow keys (Keyboard) - ImGuiNavInput_DpadRight, // - ImGuiNavInput_DpadUp, // - ImGuiNavInput_DpadDown, // - ImGuiNavInput_LStickLeft, // scroll / move window (w/ PadMenu) // e.g. Left Analog Stick Left/Right/Up/Down - ImGuiNavInput_LStickRight, // - ImGuiNavInput_LStickUp, // - ImGuiNavInput_LStickDown, // - ImGuiNavInput_FocusPrev, // next window (w/ PadMenu) // e.g. L1 or L2 (PS4), LB or LT (Xbox), L or ZL (Switch) - ImGuiNavInput_FocusNext, // prev window (w/ PadMenu) // e.g. R1 or R2 (PS4), RB or RT (Xbox), R or ZL (Switch) - ImGuiNavInput_TweakSlow, // slower tweaks // e.g. L1 or L2 (PS4), LB or LT (Xbox), L or ZL (Switch) - ImGuiNavInput_TweakFast, // faster tweaks // e.g. R1 or R2 (PS4), RB or RT (Xbox), R or ZL (Switch) - - // [Internal] Don't use directly! This is used internally to differentiate keyboard from gamepad inputs for behaviors that require to differentiate them. - // Keyboard behavior that have no corresponding gamepad mapping (e.g. CTRL+TAB) will be directly reading from io.KeysDown[] instead of io.NavInputs[]. - ImGuiNavInput_KeyMenu_, // toggle menu // = io.KeyAlt - ImGuiNavInput_KeyLeft_, // move left // = Arrow keys - ImGuiNavInput_KeyRight_, // move right - ImGuiNavInput_KeyUp_, // move up - ImGuiNavInput_KeyDown_, // move down - ImGuiNavInput_COUNT, - ImGuiNavInput_InternalStart_ = ImGuiNavInput_KeyMenu_ -}; - -// Configuration flags stored in io.ConfigFlags. Set by user/application. -enum ImGuiConfigFlags_ -{ - ImGuiConfigFlags_NavEnableKeyboard = 1 << 0, // Master keyboard navigation enable flag. NewFrame() will automatically fill io.NavInputs[] based on io.KeysDown[]. - ImGuiConfigFlags_NavEnableGamepad = 1 << 1, // Master gamepad navigation enable flag. This is mostly to instruct your imgui back-end to fill io.NavInputs[]. Back-end also needs to set ImGuiBackendFlags_HasGamepad. - ImGuiConfigFlags_NavEnableSetMousePos = 1 << 2, // Instruct navigation to move the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is awkward. Will update io.MousePos and set io.WantSetMousePos=true. If enabled you MUST honor io.WantSetMousePos requests in your binding, otherwise ImGui will react as if the mouse is jumping around back and forth. - ImGuiConfigFlags_NavNoCaptureKeyboard = 1 << 3, // Instruct navigation to not set the io.WantCaptureKeyboard flag when io.NavActive is set. - ImGuiConfigFlags_NoMouse = 1 << 4, // Instruct imgui to clear mouse position/buttons in NewFrame(). This allows ignoring the mouse information set by the back-end. - ImGuiConfigFlags_NoMouseCursorChange = 1 << 5, // Instruct back-end to not alter mouse cursor shape and visibility. Use if the back-end cursor changes are interfering with yours and you don't want to use SetMouseCursor() to change mouse cursor. You may want to honor requests from imgui by reading GetMouseCursor() yourself instead. - - // User storage (to allow your back-end/engine to communicate to code that may be shared between multiple projects. Those flags are not used by core ImGui) - ImGuiConfigFlags_IsSRGB = 1 << 20, // Application is SRGB-aware. - ImGuiConfigFlags_IsTouchScreen = 1 << 21 // Application is using a touch screen instead of a mouse. -}; - -// Back-end capabilities flags stored in io.BackendFlags. Set by imgui_impl_xxx or custom back-end. -enum ImGuiBackendFlags_ -{ - ImGuiBackendFlags_HasGamepad = 1 << 0, // Back-end supports gamepad and currently has one connected. - ImGuiBackendFlags_HasMouseCursors = 1 << 1, // Back-end supports honoring GetMouseCursor() value to change the OS cursor shape. - ImGuiBackendFlags_HasSetMousePos = 1 << 2 // Back-end supports io.WantSetMousePos requests to reposition the OS mouse position (only used if ImGuiConfigFlags_NavEnableSetMousePos is set). -}; - -// Enumeration for PushStyleColor() / PopStyleColor() -enum ImGuiCol_ -{ - ImGuiCol_Text, - ImGuiCol_TextDisabled, - ImGuiCol_WindowBg, // Background of normal windows - ImGuiCol_ChildBg, // Background of child windows - ImGuiCol_PopupBg, // Background of popups, menus, tooltips windows - ImGuiCol_Border, - ImGuiCol_BorderShadow, - ImGuiCol_FrameBg, // Background of checkbox, radio button, plot, slider, text input - ImGuiCol_FrameBgHovered, - ImGuiCol_FrameBgActive, - ImGuiCol_TitleBg, - ImGuiCol_TitleBgActive, - ImGuiCol_TitleBgCollapsed, - ImGuiCol_MenuBarBg, - ImGuiCol_ScrollbarBg, - ImGuiCol_ScrollbarGrab, - ImGuiCol_ScrollbarGrabHovered, - ImGuiCol_ScrollbarGrabActive, - ImGuiCol_CheckMark, - ImGuiCol_SliderGrab, - ImGuiCol_SliderGrabActive, - ImGuiCol_Button, - ImGuiCol_ButtonHovered, - ImGuiCol_ButtonActive, - ImGuiCol_Header, - ImGuiCol_HeaderHovered, - ImGuiCol_HeaderActive, - ImGuiCol_Separator, - ImGuiCol_SeparatorHovered, - ImGuiCol_SeparatorActive, - ImGuiCol_ResizeGrip, - ImGuiCol_ResizeGripHovered, - ImGuiCol_ResizeGripActive, - ImGuiCol_PlotLines, - ImGuiCol_PlotLinesHovered, - ImGuiCol_PlotHistogram, - ImGuiCol_PlotHistogramHovered, - ImGuiCol_TextSelectedBg, - ImGuiCol_ModalWindowDarkening, // Darken/colorize entire screen behind a modal window, when one is active - ImGuiCol_DragDropTarget, - ImGuiCol_NavHighlight, // Gamepad/keyboard: current highlighted item - ImGuiCol_NavWindowingHighlight, // Gamepad/keyboard: when holding NavMenu to focus/move/resize windows - ImGuiCol_COUNT - - // Obsolete names (will be removed) -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - , ImGuiCol_ChildWindowBg = ImGuiCol_ChildBg, ImGuiCol_Column = ImGuiCol_Separator, ImGuiCol_ColumnHovered = ImGuiCol_SeparatorHovered, ImGuiCol_ColumnActive = ImGuiCol_SeparatorActive - //ImGuiCol_CloseButton, ImGuiCol_CloseButtonActive, ImGuiCol_CloseButtonHovered, // [unused since 1.60+] the close button now uses regular button colors. - //ImGuiCol_ComboBg, // [unused since 1.53+] ComboBg has been merged with PopupBg, so a redirect isn't accurate. -#endif -}; - -// Enumeration for PushStyleVar() / PopStyleVar() to temporarily modify the ImGuiStyle structure. -// NB: the enum only refers to fields of ImGuiStyle which makes sense to be pushed/popped inside UI code. During initialization, feel free to just poke into ImGuiStyle directly. -// NB: if changing this enum, you need to update the associated internal table GStyleVarInfo[] accordingly. This is where we link enum values to members offset/type. -enum ImGuiStyleVar_ -{ - // Enum name ......................// Member in ImGuiStyle structure (see ImGuiStyle for descriptions) - ImGuiStyleVar_Alpha, // float Alpha - ImGuiStyleVar_WindowPadding, // ImVec2 WindowPadding - ImGuiStyleVar_WindowRounding, // float WindowRounding - ImGuiStyleVar_WindowBorderSize, // float WindowBorderSize - ImGuiStyleVar_WindowMinSize, // ImVec2 WindowMinSize - ImGuiStyleVar_WindowTitleAlign, // ImVec2 WindowTitleAlign - ImGuiStyleVar_ChildRounding, // float ChildRounding - ImGuiStyleVar_ChildBorderSize, // float ChildBorderSize - ImGuiStyleVar_PopupRounding, // float PopupRounding - ImGuiStyleVar_PopupBorderSize, // float PopupBorderSize - ImGuiStyleVar_FramePadding, // ImVec2 FramePadding - ImGuiStyleVar_FrameRounding, // float FrameRounding - ImGuiStyleVar_FrameBorderSize, // float FrameBorderSize - ImGuiStyleVar_ItemSpacing, // ImVec2 ItemSpacing - ImGuiStyleVar_ItemInnerSpacing, // ImVec2 ItemInnerSpacing - ImGuiStyleVar_IndentSpacing, // float IndentSpacing - ImGuiStyleVar_ScrollbarSize, // float ScrollbarSize - ImGuiStyleVar_ScrollbarRounding, // float ScrollbarRounding - ImGuiStyleVar_GrabMinSize, // float GrabMinSize - ImGuiStyleVar_GrabRounding, // float GrabRounding - ImGuiStyleVar_ButtonTextAlign, // ImVec2 ButtonTextAlign - ImGuiStyleVar_COUNT - - // Obsolete names (will be removed) -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - , ImGuiStyleVar_Count_ = ImGuiStyleVar_COUNT, ImGuiStyleVar_ChildWindowRounding = ImGuiStyleVar_ChildRounding -#endif -}; - -// Enumeration for ColorEdit3() / ColorEdit4() / ColorPicker3() / ColorPicker4() / ColorButton() -enum ImGuiColorEditFlags_ -{ - ImGuiColorEditFlags_None = 0, - ImGuiColorEditFlags_NoAlpha = 1 << 1, // // ColorEdit, ColorPicker, ColorButton: ignore Alpha component (read 3 components from the input pointer). - ImGuiColorEditFlags_NoPicker = 1 << 2, // // ColorEdit: disable picker when clicking on colored square. - ImGuiColorEditFlags_NoOptions = 1 << 3, // // ColorEdit: disable toggling options menu when right-clicking on inputs/small preview. - ImGuiColorEditFlags_NoSmallPreview = 1 << 4, // // ColorEdit, ColorPicker: disable colored square preview next to the inputs. (e.g. to show only the inputs) - ImGuiColorEditFlags_NoInputs = 1 << 5, // // ColorEdit, ColorPicker: disable inputs sliders/text widgets (e.g. to show only the small preview colored square). - ImGuiColorEditFlags_NoTooltip = 1 << 6, // // ColorEdit, ColorPicker, ColorButton: disable tooltip when hovering the preview. - ImGuiColorEditFlags_NoLabel = 1 << 7, // // ColorEdit, ColorPicker: disable display of inline text label (the label is still forwarded to the tooltip and picker). - ImGuiColorEditFlags_NoSidePreview = 1 << 8, // // ColorPicker: disable bigger color preview on right side of the picker, use small colored square preview instead. - ImGuiColorEditFlags_NoDragDrop = 1 << 9, // // ColorEdit: disable drag and drop target. ColorButton: disable drag and drop source. - - // User Options (right-click on widget to change some of them). You can set application defaults using SetColorEditOptions(). The idea is that you probably don't want to override them in most of your calls, let the user choose and/or call SetColorEditOptions() during startup. - ImGuiColorEditFlags_AlphaBar = 1 << 16, // // ColorEdit, ColorPicker: show vertical alpha bar/gradient in picker. - ImGuiColorEditFlags_AlphaPreview = 1 << 17, // // ColorEdit, ColorPicker, ColorButton: display preview as a transparent color over a checkerboard, instead of opaque. - ImGuiColorEditFlags_AlphaPreviewHalf= 1 << 18, // // ColorEdit, ColorPicker, ColorButton: display half opaque / half checkerboard, instead of opaque. - ImGuiColorEditFlags_HDR = 1 << 19, // // (WIP) ColorEdit: Currently only disable 0.0f..1.0f limits in RGBA edition (note: you probably want to use ImGuiColorEditFlags_Float flag as well). - ImGuiColorEditFlags_RGB = 1 << 20, // [Inputs] // ColorEdit: choose one among RGB/HSV/HEX. ColorPicker: choose any combination using RGB/HSV/HEX. - ImGuiColorEditFlags_HSV = 1 << 21, // [Inputs] // " - ImGuiColorEditFlags_HEX = 1 << 22, // [Inputs] // " - ImGuiColorEditFlags_Uint8 = 1 << 23, // [DataType] // ColorEdit, ColorPicker, ColorButton: _display_ values formatted as 0..255. - ImGuiColorEditFlags_Float = 1 << 24, // [DataType] // ColorEdit, ColorPicker, ColorButton: _display_ values formatted as 0.0f..1.0f floats instead of 0..255 integers. No round-trip of value via integers. - ImGuiColorEditFlags_PickerHueBar = 1 << 25, // [PickerMode] // ColorPicker: bar for Hue, rectangle for Sat/Value. - ImGuiColorEditFlags_PickerHueWheel = 1 << 26, // [PickerMode] // ColorPicker: wheel for Hue, triangle for Sat/Value. - - // [Internal] Masks - ImGuiColorEditFlags__InputsMask = ImGuiColorEditFlags_RGB|ImGuiColorEditFlags_HSV|ImGuiColorEditFlags_HEX, - ImGuiColorEditFlags__DataTypeMask = ImGuiColorEditFlags_Uint8|ImGuiColorEditFlags_Float, - ImGuiColorEditFlags__PickerMask = ImGuiColorEditFlags_PickerHueWheel|ImGuiColorEditFlags_PickerHueBar, - ImGuiColorEditFlags__OptionsDefault = ImGuiColorEditFlags_Uint8|ImGuiColorEditFlags_RGB|ImGuiColorEditFlags_PickerHueBar // Change application default using SetColorEditOptions() -}; - -// Enumeration for GetMouseCursor() -// User code may request binding to display given cursor by calling SetMouseCursor(), which is why we have some cursors that are marked unused here -enum ImGuiMouseCursor_ -{ - ImGuiMouseCursor_None = -1, - ImGuiMouseCursor_Arrow = 0, - ImGuiMouseCursor_TextInput, // When hovering over InputText, etc. - ImGuiMouseCursor_ResizeAll, // Unused by imgui functions - ImGuiMouseCursor_ResizeNS, // When hovering over an horizontal border - ImGuiMouseCursor_ResizeEW, // When hovering over a vertical border or a column - ImGuiMouseCursor_ResizeNESW, // When hovering over the bottom-left corner of a window - ImGuiMouseCursor_ResizeNWSE, // When hovering over the bottom-right corner of a window - ImGuiMouseCursor_COUNT - - // Obsolete names (will be removed) -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - , ImGuiMouseCursor_Count_ = ImGuiMouseCursor_COUNT -#endif -}; - -// Condition for ImGui::SetWindow***(), SetNextWindow***(), SetNextTreeNode***() functions -// Important: Treat as a regular enum! Do NOT combine multiple values using binary operators! All the functions above treat 0 as a shortcut to ImGuiCond_Always. -enum ImGuiCond_ -{ - ImGuiCond_Always = 1 << 0, // Set the variable - ImGuiCond_Once = 1 << 1, // Set the variable once per runtime session (only the first call with succeed) - ImGuiCond_FirstUseEver = 1 << 2, // Set the variable if the object/window has no persistently saved data (no entry in .ini file) - ImGuiCond_Appearing = 1 << 3 // Set the variable if the object/window is appearing after being hidden/inactive (or the first time) - - // Obsolete names (will be removed) -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - , ImGuiSetCond_Always = ImGuiCond_Always, ImGuiSetCond_Once = ImGuiCond_Once, ImGuiSetCond_FirstUseEver = ImGuiCond_FirstUseEver, ImGuiSetCond_Appearing = ImGuiCond_Appearing -#endif -}; - -// You may modify the ImGui::GetStyle() main instance during initialization and before NewFrame(). -// During the frame, use ImGui::PushStyleVar(ImGuiStyleVar_XXXX)/PopStyleVar() to alter the main style values, and ImGui::PushStyleColor(ImGuiCol_XXX)/PopStyleColor() for colors. -struct ImGuiStyle -{ - float Alpha; // Global alpha applies to everything in ImGui. - ImVec2 WindowPadding; // Padding within a window. - float WindowRounding; // Radius of window corners rounding. Set to 0.0f to have rectangular windows. - float WindowBorderSize; // Thickness of border around windows. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly). - ImVec2 WindowMinSize; // Minimum window size. This is a global setting. If you want to constraint individual windows, use SetNextWindowSizeConstraints(). - ImVec2 WindowTitleAlign; // Alignment for title bar text. Defaults to (0.0f,0.5f) for left-aligned,vertically centered. - float ChildRounding; // Radius of child window corners rounding. Set to 0.0f to have rectangular windows. - float ChildBorderSize; // Thickness of border around child windows. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly). - float PopupRounding; // Radius of popup window corners rounding. (Note that tooltip windows use WindowRounding) - float PopupBorderSize; // Thickness of border around popup/tooltip windows. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly). - ImVec2 FramePadding; // Padding within a framed rectangle (used by most widgets). - float FrameRounding; // Radius of frame corners rounding. Set to 0.0f to have rectangular frame (used by most widgets). - float FrameBorderSize; // Thickness of border around frames. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly). - ImVec2 ItemSpacing; // Horizontal and vertical spacing between widgets/lines. - ImVec2 ItemInnerSpacing; // Horizontal and vertical spacing between within elements of a composed widget (e.g. a slider and its label). - ImVec2 TouchExtraPadding; // Expand reactive bounding box for touch-based system where touch position is not accurate enough. Unfortunately we don't sort widgets so priority on overlap will always be given to the first widget. So don't grow this too much! - float IndentSpacing; // Horizontal indentation when e.g. entering a tree node. Generally == (FontSize + FramePadding.x*2). - float ColumnsMinSpacing; // Minimum horizontal spacing between two columns. - float ScrollbarSize; // Width of the vertical scrollbar, Height of the horizontal scrollbar. - float ScrollbarRounding; // Radius of grab corners for scrollbar. - float GrabMinSize; // Minimum width/height of a grab box for slider/scrollbar. - float GrabRounding; // Radius of grabs corners rounding. Set to 0.0f to have rectangular slider grabs. - ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f,0.5f) for horizontally+vertically centered. - ImVec2 DisplayWindowPadding; // Window positions are clamped to be visible within the display area by at least this amount. Only covers regular windows. - ImVec2 DisplaySafeAreaPadding; // If you cannot see the edges of your screen (e.g. on a TV) increase the safe area padding. Apply to popups/tooltips as well regular windows. NB: Prefer configuring your TV sets correctly! - float MouseCursorScale; // Scale software rendered mouse cursor (when io.MouseDrawCursor is enabled). May be removed later. - bool AntiAliasedLines; // Enable anti-aliasing on lines/borders. Disable if you are really tight on CPU/GPU. - bool AntiAliasedFill; // Enable anti-aliasing on filled shapes (rounded rectangles, circles, etc.) - float CurveTessellationTol; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. - ImVec4 Colors[ImGuiCol_COUNT]; - - IMGUI_API ImGuiStyle(); - IMGUI_API void ScaleAllSizes(float scale_factor); -}; - -// This is where your app communicate with ImGui. Access via ImGui::GetIO(). -// Read 'Programmer guide' section in .cpp file for general usage. -struct ImGuiIO -{ - //------------------------------------------------------------------ - // Settings (fill once) // Default value: - //------------------------------------------------------------------ - - ImGuiConfigFlags ConfigFlags; // = 0 // See ImGuiConfigFlags_ enum. Set by user/application. Gamepad/keyboard navigation options, etc. - ImGuiBackendFlags BackendFlags; // = 0 // Set ImGuiBackendFlags_ enum. Set by imgui_impl_xxx files or custom back-end to communicate features supported by the back-end. - ImVec2 DisplaySize; // // Display size, in pixels. For clamping windows positions. - float DeltaTime; // = 1.0f/60.0f // Time elapsed since last frame, in seconds. - float IniSavingRate; // = 5.0f // Minimum time between saving positions/sizes to .ini file, in seconds. - const char* IniFilename; // = "imgui.ini" // Path to .ini file. Set NULL to disable automatic .ini loading/saving, if e.g. you want to manually load/save from memory. - const char* LogFilename; // = "imgui_log.txt" // Path to .log file (default parameter to ImGui::LogToFile when no file is specified). - float MouseDoubleClickTime; // = 0.30f // Time for a double-click, in seconds. - float MouseDoubleClickMaxDist; // = 6.0f // Distance threshold to stay in to validate a double-click, in pixels. - float MouseDragThreshold; // = 6.0f // Distance threshold before considering we are dragging. - int KeyMap[ImGuiKey_COUNT]; // // Map of indices into the KeysDown[512] entries array which represent your "native" keyboard state. - float KeyRepeatDelay; // = 0.250f // When holding a key/button, time before it starts repeating, in seconds (for buttons in Repeat mode, etc.). - float KeyRepeatRate; // = 0.050f // When holding a key/button, rate at which it repeats, in seconds. - void* UserData; // = NULL // Store your own data for retrieval by callbacks. - - ImFontAtlas* Fonts; // // Load and assemble one or more fonts into a single tightly packed texture. Output to Fonts array. - float FontGlobalScale; // = 1.0f // Global scale all fonts - bool FontAllowUserScaling; // = false // Allow user scaling text of individual window with CTRL+Wheel. - ImFont* FontDefault; // = NULL // Font to use on NewFrame(). Use NULL to uses Fonts->Fonts[0]. - ImVec2 DisplayFramebufferScale; // = (1.0f,1.0f) // For retina display or other situations where window coordinates are different from framebuffer coordinates. User storage only, presently not used by ImGui. - ImVec2 DisplayVisibleMin; // (0.0f,0.0f) // If you use DisplaySize as a virtual space larger than your screen, set DisplayVisibleMin/Max to the visible area. - ImVec2 DisplayVisibleMax; // (0.0f,0.0f) // If the values are the same, we defaults to Min=(0.0f) and Max=DisplaySize - - // Advanced/subtle behaviors - bool OptMacOSXBehaviors; // = defined(__APPLE__) // OS X style: Text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl - bool OptCursorBlink; // = true // Enable blinking cursor, for users who consider it annoying. - - //------------------------------------------------------------------ - // Settings (User Functions) - //------------------------------------------------------------------ - - // Optional: access OS clipboard - // (default to use native Win32 clipboard on Windows, otherwise uses a private clipboard. Override to access OS clipboard on other architectures) - const char* (*GetClipboardTextFn)(void* user_data); - void (*SetClipboardTextFn)(void* user_data, const char* text); - void* ClipboardUserData; - - // Optional: notify OS Input Method Editor of the screen position of your cursor for text input position (e.g. when using Japanese/Chinese IME in Windows) - // (default to use native imm32 api on Windows) - void (*ImeSetInputScreenPosFn)(int x, int y); - void* ImeWindowHandle; // (Windows) Set this to your HWND to get automatic IME cursor positioning. - -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - // [OBSOLETE] Rendering function, will be automatically called in Render(). Please call your rendering function yourself now! - // You can obtain the ImDrawData* by calling ImGui::GetDrawData() after Render(). See example applications if you are unsure of how to implement this. - void (*RenderDrawListsFn)(ImDrawData* data); -#else - // This is only here to keep ImGuiIO the same size, so that IMGUI_DISABLE_OBSOLETE_FUNCTIONS can exceptionally be used outside of imconfig.h. - void* RenderDrawListsFnDummy; -#endif - - //------------------------------------------------------------------ - // Input - Fill before calling NewFrame() - //------------------------------------------------------------------ - - ImVec2 MousePos; // Mouse position, in pixels. Set to ImVec2(-FLT_MAX,-FLT_MAX) if mouse is unavailable (on another screen, etc.) - bool MouseDown[5]; // Mouse buttons: left, right, middle + extras. ImGui itself mostly only uses left button (BeginPopupContext** are using right button). Others buttons allows us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API. - float MouseWheel; // Mouse wheel Vertical: 1 unit scrolls about 5 lines text. - float MouseWheelH; // Mouse wheel Horizontal. Most users don't have a mouse with an horizontal wheel, may not be filled by all back-ends. - bool MouseDrawCursor; // Request ImGui to draw a mouse cursor for you (if you are on a platform without a mouse cursor). - bool KeyCtrl; // Keyboard modifier pressed: Control - bool KeyShift; // Keyboard modifier pressed: Shift - bool KeyAlt; // Keyboard modifier pressed: Alt - bool KeySuper; // Keyboard modifier pressed: Cmd/Super/Windows - bool KeysDown[512]; // Keyboard keys that are pressed (ideally left in the "native" order your engine has access to keyboard keys, so you can use your own defines/enums for keys). - ImWchar InputCharacters[16+1]; // List of characters input (translated by user from keypress+keyboard state). Fill using AddInputCharacter() helper. - float NavInputs[ImGuiNavInput_COUNT]; // Gamepad inputs (keyboard keys will be auto-mapped and be written here by ImGui::NewFrame, all values will be cleared back to zero in ImGui::EndFrame) - - // Functions - IMGUI_API void AddInputCharacter(ImWchar c); // Add new character into InputCharacters[] - IMGUI_API void AddInputCharactersUTF8(const char* utf8_chars); // Add new characters into InputCharacters[] from an UTF-8 string - inline void ClearInputCharacters() { InputCharacters[0] = 0; } // Clear the text input buffer manually - - //------------------------------------------------------------------ - // Output - Retrieve after calling NewFrame() - //------------------------------------------------------------------ - - bool WantCaptureMouse; // When io.WantCaptureMouse is true, imgui will use the mouse inputs, do not dispatch them to your main game/application (in both cases, always pass on mouse inputs to imgui). (e.g. unclicked mouse is hovering over an imgui window, widget is active, mouse was clicked over an imgui window, etc.). - bool WantCaptureKeyboard; // When io.WantCaptureKeyboard is true, imgui will use the keyboard inputs, do not dispatch them to your main game/application (in both cases, always pass keyboard inputs to imgui). (e.g. InputText active, or an imgui window is focused and navigation is enabled, etc.). - bool WantTextInput; // Mobile/console: when io.WantTextInput is true, you may display an on-screen keyboard. This is set by ImGui when it wants textual keyboard input to happen (e.g. when a InputText widget is active). - bool WantSetMousePos; // MousePos has been altered, back-end should reposition mouse on next frame. Set only when ImGuiConfigFlags_NavEnableSetMousePos flag is enabled. - bool WantSaveIniSettings; // When manual .ini load/save is active (io.IniFilename == NULL), this will be set to notify your application that you can call SaveIniSettingsToMemory() and save yourself. IMPORTANT: You need to clear io.WantSaveIniSettings yourself. - bool NavActive; // Directional navigation is currently allowed (will handle ImGuiKey_NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag. - bool NavVisible; // Directional navigation is visible and allowed (will handle ImGuiKey_NavXXX events). - float Framerate; // Application framerate estimation, in frame per second. Solely for convenience. Rolling average estimation based on IO.DeltaTime over 120 frames - int MetricsRenderVertices; // Vertices output during last call to Render() - int MetricsRenderIndices; // Indices output during last call to Render() = number of triangles * 3 - int MetricsActiveWindows; // Number of visible root windows (exclude child windows) - ImVec2 MouseDelta; // Mouse delta. Note that this is zero if either current or previous position are invalid (-FLT_MAX,-FLT_MAX), so a disappearing/reappearing mouse won't have a huge delta. - - //------------------------------------------------------------------ - // [Internal] ImGui will maintain those fields. Forward compatibility not guaranteed! - //------------------------------------------------------------------ - - ImVec2 MousePosPrev; // Previous mouse position temporary storage (nb: not for public use, set to MousePos in NewFrame()) - ImVec2 MouseClickedPos[5]; // Position at time of clicking - float MouseClickedTime[5]; // Time of last click (used to figure out double-click) - bool MouseClicked[5]; // Mouse button went from !Down to Down - bool MouseDoubleClicked[5]; // Has mouse button been double-clicked? - bool MouseReleased[5]; // Mouse button went from Down to !Down - bool MouseDownOwned[5]; // Track if button was clicked inside a window. We don't request mouse capture from the application if click started outside ImGui bounds. - float MouseDownDuration[5]; // Duration the mouse button has been down (0.0f == just clicked) - float MouseDownDurationPrev[5]; // Previous time the mouse button has been down - ImVec2 MouseDragMaxDistanceAbs[5]; // Maximum distance, absolute, on each axis, of how much mouse has traveled from the clicking point - float MouseDragMaxDistanceSqr[5]; // Squared maximum distance of how much mouse has traveled from the clicking point - float KeysDownDuration[512]; // Duration the keyboard key has been down (0.0f == just pressed) - float KeysDownDurationPrev[512]; // Previous duration the key has been down - float NavInputsDownDuration[ImGuiNavInput_COUNT]; - float NavInputsDownDurationPrev[ImGuiNavInput_COUNT]; - - IMGUI_API ImGuiIO(); -}; - -//----------------------------------------------------------------------------- -// Obsolete functions (Will be removed! Read 'API BREAKING CHANGES' section in imgui.cpp for details) -//----------------------------------------------------------------------------- - -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS -namespace ImGui -{ - // OBSOLETED in 1.61 (from Apr 2018) - IMGUI_API bool InputFloat(const char* label, float* v, float step, float step_fast, int decimal_precision, ImGuiInputTextFlags extra_flags = 0); // Use the 'const char* format' version instead of 'decimal_precision'! - IMGUI_API bool InputFloat2(const char* label, float v[2], int decimal_precision, ImGuiInputTextFlags extra_flags = 0); - IMGUI_API bool InputFloat3(const char* label, float v[3], int decimal_precision, ImGuiInputTextFlags extra_flags = 0); - IMGUI_API bool InputFloat4(const char* label, float v[4], int decimal_precision, ImGuiInputTextFlags extra_flags = 0); - // OBSOLETED in 1.60 (from Dec 2017) - static inline bool IsAnyWindowFocused() { return IsWindowFocused(ImGuiFocusedFlags_AnyWindow); } - static inline bool IsAnyWindowHovered() { return IsWindowHovered(ImGuiHoveredFlags_AnyWindow); } - static inline ImVec2 CalcItemRectClosestPoint(const ImVec2& pos, bool on_edge = false, float outward = 0.f) { (void)on_edge; (void)outward; IM_ASSERT(0); return pos; } - // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) - static inline void ShowTestWindow() { return ShowDemoWindow(); } - static inline bool IsRootWindowFocused() { return IsWindowFocused(ImGuiFocusedFlags_RootWindow); } - static inline bool IsRootWindowOrAnyChildFocused() { return IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows); } - static inline void SetNextWindowContentWidth(float w) { SetNextWindowContentSize(ImVec2(w, 0.0f)); } - static inline float GetItemsLineHeightWithSpacing() { return GetFrameHeightWithSpacing(); } - // OBSOLETED in 1.52 (between Aug 2017 and Oct 2017) - IMGUI_API bool Begin(const char* name, bool* p_open, const ImVec2& size_on_first_use, float bg_alpha_override = -1.0f, ImGuiWindowFlags flags = 0); // Use SetNextWindowSize(size, ImGuiCond_FirstUseEver) + SetNextWindowBgAlpha() instead. - static inline bool IsRootWindowOrAnyChildHovered() { return IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows); } - static inline void AlignFirstTextHeightToWidgets() { AlignTextToFramePadding(); } - static inline void SetNextWindowPosCenter(ImGuiCond c=0) { ImGuiIO& io = GetIO(); SetNextWindowPos(ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f), c, ImVec2(0.5f, 0.5f)); } - // OBSOLETED in 1.51 (between Jun 2017 and Aug 2017) - static inline bool IsItemHoveredRect() { return IsItemHovered(ImGuiHoveredFlags_RectOnly); } - static inline bool IsPosHoveringAnyWindow(const ImVec2&) { IM_ASSERT(0); return false; } // This was misleading and partly broken. You probably want to use the ImGui::GetIO().WantCaptureMouse flag instead. - static inline bool IsMouseHoveringAnyWindow() { return IsWindowHovered(ImGuiHoveredFlags_AnyWindow); } - static inline bool IsMouseHoveringWindow() { return IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem); } - // OBSOLETED IN 1.49 (between Apr 2016 and May 2016) - static inline bool CollapsingHeader(const char* label, const char* str_id, bool framed = true, bool default_open = false) { (void)str_id; (void)framed; ImGuiTreeNodeFlags default_open_flags = 1 << 5; return CollapsingHeader(label, (default_open ? default_open_flags : 0)); } -} -#endif - -//----------------------------------------------------------------------------- -// Helpers -//----------------------------------------------------------------------------- - -// Helper: Lightweight std::vector<> like class to avoid dragging dependencies (also: Windows implementation of STL with debug enabled is absurdly slow, so let's bypass it so our code runs fast in debug). -// *Important* Our implementation does NOT call C++ constructors/destructors. This is intentional, we do not require it but you have to be mindful of that. Do _not_ use this class as a std::vector replacement in your code! -template -class ImVector -{ -public: - int Size; - int Capacity; - T* Data; - - typedef T value_type; - typedef value_type* iterator; - typedef const value_type* const_iterator; - - inline ImVector() { Size = Capacity = 0; Data = NULL; } - inline ~ImVector() { if (Data) ImGui::MemFree(Data); } - inline ImVector(const ImVector& src) { Size = Capacity = 0; Data = NULL; operator=(src); } - inline ImVector& operator=(const ImVector& src) { clear(); resize(src.Size); memcpy(Data, src.Data, (size_t)Size * sizeof(value_type)); return *this; } - - inline bool empty() const { return Size == 0; } - inline int size() const { return Size; } - inline int capacity() const { return Capacity; } - inline value_type& operator[](int i) { IM_ASSERT(i < Size); return Data[i]; } - inline const value_type& operator[](int i) const { IM_ASSERT(i < Size); return Data[i]; } - - inline void clear() { if (Data) { Size = Capacity = 0; ImGui::MemFree(Data); Data = NULL; } } - inline iterator begin() { return Data; } - inline const_iterator begin() const { return Data; } - inline iterator end() { return Data + Size; } - inline const_iterator end() const { return Data + Size; } - inline value_type& front() { IM_ASSERT(Size > 0); return Data[0]; } - inline const value_type& front() const { IM_ASSERT(Size > 0); return Data[0]; } - inline value_type& back() { IM_ASSERT(Size > 0); return Data[Size - 1]; } - inline const value_type& back() const { IM_ASSERT(Size > 0); return Data[Size - 1]; } - inline void swap(ImVector& rhs) { int rhs_size = rhs.Size; rhs.Size = Size; Size = rhs_size; int rhs_cap = rhs.Capacity; rhs.Capacity = Capacity; Capacity = rhs_cap; value_type* rhs_data = rhs.Data; rhs.Data = Data; Data = rhs_data; } - - inline int _grow_capacity(int sz) const { int new_capacity = Capacity ? (Capacity + Capacity/2) : 8; return new_capacity > sz ? new_capacity : sz; } - inline void resize(int new_size) { if (new_size > Capacity) reserve(_grow_capacity(new_size)); Size = new_size; } - inline void resize(int new_size,const value_type& v){ if (new_size > Capacity) reserve(_grow_capacity(new_size)); if (new_size > Size) for (int n = Size; n < new_size; n++) memcpy(&Data[n], &v, sizeof(v)); Size = new_size; } - inline void reserve(int new_capacity) - { - if (new_capacity <= Capacity) - return; - value_type* new_data = (value_type*)ImGui::MemAlloc((size_t)new_capacity * sizeof(value_type)); - if (Data) - { - memcpy(new_data, Data, (size_t)Size * sizeof(value_type)); - ImGui::MemFree(Data); - } - Data = new_data; - Capacity = new_capacity; - } - - // NB: It is forbidden to call push_back/push_front/insert with a reference pointing inside the ImVector data itself! e.g. v.push_back(v[10]) is forbidden. - inline void push_back(const value_type& v) { if (Size == Capacity) reserve(_grow_capacity(Size + 1)); memcpy(&Data[Size], &v, sizeof(v)); Size++; } - inline void pop_back() { IM_ASSERT(Size > 0); Size--; } - inline void push_front(const value_type& v) { if (Size == 0) push_back(v); else insert(Data, v); } - inline iterator erase(const_iterator it) { IM_ASSERT(it >= Data && it < Data+Size); const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + 1, ((size_t)Size - (size_t)off - 1) * sizeof(value_type)); Size--; return Data + off; } - inline iterator erase(const_iterator it, const_iterator it_last){ IM_ASSERT(it >= Data && it < Data+Size && it_last > it && it_last <= Data+Size); const ptrdiff_t count = it_last - it; const ptrdiff_t off = it - Data; memmove(Data + off, Data + off + count, ((size_t)Size - (size_t)off - count) * sizeof(value_type)); Size -= (int)count; return Data + off; } - inline iterator erase_unsorted(const_iterator it) { IM_ASSERT(it >= Data && it < Data+Size); const ptrdiff_t off = it - Data; if (it < Data+Size-1) memcpy(Data + off, Data + Size - 1, sizeof(value_type)); Size--; return Data + off; } - inline iterator insert(const_iterator it, const value_type& v) { IM_ASSERT(it >= Data && it <= Data+Size); const ptrdiff_t off = it - Data; if (Size == Capacity) reserve(_grow_capacity(Size + 1)); if (off < (int)Size) memmove(Data + off + 1, Data + off, ((size_t)Size - (size_t)off) * sizeof(value_type)); memcpy(&Data[off], &v, sizeof(v)); Size++; return Data + off; } - inline bool contains(const value_type& v) const { const T* data = Data; const T* data_end = Data + Size; while (data < data_end) if (*data++ == v) return true; return false; } -}; - -// Helper: IM_NEW(), IM_PLACEMENT_NEW(), IM_DELETE() macros to call MemAlloc + Placement New, Placement Delete + MemFree -// We call C++ constructor on own allocated memory via the placement "new(ptr) Type()" syntax. -// Defining a custom placement new() with a dummy parameter allows us to bypass including which on some platforms complains when user has disabled exceptions. -struct ImNewDummy {}; -inline void* operator new(size_t, ImNewDummy, void* ptr) { return ptr; } -inline void operator delete(void*, ImNewDummy, void*) {} // This is only required so we can use the symetrical new() -#define IM_PLACEMENT_NEW(_PTR) new(ImNewDummy(), _PTR) -#define IM_NEW(_TYPE) new(ImNewDummy(), ImGui::MemAlloc(sizeof(_TYPE))) _TYPE -template void IM_DELETE(T* p) { if (p) { p->~T(); ImGui::MemFree(p); } } - -// Helper: Execute a block of code at maximum once a frame. Convenient if you want to quickly create an UI within deep-nested code that runs multiple times every frame. -// Usage: static ImGuiOnceUponAFrame oaf; if (oaf) ImGui::Text("This will be called only once per frame"); -struct ImGuiOnceUponAFrame -{ - ImGuiOnceUponAFrame() { RefFrame = -1; } - mutable int RefFrame; - operator bool() const { int current_frame = ImGui::GetFrameCount(); if (RefFrame == current_frame) return false; RefFrame = current_frame; return true; } -}; - -// Helper: Macro for ImGuiOnceUponAFrame. Attention: The macro expands into 2 statement so make sure you don't use it within e.g. an if() statement without curly braces. -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // Will obsolete -#define IMGUI_ONCE_UPON_A_FRAME static ImGuiOnceUponAFrame imgui_oaf; if (imgui_oaf) -#endif - -// Helper: Parse and apply text filters. In format "aaaaa[,bbbb][,ccccc]" -struct ImGuiTextFilter -{ - struct TextRange - { - const char* b; - const char* e; - - TextRange() { b = e = NULL; } - TextRange(const char* _b, const char* _e) { b = _b; e = _e; } - const char* begin() const { return b; } - const char* end() const { return e; } - bool empty() const { return b == e; } - char front() const { return *b; } - static bool is_blank(char c) { return c == ' ' || c == '\t'; } - void trim_blanks() { while (b < e && is_blank(*b)) b++; while (e > b && is_blank(*(e-1))) e--; } - IMGUI_API void split(char separator, ImVector& out); - }; - - char InputBuf[256]; - ImVector Filters; - int CountGrep; - - IMGUI_API ImGuiTextFilter(const char* default_filter = ""); - IMGUI_API bool Draw(const char* label = "Filter (inc,-exc)", float width = 0.0f); // Helper calling InputText+Build - IMGUI_API bool PassFilter(const char* text, const char* text_end = NULL) const; - IMGUI_API void Build(); - void Clear() { InputBuf[0] = 0; Build(); } - bool IsActive() const { return !Filters.empty(); } -}; - -// Helper: Text buffer for logging/accumulating text -struct ImGuiTextBuffer -{ - ImVector Buf; - - ImGuiTextBuffer() { Buf.push_back(0); } - inline char operator[](int i) { return Buf.Data[i]; } - const char* begin() const { return &Buf.front(); } - const char* end() const { return &Buf.back(); } // Buf is zero-terminated, so end() will point on the zero-terminator - int size() const { return Buf.Size - 1; } - bool empty() { return Buf.Size <= 1; } - void clear() { Buf.clear(); Buf.push_back(0); } - void reserve(int capacity) { Buf.reserve(capacity); } - const char* c_str() const { return Buf.Data; } - IMGUI_API void appendf(const char* fmt, ...) IM_FMTARGS(2); - IMGUI_API void appendfv(const char* fmt, va_list args) IM_FMTLIST(2); -}; - -// Helper: Simple Key->value storage -// Typically you don't have to worry about this since a storage is held within each Window. -// We use it to e.g. store collapse state for a tree (Int 0/1) -// This is optimized for efficient lookup (dichotomy into a contiguous buffer) and rare insertion (typically tied to user interactions aka max once a frame) -// You can use it as custom user storage for temporary values. Declare your own storage if, for example: -// - You want to manipulate the open/close state of a particular sub-tree in your interface (tree node uses Int 0/1 to store their state). -// - You want to store custom debug data easily without adding or editing structures in your code (probably not efficient, but convenient) -// Types are NOT stored, so it is up to you to make sure your Key don't collide with different types. -struct ImGuiStorage -{ - struct Pair - { - ImGuiID key; - union { int val_i; float val_f; void* val_p; }; - Pair(ImGuiID _key, int _val_i) { key = _key; val_i = _val_i; } - Pair(ImGuiID _key, float _val_f) { key = _key; val_f = _val_f; } - Pair(ImGuiID _key, void* _val_p) { key = _key; val_p = _val_p; } - }; - ImVector Data; - - // - Get***() functions find pair, never add/allocate. Pairs are sorted so a query is O(log N) - // - Set***() functions find pair, insertion on demand if missing. - // - Sorted insertion is costly, paid once. A typical frame shouldn't need to insert any new pair. - void Clear() { Data.clear(); } - IMGUI_API int GetInt(ImGuiID key, int default_val = 0) const; - IMGUI_API void SetInt(ImGuiID key, int val); - IMGUI_API bool GetBool(ImGuiID key, bool default_val = false) const; - IMGUI_API void SetBool(ImGuiID key, bool val); - IMGUI_API float GetFloat(ImGuiID key, float default_val = 0.0f) const; - IMGUI_API void SetFloat(ImGuiID key, float val); - IMGUI_API void* GetVoidPtr(ImGuiID key) const; // default_val is NULL - IMGUI_API void SetVoidPtr(ImGuiID key, void* val); - - // - Get***Ref() functions finds pair, insert on demand if missing, return pointer. Useful if you intend to do Get+Set. - // - References are only valid until a new value is added to the storage. Calling a Set***() function or a Get***Ref() function invalidates the pointer. - // - A typical use case where this is convenient for quick hacking (e.g. add storage during a live Edit&Continue session if you can't modify existing struct) - // float* pvar = ImGui::GetFloatRef(key); ImGui::SliderFloat("var", pvar, 0, 100.0f); some_var += *pvar; - IMGUI_API int* GetIntRef(ImGuiID key, int default_val = 0); - IMGUI_API bool* GetBoolRef(ImGuiID key, bool default_val = false); - IMGUI_API float* GetFloatRef(ImGuiID key, float default_val = 0.0f); - IMGUI_API void** GetVoidPtrRef(ImGuiID key, void* default_val = NULL); - - // Use on your own storage if you know only integer are being stored (open/close all tree nodes) - IMGUI_API void SetAllInt(int val); - - // For quicker full rebuild of a storage (instead of an incremental one), you may add all your contents and then sort once. - IMGUI_API void BuildSortByKey(); -}; - -// Shared state of InputText(), passed to callback when a ImGuiInputTextFlags_Callback* flag is used and the corresponding callback is triggered. -struct ImGuiTextEditCallbackData -{ - ImGuiInputTextFlags EventFlag; // One of ImGuiInputTextFlags_Callback* // Read-only - ImGuiInputTextFlags Flags; // What user passed to InputText() // Read-only - void* UserData; // What user passed to InputText() // Read-only - bool ReadOnly; // Read-only mode // Read-only - - // CharFilter event: - ImWchar EventChar; // Character input // Read-write (replace character or set to zero) - - // Completion,History,Always events: - // If you modify the buffer contents make sure you update 'BufTextLen' and set 'BufDirty' to true. - ImGuiKey EventKey; // Key pressed (Up/Down/TAB) // Read-only - char* Buf; // Current text buffer // Read-write (pointed data only, can't replace the actual pointer) - int BufTextLen; // Current text length in bytes // Read-write - int BufSize; // Maximum text length in bytes // Read-only - bool BufDirty; // Set if you modify Buf/BufTextLen!! // Write - int CursorPos; // // Read-write - int SelectionStart; // // Read-write (== to SelectionEnd when no selection) - int SelectionEnd; // // Read-write - - // NB: Helper functions for text manipulation. Calling those function loses selection. - IMGUI_API void DeleteChars(int pos, int bytes_count); - IMGUI_API void InsertChars(int pos, const char* text, const char* text_end = NULL); - bool HasSelection() const { return SelectionStart != SelectionEnd; } -}; - -// Resizing callback data to apply custom constraint. As enabled by SetNextWindowSizeConstraints(). Callback is called during the next Begin(). -// NB: For basic min/max size constraint on each axis you don't need to use the callback! The SetNextWindowSizeConstraints() parameters are enough. -struct ImGuiSizeCallbackData -{ - void* UserData; // Read-only. What user passed to SetNextWindowSizeConstraints() - ImVec2 Pos; // Read-only. Window position, for reference. - ImVec2 CurrentSize; // Read-only. Current window size. - ImVec2 DesiredSize; // Read-write. Desired size, based on user's mouse position. Write to this field to restrain resizing. -}; - -// Data payload for Drag and Drop operations -struct ImGuiPayload -{ - // Members - void* Data; // Data (copied and owned by dear imgui) - int DataSize; // Data size - - // [Internal] - ImGuiID SourceId; // Source item id - ImGuiID SourceParentId; // Source parent id (if available) - int DataFrameCount; // Data timestamp - char DataType[32+1]; // Data type tag (short user-supplied string, 32 characters max) - bool Preview; // Set when AcceptDragDropPayload() was called and mouse has been hovering the target item (nb: handle overlapping drag targets) - bool Delivery; // Set when AcceptDragDropPayload() was called and mouse button is released over the target item. - - ImGuiPayload() { Clear(); } - void Clear() { SourceId = SourceParentId = 0; Data = NULL; DataSize = 0; memset(DataType, 0, sizeof(DataType)); DataFrameCount = -1; Preview = Delivery = false; } - bool IsDataType(const char* type) const { return DataFrameCount != -1 && strcmp(type, DataType) == 0; } - bool IsPreview() const { return Preview; } - bool IsDelivery() const { return Delivery; } -}; - -// Helpers macros to generate 32-bits encoded colors -#ifdef IMGUI_USE_BGRA_PACKED_COLOR -#define IM_COL32_R_SHIFT 16 -#define IM_COL32_G_SHIFT 8 -#define IM_COL32_B_SHIFT 0 -#define IM_COL32_A_SHIFT 24 -#define IM_COL32_A_MASK 0xFF000000 -#else -#define IM_COL32_R_SHIFT 0 -#define IM_COL32_G_SHIFT 8 -#define IM_COL32_B_SHIFT 16 -#define IM_COL32_A_SHIFT 24 -#define IM_COL32_A_MASK 0xFF000000 -#endif -#define IM_COL32(R,G,B,A) (((ImU32)(A)<>IM_COL32_R_SHIFT)&0xFF) * sc; Value.y = (float)((rgba>>IM_COL32_G_SHIFT)&0xFF) * sc; Value.z = (float)((rgba>>IM_COL32_B_SHIFT)&0xFF) * sc; Value.w = (float)((rgba>>IM_COL32_A_SHIFT)&0xFF) * sc; } - ImColor(float r, float g, float b, float a = 1.0f) { Value.x = r; Value.y = g; Value.z = b; Value.w = a; } - ImColor(const ImVec4& col) { Value = col; } - inline operator ImU32() const { return ImGui::ColorConvertFloat4ToU32(Value); } - inline operator ImVec4() const { return Value; } - - // FIXME-OBSOLETE: May need to obsolete/cleanup those helpers. - inline void SetHSV(float h, float s, float v, float a = 1.0f){ ImGui::ColorConvertHSVtoRGB(h, s, v, Value.x, Value.y, Value.z); Value.w = a; } - static ImColor HSV(float h, float s, float v, float a = 1.0f) { float r,g,b; ImGui::ColorConvertHSVtoRGB(h, s, v, r, g, b); return ImColor(r,g,b,a); } -}; - -// Helper: Manually clip large list of items. -// If you are submitting lots of evenly spaced items and you have a random access to the list, you can perform coarse clipping based on visibility to save yourself from processing those items at all. -// The clipper calculates the range of visible items and advance the cursor to compensate for the non-visible items we have skipped. -// ImGui already clip items based on their bounds but it needs to measure text size to do so. Coarse clipping before submission makes this cost and your own data fetching/submission cost null. -// Usage: -// ImGuiListClipper clipper(1000); // we have 1000 elements, evenly spaced. -// while (clipper.Step()) -// for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) -// ImGui::Text("line number %d", i); -// - Step 0: the clipper let you process the first element, regardless of it being visible or not, so we can measure the element height (step skipped if we passed a known height as second arg to constructor). -// - Step 1: the clipper infer height from first element, calculate the actual range of elements to display, and position the cursor before the first element. -// - (Step 2: dummy step only required if an explicit items_height was passed to constructor or Begin() and user call Step(). Does nothing and switch to Step 3.) -// - Step 3: the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), advance the cursor to the end of the list and then returns 'false' to end the loop. -struct ImGuiListClipper -{ - float StartPosY; - float ItemsHeight; - int ItemsCount, StepNo, DisplayStart, DisplayEnd; - - // items_count: Use -1 to ignore (you can call Begin later). Use INT_MAX if you don't know how many items you have (in which case the cursor won't be advanced in the final step). - // items_height: Use -1.0f to be calculated automatically on first step. Otherwise pass in the distance between your items, typically GetTextLineHeightWithSpacing() or GetFrameHeightWithSpacing(). - // If you don't specify an items_height, you NEED to call Step(). If you specify items_height you may call the old Begin()/End() api directly, but prefer calling Step(). - ImGuiListClipper(int items_count = -1, float items_height = -1.0f) { Begin(items_count, items_height); } // NB: Begin() initialize every fields (as we allow user to call Begin/End multiple times on a same instance if they want). - ~ImGuiListClipper() { IM_ASSERT(ItemsCount == -1); } // Assert if user forgot to call End() or Step() until false. - - IMGUI_API bool Step(); // Call until it returns false. The DisplayStart/DisplayEnd fields will be set and you can process/draw those items. - IMGUI_API void Begin(int items_count, float items_height = -1.0f); // Automatically called by constructor if you passed 'items_count' or by Step() in Step 1. - IMGUI_API void End(); // Automatically called on the last call of Step() that returns false. -}; - -//----------------------------------------------------------------------------- -// Draw List -// Hold a series of drawing commands. The user provides a renderer for ImDrawData which essentially contains an array of ImDrawList. -//----------------------------------------------------------------------------- - -// Draw callbacks for advanced uses. -// NB- You most likely do NOT need to use draw callbacks just to create your own widget or customized UI rendering (you can poke into the draw list for that) -// Draw callback may be useful for example, A) Change your GPU render state, B) render a complex 3D scene inside a UI element (without an intermediate texture/render target), etc. -// The expected behavior from your rendering function is 'if (cmd.UserCallback != NULL) cmd.UserCallback(parent_list, cmd); else RenderTriangles()' -typedef void (*ImDrawCallback)(const ImDrawList* parent_list, const ImDrawCmd* cmd); - -// Typically, 1 command = 1 GPU draw call (unless command is a callback) -struct ImDrawCmd -{ - unsigned int ElemCount; // Number of indices (multiple of 3) to be rendered as triangles. Vertices are stored in the callee ImDrawList's vtx_buffer[] array, indices in idx_buffer[]. - ImVec4 ClipRect; // Clipping rectangle (x1, y1, x2, y2). Subtract ImDrawData->DisplayPos to get clipping rectangle in "viewport" coordinates - ImTextureID TextureId; // User-provided texture ID. Set by user in ImfontAtlas::SetTexID() for fonts or passed to Image*() functions. Ignore if never using images or multiple fonts atlas. - ImDrawCallback UserCallback; // If != NULL, call the function instead of rendering the vertices. clip_rect and texture_id will be set normally. - void* UserCallbackData; // The draw callback code can access this. - - ImDrawCmd() { ElemCount = 0; ClipRect.x = ClipRect.y = ClipRect.z = ClipRect.w = 0.0f; TextureId = NULL; UserCallback = NULL; UserCallbackData = NULL; } -}; - -// Vertex index (override with '#define ImDrawIdx unsigned int' inside in imconfig.h) -#ifndef ImDrawIdx -typedef unsigned short ImDrawIdx; -#endif - -// Vertex layout -#ifndef IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT -struct ImDrawVert -{ - ImVec2 pos; - ImVec2 uv; - ImU32 col; -}; -#else -// You can override the vertex format layout by defining IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT in imconfig.h -// The code expect ImVec2 pos (8 bytes), ImVec2 uv (8 bytes), ImU32 col (4 bytes), but you can re-order them or add other fields as needed to simplify integration in your engine. -// The type has to be described within the macro (you can either declare the struct or use a typedef) -// NOTE: IMGUI DOESN'T CLEAR THE STRUCTURE AND DOESN'T CALL A CONSTRUCTOR SO ANY CUSTOM FIELD WILL BE UNINITIALIZED. IF YOU ADD EXTRA FIELDS (SUCH AS A 'Z' COORDINATES) YOU WILL NEED TO CLEAR THEM DURING RENDER OR TO IGNORE THEM. -IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT; -#endif - -// Draw channels are used by the Columns API to "split" the render list into different channels while building, so items of each column can be batched together. -// You can also use them to simulate drawing layers and submit primitives in a different order than how they will be rendered. -struct ImDrawChannel -{ - ImVector CmdBuffer; - ImVector IdxBuffer; -}; - -enum ImDrawCornerFlags_ -{ - ImDrawCornerFlags_TopLeft = 1 << 0, // 0x1 - ImDrawCornerFlags_TopRight = 1 << 1, // 0x2 - ImDrawCornerFlags_BotLeft = 1 << 2, // 0x4 - ImDrawCornerFlags_BotRight = 1 << 3, // 0x8 - ImDrawCornerFlags_Top = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_TopRight, // 0x3 - ImDrawCornerFlags_Bot = ImDrawCornerFlags_BotLeft | ImDrawCornerFlags_BotRight, // 0xC - ImDrawCornerFlags_Left = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotLeft, // 0x5 - ImDrawCornerFlags_Right = ImDrawCornerFlags_TopRight | ImDrawCornerFlags_BotRight, // 0xA - ImDrawCornerFlags_All = 0xF // In your function calls you may use ~0 (= all bits sets) instead of ImDrawCornerFlags_All, as a convenience -}; - -enum ImDrawListFlags_ -{ - ImDrawListFlags_AntiAliasedLines = 1 << 0, - ImDrawListFlags_AntiAliasedFill = 1 << 1 -}; - -// Draw command list -// This is the low-level list of polygons that ImGui functions are filling. At the end of the frame, all command lists are passed to your ImGuiIO::RenderDrawListFn function for rendering. -// Each ImGui window contains its own ImDrawList. You can use ImGui::GetWindowDrawList() to access the current window draw list and draw custom primitives. -// You can interleave normal ImGui:: calls and adding primitives to the current draw list. -// All positions are generally in pixel coordinates (top-left at (0,0), bottom-right at io.DisplaySize), but you are totally free to apply whatever transformation matrix to want to the data (if you apply such transformation you'll want to apply it to ClipRect as well) -// Important: Primitives are always added to the list and not culled (culling is done at higher-level by ImGui:: functions), if you use this API a lot consider coarse culling your drawn objects. -struct ImDrawList -{ - // This is what you have to render - ImVector CmdBuffer; // Draw commands. Typically 1 command = 1 GPU draw call, unless the command is a callback. - ImVector IdxBuffer; // Index buffer. Each command consume ImDrawCmd::ElemCount of those - ImVector VtxBuffer; // Vertex buffer. - ImDrawListFlags Flags; // Flags, you may poke into these to adjust anti-aliasing settings per-primitive. - - // [Internal, used while building lists] - const ImDrawListSharedData* _Data; // Pointer to shared draw data (you can use ImGui::GetDrawListSharedData() to get the one from current ImGui context) - const char* _OwnerName; // Pointer to owner window's name for debugging - unsigned int _VtxCurrentIdx; // [Internal] == VtxBuffer.Size - ImDrawVert* _VtxWritePtr; // [Internal] point within VtxBuffer.Data after each add command (to avoid using the ImVector<> operators too much) - ImDrawIdx* _IdxWritePtr; // [Internal] point within IdxBuffer.Data after each add command (to avoid using the ImVector<> operators too much) - ImVector _ClipRectStack; // [Internal] - ImVector _TextureIdStack; // [Internal] - ImVector _Path; // [Internal] current path building - int _ChannelsCurrent; // [Internal] current channel number (0) - int _ChannelsCount; // [Internal] number of active channels (1+) - ImVector _Channels; // [Internal] draw channels for columns API (not resized down so _ChannelsCount may be smaller than _Channels.Size) - - // If you want to create ImDrawList instances, pass them ImGui::GetDrawListSharedData() or create and use your own ImDrawListSharedData (so you can use ImDrawList without ImGui) - ImDrawList(const ImDrawListSharedData* shared_data) { _Data = shared_data; _OwnerName = NULL; Clear(); } - ~ImDrawList() { ClearFreeMemory(); } - IMGUI_API void PushClipRect(ImVec2 clip_rect_min, ImVec2 clip_rect_max, bool intersect_with_current_clip_rect = false); // Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling) - IMGUI_API void PushClipRectFullScreen(); - IMGUI_API void PopClipRect(); - IMGUI_API void PushTextureID(ImTextureID texture_id); - IMGUI_API void PopTextureID(); - inline ImVec2 GetClipRectMin() const { const ImVec4& cr = _ClipRectStack.back(); return ImVec2(cr.x, cr.y); } - inline ImVec2 GetClipRectMax() const { const ImVec4& cr = _ClipRectStack.back(); return ImVec2(cr.z, cr.w); } - - // Primitives - IMGUI_API void AddLine(const ImVec2& a, const ImVec2& b, ImU32 col, float thickness = 1.0f); - IMGUI_API void AddRect(const ImVec2& a, const ImVec2& b, ImU32 col, float rounding = 0.0f, int rounding_corners_flags = ImDrawCornerFlags_All, float thickness = 1.0f); // a: upper-left, b: lower-right, rounding_corners_flags: 4-bits corresponding to which corner to round - IMGUI_API void AddRectFilled(const ImVec2& a, const ImVec2& b, ImU32 col, float rounding = 0.0f, int rounding_corners_flags = ImDrawCornerFlags_All); // a: upper-left, b: lower-right - IMGUI_API void AddRectFilledMultiColor(const ImVec2& a, const ImVec2& b, ImU32 col_upr_left, ImU32 col_upr_right, ImU32 col_bot_right, ImU32 col_bot_left); - IMGUI_API void AddQuad(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, ImU32 col, float thickness = 1.0f); - IMGUI_API void AddQuadFilled(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, ImU32 col); - IMGUI_API void AddTriangle(const ImVec2& a, const ImVec2& b, const ImVec2& c, ImU32 col, float thickness = 1.0f); - IMGUI_API void AddTriangleFilled(const ImVec2& a, const ImVec2& b, const ImVec2& c, ImU32 col); - IMGUI_API void AddCircle(const ImVec2& centre, float radius, ImU32 col, int num_segments = 12, float thickness = 1.0f); - IMGUI_API void AddCircleFilled(const ImVec2& centre, float radius, ImU32 col, int num_segments = 12); - IMGUI_API void AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL); - IMGUI_API void AddText(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end = NULL, float wrap_width = 0.0f, const ImVec4* cpu_fine_clip_rect = NULL); - IMGUI_API void AddImage(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a = ImVec2(0,0), const ImVec2& uv_b = ImVec2(1,1), ImU32 col = 0xFFFFFFFF); - IMGUI_API void AddImageQuad(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, const ImVec2& uv_a = ImVec2(0,0), const ImVec2& uv_b = ImVec2(1,0), const ImVec2& uv_c = ImVec2(1,1), const ImVec2& uv_d = ImVec2(0,1), ImU32 col = 0xFFFFFFFF); - IMGUI_API void AddImageRounded(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, ImU32 col, float rounding, int rounding_corners = ImDrawCornerFlags_All); - IMGUI_API void AddPolyline(const ImVec2* points, const int num_points, ImU32 col, bool closed, float thickness); - IMGUI_API void AddConvexPolyFilled(const ImVec2* points, const int num_points, ImU32 col); // Note: Anti-aliased filling requires points to be in clockwise order. - IMGUI_API void AddBezierCurve(const ImVec2& pos0, const ImVec2& cp0, const ImVec2& cp1, const ImVec2& pos1, ImU32 col, float thickness, int num_segments = 0); - - // Stateful path API, add points then finish with PathFillConvex() or PathStroke() - inline void PathClear() { _Path.resize(0); } - inline void PathLineTo(const ImVec2& pos) { _Path.push_back(pos); } - inline void PathLineToMergeDuplicate(const ImVec2& pos) { if (_Path.Size == 0 || memcmp(&_Path[_Path.Size-1], &pos, 8) != 0) _Path.push_back(pos); } - inline void PathFillConvex(ImU32 col) { AddConvexPolyFilled(_Path.Data, _Path.Size, col); PathClear(); } // Note: Anti-aliased filling requires points to be in clockwise order. - inline void PathStroke(ImU32 col, bool closed, float thickness = 1.0f) { AddPolyline(_Path.Data, _Path.Size, col, closed, thickness); PathClear(); } - IMGUI_API void PathArcTo(const ImVec2& centre, float radius, float a_min, float a_max, int num_segments = 10); - IMGUI_API void PathArcToFast(const ImVec2& centre, float radius, int a_min_of_12, int a_max_of_12); // Use precomputed angles for a 12 steps circle - IMGUI_API void PathBezierCurveTo(const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, int num_segments = 0); - IMGUI_API void PathRect(const ImVec2& rect_min, const ImVec2& rect_max, float rounding = 0.0f, int rounding_corners_flags = ImDrawCornerFlags_All); - - // Channels - // - Use to simulate layers. By switching channels to can render out-of-order (e.g. submit foreground primitives before background primitives) - // - Use to minimize draw calls (e.g. if going back-and-forth between multiple non-overlapping clipping rectangles, prefer to append into separate channels then merge at the end) - IMGUI_API void ChannelsSplit(int channels_count); - IMGUI_API void ChannelsMerge(); - IMGUI_API void ChannelsSetCurrent(int channel_index); - - // Advanced - IMGUI_API void AddCallback(ImDrawCallback callback, void* callback_data); // Your rendering function must check for 'UserCallback' in ImDrawCmd and call the function instead of rendering triangles. - IMGUI_API void AddDrawCmd(); // This is useful if you need to forcefully create a new draw call (to allow for dependent rendering / blending). Otherwise primitives are merged into the same draw-call as much as possible - IMGUI_API ImDrawList* CloneOutput() const; // Create a clone of the CmdBuffer/IdxBuffer/VtxBuffer. - - // Internal helpers - // NB: all primitives needs to be reserved via PrimReserve() beforehand! - IMGUI_API void Clear(); - IMGUI_API void ClearFreeMemory(); - IMGUI_API void PrimReserve(int idx_count, int vtx_count); - IMGUI_API void PrimRect(const ImVec2& a, const ImVec2& b, ImU32 col); // Axis aligned rectangle (composed of two triangles) - IMGUI_API void PrimRectUV(const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, ImU32 col); - IMGUI_API void PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, const ImVec2& uv_a, const ImVec2& uv_b, const ImVec2& uv_c, const ImVec2& uv_d, ImU32 col); - inline void PrimWriteVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col){ _VtxWritePtr->pos = pos; _VtxWritePtr->uv = uv; _VtxWritePtr->col = col; _VtxWritePtr++; _VtxCurrentIdx++; } - inline void PrimWriteIdx(ImDrawIdx idx) { *_IdxWritePtr = idx; _IdxWritePtr++; } - inline void PrimVtx(const ImVec2& pos, const ImVec2& uv, ImU32 col) { PrimWriteIdx((ImDrawIdx)_VtxCurrentIdx); PrimWriteVtx(pos, uv, col); } - IMGUI_API void UpdateClipRect(); - IMGUI_API void UpdateTextureID(); -}; - -// All draw data to render an ImGui frame -// (NB: the style and the naming convention here is a little inconsistent but we preserve them for backward compatibility purpose) -struct ImDrawData -{ - bool Valid; // Only valid after Render() is called and before the next NewFrame() is called. - ImDrawList** CmdLists; // Array of ImDrawList* to render. The ImDrawList are owned by ImGuiContext and only pointed to from here. - int CmdListsCount; // Number of ImDrawList* to render - int TotalIdxCount; // For convenience, sum of all ImDrawList's IdxBuffer.Size - int TotalVtxCount; // For convenience, sum of all ImDrawList's VtxBuffer.Size - ImVec2 DisplayPos; // Upper-left position of the viewport to render (== upper-left of the orthogonal projection matrix to use) - ImVec2 DisplaySize; // Size of the viewport to render (== io.DisplaySize for the main viewport) (DisplayPos + DisplaySize == lower-right of the orthogonal projection matrix to use) - - // Functions - ImDrawData() { Valid = false; Clear(); } - ~ImDrawData() { Clear(); } - void Clear() { Valid = false; CmdLists = NULL; CmdListsCount = TotalVtxCount = TotalIdxCount = 0; DisplayPos = DisplaySize = ImVec2(0.f, 0.f); } // The ImDrawList are owned by ImGuiContext! - IMGUI_API void DeIndexAllBuffers(); // Helper to convert all buffers from indexed to non-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! - IMGUI_API void ScaleClipRects(const ImVec2& sc); // Helper to scale the ClipRect field of each ImDrawCmd. Use if your final output buffer is at a different scale than ImGui expects, or if there is a difference between your window resolution and framebuffer resolution. -}; - -struct ImFontConfig -{ - void* FontData; // // TTF/OTF data - int FontDataSize; // // TTF/OTF data size - bool FontDataOwnedByAtlas; // true // TTF/OTF data ownership taken by the container ImFontAtlas (will delete memory itself). - int FontNo; // 0 // Index of font within TTF/OTF file - float SizePixels; // // Size in pixels for rasterizer (more or less maps to the resulting font height). - int OversampleH; // 3 // Rasterize at higher quality for sub-pixel positioning. We don't use sub-pixel positions on the Y axis. - int OversampleV; // 1 // Rasterize at higher quality for sub-pixel positioning. We don't use sub-pixel positions on the Y axis. - bool PixelSnapH; // false // Align every glyph to pixel boundary. Useful e.g. if you are merging a non-pixel aligned font with the default font. If enabled, you can set OversampleH/V to 1. - ImVec2 GlyphExtraSpacing; // 0, 0 // Extra spacing (in pixels) between glyphs. Only X axis is supported for now. - ImVec2 GlyphOffset; // 0, 0 // Offset all glyphs from this font input. - const ImWchar* GlyphRanges; // NULL // Pointer to a user-provided list of Unicode range (2 value per range, values are inclusive, zero-terminated list). THE ARRAY DATA NEEDS TO PERSIST AS LONG AS THE FONT IS ALIVE. - float GlyphMinAdvanceX; // 0 // Minimum AdvanceX for glyphs, set Min to align font icons, set both Min/Max to enforce mono-space font - float GlyphMaxAdvanceX; // FLT_MAX // Maximum AdvanceX for glyphs - bool MergeMode; // false // Merge into previous ImFont, so you can combine multiple inputs font into one ImFont (e.g. ASCII font + icons + Japanese glyphs). You may want to use GlyphOffset.y when merge font of different heights. - unsigned int RasterizerFlags; // 0x00 // Settings for custom font rasterizer (e.g. ImGuiFreeType). Leave as zero if you aren't using one. - float RasterizerMultiply; // 1.0f // Brighten (>1.0f) or darken (<1.0f) font output. Brightening small fonts may be a good workaround to make them more readable. - - // [Internal] - char Name[40]; // Name (strictly to ease debugging) - ImFont* DstFont; - - IMGUI_API ImFontConfig(); -}; - -struct ImFontGlyph -{ - ImWchar Codepoint; // 0x0000..0xFFFF - float AdvanceX; // Distance to next character (= data from font + ImFontConfig::GlyphExtraSpacing.x baked in) - float X0, Y0, X1, Y1; // Glyph corners - float U0, V0, U1, V1; // Texture coordinates -}; - -enum ImFontAtlasFlags_ -{ - ImFontAtlasFlags_NoPowerOfTwoHeight = 1 << 0, // Don't round the height to next power of two - ImFontAtlasFlags_NoMouseCursors = 1 << 1 // Don't build software mouse cursors into the atlas -}; - -// Load and rasterize multiple TTF/OTF fonts into a same texture. -// Sharing a texture for multiple fonts allows us to reduce the number of draw calls during rendering. -// We also add custom graphic data into the texture that serves for ImGui. -// 1. (Optional) Call AddFont*** functions. If you don't call any, the default font will be loaded for you. -// 2. Call GetTexDataAsAlpha8() or GetTexDataAsRGBA32() to build and retrieve pixels data. -// 3. Upload the pixels data into a texture within your graphics system. -// 4. Call SetTexID(my_tex_id); and pass the pointer/identifier to your texture. This value will be passed back to you during rendering to identify the texture. -// IMPORTANT: If you pass a 'glyph_ranges' array to AddFont*** functions, you need to make sure that your array persist up until the ImFont is build (when calling GetTexData*** or Build()). We only copy the pointer, not the data. -struct ImFontAtlas -{ - IMGUI_API ImFontAtlas(); - IMGUI_API ~ImFontAtlas(); - IMGUI_API ImFont* AddFont(const ImFontConfig* font_cfg); - IMGUI_API ImFont* AddFontDefault(const ImFontConfig* font_cfg = NULL); - IMGUI_API ImFont* AddFontFromFileTTF(const char* filename, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); - IMGUI_API ImFont* AddFontFromMemoryTTF(void* font_data, int font_size, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // Note: Transfer ownership of 'ttf_data' to ImFontAtlas! Will be deleted after Build(). Set font_cfg->FontDataOwnedByAtlas to false to keep ownership. - IMGUI_API ImFont* AddFontFromMemoryCompressedTTF(const void* compressed_font_data, int compressed_font_size, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data' still owned by caller. Compress with binary_to_compressed_c.cpp. - IMGUI_API ImFont* AddFontFromMemoryCompressedBase85TTF(const char* compressed_font_data_base85, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_font_data_base85' still owned by caller. Compress with binary_to_compressed_c.cpp with -base85 parameter. - IMGUI_API void ClearInputData(); // Clear input data (all ImFontConfig structures including sizes, TTF data, glyph ranges, etc.) = all the data used to build the texture and fonts. - IMGUI_API void ClearTexData(); // Clear output texture data (CPU side). Saves RAM once the texture has been copied to graphics memory. - IMGUI_API void ClearFonts(); // Clear output font data (glyphs storage, UV coordinates). - IMGUI_API void Clear(); // Clear all input and output. - - // Build atlas, retrieve pixel data. - // User is in charge of copying the pixels into graphics memory (e.g. create a texture with your engine). Then store your texture handle with SetTexID(). - // RGBA32 format is provided for convenience and compatibility, but note that unless you use CustomRect to draw color data, the RGB pixels emitted from Fonts will all be white (~75% of waste). - // Pitch = Width * BytesPerPixels - IMGUI_API bool Build(); // Build pixels data. This is called automatically for you by the GetTexData*** functions. - IMGUI_API bool IsBuilt() { return Fonts.Size > 0 && (TexPixelsAlpha8 != NULL || TexPixelsRGBA32 != NULL); } - IMGUI_API void GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 1 byte per-pixel - IMGUI_API void GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 4 bytes-per-pixel - void SetTexID(ImTextureID id) { TexID = id; } - - //------------------------------------------- - // Glyph Ranges - //------------------------------------------- - - // Helpers to retrieve list of common Unicode ranges (2 value per range, values are inclusive, zero-terminated list) - // NB: Make sure that your string are UTF-8 and NOT in your local code page. In C++11, you can create UTF-8 string literal using the u8"Hello world" syntax. See FAQ for details. - // NB: Consider using GlyphRangesBuilder to build glyph ranges from textual data. - IMGUI_API const ImWchar* GetGlyphRangesDefault(); // Basic Latin, Extended Latin - IMGUI_API const ImWchar* GetGlyphRangesKorean(); // Default + Korean characters - IMGUI_API const ImWchar* GetGlyphRangesJapanese(); // Default + Hiragana, Katakana, Half-Width, Selection of 1946 Ideographs - IMGUI_API const ImWchar* GetGlyphRangesChineseFull(); // Default + Half-Width + Japanese Hiragana/Katakana + full set of about 21000 CJK Unified Ideographs - IMGUI_API const ImWchar* GetGlyphRangesChineseSimplifiedCommon();// Default + Half-Width + Japanese Hiragana/Katakana + set of 2500 CJK Unified Ideographs for common simplified Chinese - IMGUI_API const ImWchar* GetGlyphRangesCyrillic(); // Default + about 400 Cyrillic characters - IMGUI_API const ImWchar* GetGlyphRangesThai(); // Default + Thai characters - - // Helpers to build glyph ranges from text data. Feed your application strings/characters to it then call BuildRanges(). - struct GlyphRangesBuilder - { - ImVector UsedChars; // Store 1-bit per Unicode code point (0=unused, 1=used) - GlyphRangesBuilder() { UsedChars.resize(0x10000 / 8); memset(UsedChars.Data, 0, 0x10000 / 8); } - bool GetBit(int n) const { return (UsedChars[n >> 3] & (1 << (n & 7))) != 0; } - void SetBit(int n) { UsedChars[n >> 3] |= (unsigned char) (1 << (n & 7)); } // Set bit 'c' in the array [Bruno Levy] added cast to silent warning - void AddChar(ImWchar c) { SetBit(c); } // Add character - IMGUI_API void AddText(const char* text, const char* text_end = NULL); // Add string (each character of the UTF-8 string are added) - IMGUI_API void AddRanges(const ImWchar* ranges); // Add ranges, e.g. builder.AddRanges(ImFontAtlas::GetGlyphRangesDefault()) to force add all of ASCII/Latin+Ext - IMGUI_API void BuildRanges(ImVector* out_ranges); // Output new ranges - }; - - //------------------------------------------- - // Custom Rectangles/Glyphs API - //------------------------------------------- - - // You can request arbitrary rectangles to be packed into the atlas, for your own purposes. After calling Build(), you can query the rectangle position and render your pixels. - // You can also request your rectangles to be mapped as font glyph (given a font + Unicode point), so you can render e.g. custom colorful icons and use them as regular glyphs. - struct CustomRect - { - unsigned int ID; // Input // User ID. Use <0x10000 to map into a font glyph, >=0x10000 for other/internal/custom texture data. - unsigned short Width, Height; // Input // Desired rectangle dimension - unsigned short X, Y; // Output // Packed position in Atlas - float GlyphAdvanceX; // Input // For custom font glyphs only (ID<0x10000): glyph xadvance - ImVec2 GlyphOffset; // Input // For custom font glyphs only (ID<0x10000): glyph display offset - ImFont* Font; // Input // For custom font glyphs only (ID<0x10000): target font - CustomRect() { ID = 0xFFFFFFFF; Width = Height = 0; X = Y = 0xFFFF; GlyphAdvanceX = 0.0f; GlyphOffset = ImVec2(0,0); Font = NULL; } - bool IsPacked() const { return X != 0xFFFF; } - }; - - IMGUI_API int AddCustomRectRegular(unsigned int id, int width, int height); // Id needs to be >= 0x10000. Id >= 0x80000000 are reserved for ImGui and ImDrawList - IMGUI_API int AddCustomRectFontGlyph(ImFont* font, ImWchar id, int width, int height, float advance_x, const ImVec2& offset = ImVec2(0,0)); // Id needs to be < 0x10000 to register a rectangle to map into a specific font. - const CustomRect* GetCustomRectByIndex(int index) const { if (index < 0) return NULL; return &CustomRects[index]; } - - // [Internal] - IMGUI_API void CalcCustomRectUV(const CustomRect* rect, ImVec2* out_uv_min, ImVec2* out_uv_max); - IMGUI_API bool GetMouseCursorTexData(ImGuiMouseCursor cursor, ImVec2* out_offset, ImVec2* out_size, ImVec2 out_uv_border[2], ImVec2 out_uv_fill[2]); - - //------------------------------------------- - // Members - //------------------------------------------- - - ImFontAtlasFlags Flags; // Build flags (see ImFontAtlasFlags_) - ImTextureID TexID; // User data to refer to the texture once it has been uploaded to user's graphic systems. It is passed back to you during rendering via the ImDrawCmd structure. - int TexDesiredWidth; // Texture width desired by user before Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height. - int TexGlyphPadding; // Padding between glyphs within texture in pixels. Defaults to 1. - - // [Internal] - // NB: Access texture data via GetTexData*() calls! Which will setup a default font for you. - unsigned char* TexPixelsAlpha8; // 1 component per pixel, each component is unsigned 8-bit. Total size = TexWidth * TexHeight - unsigned int* TexPixelsRGBA32; // 4 component per pixel, each component is unsigned 8-bit. Total size = TexWidth * TexHeight * 4 - int TexWidth; // Texture width calculated during Build(). - int TexHeight; // Texture height calculated during Build(). - ImVec2 TexUvScale; // = (1.0f/TexWidth, 1.0f/TexHeight) - ImVec2 TexUvWhitePixel; // Texture coordinates to a white pixel - ImVector Fonts; // Hold all the fonts returned by AddFont*. Fonts[0] is the default font upon calling ImGui::NewFrame(), use ImGui::PushFont()/PopFont() to change the current font. - ImVector CustomRects; // Rectangles for packing custom texture data into the atlas. - ImVector ConfigData; // Internal data - int CustomRectIds[1]; // Identifiers of custom texture rectangle used by ImFontAtlas/ImDrawList -}; - -// Font runtime data and rendering -// ImFontAtlas automatically loads a default embedded font for you when you call GetTexDataAsAlpha8() or GetTexDataAsRGBA32(). -struct ImFont -{ - // Members: Hot ~62/78 bytes - float FontSize; // // Height of characters, set during loading (don't change after loading) - float Scale; // = 1.f // Base font scale, multiplied by the per-window font scale which you can adjust with SetFontScale() - ImVec2 DisplayOffset; // = (0.f,0.f) // Offset font rendering by xx pixels - ImVector Glyphs; // // All glyphs. - ImVector IndexAdvanceX; // // Sparse. Glyphs->AdvanceX in a directly indexable way (more cache-friendly, for CalcTextSize functions which are often bottleneck in large UI). - ImVector IndexLookup; // // Sparse. Index glyphs by Unicode code-point. - const ImFontGlyph* FallbackGlyph; // == FindGlyph(FontFallbackChar) - float FallbackAdvanceX; // == FallbackGlyph->AdvanceX - ImWchar FallbackChar; // = '?' // Replacement glyph if one isn't found. Only set via SetFallbackChar() - - // Members: Cold ~18/26 bytes - short ConfigDataCount; // ~ 1 // Number of ImFontConfig involved in creating this font. Bigger than 1 when merging multiple font sources into one ImFont. - ImFontConfig* ConfigData; // // Pointer within ContainerAtlas->ConfigData - ImFontAtlas* ContainerAtlas; // // What we has been loaded into - float Ascent, Descent; // // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize] - bool DirtyLookupTables; - int MetricsTotalSurface;// // Total surface in pixels to get an idea of the font rasterization/texture cost (not exact, we approximate the cost of padding between glyphs) - - // Methods - IMGUI_API ImFont(); - IMGUI_API ~ImFont(); - IMGUI_API void ClearOutputData(); - IMGUI_API void BuildLookupTable(); - IMGUI_API const ImFontGlyph*FindGlyph(ImWchar c) const; - IMGUI_API const ImFontGlyph*FindGlyphNoFallback(ImWchar c) const; - IMGUI_API void SetFallbackChar(ImWchar c); - float GetCharAdvance(ImWchar c) const { return ((int)c < IndexAdvanceX.Size) ? IndexAdvanceX[(int)c] : FallbackAdvanceX; } - bool IsLoaded() const { return ContainerAtlas != NULL; } - const char* GetDebugName() const { return ConfigData ? ConfigData->Name : ""; } - - // 'max_width' stops rendering after a certain width (could be turned into a 2d size). FLT_MAX to disable. - // 'wrap_width' enable automatic word-wrapping across multiple lines to fit into given width. 0.0f to disable. - IMGUI_API ImVec2 CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end = NULL, const char** remaining = NULL) const; // utf8 - IMGUI_API const char* CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) const; - IMGUI_API void RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, unsigned short c) const; - IMGUI_API void RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width = 0.0f, bool cpu_fine_clip = false) const; - - // [Internal] - IMGUI_API void GrowIndex(int new_size); - IMGUI_API void AddGlyph(ImWchar c, float x0, float y0, float x1, float y1, float u0, float v0, float u1, float v1, float advance_x); - IMGUI_API void AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst = true); // Makes 'dst' character/glyph points to 'src' character/glyph. Currently needs to be called AFTER fonts have been built. - -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - typedef ImFontGlyph Glyph; // OBSOLETE 1.52+ -#endif -}; - -#if defined(__clang__) -#pragma clang diagnostic pop -#endif - -// Include imgui_user.h at the end of imgui.h (convenient for user to only explicitly include vanilla imgui.h) -#ifdef IMGUI_INCLUDE_IMGUI_USER_H -#include "imgui_user.h" -#endif - -// [Bruno] -namespace ImGui { - void debug_printf(const char* fmt, ...) IM_FMTARGS(1); - void show_debug_console(); - void hide_debug_console(); - void draw_debug_console(); -} - - diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_demo.cpp b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_demo.cpp deleted file mode 100644 index 67af01d5..00000000 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_demo.cpp +++ /dev/null @@ -1,3361 +0,0 @@ -// dear imgui, v1.62 -// (demo code) - -// Message to the person tempted to delete this file when integrating ImGui into their code base: -// Don't do it! Do NOT remove this file from your project! It is useful reference code that you and other users will want to refer to. -// Everything in this file will be stripped out by the linker if you don't call ImGui::ShowDemoWindow(). -// During development, you can call ImGui::ShowDemoWindow() in your code to learn about various features of ImGui. Have it wired in a debug menu! -// Removing this file from your project is hindering access to documentation for everyone in your team, likely leading you to poorer usage of the library. -// Note that you can #define IMGUI_DISABLE_DEMO_WINDOWS in imconfig.h for the same effect. -// If you want to link core ImGui in your final builds but not those demo windows, #define IMGUI_DISABLE_DEMO_WINDOWS in imconfig.h and those functions will be empty. -// In other situation, when you have ImGui available you probably want this to be available for reference and execution. -// Thank you, -// -Your beloved friend, imgui_demo.cpp (that you won't delete) - -// Message to beginner C/C++ programmers. About the meaning of 'static': in this demo code, we frequently we use 'static' variables inside functions. -// We do this as a way to gather code and data in the same place, just to make the demo code faster to read, faster to write, and use less code. -// A static variable persist across calls, so it is essentially like a global variable but declared inside the scope of the function. -// It also happens to be a convenient way of storing simple UI related information as long as your function doesn't need to be reentrant or used in threads. -// This might be a pattern you occasionally want to use in your code, but most of the real data you would be editing is likely to be stored outside your function. - -#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) -#define _CRT_SECURE_NO_WARNINGS -#endif - -#include "imgui.h" -#include // toupper, isprint -#include // INT_MIN, INT_MAX -#include // sqrtf, powf, cosf, sinf, floorf, ceilf -#include // vsnprintf, sscanf, printf -#include // NULL, malloc, free, atoi -#if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier -#include // intptr_t -#else -#include // intptr_t -#endif - -#ifdef _MSC_VER -#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen -#define snprintf _snprintf -#endif -#ifdef __clang__ -#pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse. -#pragma clang diagnostic ignored "-Wdeprecated-declarations" // warning : 'xx' is deprecated: The POSIX name for this item.. // for strdup used in demo code (so user can copy & paste the code) -#pragma clang diagnostic ignored "-Wint-to-void-pointer-cast" // warning : cast to 'void *' from smaller integer type 'int' -#pragma clang diagnostic ignored "-Wformat-security" // warning : warning: format string is not a string literal -#pragma clang diagnostic ignored "-Wexit-time-destructors" // warning : declaration requires an exit-time destructor // exit-time destruction order is undefined. if MemFree() leads to users code that has been disabled before exit it might cause problems. ImGui coding style welcomes static/globals. -#if __has_warning("-Wreserved-id-macro") -#pragma clang diagnostic ignored "-Wreserved-id-macro" // warning : macro name is a reserved identifier // -#endif -#elif defined(__GNUC__) -#pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size -#pragma GCC diagnostic ignored "-Wformat-security" // warning : format string is not a string literal (potentially insecure) -#pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function -#pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value -#if (__GNUC__ >= 6) -#pragma GCC diagnostic ignored "-Wmisleading-indentation" // warning: this 'if' clause does not guard this statement // GCC 6.0+ only. See #883 on GitHub. -#endif -#endif - -// Play it nice with Windows users. Notepad in 2017 still doesn't display text data with Unix-style \n. -#ifdef _WIN32 -#define IM_NEWLINE "\r\n" -#else -#define IM_NEWLINE "\n" -#endif - -#define IM_MAX(_A,_B) (((_A) >= (_B)) ? (_A) : (_B)) - -//----------------------------------------------------------------------------- -// DEMO CODE -//----------------------------------------------------------------------------- - -#if !defined(IMGUI_DISABLE_OBSOLETE_FUNCTIONS) && defined(IMGUI_DISABLE_TEST_WINDOWS) && !defined(IMGUI_DISABLE_DEMO_WINDOWS) // Obsolete name since 1.53, TEST->DEMO -#define IMGUI_DISABLE_DEMO_WINDOWS -#endif - -#if !defined(IMGUI_DISABLE_DEMO_WINDOWS) - -static void ShowExampleAppConsole(bool* p_open); -static void ShowExampleAppLog(bool* p_open); -static void ShowExampleAppLayout(bool* p_open); -static void ShowExampleAppPropertyEditor(bool* p_open); -static void ShowExampleAppLongText(bool* p_open); -static void ShowExampleAppAutoResize(bool* p_open); -static void ShowExampleAppConstrainedResize(bool* p_open); -static void ShowExampleAppSimpleOverlay(bool* p_open); -static void ShowExampleAppWindowTitles(bool* p_open); -static void ShowExampleAppCustomRendering(bool* p_open); -static void ShowExampleAppMainMenuBar(); -static void ShowExampleMenuFile(); - -static void ShowHelpMarker(const char* desc) -{ - ImGui::TextDisabled("(?)"); - if (ImGui::IsItemHovered()) - { - ImGui::BeginTooltip(); - ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); - ImGui::TextUnformatted(desc); - ImGui::PopTextWrapPos(); - ImGui::EndTooltip(); - } -} - -void ImGui::ShowUserGuide() -{ - ImGui::BulletText("Double-click on title bar to collapse window."); - ImGui::BulletText("Click and drag on lower right corner to resize window\n(double-click to auto fit window to its contents)."); - ImGui::BulletText("Click and drag on any empty space to move window."); - ImGui::BulletText("TAB/SHIFT+TAB to cycle through keyboard editable fields."); - ImGui::BulletText("CTRL+Click on a slider or drag box to input value as text."); - if (ImGui::GetIO().FontAllowUserScaling) - ImGui::BulletText("CTRL+Mouse Wheel to zoom window contents."); - ImGui::BulletText("Mouse Wheel to scroll."); - ImGui::BulletText("While editing text:\n"); - ImGui::Indent(); - ImGui::BulletText("Hold SHIFT or use mouse to select text."); - ImGui::BulletText("CTRL+Left/Right to word jump."); - ImGui::BulletText("CTRL+A or double-click to select all."); - ImGui::BulletText("CTRL+X,CTRL+C,CTRL+V to use clipboard."); - ImGui::BulletText("CTRL+Z,CTRL+Y to undo/redo."); - ImGui::BulletText("ESCAPE to revert."); - ImGui::BulletText("You can apply arithmetic operators +,*,/ on numerical values.\nUse +- to subtract."); - ImGui::Unindent(); -} - - -static bool show_app_log = true; // [Bruno] moved to global. - -// Demonstrate most ImGui features (big function!) -void ImGui::ShowDemoWindow(bool* p_open) -{ - // Examples apps - static bool show_app_main_menu_bar = false; - static bool show_app_console = false; - static bool show_app_layout = false; - static bool show_app_property_editor = false; - static bool show_app_long_text = false; - static bool show_app_auto_resize = false; - static bool show_app_constrained_resize = false; - static bool show_app_simple_overlay = false; - static bool show_app_window_titles = false; - static bool show_app_custom_rendering = false; - static bool show_app_style_editor = false; - - static bool show_app_metrics = false; - static bool show_app_about = false; - - if (show_app_main_menu_bar) ShowExampleAppMainMenuBar(); - if (show_app_console) { - ImGui::SetNextWindowPos( - ImVec2(80.0f, 900.0f), - ImGuiCond_Once - ); - ShowExampleAppConsole(&show_app_console); - } - if (show_app_log) { - ImGui::SetNextWindowPos( - ImVec2(80.0f, 900.0f), - ImGuiCond_Once - ); - ImGui::SetNextWindowSize( - ImVec2(1200.0f, 900.0f), - ImGuiCond_Once - ); - ShowExampleAppLog(&show_app_log); - } - if (show_app_layout) ShowExampleAppLayout(&show_app_layout); - if (show_app_property_editor) ShowExampleAppPropertyEditor(&show_app_property_editor); - if (show_app_long_text) ShowExampleAppLongText(&show_app_long_text); - if (show_app_auto_resize) ShowExampleAppAutoResize(&show_app_auto_resize); - if (show_app_constrained_resize) ShowExampleAppConstrainedResize(&show_app_constrained_resize); - if (show_app_simple_overlay) ShowExampleAppSimpleOverlay(&show_app_simple_overlay); - if (show_app_window_titles) ShowExampleAppWindowTitles(&show_app_window_titles); - if (show_app_custom_rendering) ShowExampleAppCustomRendering(&show_app_custom_rendering); - - if (show_app_metrics) { ImGui::ShowMetricsWindow(&show_app_metrics); } - if (show_app_style_editor) { ImGui::Begin("Style Editor", &show_app_style_editor); ImGui::ShowStyleEditor(); ImGui::End(); } - if (show_app_about) - { - ImGui::Begin("About Dear ImGui", &show_app_about, ImGuiWindowFlags_AlwaysAutoResize); - ImGui::Text("Dear ImGui, %s", ImGui::GetVersion()); - ImGui::Separator(); - ImGui::Text("By Omar Cornut and all dear imgui contributors."); - ImGui::Text("Dear ImGui is licensed under the MIT License, see LICENSE for more information."); - ImGui::End(); - } - - static bool no_titlebar = false; - static bool no_scrollbar = false; - static bool no_menu = false; - static bool no_move = false; - static bool no_resize = false; - static bool no_collapse = false; - static bool no_close = false; - static bool no_nav = false; - - // Demonstrate the various window flags. Typically you would just use the default. - ImGuiWindowFlags window_flags = 0; - if (no_titlebar) window_flags |= ImGuiWindowFlags_NoTitleBar; - if (no_scrollbar) window_flags |= ImGuiWindowFlags_NoScrollbar; - if (!no_menu) window_flags |= ImGuiWindowFlags_MenuBar; - if (no_move) window_flags |= ImGuiWindowFlags_NoMove; - if (no_resize) window_flags |= ImGuiWindowFlags_NoResize; - if (no_collapse) window_flags |= ImGuiWindowFlags_NoCollapse; - if (no_nav) window_flags |= ImGuiWindowFlags_NoNav; - if (no_close) p_open = NULL; // Don't pass our bool* to Begin - - ImGui::SetNextWindowSize(ImVec2(550,680), ImGuiCond_FirstUseEver); - if (!ImGui::Begin("ImGui Demo", p_open, window_flags)) - { - // Early out if the window is collapsed, as an optimization. - ImGui::End(); - return; - } - - //ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.65f); // 2/3 of the space for widget and 1/3 for labels - ImGui::PushItemWidth(-140); // Right align, keep 140 pixels for labels - - ImGui::Text("dear imgui says hello. (%s)", IMGUI_VERSION); - - // Menu - if (ImGui::BeginMenuBar()) - { - if (ImGui::BeginMenu("Menu")) - { - ShowExampleMenuFile(); - ImGui::EndMenu(); - } - if (ImGui::BeginMenu("Examples")) - { - ImGui::MenuItem("Main menu bar", NULL, &show_app_main_menu_bar); - ImGui::MenuItem("Console", NULL, &show_app_console); - ImGui::MenuItem("Log", NULL, &show_app_log); - ImGui::MenuItem("Simple layout", NULL, &show_app_layout); - ImGui::MenuItem("Property editor", NULL, &show_app_property_editor); - ImGui::MenuItem("Long text display", NULL, &show_app_long_text); - ImGui::MenuItem("Auto-resizing window", NULL, &show_app_auto_resize); - ImGui::MenuItem("Constrained-resizing window", NULL, &show_app_constrained_resize); - ImGui::MenuItem("Simple overlay", NULL, &show_app_simple_overlay); - ImGui::MenuItem("Manipulating window titles", NULL, &show_app_window_titles); - ImGui::MenuItem("Custom rendering", NULL, &show_app_custom_rendering); - ImGui::EndMenu(); - } - if (ImGui::BeginMenu("Help")) - { - ImGui::MenuItem("Metrics", NULL, &show_app_metrics); - ImGui::MenuItem("Style Editor", NULL, &show_app_style_editor); - ImGui::MenuItem("About Dear ImGui", NULL, &show_app_about); - ImGui::EndMenu(); - } - ImGui::EndMenuBar(); - } - - ImGui::Spacing(); - if (ImGui::CollapsingHeader("Help")) - { - ImGui::TextWrapped("This window is being created by the ShowDemoWindow() function. Please refer to the code in imgui_demo.cpp for reference.\n\n"); - ImGui::Text("USER GUIDE:"); - ImGui::ShowUserGuide(); - } - - if (ImGui::CollapsingHeader("Window options")) - { - ImGui::Checkbox("No titlebar", &no_titlebar); ImGui::SameLine(150); - ImGui::Checkbox("No scrollbar", &no_scrollbar); ImGui::SameLine(300); - ImGui::Checkbox("No menu", &no_menu); - ImGui::Checkbox("No move", &no_move); ImGui::SameLine(150); - ImGui::Checkbox("No resize", &no_resize); ImGui::SameLine(300); - ImGui::Checkbox("No collapse", &no_collapse); - ImGui::Checkbox("No close", &no_close); ImGui::SameLine(150); - ImGui::Checkbox("No nav", &no_nav); - - if (ImGui::TreeNode("Style")) - { - ImGui::ShowStyleEditor(); - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Capture/Logging")) - { - ImGui::TextWrapped("The logging API redirects all text output so you can easily capture the content of a window or a block. Tree nodes can be automatically expanded. You can also call ImGui::LogText() to output directly to the log without a visual output."); - ImGui::LogButtons(); - ImGui::TreePop(); - } - } - - if (ImGui::CollapsingHeader("Widgets")) - { - if (ImGui::TreeNode("Basic")) - { - static int clicked = 0; - if (ImGui::Button("Button")) - clicked++; - if (clicked & 1) - { - ImGui::SameLine(); - ImGui::Text("Thanks for clicking me!"); - } - - static bool check = true; - ImGui::Checkbox("checkbox", &check); - - static int e = 0; - ImGui::RadioButton("radio a", &e, 0); ImGui::SameLine(); - ImGui::RadioButton("radio b", &e, 1); ImGui::SameLine(); - ImGui::RadioButton("radio c", &e, 2); - - // Color buttons, demonstrate using PushID() to add unique identifier in the ID stack, and changing style. - for (int i = 0; i < 7; i++) - { - if (i > 0) ImGui::SameLine(); - ImGui::PushID(i); - ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(i/7.0f, 0.6f, 0.6f)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(i/7.0f, 0.7f, 0.7f)); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(i/7.0f, 0.8f, 0.8f)); - ImGui::Button("Click"); - ImGui::PopStyleColor(3); - ImGui::PopID(); - } - - // Arrow buttons - float spacing = ImGui::GetStyle().ItemInnerSpacing.x; - if (ImGui::ArrowButton("##left", ImGuiDir_Left)) {} - ImGui::SameLine(0.0f, spacing); - if (ImGui::ArrowButton("##left", ImGuiDir_Right)) {} - - ImGui::Text("Hover over me"); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("I am a tooltip"); - - ImGui::SameLine(); - ImGui::Text("- or me"); - if (ImGui::IsItemHovered()) - { - ImGui::BeginTooltip(); - ImGui::Text("I am a fancy tooltip"); - static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; - ImGui::PlotLines("Curve", arr, IM_ARRAYSIZE(arr)); - ImGui::EndTooltip(); - } - - ImGui::Separator(); - - ImGui::LabelText("label", "Value"); - - { - // Using the _simplified_ one-liner Combo() api here - // See "Combo" section for examples of how to use the more complete BeginCombo()/EndCombo() api. - const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" }; - static int item_current = 0; - ImGui::Combo("combo", &item_current, items, IM_ARRAYSIZE(items)); - ImGui::SameLine(); ShowHelpMarker("Refer to the \"Combo\" section below for an explanation of the full BeginCombo/EndCombo API, and demonstration of various flags.\n"); - } - - { - static char str0[128] = "Hello, world!"; - static int i0 = 123; - ImGui::InputText("input text", str0, IM_ARRAYSIZE(str0)); - ImGui::SameLine(); ShowHelpMarker("Hold SHIFT or use mouse to select text.\n" "CTRL+Left/Right to word jump.\n" "CTRL+A or double-click to select all.\n" "CTRL+X,CTRL+C,CTRL+V clipboard.\n" "CTRL+Z,CTRL+Y undo/redo.\n" "ESCAPE to revert.\n"); - - ImGui::InputInt("input int", &i0); - ImGui::SameLine(); ShowHelpMarker("You can apply arithmetic operators +,*,/ on numerical values.\n e.g. [ 100 ], input \'*2\', result becomes [ 200 ]\nUse +- to subtract.\n"); - - static float f0 = 0.001f; - ImGui::InputFloat("input float", &f0, 0.01f, 1.0f); - - static double d0 = 999999.00000001; - ImGui::InputDouble("input double", &d0, 0.01f, 1.0f, "%.8f"); - - static float f1 = 1.e10f; - ImGui::InputFloat("input scientific", &f1, 0.0f, 0.0f, "%e"); - ImGui::SameLine(); ShowHelpMarker("You can input value using the scientific notation,\n e.g. \"1e+8\" becomes \"100000000\".\n"); - - static float vec4a[4] = { 0.10f, 0.20f, 0.30f, 0.44f }; - ImGui::InputFloat3("input float3", vec4a); - } - - { - static int i1 = 50, i2 = 42; - ImGui::DragInt("drag int", &i1, 1); - ImGui::SameLine(); ShowHelpMarker("Click and drag to edit value.\nHold SHIFT/ALT for faster/slower edit.\nDouble-click or CTRL+click to input value."); - - ImGui::DragInt("drag int 0..100", &i2, 1, 0, 100, "%d%%"); - - static float f1=1.00f, f2=0.0067f; - ImGui::DragFloat("drag float", &f1, 0.005f); - ImGui::DragFloat("drag small float", &f2, 0.0001f, 0.0f, 0.0f, "%.06f ns"); - } - - { - static int i1=0; - ImGui::SliderInt("slider int", &i1, -1, 3); - ImGui::SameLine(); ShowHelpMarker("CTRL+click to input value."); - - static float f1=0.123f, f2=0.0f; - ImGui::SliderFloat("slider float", &f1, 0.0f, 1.0f, "ratio = %.3f"); - ImGui::SliderFloat("slider float (curve)", &f2, -10.0f, 10.0f, "%.4f", 2.0f); - static float angle = 0.0f; - ImGui::SliderAngle("slider angle", &angle); - } - - { - static float col1[3] = { 1.0f,0.0f,0.2f }; - static float col2[4] = { 0.4f,0.7f,0.0f,0.5f }; - ImGui::ColorEdit3("color 1", col1); - ImGui::SameLine(); ShowHelpMarker("Click on the colored square to open a color picker.\nRight-click on the colored square to show options.\nCTRL+click on individual component to input value.\n"); - - ImGui::ColorEdit4("color 2", col2); - } - - { - // List box - const char* listbox_items[] = { "Apple", "Banana", "Cherry", "Kiwi", "Mango", "Orange", "Pineapple", "Strawberry", "Watermelon" }; - static int listbox_item_current = 1; - ImGui::ListBox("listbox\n(single select)", &listbox_item_current, listbox_items, IM_ARRAYSIZE(listbox_items), 4); - - //static int listbox_item_current2 = 2; - //ImGui::PushItemWidth(-1); - //ImGui::ListBox("##listbox2", &listbox_item_current2, listbox_items, IM_ARRAYSIZE(listbox_items), 4); - //ImGui::PopItemWidth(); - } - - ImGui::TreePop(); - } - - // Testing ImGuiOnceUponAFrame helper. - //static ImGuiOnceUponAFrame once; - //for (int i = 0; i < 5; i++) - // if (once) - // ImGui::Text("This will be displayed only once."); - - if (ImGui::TreeNode("Trees")) - { - if (ImGui::TreeNode("Basic trees")) - { - for (int i = 0; i < 5; i++) - if (ImGui::TreeNode((void*)(intptr_t)i, "Child %d", i)) - { - ImGui::Text("blah blah"); - ImGui::SameLine(); - if (ImGui::SmallButton("button")) { }; - ImGui::TreePop(); - } - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Advanced, with Selectable nodes")) - { - ShowHelpMarker("This is a more standard looking tree with selectable nodes.\nClick to select, CTRL+Click to toggle, click on arrows or double-click to open."); - static bool align_label_with_current_x_position = false; - ImGui::Checkbox("Align label with current X position)", &align_label_with_current_x_position); - ImGui::Text("Hello!"); - if (align_label_with_current_x_position) - ImGui::Unindent(ImGui::GetTreeNodeToLabelSpacing()); - - static int selection_mask = (1 << 2); // Dumb representation of what may be user-side selection state. You may carry selection state inside or outside your objects in whatever format you see fit. - int node_clicked = -1; // Temporary storage of what node we have clicked to process selection at the end of the loop. May be a pointer to your own node type, etc. - ImGui::PushStyleVar(ImGuiStyleVar_IndentSpacing, ImGui::GetFontSize()*3); // Increase spacing to differentiate leaves from expanded contents. - for (int i = 0; i < 6; i++) - { - // Disable the default open on single-click behavior and pass in Selected flag according to our selection state. - ImGuiTreeNodeFlags node_flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick | ((selection_mask & (1 << i)) ? ImGuiTreeNodeFlags_Selected : 0); - if (i < 3) - { - // Node - bool node_open = ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Node %d", i); - if (ImGui::IsItemClicked()) - node_clicked = i; - if (node_open) - { - ImGui::Text("Blah blah\nBlah Blah"); - ImGui::TreePop(); - } - } - else - { - // Leaf: The only reason we have a TreeNode at all is to allow selection of the leaf. Otherwise we can use BulletText() or TreeAdvanceToLabelPos()+Text(). - node_flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; // ImGuiTreeNodeFlags_Bullet - ImGui::TreeNodeEx((void*)(intptr_t)i, node_flags, "Selectable Leaf %d", i); - if (ImGui::IsItemClicked()) - node_clicked = i; - } - } - if (node_clicked != -1) - { - // Update selection state. Process outside of tree loop to avoid visual inconsistencies during the clicking-frame. - if (ImGui::GetIO().KeyCtrl) - selection_mask ^= (1 << node_clicked); // CTRL+click to toggle - else //if (!(selection_mask & (1 << node_clicked))) // Depending on selection behavior you want, this commented bit preserve selection when clicking on item that is part of the selection - selection_mask = (1 << node_clicked); // Click to single-select - } - ImGui::PopStyleVar(); - if (align_label_with_current_x_position) - ImGui::Indent(ImGui::GetTreeNodeToLabelSpacing()); - ImGui::TreePop(); - } - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Collapsing Headers")) - { - static bool closable_group = true; - ImGui::Checkbox("Enable extra group", &closable_group); - if (ImGui::CollapsingHeader("Header")) - { - ImGui::Text("IsItemHovered: %d", IsItemHovered()); - for (int i = 0; i < 5; i++) - ImGui::Text("Some content %d", i); - } - if (ImGui::CollapsingHeader("Header with a close button", &closable_group)) - { - ImGui::Text("IsItemHovered: %d", IsItemHovered()); - for (int i = 0; i < 5; i++) - ImGui::Text("More content %d", i); - } - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Bullets")) - { - ImGui::BulletText("Bullet point 1"); - ImGui::BulletText("Bullet point 2\nOn multiple lines"); - ImGui::Bullet(); ImGui::Text("Bullet point 3 (two calls)"); - ImGui::Bullet(); ImGui::SmallButton("Button"); - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Text")) - { - if (ImGui::TreeNode("Colored Text")) - { - // Using shortcut. You can use PushStyleColor()/PopStyleColor() for more flexibility. - ImGui::TextColored(ImVec4(1.0f,0.0f,1.0f,1.0f), "Pink"); - ImGui::TextColored(ImVec4(1.0f,1.0f,0.0f,1.0f), "Yellow"); - ImGui::TextDisabled("Disabled"); - ImGui::SameLine(); ShowHelpMarker("The TextDisabled color is stored in ImGuiStyle."); - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Word Wrapping")) - { - // Using shortcut. You can use PushTextWrapPos()/PopTextWrapPos() for more flexibility. - ImGui::TextWrapped("This text should automatically wrap on the edge of the window. The current implementation for text wrapping follows simple rules suitable for English and possibly other languages."); - ImGui::Spacing(); - - static float wrap_width = 200.0f; - ImGui::SliderFloat("Wrap width", &wrap_width, -20, 600, "%.0f"); - - ImGui::Text("Test paragraph 1:"); - ImVec2 pos = ImGui::GetCursorScreenPos(); - ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(pos.x + wrap_width, pos.y), ImVec2(pos.x + wrap_width + 10, pos.y + ImGui::GetTextLineHeight()), IM_COL32(255,0,255,255)); - ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + wrap_width); - ImGui::Text("The lazy dog is a good dog. This paragraph is made to fit within %.0f pixels. Testing a 1 character word. The quick brown fox jumps over the lazy dog.", wrap_width); - ImGui::GetWindowDrawList()->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(255,255,0,255)); - ImGui::PopTextWrapPos(); - - ImGui::Text("Test paragraph 2:"); - pos = ImGui::GetCursorScreenPos(); - ImGui::GetWindowDrawList()->AddRectFilled(ImVec2(pos.x + wrap_width, pos.y), ImVec2(pos.x + wrap_width + 10, pos.y + ImGui::GetTextLineHeight()), IM_COL32(255,0,255,255)); - ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + wrap_width); - ImGui::Text("aaaaaaaa bbbbbbbb, c cccccccc,dddddddd. d eeeeeeee ffffffff. gggggggg!hhhhhhhh"); - ImGui::GetWindowDrawList()->AddRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax(), IM_COL32(255,255,0,255)); - ImGui::PopTextWrapPos(); - - ImGui::TreePop(); - } - - if (ImGui::TreeNode("UTF-8 Text")) - { - // UTF-8 test with Japanese characters - // (Needs a suitable font, try Noto, or Arial Unicode, or M+ fonts. Read misc/fonts/README.txt for details.) - // - From C++11 you can use the u8"my text" syntax to encode literal strings as UTF-8 - // - For earlier compiler, you may be able to encode your sources as UTF-8 (e.g. Visual Studio save your file as 'UTF-8 without signature') - // - FOR THIS DEMO FILE ONLY, BECAUSE WE WANT TO SUPPORT OLD COMPILERS, WE ARE *NOT* INCLUDING RAW UTF-8 CHARACTERS IN THIS SOURCE FILE. - // Instead we are encoding a few strings with hexadecimal constants. Don't do this in your application! - // Please use u8"text in any language" in your application! - // Note that characters values are preserved even by InputText() if the font cannot be displayed, so you can safely copy & paste garbled characters into another application. - ImGui::TextWrapped("CJK text will only appears if the font was loaded with the appropriate CJK character ranges. Call io.Font->LoadFromFileTTF() manually to load extra character ranges. Read misc/fonts/README.txt for details."); - ImGui::Text("Hiragana: \xe3\x81\x8b\xe3\x81\x8d\xe3\x81\x8f\xe3\x81\x91\xe3\x81\x93 (kakikukeko)"); // Normally we would use u8"blah blah" with the proper characters directly in the string. - ImGui::Text("Kanjis: \xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e (nihongo)"); - static char buf[32] = "\xe6\x97\xa5\xe6\x9c\xac\xe8\xaa\x9e"; - //static char buf[32] = u8"NIHONGO"; // <- this is how you would write it with C++11, using real kanjis - ImGui::InputText("UTF-8 input", buf, IM_ARRAYSIZE(buf)); - ImGui::TreePop(); - } - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Images")) - { - ImGuiIO& io = ImGui::GetIO(); - ImGui::TextWrapped("Below we are displaying the font texture (which is the only texture we have access to in this demo). Use the 'ImTextureID' type as storage to pass pointers or identifier to your own texture data. Hover the texture for a zoomed view!"); - - // Here we are grabbing the font texture because that's the only one we have access to inside the demo code. - // Remember that ImTextureID is just storage for whatever you want it to be, it is essentially a value that will be passed to the render function inside the ImDrawCmd structure. - // If you use one of the default imgui_impl_XXXX.cpp renderer, they all have comments at the top of their file to specify what they expect to be stored in ImTextureID. - // (for example, the imgui_impl_dx11.cpp renderer expect a 'ID3D11ShaderResourceView*' pointer. The imgui_impl_glfw_gl3.cpp renderer expect a GLuint OpenGL texture identifier etc.) - // If you decided that ImTextureID = MyEngineTexture*, then you can pass your MyEngineTexture* pointers to ImGui::Image(), and gather width/height through your own functions, etc. - // Using ShowMetricsWindow() as a "debugger" to inspect the draw data that are being passed to your render will help you debug issues if you are confused about this. - // Consider using the lower-level ImDrawList::AddImage() API, via ImGui::GetWindowDrawList()->AddImage(). - ImTextureID my_tex_id = io.Fonts->TexID; - float my_tex_w = (float)io.Fonts->TexWidth; - float my_tex_h = (float)io.Fonts->TexHeight; - - ImGui::Text("%.0fx%.0f", my_tex_w, my_tex_h); - ImVec2 pos = ImGui::GetCursorScreenPos(); - ImGui::Image(my_tex_id, ImVec2(my_tex_w, my_tex_h), ImVec2(0,0), ImVec2(1,1), ImColor(255,255,255,255), ImColor(255,255,255,128)); - if (ImGui::IsItemHovered()) - { - ImGui::BeginTooltip(); - float region_sz = 32.0f; - float region_x = io.MousePos.x - pos.x - region_sz * 0.5f; if (region_x < 0.0f) region_x = 0.0f; else if (region_x > my_tex_w - region_sz) region_x = my_tex_w - region_sz; - float region_y = io.MousePos.y - pos.y - region_sz * 0.5f; if (region_y < 0.0f) region_y = 0.0f; else if (region_y > my_tex_h - region_sz) region_y = my_tex_h - region_sz; - float zoom = 4.0f; - ImGui::Text("Min: (%.2f, %.2f)", region_x, region_y); - ImGui::Text("Max: (%.2f, %.2f)", region_x + region_sz, region_y + region_sz); - ImVec2 uv0 = ImVec2((region_x) / my_tex_w, (region_y) / my_tex_h); - ImVec2 uv1 = ImVec2((region_x + region_sz) / my_tex_w, (region_y + region_sz) / my_tex_h); - ImGui::Image(my_tex_id, ImVec2(region_sz * zoom, region_sz * zoom), uv0, uv1, ImColor(255,255,255,255), ImColor(255,255,255,128)); - ImGui::EndTooltip(); - } - ImGui::TextWrapped("And now some textured buttons.."); - static int pressed_count = 0; - for (int i = 0; i < 8; i++) - { - ImGui::PushID(i); - int frame_padding = -1 + i; // -1 = uses default padding - if (ImGui::ImageButton(my_tex_id, ImVec2(32,32), ImVec2(0,0), ImVec2(32.0f/my_tex_w,32/my_tex_h), frame_padding, ImColor(0,0,0,255))) - pressed_count += 1; - ImGui::PopID(); - ImGui::SameLine(); - } - ImGui::NewLine(); - ImGui::Text("Pressed %d times.", pressed_count); - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Combo")) - { - // Expose flags as checkbox for the demo - static ImGuiComboFlags flags = 0; - ImGui::CheckboxFlags("ImGuiComboFlags_PopupAlignLeft", (unsigned int*)&flags, ImGuiComboFlags_PopupAlignLeft); - if (ImGui::CheckboxFlags("ImGuiComboFlags_NoArrowButton", (unsigned int*)&flags, ImGuiComboFlags_NoArrowButton)) - flags &= ~ImGuiComboFlags_NoPreview; // Clear the other flag, as we cannot combine both - if (ImGui::CheckboxFlags("ImGuiComboFlags_NoPreview", (unsigned int*)&flags, ImGuiComboFlags_NoPreview)) - flags &= ~ImGuiComboFlags_NoArrowButton; // Clear the other flag, as we cannot combine both - - // General BeginCombo() API, you have full control over your selection data and display type. - // (your selection data could be an index, a pointer to the object, an id for the object, a flag stored in the object itself, etc.) - const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLLLLL", "MMMM", "OOOOOOO" }; - static const char* item_current = items[0]; // Here our selection is a single pointer stored outside the object. - if (ImGui::BeginCombo("combo 1", item_current, flags)) // The second parameter is the label previewed before opening the combo. - { - for (int n = 0; n < IM_ARRAYSIZE(items); n++) - { - bool is_selected = (item_current == items[n]); - if (ImGui::Selectable(items[n], is_selected)) - item_current = items[n]; - if (is_selected) - ImGui::SetItemDefaultFocus(); // Set the initial focus when opening the combo (scrolling + for keyboard navigation support in the upcoming navigation branch) - } - ImGui::EndCombo(); - } - - // Simplified one-liner Combo() API, using values packed in a single constant string - static int item_current_2 = 0; - ImGui::Combo("combo 2 (one-liner)", &item_current_2, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0"); - - // Simplified one-liner Combo() using an array of const char* - static int item_current_3 = -1; // If the selection isn't within 0..count, Combo won't display a preview - ImGui::Combo("combo 3 (array)", &item_current_3, items, IM_ARRAYSIZE(items)); - - // Simplified one-liner Combo() using an accessor function - struct FuncHolder { static bool ItemGetter(void* data, int idx, const char** out_str) { *out_str = ((const char**)data)[idx]; return true; } }; - static int item_current_4 = 0; - ImGui::Combo("combo 4 (function)", &item_current_4, &FuncHolder::ItemGetter, items, IM_ARRAYSIZE(items)); - - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Selectables")) - { - // Selectable() has 2 overloads: - // - The one taking "bool selected" as a read-only selection information. When Selectable() has been clicked is returns true and you can alter selection state accordingly. - // - The one taking "bool* p_selected" as a read-write selection information (convenient in some cases) - // The earlier is more flexible, as in real application your selection may be stored in a different manner (in flags within objects, as an external list, etc). - if (ImGui::TreeNode("Basic")) - { - static bool selection[5] = { false, true, false, false, false }; - ImGui::Selectable("1. I am selectable", &selection[0]); - ImGui::Selectable("2. I am selectable", &selection[1]); - ImGui::Text("3. I am not selectable"); - ImGui::Selectable("4. I am selectable", &selection[3]); - if (ImGui::Selectable("5. I am double clickable", selection[4], ImGuiSelectableFlags_AllowDoubleClick)) - if (ImGui::IsMouseDoubleClicked(0)) - selection[4] = !selection[4]; - ImGui::TreePop(); - } - if (ImGui::TreeNode("Selection State: Single Selection")) - { - static int selected = -1; - for (int n = 0; n < 5; n++) - { - char buf[32]; - sprintf(buf, "Object %d", n); - if (ImGui::Selectable(buf, selected == n)) - selected = n; - } - ImGui::TreePop(); - } - if (ImGui::TreeNode("Selection State: Multiple Selection")) - { - ShowHelpMarker("Hold CTRL and click to select multiple items."); - static bool selection[5] = { false, false, false, false, false }; - for (int n = 0; n < 5; n++) - { - char buf[32]; - sprintf(buf, "Object %d", n); - if (ImGui::Selectable(buf, selection[n])) - { - if (!ImGui::GetIO().KeyCtrl) // Clear selection when CTRL is not held - memset(selection, 0, sizeof(selection)); - selection[n] ^= 1; - } - } - ImGui::TreePop(); - } - if (ImGui::TreeNode("Rendering more text into the same line")) - { - // Using the Selectable() override that takes "bool* p_selected" parameter and toggle your booleans automatically. - static bool selected[3] = { false, false, false }; - ImGui::Selectable("main.c", &selected[0]); ImGui::SameLine(300); ImGui::Text(" 2,345 bytes"); - ImGui::Selectable("Hello.cpp", &selected[1]); ImGui::SameLine(300); ImGui::Text("12,345 bytes"); - ImGui::Selectable("Hello.h", &selected[2]); ImGui::SameLine(300); ImGui::Text(" 2,345 bytes"); - ImGui::TreePop(); - } - if (ImGui::TreeNode("In columns")) - { - ImGui::Columns(3, NULL, false); - static bool selected[16] = { 0 }; - for (int i = 0; i < 16; i++) - { - char label[32]; sprintf(label, "Item %d", i); - if (ImGui::Selectable(label, &selected[i])) {} - ImGui::NextColumn(); - } - ImGui::Columns(1); - ImGui::TreePop(); - } - if (ImGui::TreeNode("Grid")) - { - static bool selected[16] = { true, false, false, false, false, true, false, false, false, false, true, false, false, false, false, true }; - for (int i = 0; i < 16; i++) - { - ImGui::PushID(i); - if (ImGui::Selectable("Sailor", &selected[i], 0, ImVec2(50,50))) - { - int x = i % 4, y = i / 4; - if (x > 0) selected[i - 1] ^= 1; - if (x < 3) selected[i + 1] ^= 1; - if (y > 0) selected[i - 4] ^= 1; - if (y < 3) selected[i + 4] ^= 1; - } - if ((i % 4) < 3) ImGui::SameLine(); - ImGui::PopID(); - } - ImGui::TreePop(); - } - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Filtered Text Input")) - { - static char buf1[64] = ""; ImGui::InputText("default", buf1, 64); - static char buf2[64] = ""; ImGui::InputText("decimal", buf2, 64, ImGuiInputTextFlags_CharsDecimal); - static char buf3[64] = ""; ImGui::InputText("hexadecimal", buf3, 64, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase); - static char buf4[64] = ""; ImGui::InputText("uppercase", buf4, 64, ImGuiInputTextFlags_CharsUppercase); - static char buf5[64] = ""; ImGui::InputText("no blank", buf5, 64, ImGuiInputTextFlags_CharsNoBlank); - struct TextFilters { static int FilterImGuiLetters(ImGuiTextEditCallbackData* data) { if (data->EventChar < 256 && strchr("imgui", (char)data->EventChar)) return 0; return 1; } }; - static char buf6[64] = ""; ImGui::InputText("\"imgui\" letters", buf6, 64, ImGuiInputTextFlags_CallbackCharFilter, TextFilters::FilterImGuiLetters); - - ImGui::Text("Password input"); - static char bufpass[64] = "password123"; - ImGui::InputText("password", bufpass, 64, ImGuiInputTextFlags_Password | ImGuiInputTextFlags_CharsNoBlank); - ImGui::SameLine(); ShowHelpMarker("Display all characters as '*'.\nDisable clipboard cut and copy.\nDisable logging.\n"); - ImGui::InputText("password (clear)", bufpass, 64, ImGuiInputTextFlags_CharsNoBlank); - - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Multi-line Text Input")) - { - static bool read_only = false; - static char text[1024*16] = - "/*\n" - " The Pentium F00F bug, shorthand for F0 0F C7 C8,\n" - " the hexadecimal encoding of one offending instruction,\n" - " more formally, the invalid operand with locked CMPXCHG8B\n" - " instruction bug, is a design flaw in the majority of\n" - " Intel Pentium, Pentium MMX, and Pentium OverDrive\n" - " processors (all in the P5 microarchitecture).\n" - "*/\n\n" - "label:\n" - "\tlock cmpxchg8b eax\n"; - - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0,0)); - ImGui::Checkbox("Read-only", &read_only); - ImGui::PopStyleVar(); - ImGui::InputTextMultiline("##source", text, IM_ARRAYSIZE(text), ImVec2(-1.0f, ImGui::GetTextLineHeight() * 16), ImGuiInputTextFlags_AllowTabInput | (read_only ? ImGuiInputTextFlags_ReadOnly : 0)); - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Plots Widgets")) - { - static bool animate = true; - ImGui::Checkbox("Animate", &animate); - - static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; - ImGui::PlotLines("Frame Times", arr, IM_ARRAYSIZE(arr)); - - // Create a dummy array of contiguous float values to plot - // Tip: If your float aren't contiguous but part of a structure, you can pass a pointer to your first float and the sizeof() of your structure in the Stride parameter. - static float values[90] = { 0 }; - static int values_offset = 0; - static float refresh_time = 0.0f; - if (!animate || refresh_time == 0.0f) - refresh_time = ImGui::GetTime(); - while (refresh_time < ImGui::GetTime()) // Create dummy data at fixed 60 hz rate for the demo - { - static float phase = 0.0f; - values[values_offset] = cosf(phase); - values_offset = (values_offset+1) % IM_ARRAYSIZE(values); - phase += 0.10f*values_offset; - refresh_time += 1.0f/60.0f; - } - ImGui::PlotLines("Lines", values, IM_ARRAYSIZE(values), values_offset, "avg 0.0", -1.0f, 1.0f, ImVec2(0,80)); - ImGui::PlotHistogram("Histogram", arr, IM_ARRAYSIZE(arr), 0, NULL, 0.0f, 1.0f, ImVec2(0,80)); - - // Use functions to generate output - // FIXME: This is rather awkward because current plot API only pass in indices. We probably want an API passing floats and user provide sample rate/count. - struct Funcs - { - static float Sin(void*, int i) { return sinf(i * 0.1f); } - static float Saw(void*, int i) { return (i & 1) ? 1.0f : -1.0f; } - }; - static int func_type = 0, display_count = 70; - ImGui::Separator(); - ImGui::PushItemWidth(100); ImGui::Combo("func", &func_type, "Sin\0Saw\0"); ImGui::PopItemWidth(); - ImGui::SameLine(); - ImGui::SliderInt("Sample count", &display_count, 1, 400); - float (*func)(void*, int) = (func_type == 0) ? Funcs::Sin : Funcs::Saw; - ImGui::PlotLines("Lines", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0,80)); - ImGui::PlotHistogram("Histogram", func, NULL, display_count, 0, NULL, -1.0f, 1.0f, ImVec2(0,80)); - ImGui::Separator(); - - // Animate a simple progress bar - static float progress = 0.0f, progress_dir = 1.0f; - if (animate) - { - progress += progress_dir * 0.4f * ImGui::GetIO().DeltaTime; - if (progress >= +1.1f) { progress = +1.1f; progress_dir *= -1.0f; } - if (progress <= -0.1f) { progress = -0.1f; progress_dir *= -1.0f; } - } - - // Typically we would use ImVec2(-1.0f,0.0f) to use all available width, or ImVec2(width,0.0f) for a specified width. ImVec2(0.0f,0.0f) uses ItemWidth. - ImGui::ProgressBar(progress, ImVec2(0.0f,0.0f)); - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); - ImGui::Text("Progress Bar"); - - float progress_saturated = (progress < 0.0f) ? 0.0f : (progress > 1.0f) ? 1.0f : progress; - char buf[32]; - sprintf(buf, "%d/%d", (int)(progress_saturated*1753), 1753); - ImGui::ProgressBar(progress, ImVec2(0.f,0.f), buf); - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Color/Picker Widgets")) - { - static ImVec4 color = ImColor(114, 144, 154, 200); - - static bool alpha_preview = true; - static bool alpha_half_preview = false; - static bool drag_and_drop = true; - static bool options_menu = true; - static bool hdr = false; - ImGui::Checkbox("With Alpha Preview", &alpha_preview); - ImGui::Checkbox("With Half Alpha Preview", &alpha_half_preview); - ImGui::Checkbox("With Drag and Drop", &drag_and_drop); - ImGui::Checkbox("With Options Menu", &options_menu); ImGui::SameLine(); ShowHelpMarker("Right-click on the individual color widget to show options."); - ImGui::Checkbox("With HDR", &hdr); ImGui::SameLine(); ShowHelpMarker("Currently all this does is to lift the 0..1 limits on dragging widgets."); - int misc_flags = (hdr ? ImGuiColorEditFlags_HDR : 0) | (drag_and_drop ? 0 : ImGuiColorEditFlags_NoDragDrop) | (alpha_half_preview ? ImGuiColorEditFlags_AlphaPreviewHalf : (alpha_preview ? ImGuiColorEditFlags_AlphaPreview : 0)) | (options_menu ? 0 : ImGuiColorEditFlags_NoOptions); - - ImGui::Text("Color widget:"); - ImGui::SameLine(); ShowHelpMarker("Click on the colored square to open a color picker.\nCTRL+click on individual component to input value.\n"); - ImGui::ColorEdit3("MyColor##1", (float*)&color, misc_flags); - - ImGui::Text("Color widget HSV with Alpha:"); - ImGui::ColorEdit4("MyColor##2", (float*)&color, ImGuiColorEditFlags_HSV | misc_flags); - - ImGui::Text("Color widget with Float Display:"); - ImGui::ColorEdit4("MyColor##2f", (float*)&color, ImGuiColorEditFlags_Float | misc_flags); - - ImGui::Text("Color button with Picker:"); - ImGui::SameLine(); ShowHelpMarker("With the ImGuiColorEditFlags_NoInputs flag you can hide all the slider/text inputs.\nWith the ImGuiColorEditFlags_NoLabel flag you can pass a non-empty label which will only be used for the tooltip and picker popup."); - ImGui::ColorEdit4("MyColor##3", (float*)&color, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | misc_flags); - - ImGui::Text("Color button with Custom Picker Popup:"); - - // Generate a dummy palette - static bool saved_palette_inited = false; - static ImVec4 saved_palette[32]; - if (!saved_palette_inited) - for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++) - { - ImGui::ColorConvertHSVtoRGB(n / 31.0f, 0.8f, 0.8f, saved_palette[n].x, saved_palette[n].y, saved_palette[n].z); - saved_palette[n].w = 1.0f; // Alpha - } - saved_palette_inited = true; - - static ImVec4 backup_color; - bool open_popup = ImGui::ColorButton("MyColor##3b", color, misc_flags); - ImGui::SameLine(); - open_popup |= ImGui::Button("Palette"); - if (open_popup) - { - ImGui::OpenPopup("mypicker"); - backup_color = color; - } - if (ImGui::BeginPopup("mypicker")) - { - // FIXME: Adding a drag and drop example here would be perfect! - ImGui::Text("MY CUSTOM COLOR PICKER WITH AN AMAZING PALETTE!"); - ImGui::Separator(); - ImGui::ColorPicker4("##picker", (float*)&color, misc_flags | ImGuiColorEditFlags_NoSidePreview | ImGuiColorEditFlags_NoSmallPreview); - ImGui::SameLine(); - ImGui::BeginGroup(); - ImGui::Text("Current"); - ImGui::ColorButton("##current", color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, ImVec2(60,40)); - ImGui::Text("Previous"); - if (ImGui::ColorButton("##previous", backup_color, ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf, ImVec2(60,40))) - color = backup_color; - ImGui::Separator(); - ImGui::Text("Palette"); - for (int n = 0; n < IM_ARRAYSIZE(saved_palette); n++) - { - ImGui::PushID(n); - if ((n % 8) != 0) - ImGui::SameLine(0.0f, ImGui::GetStyle().ItemSpacing.y); - if (ImGui::ColorButton("##palette", saved_palette[n], ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_NoTooltip, ImVec2(20,20))) - color = ImVec4(saved_palette[n].x, saved_palette[n].y, saved_palette[n].z, color.w); // Preserve alpha! - - if (ImGui::BeginDragDropTarget()) - { - if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F)) - memcpy((float*)&saved_palette[n], payload->Data, sizeof(float) * 3); - if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F)) - memcpy((float*)&saved_palette[n], payload->Data, sizeof(float) * 4); - EndDragDropTarget(); - } - - ImGui::PopID(); - } - ImGui::EndGroup(); - ImGui::EndPopup(); - } - - ImGui::Text("Color button only:"); - ImGui::ColorButton("MyColor##3c", *(ImVec4*)&color, misc_flags, ImVec2(80,80)); - - ImGui::Text("Color picker:"); - static bool alpha = true; - static bool alpha_bar = true; - static bool side_preview = true; - static bool ref_color = false; - static ImVec4 ref_color_v(1.0f,0.0f,1.0f,0.5f); - static int inputs_mode = 2; - static int picker_mode = 0; - ImGui::Checkbox("With Alpha", &alpha); - ImGui::Checkbox("With Alpha Bar", &alpha_bar); - ImGui::Checkbox("With Side Preview", &side_preview); - if (side_preview) - { - ImGui::SameLine(); - ImGui::Checkbox("With Ref Color", &ref_color); - if (ref_color) - { - ImGui::SameLine(); - ImGui::ColorEdit4("##RefColor", &ref_color_v.x, ImGuiColorEditFlags_NoInputs | misc_flags); - } - } - ImGui::Combo("Inputs Mode", &inputs_mode, "All Inputs\0No Inputs\0RGB Input\0HSV Input\0HEX Input\0"); - ImGui::Combo("Picker Mode", &picker_mode, "Auto/Current\0Hue bar + SV rect\0Hue wheel + SV triangle\0"); - ImGui::SameLine(); ShowHelpMarker("User can right-click the picker to change mode."); - ImGuiColorEditFlags flags = misc_flags; - if (!alpha) flags |= ImGuiColorEditFlags_NoAlpha; // This is by default if you call ColorPicker3() instead of ColorPicker4() - if (alpha_bar) flags |= ImGuiColorEditFlags_AlphaBar; - if (!side_preview) flags |= ImGuiColorEditFlags_NoSidePreview; - if (picker_mode == 1) flags |= ImGuiColorEditFlags_PickerHueBar; - if (picker_mode == 2) flags |= ImGuiColorEditFlags_PickerHueWheel; - if (inputs_mode == 1) flags |= ImGuiColorEditFlags_NoInputs; - if (inputs_mode == 2) flags |= ImGuiColorEditFlags_RGB; - if (inputs_mode == 3) flags |= ImGuiColorEditFlags_HSV; - if (inputs_mode == 4) flags |= ImGuiColorEditFlags_HEX; - ImGui::ColorPicker4("MyColor##4", (float*)&color, flags, ref_color ? &ref_color_v.x : NULL); - - ImGui::Text("Programmatically set defaults:"); - ImGui::SameLine(); ShowHelpMarker("SetColorEditOptions() is designed to allow you to set boot-time default.\nWe don't have Push/Pop functions because you can force options on a per-widget basis if needed, and the user can change non-forced ones with the options menu.\nWe don't have a getter to avoid encouraging you to persistently save values that aren't forward-compatible."); - if (ImGui::Button("Default: Uint8 + HSV + Hue Bar")) - ImGui::SetColorEditOptions(ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_HSV | ImGuiColorEditFlags_PickerHueBar); - if (ImGui::Button("Default: Float + HDR + Hue Wheel")) - ImGui::SetColorEditOptions(ImGuiColorEditFlags_Float | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_PickerHueWheel); - - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Range Widgets")) - { - static float begin = 10, end = 90; - static int begin_i = 100, end_i = 1000; - ImGui::DragFloatRange2("range", &begin, &end, 0.25f, 0.0f, 100.0f, "Min: %.1f %%", "Max: %.1f %%"); - ImGui::DragIntRange2("range int (no bounds)", &begin_i, &end_i, 5, 0, 0, "Min: %d units", "Max: %d units"); - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Data Types")) - { - // The DragScalar, InputScalar, SliderScalar functions allow manipulating most common data types: signed/unsigned int/long long and float/double - // To avoid polluting the public API with all possible combinations, we use the ImGuiDataType enum to pass the type, and argument-by-values are turned into argument-by-address. - // This is the reason the test code below creates local variables to hold "zero" "one" etc. for each types. - // In practice, if you frequently use a given type that is not covered by the normal API entry points, you may want to wrap it yourself inside a 1 line function - // which can take typed values argument instead of void*, and then pass their address to the generic function. For example: - // bool SliderU64(const char *label, u64* value, u64 min = 0, u64 max = 0, const char* format = "%lld") { return SliderScalar(label, ImGuiDataType_U64, value, &min, &max, format); } - // Below are helper variables we can take the address of to work-around this: - // Note that the SliderScalar function has a maximum usable range of half the natural type maximum, hence the /2 below. - const ImS32 s32_zero = 0, s32_one = 1, s32_fifty = 50, s32_min = INT_MIN/2, s32_max = INT_MAX/2, s32_hi_a = INT_MAX/2 - 100, s32_hi_b = INT_MAX/2; - const ImU32 u32_zero = 0, u32_one = 1, u32_fifty = 50, u32_min = 0, u32_max = UINT_MAX/2, u32_hi_a = UINT_MAX/2 - 100, u32_hi_b = UINT_MAX/2; - const ImS64 s64_zero = 0, s64_one = 1, s64_fifty = 50, s64_min = LLONG_MIN/2, s64_max = LLONG_MAX/2, s64_hi_a = LLONG_MAX/2 - 100, s64_hi_b = LLONG_MAX/2; - const ImU64 u64_zero = 0, u64_one = 1, u64_fifty = 50, u64_min = 0, u64_max = ULLONG_MAX/2, u64_hi_a = ULLONG_MAX/2 - 100, u64_hi_b = ULLONG_MAX/2; - const float f32_zero = 0.f, f32_one = 1.f, f32_lo_a = -10000000000.0f, f32_hi_a = +10000000000.0f; - const double f64_zero = 0., f64_one = 1., f64_lo_a = -1000000000000000, f64_hi_a = +1000000000000000; - - // State - static ImS32 s32_v = -1; - static ImU32 u32_v = (ImU32)-1; - static ImS64 s64_v = -1; - static ImU64 u64_v = (ImU64)-1; - static float f32_v = 0.123f; - static double f64_v = 90000.01234567890123456789; - - const float drag_speed = 0.2f; - static bool drag_clamp = false; - ImGui::Text("Drags:"); - ImGui::Checkbox("Clamp integers to 0..50", &drag_clamp); ImGui::SameLine(); ShowHelpMarker("As with every widgets in dear imgui, we never modify values unless there is a user interaction.\nYou can override the clamping limits by using CTRL+Click to input a value."); - ImGui::DragScalar("drag s32", ImGuiDataType_S32, &s32_v, drag_speed, drag_clamp ? &s32_zero : NULL, drag_clamp ? &s32_fifty : NULL); - ImGui::DragScalar("drag u32", ImGuiDataType_U32, &u32_v, drag_speed, drag_clamp ? &u32_zero : NULL, drag_clamp ? &u32_fifty : NULL, "%u ms"); - ImGui::DragScalar("drag s64", ImGuiDataType_S64, &s64_v, drag_speed, drag_clamp ? &s64_zero : NULL, drag_clamp ? &s64_fifty : NULL); - ImGui::DragScalar("drag u64", ImGuiDataType_U64, &u64_v, drag_speed, drag_clamp ? &u64_zero : NULL, drag_clamp ? &u64_fifty : NULL); - ImGui::DragScalar("drag float", ImGuiDataType_Float, &f32_v, 0.005f, &f32_zero, &f32_one, "%f", 1.0f); - ImGui::DragScalar("drag float ^2", ImGuiDataType_Float, &f32_v, 0.005f, &f32_zero, &f32_one, "%f", 2.0f); ImGui::SameLine(); ShowHelpMarker("You can use the 'power' parameter to increase tweaking precision on one side of the range."); - ImGui::DragScalar("drag double", ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, NULL, "%.10f grams", 1.0f); - ImGui::DragScalar("drag double ^2", ImGuiDataType_Double, &f64_v, 0.0005f, &f64_zero, &f64_one, "0 < %.10f < 1", 2.0f); - - ImGui::Text("Sliders"); - ImGui::SliderScalar("slider s32 low", ImGuiDataType_S32, &s32_v, &s32_zero, &s32_fifty,"%d"); - ImGui::SliderScalar("slider s32 high", ImGuiDataType_S32, &s32_v, &s32_hi_a, &s32_hi_b, "%d"); - ImGui::SliderScalar("slider s32 full", ImGuiDataType_S32, &s32_v, &s32_min, &s32_max, "%d"); - ImGui::SliderScalar("slider u32 low", ImGuiDataType_U32, &u32_v, &u32_zero, &u32_fifty,"%u"); - ImGui::SliderScalar("slider u32 high", ImGuiDataType_U32, &u32_v, &u32_hi_a, &u32_hi_b, "%u"); - ImGui::SliderScalar("slider u32 full", ImGuiDataType_U32, &u32_v, &u32_min, &u32_max, "%u"); - ImGui::SliderScalar("slider s64 low", ImGuiDataType_S64, &s64_v, &s64_zero, &s64_fifty,"%I64d"); - ImGui::SliderScalar("slider s64 high", ImGuiDataType_S64, &s64_v, &s64_hi_a, &s64_hi_b, "%I64d"); - ImGui::SliderScalar("slider s64 full", ImGuiDataType_S64, &s64_v, &s64_min, &s64_max, "%I64d"); - ImGui::SliderScalar("slider u64 low", ImGuiDataType_U64, &u64_v, &u64_zero, &u64_fifty,"%I64u ms"); - ImGui::SliderScalar("slider u64 high", ImGuiDataType_U64, &u64_v, &u64_hi_a, &u64_hi_b, "%I64u ms"); - ImGui::SliderScalar("slider u64 full", ImGuiDataType_U64, &u64_v, &u64_min, &u64_max, "%I64u ms"); - ImGui::SliderScalar("slider float low", ImGuiDataType_Float, &f32_v, &f32_zero, &f32_one); - ImGui::SliderScalar("slider float low^2", ImGuiDataType_Float, &f32_v, &f32_zero, &f32_one, "%.10f", 2.0f); - ImGui::SliderScalar("slider float high", ImGuiDataType_Float, &f32_v, &f32_lo_a, &f32_hi_a, "%e"); - ImGui::SliderScalar("slider double low", ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one, "%.10f grams", 1.0f); - ImGui::SliderScalar("slider double low^2",ImGuiDataType_Double, &f64_v, &f64_zero, &f64_one, "%.10f", 2.0f); - ImGui::SliderScalar("slider double high", ImGuiDataType_Double, &f64_v, &f64_lo_a, &f64_hi_a, "%e grams", 1.0f); - - static bool inputs_step = true; - ImGui::Text("Inputs"); - ImGui::Checkbox("Show step buttons", &inputs_step); - ImGui::InputScalar("input s32", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%d"); - ImGui::InputScalar("input s32 hex", ImGuiDataType_S32, &s32_v, inputs_step ? &s32_one : NULL, NULL, "%08X", ImGuiInputTextFlags_CharsHexadecimal); - ImGui::InputScalar("input u32", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%u"); - ImGui::InputScalar("input u32 hex", ImGuiDataType_U32, &u32_v, inputs_step ? &u32_one : NULL, NULL, "%08X", ImGuiInputTextFlags_CharsHexadecimal); - ImGui::InputScalar("input s64", ImGuiDataType_S64, &s64_v, inputs_step ? &s64_one : NULL); - ImGui::InputScalar("input u64", ImGuiDataType_U64, &u64_v, inputs_step ? &u64_one : NULL); - ImGui::InputScalar("input float", ImGuiDataType_Float, &f32_v, inputs_step ? &f32_one : NULL); - ImGui::InputScalar("input double", ImGuiDataType_Double, &f64_v, inputs_step ? &f64_one : NULL); - - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Multi-component Widgets")) - { - static float vec4f[4] = { 0.10f, 0.20f, 0.30f, 0.44f }; - static int vec4i[4] = { 1, 5, 100, 255 }; - - ImGui::InputFloat2("input float2", vec4f); - ImGui::DragFloat2("drag float2", vec4f, 0.01f, 0.0f, 1.0f); - ImGui::SliderFloat2("slider float2", vec4f, 0.0f, 1.0f); - ImGui::InputInt2("input int2", vec4i); - ImGui::DragInt2("drag int2", vec4i, 1, 0, 255); - ImGui::SliderInt2("slider int2", vec4i, 0, 255); - ImGui::Spacing(); - - ImGui::InputFloat3("input float3", vec4f); - ImGui::DragFloat3("drag float3", vec4f, 0.01f, 0.0f, 1.0f); - ImGui::SliderFloat3("slider float3", vec4f, 0.0f, 1.0f); - ImGui::InputInt3("input int3", vec4i); - ImGui::DragInt3("drag int3", vec4i, 1, 0, 255); - ImGui::SliderInt3("slider int3", vec4i, 0, 255); - ImGui::Spacing(); - - ImGui::InputFloat4("input float4", vec4f); - ImGui::DragFloat4("drag float4", vec4f, 0.01f, 0.0f, 1.0f); - ImGui::SliderFloat4("slider float4", vec4f, 0.0f, 1.0f); - ImGui::InputInt4("input int4", vec4i); - ImGui::DragInt4("drag int4", vec4i, 1, 0, 255); - ImGui::SliderInt4("slider int4", vec4i, 0, 255); - - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Vertical Sliders")) - { - const float spacing = 4; - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(spacing, spacing)); - - static int int_value = 0; - ImGui::VSliderInt("##int", ImVec2(18,160), &int_value, 0, 5); - ImGui::SameLine(); - - static float values[7] = { 0.0f, 0.60f, 0.35f, 0.9f, 0.70f, 0.20f, 0.0f }; - ImGui::PushID("set1"); - for (int i = 0; i < 7; i++) - { - if (i > 0) ImGui::SameLine(); - ImGui::PushID(i); - ImGui::PushStyleColor(ImGuiCol_FrameBg, (ImVec4)ImColor::HSV(i/7.0f, 0.5f, 0.5f)); - ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, (ImVec4)ImColor::HSV(i/7.0f, 0.6f, 0.5f)); - ImGui::PushStyleColor(ImGuiCol_FrameBgActive, (ImVec4)ImColor::HSV(i/7.0f, 0.7f, 0.5f)); - ImGui::PushStyleColor(ImGuiCol_SliderGrab, (ImVec4)ImColor::HSV(i/7.0f, 0.9f, 0.9f)); - ImGui::VSliderFloat("##v", ImVec2(18,160), &values[i], 0.0f, 1.0f, ""); - if (ImGui::IsItemActive() || ImGui::IsItemHovered()) - ImGui::SetTooltip("%.3f", values[i]); - ImGui::PopStyleColor(4); - ImGui::PopID(); - } - ImGui::PopID(); - - ImGui::SameLine(); - ImGui::PushID("set2"); - static float values2[4] = { 0.20f, 0.80f, 0.40f, 0.25f }; - const int rows = 3; - const ImVec2 small_slider_size(18, (160.0f-(rows-1)*spacing)/rows); - for (int nx = 0; nx < 4; nx++) - { - if (nx > 0) ImGui::SameLine(); - ImGui::BeginGroup(); - for (int ny = 0; ny < rows; ny++) - { - ImGui::PushID(nx*rows+ny); - ImGui::VSliderFloat("##v", small_slider_size, &values2[nx], 0.0f, 1.0f, ""); - if (ImGui::IsItemActive() || ImGui::IsItemHovered()) - ImGui::SetTooltip("%.3f", values2[nx]); - ImGui::PopID(); - } - ImGui::EndGroup(); - } - ImGui::PopID(); - - ImGui::SameLine(); - ImGui::PushID("set3"); - for (int i = 0; i < 4; i++) - { - if (i > 0) ImGui::SameLine(); - ImGui::PushID(i); - ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize, 40); - ImGui::VSliderFloat("##v", ImVec2(40,160), &values[i], 0.0f, 1.0f, "%.2f\nsec"); - ImGui::PopStyleVar(); - ImGui::PopID(); - } - ImGui::PopID(); - ImGui::PopStyleVar(); - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Active, Focused, Hovered & Focused Tests")) - { - // Display the value of IsItemHovered() and other common item state functions. Note that the flags can be combined. - // (because BulletText is an item itself and that would affect the output of IsItemHovered() we pass all state in a single call to simplify the code). - static int item_type = 1; - static bool b = false; - static float col4f[4] = { 1.0f, 0.5, 0.0f, 1.0f }; - ImGui::RadioButton("Text", &item_type, 0); ImGui::SameLine(); - ImGui::RadioButton("Button", &item_type, 1); ImGui::SameLine(); - ImGui::RadioButton("CheckBox", &item_type, 2); ImGui::SameLine(); - ImGui::RadioButton("SliderFloat", &item_type, 3); ImGui::SameLine(); - ImGui::RadioButton("ColorEdit4", &item_type, 4); ImGui::SameLine(); - ImGui::RadioButton("ListBox", &item_type, 5); - bool ret = false; - if (item_type == 0) { ImGui::Text("ITEM: Text"); } // Testing text items with no identifier/interaction - if (item_type == 1) { ret = ImGui::Button("ITEM: Button"); } // Testing button - if (item_type == 2) { ret = ImGui::Checkbox("ITEM: CheckBox", &b); } // Testing checkbox - if (item_type == 3) { ret = ImGui::SliderFloat("ITEM: SliderFloat", &col4f[0], 0.0f, 1.0f); } // Testing basic item - if (item_type == 4) { ret = ImGui::ColorEdit4("ITEM: ColorEdit4", col4f); } // Testing multi-component items (IsItemXXX flags are reported merged) - if (item_type == 5) { const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::ListBox("ITEM: ListBox", ¤t, items, IM_ARRAYSIZE(items), IM_ARRAYSIZE(items)); } - ImGui::BulletText( - "Return value = %d\n" - "IsItemFocused() = %d\n" - "IsItemHovered() = %d\n" - "IsItemHovered(_AllowWhenBlockedByPopup) = %d\n" - "IsItemHovered(_AllowWhenBlockedByActiveItem) = %d\n" - "IsItemHovered(_AllowWhenOverlapped) = %d\n" - "IsItemHovered(_RectOnly) = %d\n" - "IsItemActive() = %d\n" - "IsItemDeactivated() = %d\n" - "IsItemDeactivatedAfterChange() = %d\n" - "IsItemVisible() = %d\n", - ret, - ImGui::IsItemFocused(), - ImGui::IsItemHovered(), - ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup), - ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), - ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenOverlapped), - ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly), - ImGui::IsItemActive(), - ImGui::IsItemDeactivated(), - ImGui::IsItemDeactivatedAfterChange(), - ImGui::IsItemVisible() - ); - - static bool embed_all_inside_a_child_window = false; - ImGui::Checkbox("Embed everything inside a child window (for additional testing)", &embed_all_inside_a_child_window); - if (embed_all_inside_a_child_window) - ImGui::BeginChild("outer_child", ImVec2(0, ImGui::GetFontSize() * 20), true); - - // Testing IsWindowFocused() function with its various flags. Note that the flags can be combined. - ImGui::BulletText( - "IsWindowFocused() = %d\n" - "IsWindowFocused(_ChildWindows) = %d\n" - "IsWindowFocused(_ChildWindows|_RootWindow) = %d\n" - "IsWindowFocused(_RootWindow) = %d\n" - "IsWindowFocused(_AnyWindow) = %d\n", - ImGui::IsWindowFocused(), - ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows), - ImGui::IsWindowFocused(ImGuiFocusedFlags_ChildWindows | ImGuiFocusedFlags_RootWindow), - ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow), - ImGui::IsWindowFocused(ImGuiFocusedFlags_AnyWindow)); - - // Testing IsWindowHovered() function with its various flags. Note that the flags can be combined. - ImGui::BulletText( - "IsWindowHovered() = %d\n" - "IsWindowHovered(_AllowWhenBlockedByPopup) = %d\n" - "IsWindowHovered(_AllowWhenBlockedByActiveItem) = %d\n" - "IsWindowHovered(_ChildWindows) = %d\n" - "IsWindowHovered(_ChildWindows|_RootWindow) = %d\n" - "IsWindowHovered(_RootWindow) = %d\n" - "IsWindowHovered(_AnyWindow) = %d\n", - ImGui::IsWindowHovered(), - ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup), - ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), - ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows), - ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow), - ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow), - ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)); - - ImGui::BeginChild("child", ImVec2(0, 50), true); - ImGui::Text("This is another child window for testing with the _ChildWindows flag."); - ImGui::EndChild(); - if (embed_all_inside_a_child_window) - EndChild(); - - ImGui::TreePop(); - } - } - - if (ImGui::CollapsingHeader("Layout")) - { - if (ImGui::TreeNode("Child regions")) - { - static bool disable_mouse_wheel = false; - static bool disable_menu = false; - ImGui::Checkbox("Disable Mouse Wheel", &disable_mouse_wheel); - ImGui::Checkbox("Disable Menu", &disable_menu); - - static int line = 50; - bool goto_line = ImGui::Button("Goto"); - ImGui::SameLine(); - ImGui::PushItemWidth(100); - goto_line |= ImGui::InputInt("##Line", &line, 0, 0, ImGuiInputTextFlags_EnterReturnsTrue); - ImGui::PopItemWidth(); - - // Child 1: no border, enable horizontal scrollbar - { - ImGui::BeginChild("Child1", ImVec2(ImGui::GetWindowContentRegionWidth() * 0.5f, 300), false, ImGuiWindowFlags_HorizontalScrollbar | (disable_mouse_wheel ? ImGuiWindowFlags_NoScrollWithMouse : 0)); - for (int i = 0; i < 100; i++) - { - ImGui::Text("%04d: scrollable region", i); - if (goto_line && line == i) - ImGui::SetScrollHere(); - } - if (goto_line && line >= 100) - ImGui::SetScrollHere(); - ImGui::EndChild(); - } - - ImGui::SameLine(); - - // Child 2: rounded border - { - ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0f); - ImGui::BeginChild("Child2", ImVec2(0,300), true, (disable_mouse_wheel ? ImGuiWindowFlags_NoScrollWithMouse : 0) | (disable_menu ? 0 : ImGuiWindowFlags_MenuBar)); - if (!disable_menu && ImGui::BeginMenuBar()) - { - if (ImGui::BeginMenu("Menu")) - { - ShowExampleMenuFile(); - ImGui::EndMenu(); - } - ImGui::EndMenuBar(); - } - ImGui::Columns(2); - for (int i = 0; i < 100; i++) - { - char buf[32]; - sprintf(buf, "%03d", i); - ImGui::Button(buf, ImVec2(-1.0f, 0.0f)); - ImGui::NextColumn(); - } - ImGui::EndChild(); - ImGui::PopStyleVar(); - } - - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Widgets Width")) - { - static float f = 0.0f; - ImGui::Text("PushItemWidth(100)"); - ImGui::SameLine(); ShowHelpMarker("Fixed width."); - ImGui::PushItemWidth(100); - ImGui::DragFloat("float##1", &f); - ImGui::PopItemWidth(); - - ImGui::Text("PushItemWidth(GetWindowWidth() * 0.5f)"); - ImGui::SameLine(); ShowHelpMarker("Half of window width."); - ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.5f); - ImGui::DragFloat("float##2", &f); - ImGui::PopItemWidth(); - - ImGui::Text("PushItemWidth(GetContentRegionAvailWidth() * 0.5f)"); - ImGui::SameLine(); ShowHelpMarker("Half of available width.\n(~ right-cursor_pos)\n(works within a column set)"); - ImGui::PushItemWidth(ImGui::GetContentRegionAvailWidth() * 0.5f); - ImGui::DragFloat("float##3", &f); - ImGui::PopItemWidth(); - - ImGui::Text("PushItemWidth(-100)"); - ImGui::SameLine(); ShowHelpMarker("Align to right edge minus 100"); - ImGui::PushItemWidth(-100); - ImGui::DragFloat("float##4", &f); - ImGui::PopItemWidth(); - - ImGui::Text("PushItemWidth(-1)"); - ImGui::SameLine(); ShowHelpMarker("Align to right edge"); - ImGui::PushItemWidth(-1); - ImGui::DragFloat("float##5", &f); - ImGui::PopItemWidth(); - - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Basic Horizontal Layout")) - { - ImGui::TextWrapped("(Use ImGui::SameLine() to keep adding items to the right of the preceding item)"); - - // Text - ImGui::Text("Two items: Hello"); ImGui::SameLine(); - ImGui::TextColored(ImVec4(1,1,0,1), "Sailor"); - - // Adjust spacing - ImGui::Text("More spacing: Hello"); ImGui::SameLine(0, 20); - ImGui::TextColored(ImVec4(1,1,0,1), "Sailor"); - - // Button - ImGui::AlignTextToFramePadding(); - ImGui::Text("Normal buttons"); ImGui::SameLine(); - ImGui::Button("Banana"); ImGui::SameLine(); - ImGui::Button("Apple"); ImGui::SameLine(); - ImGui::Button("Corniflower"); - - // Button - ImGui::Text("Small buttons"); ImGui::SameLine(); - ImGui::SmallButton("Like this one"); ImGui::SameLine(); - ImGui::Text("can fit within a text block."); - - // Aligned to arbitrary position. Easy/cheap column. - ImGui::Text("Aligned"); - ImGui::SameLine(150); ImGui::Text("x=150"); - ImGui::SameLine(300); ImGui::Text("x=300"); - ImGui::Text("Aligned"); - ImGui::SameLine(150); ImGui::SmallButton("x=150"); - ImGui::SameLine(300); ImGui::SmallButton("x=300"); - - // Checkbox - static bool c1=false,c2=false,c3=false,c4=false; - ImGui::Checkbox("My", &c1); ImGui::SameLine(); - ImGui::Checkbox("Tailor", &c2); ImGui::SameLine(); - ImGui::Checkbox("Is", &c3); ImGui::SameLine(); - ImGui::Checkbox("Rich", &c4); - - // Various - static float f0=1.0f, f1=2.0f, f2=3.0f; - ImGui::PushItemWidth(80); - const char* items[] = { "AAAA", "BBBB", "CCCC", "DDDD" }; - static int item = -1; - ImGui::Combo("Combo", &item, items, IM_ARRAYSIZE(items)); ImGui::SameLine(); - ImGui::SliderFloat("X", &f0, 0.0f,5.0f); ImGui::SameLine(); - ImGui::SliderFloat("Y", &f1, 0.0f,5.0f); ImGui::SameLine(); - ImGui::SliderFloat("Z", &f2, 0.0f,5.0f); - ImGui::PopItemWidth(); - - ImGui::PushItemWidth(80); - ImGui::Text("Lists:"); - static int selection[4] = { 0, 1, 2, 3 }; - for (int i = 0; i < 4; i++) - { - if (i > 0) ImGui::SameLine(); - ImGui::PushID(i); - ImGui::ListBox("", &selection[i], items, IM_ARRAYSIZE(items)); - ImGui::PopID(); - //if (ImGui::IsItemHovered()) ImGui::SetTooltip("ListBox %d hovered", i); - } - ImGui::PopItemWidth(); - - // Dummy - ImVec2 sz(30,30); - ImGui::Button("A", sz); ImGui::SameLine(); - ImGui::Dummy(sz); ImGui::SameLine(); - ImGui::Button("B", sz); - - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Groups")) - { - ImGui::TextWrapped("(Using ImGui::BeginGroup()/EndGroup() to layout items. BeginGroup() basically locks the horizontal position. EndGroup() bundles the whole group so that you can use functions such as IsItemHovered() on it.)"); - ImGui::BeginGroup(); - { - ImGui::BeginGroup(); - ImGui::Button("AAA"); - ImGui::SameLine(); - ImGui::Button("BBB"); - ImGui::SameLine(); - ImGui::BeginGroup(); - ImGui::Button("CCC"); - ImGui::Button("DDD"); - ImGui::EndGroup(); - ImGui::SameLine(); - ImGui::Button("EEE"); - ImGui::EndGroup(); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("First group hovered"); - } - // Capture the group size and create widgets using the same size - ImVec2 size = ImGui::GetItemRectSize(); - const float values[5] = { 0.5f, 0.20f, 0.80f, 0.60f, 0.25f }; - ImGui::PlotHistogram("##values", values, IM_ARRAYSIZE(values), 0, NULL, 0.0f, 1.0f, size); - - ImGui::Button("ACTION", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x)*0.5f,size.y)); - ImGui::SameLine(); - ImGui::Button("REACTION", ImVec2((size.x - ImGui::GetStyle().ItemSpacing.x)*0.5f,size.y)); - ImGui::EndGroup(); - ImGui::SameLine(); - - ImGui::Button("LEVERAGE\nBUZZWORD", size); - ImGui::SameLine(); - - if (ImGui::ListBoxHeader("List", size)) - { - ImGui::Selectable("Selected", true); - ImGui::Selectable("Not Selected", false); - ImGui::ListBoxFooter(); - } - - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Text Baseline Alignment")) - { - ImGui::TextWrapped("(This is testing the vertical alignment that occurs on text to keep it at the same baseline as widgets. Lines only composed of text or \"small\" widgets fit in less vertical spaces than lines with normal widgets)"); - - ImGui::Text("One\nTwo\nThree"); ImGui::SameLine(); - ImGui::Text("Hello\nWorld"); ImGui::SameLine(); - ImGui::Text("Banana"); - - ImGui::Text("Banana"); ImGui::SameLine(); - ImGui::Text("Hello\nWorld"); ImGui::SameLine(); - ImGui::Text("One\nTwo\nThree"); - - ImGui::Button("HOP##1"); ImGui::SameLine(); - ImGui::Text("Banana"); ImGui::SameLine(); - ImGui::Text("Hello\nWorld"); ImGui::SameLine(); - ImGui::Text("Banana"); - - ImGui::Button("HOP##2"); ImGui::SameLine(); - ImGui::Text("Hello\nWorld"); ImGui::SameLine(); - ImGui::Text("Banana"); - - ImGui::Button("TEST##1"); ImGui::SameLine(); - ImGui::Text("TEST"); ImGui::SameLine(); - ImGui::SmallButton("TEST##2"); - - ImGui::AlignTextToFramePadding(); // If your line starts with text, call this to align it to upcoming widgets. - ImGui::Text("Text aligned to Widget"); ImGui::SameLine(); - ImGui::Button("Widget##1"); ImGui::SameLine(); - ImGui::Text("Widget"); ImGui::SameLine(); - ImGui::SmallButton("Widget##2"); ImGui::SameLine(); - ImGui::Button("Widget##3"); - - // Tree - const float spacing = ImGui::GetStyle().ItemInnerSpacing.x; - ImGui::Button("Button##1"); - ImGui::SameLine(0.0f, spacing); - if (ImGui::TreeNode("Node##1")) { for (int i = 0; i < 6; i++) ImGui::BulletText("Item %d..", i); ImGui::TreePop(); } // Dummy tree data - - ImGui::AlignTextToFramePadding(); // Vertically align text node a bit lower so it'll be vertically centered with upcoming widget. Otherwise you can use SmallButton (smaller fit). - bool node_open = ImGui::TreeNode("Node##2"); // Common mistake to avoid: if we want to SameLine after TreeNode we need to do it before we add child content. - ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##2"); - if (node_open) { for (int i = 0; i < 6; i++) ImGui::BulletText("Item %d..", i); ImGui::TreePop(); } // Dummy tree data - - // Bullet - ImGui::Button("Button##3"); - ImGui::SameLine(0.0f, spacing); - ImGui::BulletText("Bullet text"); - - ImGui::AlignTextToFramePadding(); - ImGui::BulletText("Node"); - ImGui::SameLine(0.0f, spacing); ImGui::Button("Button##4"); - - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Scrolling")) - { - ImGui::TextWrapped("(Use SetScrollHere() or SetScrollFromPosY() to scroll to a given position.)"); - static bool track = true; - static int track_line = 50, scroll_to_px = 200; - ImGui::Checkbox("Track", &track); - ImGui::PushItemWidth(100); - ImGui::SameLine(130); track |= ImGui::DragInt("##line", &track_line, 0.25f, 0, 99, "Line = %d"); - bool scroll_to = ImGui::Button("Scroll To Pos"); - ImGui::SameLine(130); scroll_to |= ImGui::DragInt("##pos_y", &scroll_to_px, 1.00f, 0, 9999, "Y = %d px"); - ImGui::PopItemWidth(); - if (scroll_to) track = false; - - for (int i = 0; i < 5; i++) - { - if (i > 0) ImGui::SameLine(); - ImGui::BeginGroup(); - ImGui::Text("%s", i == 0 ? "Top" : i == 1 ? "25%" : i == 2 ? "Center" : i == 3 ? "75%" : "Bottom"); - ImGui::BeginChild(ImGui::GetID((void*)(intptr_t)i), ImVec2(ImGui::GetWindowWidth() * 0.17f, 200.0f), true); - if (scroll_to) - ImGui::SetScrollFromPosY(ImGui::GetCursorStartPos().y + scroll_to_px, i * 0.25f); - for (int line = 0; line < 100; line++) - { - if (track && line == track_line) - { - ImGui::TextColored(ImColor(255,255,0), "Line %d", line); - ImGui::SetScrollHere(i * 0.25f); // 0.0f:top, 0.5f:center, 1.0f:bottom - } - else - { - ImGui::Text("Line %d", line); - } - } - float scroll_y = ImGui::GetScrollY(), scroll_max_y = ImGui::GetScrollMaxY(); - ImGui::EndChild(); - ImGui::Text("%.0f/%0.f", scroll_y, scroll_max_y); - ImGui::EndGroup(); - } - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Horizontal Scrolling")) - { - ImGui::Bullet(); ImGui::TextWrapped("Horizontal scrolling for a window has to be enabled explicitly via the ImGuiWindowFlags_HorizontalScrollbar flag."); - ImGui::Bullet(); ImGui::TextWrapped("You may want to explicitly specify content width by calling SetNextWindowContentWidth() before Begin()."); - static int lines = 7; - ImGui::SliderInt("Lines", &lines, 1, 15); - ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f); - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2.0f, 1.0f)); - ImGui::BeginChild("scrolling", ImVec2(0, ImGui::GetFrameHeightWithSpacing()*7 + 30), true, ImGuiWindowFlags_HorizontalScrollbar); - for (int line = 0; line < lines; line++) - { - // Display random stuff (for the sake of this trivial demo we are using basic Button+SameLine. If you want to create your own time line for a real application you may be better off - // manipulating the cursor position yourself, aka using SetCursorPos/SetCursorScreenPos to position the widgets yourself. You may also want to use the lower-level ImDrawList API) - int num_buttons = 10 + ((line & 1) ? line * 9 : line * 3); - for (int n = 0; n < num_buttons; n++) - { - if (n > 0) ImGui::SameLine(); - ImGui::PushID(n + line * 1000); - char num_buf[16]; - sprintf(num_buf, "%d", n); - const char* label = (!(n%15)) ? "FizzBuzz" : (!(n%3)) ? "Fizz" : (!(n%5)) ? "Buzz" : num_buf; - float hue = n*0.05f; - ImGui::PushStyleColor(ImGuiCol_Button, (ImVec4)ImColor::HSV(hue, 0.6f, 0.6f)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, (ImVec4)ImColor::HSV(hue, 0.7f, 0.7f)); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, (ImVec4)ImColor::HSV(hue, 0.8f, 0.8f)); - ImGui::Button(label, ImVec2(40.0f + sinf((float)(line + n)) * 20.0f, 0.0f)); - ImGui::PopStyleColor(3); - ImGui::PopID(); - } - } - float scroll_x = ImGui::GetScrollX(), scroll_max_x = ImGui::GetScrollMaxX(); - ImGui::EndChild(); - ImGui::PopStyleVar(2); - float scroll_x_delta = 0.0f; - ImGui::SmallButton("<<"); if (ImGui::IsItemActive()) scroll_x_delta = -ImGui::GetIO().DeltaTime * 1000.0f; ImGui::SameLine(); - ImGui::Text("Scroll from code"); ImGui::SameLine(); - ImGui::SmallButton(">>"); if (ImGui::IsItemActive()) scroll_x_delta = +ImGui::GetIO().DeltaTime * 1000.0f; ImGui::SameLine(); - ImGui::Text("%.0f/%.0f", scroll_x, scroll_max_x); - if (scroll_x_delta != 0.0f) - { - ImGui::BeginChild("scrolling"); // Demonstrate a trick: you can use Begin to set yourself in the context of another window (here we are already out of your child window) - ImGui::SetScrollX(ImGui::GetScrollX() + scroll_x_delta); - ImGui::End(); - } - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Clipping")) - { - static ImVec2 size(100, 100), offset(50, 20); - ImGui::TextWrapped("On a per-widget basis we are occasionally clipping text CPU-side if it won't fit in its frame. Otherwise we are doing coarser clipping + passing a scissor rectangle to the renderer. The system is designed to try minimizing both execution and CPU/GPU rendering cost."); - ImGui::DragFloat2("size", (float*)&size, 0.5f, 0.0f, 200.0f, "%.0f"); - ImGui::TextWrapped("(Click and drag)"); - ImVec2 pos = ImGui::GetCursorScreenPos(); - ImVec4 clip_rect(pos.x, pos.y, pos.x+size.x, pos.y+size.y); - ImGui::InvisibleButton("##dummy", size); - if (ImGui::IsItemActive() && ImGui::IsMouseDragging()) { offset.x += ImGui::GetIO().MouseDelta.x; offset.y += ImGui::GetIO().MouseDelta.y; } - ImGui::GetWindowDrawList()->AddRectFilled(pos, ImVec2(pos.x+size.x,pos.y+size.y), IM_COL32(90,90,120,255)); - ImGui::GetWindowDrawList()->AddText(ImGui::GetFont(), ImGui::GetFontSize()*2.0f, ImVec2(pos.x+offset.x,pos.y+offset.y), IM_COL32(255,255,255,255), "Line 1 hello\nLine 2 clip me!", NULL, 0.0f, &clip_rect); - ImGui::TreePop(); - } - } - - if (ImGui::CollapsingHeader("Popups & Modal windows")) - { - if (ImGui::TreeNode("Popups")) - { - ImGui::TextWrapped("When a popup is active, it inhibits interacting with windows that are behind the popup. Clicking outside the popup closes it."); - - static int selected_fish = -1; - const char* names[] = { "Bream", "Haddock", "Mackerel", "Pollock", "Tilefish" }; - static bool toggles[] = { true, false, false, false, false }; - - // Simple selection popup - // (If you want to show the current selection inside the Button itself, you may want to build a string using the "###" operator to preserve a constant ID with a variable label) - if (ImGui::Button("Select..")) - ImGui::OpenPopup("select"); - ImGui::SameLine(); - ImGui::TextUnformatted(selected_fish == -1 ? "" : names[selected_fish]); - if (ImGui::BeginPopup("select")) - { - ImGui::Text("Aquarium"); - ImGui::Separator(); - for (int i = 0; i < IM_ARRAYSIZE(names); i++) - if (ImGui::Selectable(names[i])) - selected_fish = i; - ImGui::EndPopup(); - } - - // Showing a menu with toggles - if (ImGui::Button("Toggle..")) - ImGui::OpenPopup("toggle"); - if (ImGui::BeginPopup("toggle")) - { - for (int i = 0; i < IM_ARRAYSIZE(names); i++) - ImGui::MenuItem(names[i], "", &toggles[i]); - if (ImGui::BeginMenu("Sub-menu")) - { - ImGui::MenuItem("Click me"); - ImGui::EndMenu(); - } - - ImGui::Separator(); - ImGui::Text("Tooltip here"); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("I am a tooltip over a popup"); - - if (ImGui::Button("Stacked Popup")) - ImGui::OpenPopup("another popup"); - if (ImGui::BeginPopup("another popup")) - { - for (int i = 0; i < IM_ARRAYSIZE(names); i++) - ImGui::MenuItem(names[i], "", &toggles[i]); - if (ImGui::BeginMenu("Sub-menu")) - { - ImGui::MenuItem("Click me"); - ImGui::EndMenu(); - } - ImGui::EndPopup(); - } - ImGui::EndPopup(); - } - - if (ImGui::Button("Popup Menu..")) - ImGui::OpenPopup("FilePopup"); - if (ImGui::BeginPopup("FilePopup")) - { - ShowExampleMenuFile(); - ImGui::EndPopup(); - } - - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Context menus")) - { - // BeginPopupContextItem() is a helper to provide common/simple popup behavior of essentially doing: - // if (IsItemHovered() && IsMouseClicked(0)) - // OpenPopup(id); - // return BeginPopup(id); - // For more advanced uses you may want to replicate and cuztomize this code. This the comments inside BeginPopupContextItem() implementation. - static float value = 0.5f; - ImGui::Text("Value = %.3f (<-- right-click here)", value); - if (ImGui::BeginPopupContextItem("item context menu")) - { - if (ImGui::Selectable("Set to zero")) value = 0.0f; - if (ImGui::Selectable("Set to PI")) value = 3.1415f; - ImGui::PushItemWidth(-1); - ImGui::DragFloat("##Value", &value, 0.1f, 0.0f, 0.0f); - ImGui::PopItemWidth(); - ImGui::EndPopup(); - } - - static char name[32] = "Label1"; - char buf[64]; sprintf(buf, "Button: %s###Button", name); // ### operator override ID ignoring the preceding label - ImGui::Button(buf); - if (ImGui::BeginPopupContextItem()) // When used after an item that has an ID (here the Button), we can skip providing an ID to BeginPopupContextItem(). - { - ImGui::Text("Edit name:"); - ImGui::InputText("##edit", name, IM_ARRAYSIZE(name)); - if (ImGui::Button("Close")) - ImGui::CloseCurrentPopup(); - ImGui::EndPopup(); - } - ImGui::SameLine(); ImGui::Text("(<-- right-click here)"); - - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Modals")) - { - ImGui::TextWrapped("Modal windows are like popups but the user cannot close them by clicking outside the window."); - - if (ImGui::Button("Delete..")) - ImGui::OpenPopup("Delete?"); - if (ImGui::BeginPopupModal("Delete?", NULL, ImGuiWindowFlags_AlwaysAutoResize)) - { - ImGui::Text("All those beautiful files will be deleted.\nThis operation cannot be undone!\n\n"); - ImGui::Separator(); - - //static int dummy_i = 0; - //ImGui::Combo("Combo", &dummy_i, "Delete\0Delete harder\0"); - - static bool dont_ask_me_next_time = false; - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0,0)); - ImGui::Checkbox("Don't ask me next time", &dont_ask_me_next_time); - ImGui::PopStyleVar(); - - if (ImGui::Button("OK", ImVec2(120,0))) { ImGui::CloseCurrentPopup(); } - ImGui::SetItemDefaultFocus(); - ImGui::SameLine(); - if (ImGui::Button("Cancel", ImVec2(120,0))) { ImGui::CloseCurrentPopup(); } - ImGui::EndPopup(); - } - - if (ImGui::Button("Stacked modals..")) - ImGui::OpenPopup("Stacked 1"); - if (ImGui::BeginPopupModal("Stacked 1")) - { - ImGui::Text("Hello from Stacked The First\nUsing style.Colors[ImGuiCol_ModalWindowDarkening] for darkening."); - static int item = 1; - ImGui::Combo("Combo", &item, "aaaa\0bbbb\0cccc\0dddd\0eeee\0\0"); - static float color[4] = { 0.4f,0.7f,0.0f,0.5f }; - ImGui::ColorEdit4("color", color); // This is to test behavior of stacked regular popups over a modal - - if (ImGui::Button("Add another modal..")) - ImGui::OpenPopup("Stacked 2"); - if (ImGui::BeginPopupModal("Stacked 2")) - { - ImGui::Text("Hello from Stacked The Second!"); - if (ImGui::Button("Close")) - ImGui::CloseCurrentPopup(); - ImGui::EndPopup(); - } - - if (ImGui::Button("Close")) - ImGui::CloseCurrentPopup(); - ImGui::EndPopup(); - } - - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Menus inside a regular window")) - { - ImGui::TextWrapped("Below we are testing adding menu items to a regular window. It's rather unusual but should work!"); - ImGui::Separator(); - // NB: As a quirk in this very specific example, we want to differentiate the parent of this menu from the parent of the various popup menus above. - // To do so we are encloding the items in a PushID()/PopID() block to make them two different menusets. If we don't, opening any popup above and hovering our menu here - // would open it. This is because once a menu is active, we allow to switch to a sibling menu by just hovering on it, which is the desired behavior for regular menus. - ImGui::PushID("foo"); - ImGui::MenuItem("Menu item", "CTRL+M"); - if (ImGui::BeginMenu("Menu inside a regular window")) - { - ShowExampleMenuFile(); - ImGui::EndMenu(); - } - ImGui::PopID(); - ImGui::Separator(); - ImGui::TreePop(); - } - } - - if (ImGui::CollapsingHeader("Columns")) - { - ImGui::PushID("Columns"); - - // Basic columns - if (ImGui::TreeNode("Basic")) - { - ImGui::Text("Without border:"); - ImGui::Columns(3, "mycolumns3", false); // 3-ways, no border - ImGui::Separator(); - for (int n = 0; n < 14; n++) - { - char label[32]; - sprintf(label, "Item %d", n); - if (ImGui::Selectable(label)) {} - //if (ImGui::Button(label, ImVec2(-1,0))) {} - ImGui::NextColumn(); - } - ImGui::Columns(1); - ImGui::Separator(); - - ImGui::Text("With border:"); - ImGui::Columns(4, "mycolumns"); // 4-ways, with border - ImGui::Separator(); - ImGui::Text("ID"); ImGui::NextColumn(); - ImGui::Text("Name"); ImGui::NextColumn(); - ImGui::Text("Path"); ImGui::NextColumn(); - ImGui::Text("Hovered"); ImGui::NextColumn(); - ImGui::Separator(); - const char* names[3] = { "One", "Two", "Three" }; - const char* paths[3] = { "/path/one", "/path/two", "/path/three" }; - static int selected = -1; - for (int i = 0; i < 3; i++) - { - char label[32]; - sprintf(label, "%04d", i); - if (ImGui::Selectable(label, selected == i, ImGuiSelectableFlags_SpanAllColumns)) - selected = i; - bool hovered = ImGui::IsItemHovered(); - ImGui::NextColumn(); - ImGui::Text(names[i]); ImGui::NextColumn(); - ImGui::Text(paths[i]); ImGui::NextColumn(); - ImGui::Text("%d", hovered); ImGui::NextColumn(); - } - ImGui::Columns(1); - ImGui::Separator(); - ImGui::TreePop(); - } - - // Create multiple items in a same cell before switching to next column - if (ImGui::TreeNode("Mixed items")) - { - ImGui::Columns(3, "mixed"); - ImGui::Separator(); - - ImGui::Text("Hello"); - ImGui::Button("Banana"); - ImGui::NextColumn(); - - ImGui::Text("ImGui"); - ImGui::Button("Apple"); - static float foo = 1.0f; - ImGui::InputFloat("red", &foo, 0.05f, 0, "%.3f"); - ImGui::Text("An extra line here."); - ImGui::NextColumn(); - - ImGui::Text("Sailor"); - ImGui::Button("Corniflower"); - static float bar = 1.0f; - ImGui::InputFloat("blue", &bar, 0.05f, 0, "%.3f"); - ImGui::NextColumn(); - - if (ImGui::CollapsingHeader("Category A")) { ImGui::Text("Blah blah blah"); } ImGui::NextColumn(); - if (ImGui::CollapsingHeader("Category B")) { ImGui::Text("Blah blah blah"); } ImGui::NextColumn(); - if (ImGui::CollapsingHeader("Category C")) { ImGui::Text("Blah blah blah"); } ImGui::NextColumn(); - ImGui::Columns(1); - ImGui::Separator(); - ImGui::TreePop(); - } - - // Word wrapping - if (ImGui::TreeNode("Word-wrapping")) - { - ImGui::Columns(2, "word-wrapping"); - ImGui::Separator(); - ImGui::TextWrapped("The quick brown fox jumps over the lazy dog."); - ImGui::TextWrapped("Hello Left"); - ImGui::NextColumn(); - ImGui::TextWrapped("The quick brown fox jumps over the lazy dog."); - ImGui::TextWrapped("Hello Right"); - ImGui::Columns(1); - ImGui::Separator(); - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Borders")) - { - // NB: Future columns API should allow automatic horizontal borders. - static bool h_borders = true; - static bool v_borders = true; - ImGui::Checkbox("horizontal", &h_borders); - ImGui::SameLine(); - ImGui::Checkbox("vertical", &v_borders); - ImGui::Columns(4, NULL, v_borders); - for (int i = 0; i < 4*3; i++) - { - if (h_borders && ImGui::GetColumnIndex() == 0) - ImGui::Separator(); - ImGui::Text("%c%c%c", 'a'+i, 'a'+i, 'a'+i); - ImGui::Text("Width %.2f\nOffset %.2f", ImGui::GetColumnWidth(), ImGui::GetColumnOffset()); - ImGui::NextColumn(); - } - ImGui::Columns(1); - if (h_borders) - ImGui::Separator(); - ImGui::TreePop(); - } - - // Scrolling columns - /* - if (ImGui::TreeNode("Vertical Scrolling")) - { - ImGui::BeginChild("##header", ImVec2(0, ImGui::GetTextLineHeightWithSpacing()+ImGui::GetStyle().ItemSpacing.y)); - ImGui::Columns(3); - ImGui::Text("ID"); ImGui::NextColumn(); - ImGui::Text("Name"); ImGui::NextColumn(); - ImGui::Text("Path"); ImGui::NextColumn(); - ImGui::Columns(1); - ImGui::Separator(); - ImGui::EndChild(); - ImGui::BeginChild("##scrollingregion", ImVec2(0, 60)); - ImGui::Columns(3); - for (int i = 0; i < 10; i++) - { - ImGui::Text("%04d", i); ImGui::NextColumn(); - ImGui::Text("Foobar"); ImGui::NextColumn(); - ImGui::Text("/path/foobar/%04d/", i); ImGui::NextColumn(); - } - ImGui::Columns(1); - ImGui::EndChild(); - ImGui::TreePop(); - } - */ - - if (ImGui::TreeNode("Horizontal Scrolling")) - { - ImGui::SetNextWindowContentSize(ImVec2(1500.0f, 0.0f)); - ImGui::BeginChild("##ScrollingRegion", ImVec2(0, ImGui::GetFontSize() * 20), false, ImGuiWindowFlags_HorizontalScrollbar); - ImGui::Columns(10); - int ITEMS_COUNT = 2000; - ImGuiListClipper clipper(ITEMS_COUNT); // Also demonstrate using the clipper for large list - while (clipper.Step()) - { - for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) - for (int j = 0; j < 10; j++) - { - ImGui::Text("Line %d Column %d...", i, j); - ImGui::NextColumn(); - } - } - ImGui::Columns(1); - ImGui::EndChild(); - ImGui::TreePop(); - } - - bool node_open = ImGui::TreeNode("Tree within single cell"); - ImGui::SameLine(); ShowHelpMarker("NB: Tree node must be poped before ending the cell. There's no storage of state per-cell."); - if (node_open) - { - ImGui::Columns(2, "tree items"); - ImGui::Separator(); - if (ImGui::TreeNode("Hello")) { ImGui::BulletText("Sailor"); ImGui::TreePop(); } ImGui::NextColumn(); - if (ImGui::TreeNode("Bonjour")) { ImGui::BulletText("Marin"); ImGui::TreePop(); } ImGui::NextColumn(); - ImGui::Columns(1); - ImGui::Separator(); - ImGui::TreePop(); - } - ImGui::PopID(); - } - - if (ImGui::CollapsingHeader("Filtering")) - { - static ImGuiTextFilter filter; - ImGui::Text("Filter usage:\n" - " \"\" display all lines\n" - " \"xxx\" display lines containing \"xxx\"\n" - " \"xxx,yyy\" display lines containing \"xxx\" or \"yyy\"\n" - " \"-xxx\" hide lines containing \"xxx\""); - filter.Draw(); - const char* lines[] = { "aaa1.c", "bbb1.c", "ccc1.c", "aaa2.cpp", "bbb2.cpp", "ccc2.cpp", "abc.h", "hello, world" }; - for (int i = 0; i < IM_ARRAYSIZE(lines); i++) - if (filter.PassFilter(lines[i])) - ImGui::BulletText("%s", lines[i]); - } - - if (ImGui::CollapsingHeader("Inputs, Navigation & Focus")) - { - ImGuiIO& io = ImGui::GetIO(); - - ImGui::Text("WantCaptureMouse: %d", io.WantCaptureMouse); - ImGui::Text("WantCaptureKeyboard: %d", io.WantCaptureKeyboard); - ImGui::Text("WantTextInput: %d", io.WantTextInput); - ImGui::Text("WantSetMousePos: %d", io.WantSetMousePos); - ImGui::Text("NavActive: %d, NavVisible: %d", io.NavActive, io.NavVisible); - - ImGui::Checkbox("io.MouseDrawCursor", &io.MouseDrawCursor); - ImGui::SameLine(); ShowHelpMarker("Instruct ImGui to render a mouse cursor for you in software. Note that a mouse cursor rendered via your application GPU rendering path will feel more laggy than hardware cursor, but will be more in sync with your other visuals.\n\nSome desktop applications may use both kinds of cursors (e.g. enable software cursor only when resizing/dragging something)."); - - ImGui::CheckboxFlags("io.ConfigFlags: NavEnableGamepad [beta]", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NavEnableGamepad); - ImGui::CheckboxFlags("io.ConfigFlags: NavEnableKeyboard [beta]", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NavEnableKeyboard); - ImGui::CheckboxFlags("io.ConfigFlags: NavEnableSetMousePos", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NavEnableSetMousePos); - ImGui::SameLine(); ShowHelpMarker("Instruct navigation to move the mouse cursor. See comment for ImGuiConfigFlags_NavEnableSetMousePos."); - ImGui::CheckboxFlags("io.ConfigFlags: NoMouseCursorChange", (unsigned int *)&io.ConfigFlags, ImGuiConfigFlags_NoMouseCursorChange); - ImGui::SameLine(); ShowHelpMarker("Instruct back-end to not alter mouse cursor shape and visibility."); - - if (ImGui::TreeNode("Keyboard, Mouse & Navigation State")) - { - if (ImGui::IsMousePosValid()) - ImGui::Text("Mouse pos: (%g, %g)", io.MousePos.x, io.MousePos.y); - else - ImGui::Text("Mouse pos: "); - ImGui::Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y); - ImGui::Text("Mouse down:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (io.MouseDownDuration[i] >= 0.0f) { ImGui::SameLine(); ImGui::Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } - ImGui::Text("Mouse clicked:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } - ImGui::Text("Mouse dbl-clicked:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseDoubleClicked(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } - ImGui::Text("Mouse released:"); for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) if (ImGui::IsMouseReleased(i)) { ImGui::SameLine(); ImGui::Text("b%d", i); } - ImGui::Text("Mouse wheel: %.1f", io.MouseWheel); - - ImGui::Text("Keys down:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (io.KeysDownDuration[i] >= 0.0f) { ImGui::SameLine(); ImGui::Text("%d (%.02f secs)", i, io.KeysDownDuration[i]); } - ImGui::Text("Keys pressed:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyPressed(i)) { ImGui::SameLine(); ImGui::Text("%d", i); } - ImGui::Text("Keys release:"); for (int i = 0; i < IM_ARRAYSIZE(io.KeysDown); i++) if (ImGui::IsKeyReleased(i)) { ImGui::SameLine(); ImGui::Text("%d", i); } - ImGui::Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : ""); - - ImGui::Text("NavInputs down:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputs[i] > 0.0f) { ImGui::SameLine(); ImGui::Text("[%d] %.2f", i, io.NavInputs[i]); } - ImGui::Text("NavInputs pressed:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputsDownDuration[i] == 0.0f) { ImGui::SameLine(); ImGui::Text("[%d]", i); } - ImGui::Text("NavInputs duration:"); for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) if (io.NavInputsDownDuration[i] >= 0.0f) { ImGui::SameLine(); ImGui::Text("[%d] %.2f", i, io.NavInputsDownDuration[i]); } - - ImGui::Button("Hovering me sets the\nkeyboard capture flag"); - if (ImGui::IsItemHovered()) - ImGui::CaptureKeyboardFromApp(true); - ImGui::SameLine(); - ImGui::Button("Holding me clears the\nthe keyboard capture flag"); - if (ImGui::IsItemActive()) - ImGui::CaptureKeyboardFromApp(false); - - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Tabbing")) - { - ImGui::Text("Use TAB/SHIFT+TAB to cycle through keyboard editable fields."); - static char buf[32] = "dummy"; - ImGui::InputText("1", buf, IM_ARRAYSIZE(buf)); - ImGui::InputText("2", buf, IM_ARRAYSIZE(buf)); - ImGui::InputText("3", buf, IM_ARRAYSIZE(buf)); - ImGui::PushAllowKeyboardFocus(false); - ImGui::InputText("4 (tab skip)", buf, IM_ARRAYSIZE(buf)); - //ImGui::SameLine(); ShowHelperMarker("Use ImGui::PushAllowKeyboardFocus(bool)\nto disable tabbing through certain widgets."); - ImGui::PopAllowKeyboardFocus(); - ImGui::InputText("5", buf, IM_ARRAYSIZE(buf)); - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Focus from code")) - { - bool focus_1 = ImGui::Button("Focus on 1"); ImGui::SameLine(); - bool focus_2 = ImGui::Button("Focus on 2"); ImGui::SameLine(); - bool focus_3 = ImGui::Button("Focus on 3"); - int has_focus = 0; - static char buf[128] = "click on a button to set focus"; - - if (focus_1) ImGui::SetKeyboardFocusHere(); - ImGui::InputText("1", buf, IM_ARRAYSIZE(buf)); - if (ImGui::IsItemActive()) has_focus = 1; - - if (focus_2) ImGui::SetKeyboardFocusHere(); - ImGui::InputText("2", buf, IM_ARRAYSIZE(buf)); - if (ImGui::IsItemActive()) has_focus = 2; - - ImGui::PushAllowKeyboardFocus(false); - if (focus_3) ImGui::SetKeyboardFocusHere(); - ImGui::InputText("3 (tab skip)", buf, IM_ARRAYSIZE(buf)); - if (ImGui::IsItemActive()) has_focus = 3; - ImGui::PopAllowKeyboardFocus(); - - if (has_focus) - ImGui::Text("Item with focus: %d", has_focus); - else - ImGui::Text("Item with focus: "); - - // Use >= 0 parameter to SetKeyboardFocusHere() to focus an upcoming item - static float f3[3] = { 0.0f, 0.0f, 0.0f }; - int focus_ahead = -1; - if (ImGui::Button("Focus on X")) focus_ahead = 0; ImGui::SameLine(); - if (ImGui::Button("Focus on Y")) focus_ahead = 1; ImGui::SameLine(); - if (ImGui::Button("Focus on Z")) focus_ahead = 2; - if (focus_ahead != -1) ImGui::SetKeyboardFocusHere(focus_ahead); - ImGui::SliderFloat3("Float3", &f3[0], 0.0f, 1.0f); - - ImGui::TextWrapped("NB: Cursor & selection are preserved when refocusing last used item in code."); - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Dragging")) - { - ImGui::TextWrapped("You can use ImGui::GetMouseDragDelta(0) to query for the dragged amount on any widget."); - for (int button = 0; button < 3; button++) - ImGui::Text("IsMouseDragging(%d):\n w/ default threshold: %d,\n w/ zero threshold: %d\n w/ large threshold: %d", - button, ImGui::IsMouseDragging(button), ImGui::IsMouseDragging(button, 0.0f), ImGui::IsMouseDragging(button, 20.0f)); - ImGui::Button("Drag Me"); - if (ImGui::IsItemActive()) - { - // Draw a line between the button and the mouse cursor - ImDrawList* draw_list = ImGui::GetWindowDrawList(); - draw_list->PushClipRectFullScreen(); - draw_list->AddLine(io.MouseClickedPos[0], io.MousePos, ImGui::GetColorU32(ImGuiCol_Button), 4.0f); - draw_list->PopClipRect(); - - // Drag operations gets "unlocked" when the mouse has moved past a certain threshold (the default threshold is stored in io.MouseDragThreshold) - // You can request a lower or higher threshold using the second parameter of IsMouseDragging() and GetMouseDragDelta() - ImVec2 value_raw = ImGui::GetMouseDragDelta(0, 0.0f); - ImVec2 value_with_lock_threshold = ImGui::GetMouseDragDelta(0); - ImVec2 mouse_delta = io.MouseDelta; - ImGui::SameLine(); ImGui::Text("Raw (%.1f, %.1f), WithLockThresold (%.1f, %.1f), MouseDelta (%.1f, %.1f)", value_raw.x, value_raw.y, value_with_lock_threshold.x, value_with_lock_threshold.y, mouse_delta.x, mouse_delta.y); - } - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Mouse cursors")) - { - const char* mouse_cursors_names[] = { "Arrow", "TextInput", "Move", "ResizeNS", "ResizeEW", "ResizeNESW", "ResizeNWSE" }; - IM_ASSERT(IM_ARRAYSIZE(mouse_cursors_names) == ImGuiMouseCursor_COUNT); - - ImGui::Text("Current mouse cursor = %d: %s", ImGui::GetMouseCursor(), mouse_cursors_names[ImGui::GetMouseCursor()]); - ImGui::Text("Hover to see mouse cursors:"); - ImGui::SameLine(); ShowHelpMarker("Your application can render a different mouse cursor based on what ImGui::GetMouseCursor() returns. If software cursor rendering (io.MouseDrawCursor) is set ImGui will draw the right cursor for you, otherwise your backend needs to handle it."); - for (int i = 0; i < ImGuiMouseCursor_COUNT; i++) - { - char label[32]; - sprintf(label, "Mouse cursor %d: %s", i, mouse_cursors_names[i]); - ImGui::Bullet(); ImGui::Selectable(label, false); - if (ImGui::IsItemHovered() || ImGui::IsItemFocused()) - ImGui::SetMouseCursor(i); - } - ImGui::TreePop(); - } - } - - ImGui::End(); -} - -// Demo helper function to select among default colors. See ShowStyleEditor() for more advanced options. -// Here we use the simplified Combo() api that packs items into a single literal string. Useful for quick combo boxes where the choices are known locally. -bool ImGui::ShowStyleSelector(const char* label) -{ - static int style_idx = -1; - if (ImGui::Combo(label, &style_idx, "Classic\0Dark\0Light\0")) - { - switch (style_idx) - { - case 0: ImGui::StyleColorsClassic(); break; - case 1: ImGui::StyleColorsDark(); break; - case 2: ImGui::StyleColorsLight(); break; - } - return true; - } - return false; -} - -// Demo helper function to select among loaded fonts. -// Here we use the regular BeginCombo()/EndCombo() api which is more the more flexible one. -void ImGui::ShowFontSelector(const char* label) -{ - ImGuiIO& io = ImGui::GetIO(); - ImFont* font_current = ImGui::GetFont(); - if (ImGui::BeginCombo(label, font_current->GetDebugName())) - { - for (int n = 0; n < io.Fonts->Fonts.Size; n++) - if (ImGui::Selectable(io.Fonts->Fonts[n]->GetDebugName(), io.Fonts->Fonts[n] == font_current)) - io.FontDefault = io.Fonts->Fonts[n]; - ImGui::EndCombo(); - } - ImGui::SameLine(); - ShowHelpMarker( - "- Load additional fonts with io.Fonts->AddFontFromFileTTF().\n" - "- The font atlas is built when calling io.Fonts->GetTexDataAsXXXX() or io.Fonts->Build().\n" - "- Read FAQ and documentation in misc/fonts/ for more details.\n" - "- If you need to add/remove fonts at runtime (e.g. for DPI change), do it before calling NewFrame()."); -} - -void ImGui::ShowStyleEditor(ImGuiStyle* ref) -{ - // You can pass in a reference ImGuiStyle structure to compare to, revert to and save to (else it compares to an internally stored reference) - ImGuiStyle& style = ImGui::GetStyle(); - static ImGuiStyle ref_saved_style; - - // Default to using internal storage as reference - static bool init = true; - if (init && ref == NULL) - ref_saved_style = style; - init = false; - if (ref == NULL) - ref = &ref_saved_style; - - ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.50f); - - if (ImGui::ShowStyleSelector("Colors##Selector")) - ref_saved_style = style; - ImGui::ShowFontSelector("Fonts##Selector"); - - // Simplified Settings - if (ImGui::SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f")) - style.GrabRounding = style.FrameRounding; // Make GrabRounding always the same value as FrameRounding - { bool window_border = (style.WindowBorderSize > 0.0f); if (ImGui::Checkbox("WindowBorder", &window_border)) style.WindowBorderSize = window_border ? 1.0f : 0.0f; } - ImGui::SameLine(); - { bool frame_border = (style.FrameBorderSize > 0.0f); if (ImGui::Checkbox("FrameBorder", &frame_border)) style.FrameBorderSize = frame_border ? 1.0f : 0.0f; } - ImGui::SameLine(); - { bool popup_border = (style.PopupBorderSize > 0.0f); if (ImGui::Checkbox("PopupBorder", &popup_border)) style.PopupBorderSize = popup_border ? 1.0f : 0.0f; } - - // Save/Revert button - if (ImGui::Button("Save Ref")) - *ref = ref_saved_style = style; - ImGui::SameLine(); - if (ImGui::Button("Revert Ref")) - style = *ref; - ImGui::SameLine(); - ShowHelpMarker("Save/Revert in local non-persistent storage. Default Colors definition are not affected. Use \"Export Colors\" below to save them somewhere."); - - if (ImGui::TreeNode("Rendering")) - { - ImGui::Checkbox("Anti-aliased lines", &style.AntiAliasedLines); ImGui::SameLine(); ShowHelpMarker("When disabling anti-aliasing lines, you'll probably want to disable borders in your style as well."); - ImGui::Checkbox("Anti-aliased fill", &style.AntiAliasedFill); - ImGui::PushItemWidth(100); - ImGui::DragFloat("Curve Tessellation Tolerance", &style.CurveTessellationTol, 0.02f, 0.10f, FLT_MAX, NULL, 2.0f); - if (style.CurveTessellationTol < 0.0f) style.CurveTessellationTol = 0.10f; - ImGui::DragFloat("Global Alpha", &style.Alpha, 0.005f, 0.20f, 1.0f, "%.2f"); // Not exposing zero here so user doesn't "lose" the UI (zero alpha clips all widgets). But application code could have a toggle to switch between zero and non-zero. - ImGui::PopItemWidth(); - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Settings")) - { - ImGui::SliderFloat2("WindowPadding", (float*)&style.WindowPadding, 0.0f, 20.0f, "%.0f"); - ImGui::SliderFloat("PopupRounding", &style.PopupRounding, 0.0f, 16.0f, "%.0f"); - ImGui::SliderFloat2("FramePadding", (float*)&style.FramePadding, 0.0f, 20.0f, "%.0f"); - ImGui::SliderFloat2("ItemSpacing", (float*)&style.ItemSpacing, 0.0f, 20.0f, "%.0f"); - ImGui::SliderFloat2("ItemInnerSpacing", (float*)&style.ItemInnerSpacing, 0.0f, 20.0f, "%.0f"); - ImGui::SliderFloat2("TouchExtraPadding", (float*)&style.TouchExtraPadding, 0.0f, 10.0f, "%.0f"); - ImGui::SliderFloat("IndentSpacing", &style.IndentSpacing, 0.0f, 30.0f, "%.0f"); - ImGui::SliderFloat("ScrollbarSize", &style.ScrollbarSize, 1.0f, 100.0f, "%.0f"); // [Bruno] increase max to 100.0f. - ImGui::SliderFloat("GrabMinSize", &style.GrabMinSize, 1.0f, 20.0f, "%.0f"); - ImGui::Text("BorderSize"); - ImGui::SliderFloat("WindowBorderSize", &style.WindowBorderSize, 0.0f, 1.0f, "%.0f"); - ImGui::SliderFloat("ChildBorderSize", &style.ChildBorderSize, 0.0f, 1.0f, "%.0f"); - ImGui::SliderFloat("PopupBorderSize", &style.PopupBorderSize, 0.0f, 1.0f, "%.0f"); - ImGui::SliderFloat("FrameBorderSize", &style.FrameBorderSize, 0.0f, 1.0f, "%.0f"); - ImGui::Text("Rounding"); - ImGui::SliderFloat("WindowRounding", &style.WindowRounding, 0.0f, 14.0f, "%.0f"); - ImGui::SliderFloat("ChildRounding", &style.ChildRounding, 0.0f, 16.0f, "%.0f"); - ImGui::SliderFloat("FrameRounding", &style.FrameRounding, 0.0f, 12.0f, "%.0f"); - ImGui::SliderFloat("ScrollbarRounding", &style.ScrollbarRounding, 0.0f, 12.0f, "%.0f"); - ImGui::SliderFloat("GrabRounding", &style.GrabRounding, 0.0f, 12.0f, "%.0f"); - ImGui::Text("Alignment"); - ImGui::SliderFloat2("WindowTitleAlign", (float*)&style.WindowTitleAlign, 0.0f, 1.0f, "%.2f"); - ImGui::SliderFloat2("ButtonTextAlign", (float*)&style.ButtonTextAlign, 0.0f, 1.0f, "%.2f"); ImGui::SameLine(); ShowHelpMarker("Alignment applies when a button is larger than its text content."); - ImGui::Text("Safe Area Padding"); ImGui::SameLine(); ShowHelpMarker("Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been configured)."); - ImGui::SliderFloat2("DisplaySafeAreaPadding", (float*)&style.DisplaySafeAreaPadding, 0.0f, 30.0f, "%.0f"); - ImGui::TreePop(); - } - - if (ImGui::TreeNode("Colors")) - { - static int output_dest = 0; - static bool output_only_modified = true; - if (ImGui::Button("Export Unsaved")) - { - if (output_dest == 0) - ImGui::LogToClipboard(); - else - ImGui::LogToTTY(); - ImGui::LogText("ImVec4* colors = ImGui::GetStyle().Colors;" IM_NEWLINE); - for (int i = 0; i < ImGuiCol_COUNT; i++) - { - const ImVec4& col = style.Colors[i]; - const char* name = ImGui::GetStyleColorName(i); - if (!output_only_modified || memcmp(&col, &ref->Colors[i], sizeof(ImVec4)) != 0) - ImGui::LogText("colors[ImGuiCol_%s]%*s= ImVec4(%.2ff, %.2ff, %.2ff, %.2ff);" IM_NEWLINE, name, 23-(int)strlen(name), "", col.x, col.y, col.z, col.w); - } - ImGui::LogFinish(); - } - ImGui::SameLine(); ImGui::PushItemWidth(120); ImGui::Combo("##output_type", &output_dest, "To Clipboard\0To TTY\0"); ImGui::PopItemWidth(); - ImGui::SameLine(); ImGui::Checkbox("Only Modified Colors", &output_only_modified); - - ImGui::Text("Tip: Left-click on colored square to open color picker,\nRight-click to open edit options menu."); - - static ImGuiTextFilter filter; - filter.Draw("Filter colors", 200); - - static ImGuiColorEditFlags alpha_flags = 0; - ImGui::RadioButton("Opaque", &alpha_flags, 0); ImGui::SameLine(); - ImGui::RadioButton("Alpha", &alpha_flags, ImGuiColorEditFlags_AlphaPreview); ImGui::SameLine(); - ImGui::RadioButton("Both", &alpha_flags, ImGuiColorEditFlags_AlphaPreviewHalf); - - ImGui::BeginChild("#colors", ImVec2(0, 300), true, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar | ImGuiWindowFlags_NavFlattened); - ImGui::PushItemWidth(-160); - for (int i = 0; i < ImGuiCol_COUNT; i++) - { - const char* name = ImGui::GetStyleColorName(i); - if (!filter.PassFilter(name)) - continue; - ImGui::PushID(i); - ImGui::ColorEdit4("##color", (float*)&style.Colors[i], ImGuiColorEditFlags_AlphaBar | alpha_flags); - if (memcmp(&style.Colors[i], &ref->Colors[i], sizeof(ImVec4)) != 0) - { - // Tips: in a real user application, you may want to merge and use an icon font into the main font, so instead of "Save"/"Revert" you'd use icons. - // Read the FAQ and misc/fonts/README.txt about using icon fonts. It's really easy and super convenient! - ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); if (ImGui::Button("Save")) ref->Colors[i] = style.Colors[i]; - ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); if (ImGui::Button("Revert")) style.Colors[i] = ref->Colors[i]; - } - ImGui::SameLine(0.0f, style.ItemInnerSpacing.x); - ImGui::TextUnformatted(name); - ImGui::PopID(); - } - ImGui::PopItemWidth(); - ImGui::EndChild(); - - ImGui::TreePop(); - } - - bool fonts_opened = ImGui::TreeNode("Fonts", "Fonts (%d)", ImGui::GetIO().Fonts->Fonts.Size); - if (fonts_opened) - { - ImFontAtlas* atlas = ImGui::GetIO().Fonts; - if (ImGui::TreeNode("Atlas texture", "Atlas texture (%dx%d pixels)", atlas->TexWidth, atlas->TexHeight)) - { - ImGui::Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0,0), ImVec2(1,1), ImColor(255,255,255,255), ImColor(255,255,255,128)); - ImGui::TreePop(); - } - ImGui::PushItemWidth(100); - for (int i = 0; i < atlas->Fonts.Size; i++) - { - ImFont* font = atlas->Fonts[i]; - ImGui::PushID(font); - bool font_details_opened = ImGui::TreeNode(font, "Font %d: \'%s\', %.2f px, %d glyphs", i, font->ConfigData ? font->ConfigData[0].Name : "", font->FontSize, font->Glyphs.Size); - ImGui::SameLine(); if (ImGui::SmallButton("Set as default")) ImGui::GetIO().FontDefault = font; - if (font_details_opened) - { - ImGui::PushFont(font); - ImGui::Text("The quick brown fox jumps over the lazy dog"); - ImGui::PopFont(); - ImGui::DragFloat("Font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f"); // Scale only this font - ImGui::InputFloat("Font offset", &font->DisplayOffset.y, 1, 1, 0); - ImGui::SameLine(); ShowHelpMarker("Note than the default embedded font is NOT meant to be scaled.\n\nFont are currently rendered into bitmaps at a given size at the time of building the atlas. You may oversample them to get some flexibility with scaling. You can also render at multiple sizes and select which one to use at runtime.\n\n(Glimmer of hope: the atlas system should hopefully be rewritten in the future to make scaling more natural and automatic.)"); - ImGui::Text("Ascent: %f, Descent: %f, Height: %f", font->Ascent, font->Descent, font->Ascent - font->Descent); - ImGui::Text("Fallback character: '%c' (%d)", font->FallbackChar, font->FallbackChar); - ImGui::Text("Texture surface: %d pixels (approx) ~ %dx%d", font->MetricsTotalSurface, (int)sqrtf((float)font->MetricsTotalSurface), (int)sqrtf((float)font->MetricsTotalSurface)); - for (int config_i = 0; config_i < font->ConfigDataCount; config_i++) - if (ImFontConfig* cfg = &font->ConfigData[config_i]) - ImGui::BulletText("Input %d: \'%s\', Oversample: (%d,%d), PixelSnapH: %d", config_i, cfg->Name, cfg->OversampleH, cfg->OversampleV, cfg->PixelSnapH); - if (ImGui::TreeNode("Glyphs", "Glyphs (%d)", font->Glyphs.Size)) - { - // Display all glyphs of the fonts in separate pages of 256 characters - for (int base = 0; base < 0x10000; base += 256) - { - int count = 0; - for (int n = 0; n < 256; n++) - count += font->FindGlyphNoFallback((ImWchar)(base + n)) ? 1 : 0; - if (count > 0 && ImGui::TreeNode((void*)(intptr_t)base, "U+%04X..U+%04X (%d %s)", base, base+255, count, count > 1 ? "glyphs" : "glyph")) - { - float cell_size = font->FontSize * 1; - float cell_spacing = style.ItemSpacing.y; - ImVec2 base_pos = ImGui::GetCursorScreenPos(); - ImDrawList* draw_list = ImGui::GetWindowDrawList(); - for (int n = 0; n < 256; n++) - { - ImVec2 cell_p1(base_pos.x + (n % 16) * (cell_size + cell_spacing), base_pos.y + (n / 16) * (cell_size + cell_spacing)); - ImVec2 cell_p2(cell_p1.x + cell_size, cell_p1.y + cell_size); - const ImFontGlyph* glyph = font->FindGlyphNoFallback((ImWchar)(base+n)); - draw_list->AddRect(cell_p1, cell_p2, glyph ? IM_COL32(255,255,255,100) : IM_COL32(255,255,255,50)); - if (glyph) - font->RenderChar(draw_list, cell_size, cell_p1, ImGui::GetColorU32(ImGuiCol_Text), (ImWchar)(base+n)); // We use ImFont::RenderChar as a shortcut because we don't have UTF-8 conversion functions available to generate a string. - if (glyph && ImGui::IsMouseHoveringRect(cell_p1, cell_p2)) - { - ImGui::BeginTooltip(); - ImGui::Text("Codepoint: U+%04X", base+n); - ImGui::Separator(); - ImGui::Text("AdvanceX: %.1f", glyph->AdvanceX); - ImGui::Text("Pos: (%.2f,%.2f)->(%.2f,%.2f)", glyph->X0, glyph->Y0, glyph->X1, glyph->Y1); - ImGui::Text("UV: (%.3f,%.3f)->(%.3f,%.3f)", glyph->U0, glyph->V0, glyph->U1, glyph->V1); - ImGui::EndTooltip(); - } - } - ImGui::Dummy(ImVec2((cell_size + cell_spacing) * 16, (cell_size + cell_spacing) * 16)); - ImGui::TreePop(); - } - } - ImGui::TreePop(); - } - ImGui::TreePop(); - } - ImGui::PopID(); - } - static float window_scale = 1.0f; - ImGui::DragFloat("this window scale", &window_scale, 0.005f, 0.3f, 2.0f, "%.1f"); // scale only this window - ImGui::DragFloat("global scale", &ImGui::GetIO().FontGlobalScale, 0.005f, 0.3f, 2.0f, "%.1f"); // scale everything - ImGui::PopItemWidth(); - ImGui::SetWindowFontScale(window_scale); - ImGui::TreePop(); - } - - ImGui::PopItemWidth(); -} - -// Demonstrate creating a fullscreen menu bar and populating it. -static void ShowExampleAppMainMenuBar() -{ - if (ImGui::BeginMainMenuBar()) - { - if (ImGui::BeginMenu("File")) - { - ShowExampleMenuFile(); - ImGui::EndMenu(); - } - if (ImGui::BeginMenu("Edit")) - { - if (ImGui::MenuItem("Undo", "CTRL+Z")) {} - if (ImGui::MenuItem("Redo", "CTRL+Y", false, false)) {} // Disabled item - ImGui::Separator(); - if (ImGui::MenuItem("Cut", "CTRL+X")) {} - if (ImGui::MenuItem("Copy", "CTRL+C")) {} - if (ImGui::MenuItem("Paste", "CTRL+V")) {} - ImGui::EndMenu(); - } - ImGui::EndMainMenuBar(); - } -} - -static void ShowExampleMenuFile() -{ - ImGui::MenuItem("(dummy menu)", NULL, false, false); - if (ImGui::MenuItem("New")) {} - if (ImGui::MenuItem("Open", "Ctrl+O")) {} - if (ImGui::BeginMenu("Open Recent")) - { - ImGui::MenuItem("fish_hat.c"); - ImGui::MenuItem("fish_hat.inl"); - ImGui::MenuItem("fish_hat.h"); - if (ImGui::BeginMenu("More..")) - { - ImGui::MenuItem("Hello"); - ImGui::MenuItem("Sailor"); - if (ImGui::BeginMenu("Recurse..")) - { - ShowExampleMenuFile(); - ImGui::EndMenu(); - } - ImGui::EndMenu(); - } - ImGui::EndMenu(); - } - if (ImGui::MenuItem("Save", "Ctrl+S")) {} - if (ImGui::MenuItem("Save As..")) {} - ImGui::Separator(); - if (ImGui::BeginMenu("Options")) - { - static bool enabled = true; - ImGui::MenuItem("Enabled", "", &enabled); - ImGui::BeginChild("child", ImVec2(0, 60), true); - for (int i = 0; i < 10; i++) - ImGui::Text("Scrolling Text %d", i); - ImGui::EndChild(); - static float f = 0.5f; - static int n = 0; - static bool b = true; - ImGui::SliderFloat("Value", &f, 0.0f, 1.0f); - ImGui::InputFloat("Input", &f, 0.1f); - ImGui::Combo("Combo", &n, "Yes\0No\0Maybe\0\0"); - ImGui::Checkbox("Check", &b); - ImGui::EndMenu(); - } - if (ImGui::BeginMenu("Colors")) - { - float sz = ImGui::GetTextLineHeight(); - for (int i = 0; i < ImGuiCol_COUNT; i++) - { - const char* name = ImGui::GetStyleColorName((ImGuiCol)i); - ImVec2 p = ImGui::GetCursorScreenPos(); - ImGui::GetWindowDrawList()->AddRectFilled(p, ImVec2(p.x+sz, p.y+sz), ImGui::GetColorU32((ImGuiCol)i)); - ImGui::Dummy(ImVec2(sz, sz)); - ImGui::SameLine(); - ImGui::MenuItem(name); - } - ImGui::EndMenu(); - } - if (ImGui::BeginMenu("Disabled", false)) // Disabled - { - IM_ASSERT(0); - } - if (ImGui::MenuItem("Checked", NULL, true)) {} - if (ImGui::MenuItem("Quit", "Alt+F4")) {} -} - -// Demonstrate creating a window which gets auto-resized according to its content. -static void ShowExampleAppAutoResize(bool* p_open) -{ - if (!ImGui::Begin("Example: Auto-resizing window", p_open, ImGuiWindowFlags_AlwaysAutoResize)) - { - ImGui::End(); - return; - } - - static int lines = 10; - ImGui::Text("Window will resize every-frame to the size of its content.\nNote that you probably don't want to query the window size to\noutput your content because that would create a feedback loop."); - ImGui::SliderInt("Number of lines", &lines, 1, 20); - for (int i = 0; i < lines; i++) - ImGui::Text("%*sThis is line %d", i*4, "", i); // Pad with space to extend size horizontally - ImGui::End(); -} - -// Demonstrate creating a window with custom resize constraints. -static void ShowExampleAppConstrainedResize(bool* p_open) -{ - struct CustomConstraints // Helper functions to demonstrate programmatic constraints - { - static void Square(ImGuiSizeCallbackData* data) { data->DesiredSize = ImVec2(IM_MAX(data->DesiredSize.x, data->DesiredSize.y), IM_MAX(data->DesiredSize.x, data->DesiredSize.y)); } - static void Step(ImGuiSizeCallbackData* data) { float step = (float)(int)(intptr_t)data->UserData; data->DesiredSize = ImVec2((int)(data->DesiredSize.x / step + 0.5f) * step, (int)(data->DesiredSize.y / step + 0.5f) * step); } - }; - - static bool auto_resize = false; - static int type = 0; - static int display_lines = 10; - if (type == 0) ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 0), ImVec2(-1, FLT_MAX)); // Vertical only - if (type == 1) ImGui::SetNextWindowSizeConstraints(ImVec2(0, -1), ImVec2(FLT_MAX, -1)); // Horizontal only - if (type == 2) ImGui::SetNextWindowSizeConstraints(ImVec2(100, 100), ImVec2(FLT_MAX, FLT_MAX)); // Width > 100, Height > 100 - if (type == 3) ImGui::SetNextWindowSizeConstraints(ImVec2(400, -1), ImVec2(500, -1)); // Width 400-500 - if (type == 4) ImGui::SetNextWindowSizeConstraints(ImVec2(-1, 400), ImVec2(-1, 500)); // Height 400-500 - if (type == 5) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Square); // Always Square - if (type == 6) ImGui::SetNextWindowSizeConstraints(ImVec2(0, 0), ImVec2(FLT_MAX, FLT_MAX), CustomConstraints::Step, (void*)100);// Fixed Step - - ImGuiWindowFlags flags = auto_resize ? ImGuiWindowFlags_AlwaysAutoResize : 0; - if (ImGui::Begin("Example: Constrained Resize", p_open, flags)) - { - const char* desc[] = - { - "Resize vertical only", - "Resize horizontal only", - "Width > 100, Height > 100", - "Width 400-500", - "Height 400-500", - "Custom: Always Square", - "Custom: Fixed Steps (100)", - }; - if (ImGui::Button("200x200")) { ImGui::SetWindowSize(ImVec2(200, 200)); } ImGui::SameLine(); - if (ImGui::Button("500x500")) { ImGui::SetWindowSize(ImVec2(500, 500)); } ImGui::SameLine(); - if (ImGui::Button("800x200")) { ImGui::SetWindowSize(ImVec2(800, 200)); } - ImGui::PushItemWidth(200); - ImGui::Combo("Constraint", &type, desc, IM_ARRAYSIZE(desc)); - ImGui::DragInt("Lines", &display_lines, 0.2f, 1, 100); - ImGui::PopItemWidth(); - ImGui::Checkbox("Auto-resize", &auto_resize); - for (int i = 0; i < display_lines; i++) - ImGui::Text("%*sHello, sailor! Making this line long enough for the example.", i * 4, ""); - } - ImGui::End(); -} - -// Demonstrate creating a simple static window with no decoration + a context-menu to choose which corner of the screen to use. -static void ShowExampleAppSimpleOverlay(bool* p_open) -{ - const float DISTANCE = 10.0f; - static int corner = 0; - ImVec2 window_pos = ImVec2((corner & 1) ? ImGui::GetIO().DisplaySize.x - DISTANCE : DISTANCE, (corner & 2) ? ImGui::GetIO().DisplaySize.y - DISTANCE : DISTANCE); - ImVec2 window_pos_pivot = ImVec2((corner & 1) ? 1.0f : 0.0f, (corner & 2) ? 1.0f : 0.0f); - if (corner != -1) - ImGui::SetNextWindowPos(window_pos, ImGuiCond_Always, window_pos_pivot); - ImGui::SetNextWindowBgAlpha(0.3f); // Transparent background - if (ImGui::Begin("Example: Simple Overlay", p_open, (corner != -1 ? ImGuiWindowFlags_NoMove : 0) | ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_AlwaysAutoResize|ImGuiWindowFlags_NoSavedSettings|ImGuiWindowFlags_NoFocusOnAppearing|ImGuiWindowFlags_NoNav)) - { - ImGui::Text("Simple overlay\n" "in the corner of the screen.\n" "(right-click to change position)"); - ImGui::Separator(); - if (ImGui::IsMousePosValid()) - ImGui::Text("Mouse Position: (%.1f,%.1f)", ImGui::GetIO().MousePos.x, ImGui::GetIO().MousePos.y); - else - ImGui::Text("Mouse Position: "); - if (ImGui::BeginPopupContextWindow()) - { - if (ImGui::MenuItem("Custom", NULL, corner == -1)) corner = -1; - if (ImGui::MenuItem("Top-left", NULL, corner == 0)) corner = 0; - if (ImGui::MenuItem("Top-right", NULL, corner == 1)) corner = 1; - if (ImGui::MenuItem("Bottom-left", NULL, corner == 2)) corner = 2; - if (ImGui::MenuItem("Bottom-right", NULL, corner == 3)) corner = 3; - if (p_open && ImGui::MenuItem("Close")) *p_open = false; - ImGui::EndPopup(); - } - ImGui::End(); - } -} - -// Demonstrate using "##" and "###" in identifiers to manipulate ID generation. -// This apply to regular items as well. Read FAQ section "How can I have multiple widgets with the same label? Can I have widget without a label? (Yes). A primer on the purpose of labels/IDs." for details. -static void ShowExampleAppWindowTitles(bool*) -{ - // By default, Windows are uniquely identified by their title. - // You can use the "##" and "###" markers to manipulate the display/ID. - - // Using "##" to display same title but have unique identifier. - ImGui::SetNextWindowPos(ImVec2(100,100), ImGuiCond_FirstUseEver); - ImGui::Begin("Same title as another window##1"); - ImGui::Text("This is window 1.\nMy title is the same as window 2, but my identifier is unique."); - ImGui::End(); - - ImGui::SetNextWindowPos(ImVec2(100,200), ImGuiCond_FirstUseEver); - ImGui::Begin("Same title as another window##2"); - ImGui::Text("This is window 2.\nMy title is the same as window 1, but my identifier is unique."); - ImGui::End(); - - // Using "###" to display a changing title but keep a static identifier "AnimatedTitle" - char buf[128]; - sprintf(buf, "Animated title %c %d###AnimatedTitle", "|/-\\"[(int)(ImGui::GetTime()/0.25f)&3], ImGui::GetFrameCount()); - ImGui::SetNextWindowPos(ImVec2(100,300), ImGuiCond_FirstUseEver); - ImGui::Begin(buf); - ImGui::Text("This window has a changing title."); - ImGui::End(); -} - -// Demonstrate using the low-level ImDrawList to draw custom shapes. -static void ShowExampleAppCustomRendering(bool* p_open) -{ - ImGui::SetNextWindowSize(ImVec2(350,560), ImGuiCond_FirstUseEver); - if (!ImGui::Begin("Example: Custom rendering", p_open)) - { - ImGui::End(); - return; - } - - // Tip: If you do a lot of custom rendering, you probably want to use your own geometrical types and benefit of overloaded operators, etc. - // Define IM_VEC2_CLASS_EXTRA in imconfig.h to create implicit conversions between your types and ImVec2/ImVec4. - // ImGui defines overloaded operators but they are internal to imgui.cpp and not exposed outside (to avoid messing with your types) - // In this example we are not using the maths operators! - ImDrawList* draw_list = ImGui::GetWindowDrawList(); - - // Primitives - ImGui::Text("Primitives"); - static float sz = 36.0f; - static float thickness = 4.0f; - static ImVec4 col = ImVec4(1.0f,1.0f,0.4f,1.0f); - ImGui::DragFloat("Size", &sz, 0.2f, 2.0f, 72.0f, "%.0f"); - ImGui::DragFloat("Thickness", &thickness, 0.05f, 1.0f, 8.0f, "%.02f"); - ImGui::ColorEdit3("Color", &col.x); - { - const ImVec2 p = ImGui::GetCursorScreenPos(); - const ImU32 col32 = ImColor(col); - float x = p.x + 4.0f, y = p.y + 4.0f, spacing = 8.0f; - for (int n = 0; n < 2; n++) - { - float curr_thickness = (n == 0) ? 1.0f : thickness; - draw_list->AddCircle(ImVec2(x+sz*0.5f, y+sz*0.5f), sz*0.5f, col32, 20, curr_thickness); x += sz+spacing; - draw_list->AddRect(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, 0.0f, ImDrawCornerFlags_All, curr_thickness); x += sz+spacing; - draw_list->AddRect(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, 10.0f, ImDrawCornerFlags_All, curr_thickness); x += sz+spacing; - draw_list->AddRect(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, 10.0f, ImDrawCornerFlags_TopLeft|ImDrawCornerFlags_BotRight, curr_thickness); x += sz+spacing; - draw_list->AddTriangle(ImVec2(x+sz*0.5f, y), ImVec2(x+sz,y+sz-0.5f), ImVec2(x,y+sz-0.5f), col32, curr_thickness); x += sz+spacing; - draw_list->AddLine(ImVec2(x, y), ImVec2(x+sz, y ), col32, curr_thickness); x += sz+spacing; // Horizontal line (note: drawing a filled rectangle will be faster!) - draw_list->AddLine(ImVec2(x, y), ImVec2(x, y+sz), col32, curr_thickness); x += spacing; // Vertical line (note: drawing a filled rectangle will be faster!) - draw_list->AddLine(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, curr_thickness); x += sz+spacing; // Diagonal line - draw_list->AddBezierCurve(ImVec2(x, y), ImVec2(x+sz*1.3f,y+sz*0.3f), ImVec2(x+sz-sz*1.3f,y+sz-sz*0.3f), ImVec2(x+sz, y+sz), col32, curr_thickness); - x = p.x + 4; - y += sz+spacing; - } - draw_list->AddCircleFilled(ImVec2(x+sz*0.5f, y+sz*0.5f), sz*0.5f, col32, 32); x += sz+spacing; - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x+sz, y+sz), col32); x += sz+spacing; - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, 10.0f); x += sz+spacing; - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x+sz, y+sz), col32, 10.0f, ImDrawCornerFlags_TopLeft|ImDrawCornerFlags_BotRight); x += sz+spacing; - draw_list->AddTriangleFilled(ImVec2(x+sz*0.5f, y), ImVec2(x+sz,y+sz-0.5f), ImVec2(x,y+sz-0.5f), col32); x += sz+spacing; - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x+sz, y+thickness), col32); x += sz+spacing; // Horizontal line (faster than AddLine, but only handle integer thickness) - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x+thickness, y+sz), col32); x += spacing+spacing; // Vertical line (faster than AddLine, but only handle integer thickness) - draw_list->AddRectFilled(ImVec2(x, y), ImVec2(x+1, y+1), col32); x += sz; // Pixel (faster than AddLine) - draw_list->AddRectFilledMultiColor(ImVec2(x, y), ImVec2(x+sz, y+sz), IM_COL32(0,0,0,255), IM_COL32(255,0,0,255), IM_COL32(255,255,0,255), IM_COL32(0,255,0,255)); - ImGui::Dummy(ImVec2((sz+spacing)*8, (sz+spacing)*3)); - } - ImGui::Separator(); - { - static ImVector points; - static bool adding_line = false; - ImGui::Text("Canvas example"); - if (ImGui::Button("Clear")) points.clear(); - if (points.Size >= 2) { ImGui::SameLine(); if (ImGui::Button("Undo")) { points.pop_back(); points.pop_back(); } } - ImGui::Text("Left-click and drag to add lines,\nRight-click to undo"); - - // Here we are using InvisibleButton() as a convenience to 1) advance the cursor and 2) allows us to use IsItemHovered() - // But you can also draw directly and poll mouse/keyboard by yourself. You can manipulate the cursor using GetCursorPos() and SetCursorPos(). - // If you only use the ImDrawList API, you can notify the owner window of its extends by using SetCursorPos(max). - ImVec2 canvas_pos = ImGui::GetCursorScreenPos(); // ImDrawList API uses screen coordinates! - ImVec2 canvas_size = ImGui::GetContentRegionAvail(); // Resize canvas to what's available - if (canvas_size.x < 50.0f) canvas_size.x = 50.0f; - if (canvas_size.y < 50.0f) canvas_size.y = 50.0f; - draw_list->AddRectFilledMultiColor(canvas_pos, ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y), IM_COL32(50,50,50,255), IM_COL32(50,50,60,255), IM_COL32(60,60,70,255), IM_COL32(50,50,60,255)); - draw_list->AddRect(canvas_pos, ImVec2(canvas_pos.x + canvas_size.x, canvas_pos.y + canvas_size.y), IM_COL32(255,255,255,255)); - - bool adding_preview = false; - ImGui::InvisibleButton("canvas", canvas_size); - ImVec2 mouse_pos_in_canvas = ImVec2(ImGui::GetIO().MousePos.x - canvas_pos.x, ImGui::GetIO().MousePos.y - canvas_pos.y); - if (adding_line) - { - adding_preview = true; - points.push_back(mouse_pos_in_canvas); - if (!ImGui::IsMouseDown(0)) - adding_line = adding_preview = false; - } - if (ImGui::IsItemHovered()) - { - if (!adding_line && ImGui::IsMouseClicked(0)) - { - points.push_back(mouse_pos_in_canvas); - adding_line = true; - } - if (ImGui::IsMouseClicked(1) && !points.empty()) - { - adding_line = adding_preview = false; - points.pop_back(); - points.pop_back(); - } - } - draw_list->PushClipRect(canvas_pos, ImVec2(canvas_pos.x+canvas_size.x, canvas_pos.y+canvas_size.y), true); // clip lines within the canvas (if we resize it, etc.) - for (int i = 0; i < points.Size - 1; i += 2) - draw_list->AddLine(ImVec2(canvas_pos.x + points[i].x, canvas_pos.y + points[i].y), ImVec2(canvas_pos.x + points[i+1].x, canvas_pos.y + points[i+1].y), IM_COL32(255,255,0,255), 2.0f); - draw_list->PopClipRect(); - if (adding_preview) - points.pop_back(); - } - ImGui::End(); -} - -// Demonstrating creating a simple console window, with scrolling, filtering, completion and history. -// For the console example, here we are using a more C++ like approach of declaring a class to hold the data and the functions. -struct ExampleAppConsole -{ - char InputBuf[256]; - ImVector Items; - bool ScrollToBottom; - ImVector History; - int HistoryPos; // -1: new line, 0..History.Size-1 browsing history. - ImVector Commands; - - ExampleAppConsole() - { - ClearLog(); - memset(InputBuf, 0, sizeof(InputBuf)); - HistoryPos = -1; - Commands.push_back("HELP"); - Commands.push_back("HISTORY"); - Commands.push_back("CLEAR"); - Commands.push_back("CLASSIFY"); // "classify" is here to provide an example of "C"+[tab] completing to "CL" and displaying matches. - AddLog("Welcome to Dear ImGui!"); - } - ~ExampleAppConsole() - { - ClearLog(); - for (int i = 0; i < History.Size; i++) - free(History[i]); - } - - // Portable helpers - static int Stricmp(const char* str1, const char* str2) { int d; while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; } return d; } - static int Strnicmp(const char* str1, const char* str2, int n) { int d = 0; while (n > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; n--; } return d; } - static char* Strdup(const char *str) { size_t len = strlen(str) + 1; void* buff = malloc(len); return (char*)memcpy(buff, (const void*)str, len); } - static void Strtrim(char* str) { char* str_end = str + strlen(str); while (str_end > str && str_end[-1] == ' ') str_end--; *str_end = 0; } - - void ClearLog() - { - for (int i = 0; i < Items.Size; i++) - free(Items[i]); - Items.clear(); - ScrollToBottom = true; - } - - void AddLog(const char* fmt, ...) IM_FMTARGS(2) - { - // FIXME-OPT - char buf[1024]; - va_list args; - va_start(args, fmt); - vsnprintf(buf, IM_ARRAYSIZE(buf), fmt, args); - buf[IM_ARRAYSIZE(buf)-1] = 0; - va_end(args); - Items.push_back(Strdup(buf)); - ScrollToBottom = true; - } - - void Draw(const char* title, bool* p_open) - { - ImGui::SetNextWindowSize(ImVec2(520,600), ImGuiCond_FirstUseEver); - if (!ImGui::Begin(title, p_open)) - { - ImGui::End(); - return; - } - - // As a specific feature guaranteed by the library, after calling Begin() the last Item represent the title bar. So e.g. IsItemHovered() will return true when hovering the title bar. - // Here we create a context menu only available from the title bar. - if (ImGui::BeginPopupContextItem()) - { - if (ImGui::MenuItem("Close")) - *p_open = false; - ImGui::EndPopup(); - } - - ImGui::TextWrapped("This example implements a console with basic coloring, completion and history. A more elaborate implementation may want to store entries along with extra data such as timestamp, emitter, etc."); - ImGui::TextWrapped("Enter 'HELP' for help, press TAB to use text completion."); - - // TODO: display items starting from the bottom - - if (ImGui::SmallButton("Add Dummy Text")) { AddLog("%d some text", Items.Size); AddLog("some more text"); AddLog("display very important message here!"); } ImGui::SameLine(); - if (ImGui::SmallButton("Add Dummy Error")) { AddLog("[error] something went wrong"); } ImGui::SameLine(); - if (ImGui::SmallButton("Clear")) { ClearLog(); } ImGui::SameLine(); - bool copy_to_clipboard = ImGui::SmallButton("Copy"); ImGui::SameLine(); - if (ImGui::SmallButton("Scroll to bottom")) ScrollToBottom = true; - //static float t = 0.0f; if (ImGui::GetTime() - t > 0.02f) { t = ImGui::GetTime(); AddLog("Spam %f", t); } - - ImGui::Separator(); - - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0,0)); - static ImGuiTextFilter filter; - filter.Draw("Filter (\"incl,-excl\") (\"error\")", 180); - ImGui::PopStyleVar(); - ImGui::Separator(); - - const float footer_height_to_reserve = ImGui::GetStyle().ItemSpacing.y + ImGui::GetFrameHeightWithSpacing(); // 1 separator, 1 input text - ImGui::BeginChild("ScrollingRegion", ImVec2(0, -footer_height_to_reserve), false, ImGuiWindowFlags_HorizontalScrollbar); // Leave room for 1 separator + 1 InputText - if (ImGui::BeginPopupContextWindow()) - { - if (ImGui::Selectable("Clear")) ClearLog(); - ImGui::EndPopup(); - } - - // Display every line as a separate entry so we can change their color or add custom widgets. If you only want raw text you can use ImGui::TextUnformatted(log.begin(), log.end()); - // NB- if you have thousands of entries this approach may be too inefficient and may require user-side clipping to only process visible items. - // You can seek and display only the lines that are visible using the ImGuiListClipper helper, if your elements are evenly spaced and you have cheap random access to the elements. - // To use the clipper we could replace the 'for (int i = 0; i < Items.Size; i++)' loop with: - // ImGuiListClipper clipper(Items.Size); - // while (clipper.Step()) - // for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) - // However, note that you can not use this code as is if a filter is active because it breaks the 'cheap random-access' property. We would need random-access on the post-filtered list. - // A typical application wanting coarse clipping and filtering may want to pre-compute an array of indices that passed the filtering test, recomputing this array when user changes the filter, - // and appending newly elements as they are inserted. This is left as a task to the user until we can manage to improve this example code! - // If your items are of variable size you may want to implement code similar to what ImGuiListClipper does. Or split your data into fixed height items to allow random-seeking into your list. - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(4,1)); // Tighten spacing - if (copy_to_clipboard) - ImGui::LogToClipboard(); - ImVec4 col_default_text = ImGui::GetStyleColorVec4(ImGuiCol_Text); - for (int i = 0; i < Items.Size; i++) - { - const char* item = Items[i]; - if (!filter.PassFilter(item)) - continue; - ImVec4 col = col_default_text; - if (strstr(item, "[error]")) col = ImColor(1.0f,0.4f,0.4f,1.0f); - else if (strncmp(item, "# ", 2) == 0) col = ImColor(1.0f,0.78f,0.58f,1.0f); - ImGui::PushStyleColor(ImGuiCol_Text, col); - ImGui::TextUnformatted(item); - ImGui::PopStyleColor(); - } - if (copy_to_clipboard) - ImGui::LogFinish(); - if (ScrollToBottom) - ImGui::SetScrollHere(1.0f); - ScrollToBottom = false; - ImGui::PopStyleVar(); - ImGui::EndChild(); - ImGui::Separator(); - - // Command-line - bool reclaim_focus = false; - if (ImGui::InputText("Input", InputBuf, IM_ARRAYSIZE(InputBuf), ImGuiInputTextFlags_EnterReturnsTrue|ImGuiInputTextFlags_CallbackCompletion|ImGuiInputTextFlags_CallbackHistory, &TextEditCallbackStub, (void*)this)) - { - Strtrim(InputBuf); - if (InputBuf[0]) - ExecCommand(InputBuf); - strcpy(InputBuf, ""); - reclaim_focus = true; - } - - // Demonstrate keeping focus on the input box - ImGui::SetItemDefaultFocus(); - if (reclaim_focus) - ImGui::SetKeyboardFocusHere(-1); // Auto focus previous widget - - ImGui::End(); - } - - void ExecCommand(const char* command_line) - { - AddLog("# %s\n", command_line); - - // Insert into history. First find match and delete it so it can be pushed to the back. This isn't trying to be smart or optimal. - HistoryPos = -1; - for (int i = History.Size-1; i >= 0; i--) - if (Stricmp(History[i], command_line) == 0) - { - free(History[i]); - History.erase(History.begin() + i); - break; - } - History.push_back(Strdup(command_line)); - - // Process command - if (Stricmp(command_line, "CLEAR") == 0) - { - ClearLog(); - } - else if (Stricmp(command_line, "HELP") == 0) - { - AddLog("Commands:"); - for (int i = 0; i < Commands.Size; i++) - AddLog("- %s", Commands[i]); - } - else if (Stricmp(command_line, "HISTORY") == 0) - { - int first = History.Size - 10; - for (int i = first > 0 ? first : 0; i < History.Size; i++) - AddLog("%3d: %s\n", i, History[i]); - } - else - { - AddLog("Unknown command: '%s'\n", command_line); - } - } - - static int TextEditCallbackStub(ImGuiTextEditCallbackData* data) // In C++11 you are better off using lambdas for this sort of forwarding callbacks - { - ExampleAppConsole* console = (ExampleAppConsole*)data->UserData; - return console->TextEditCallback(data); - } - - int TextEditCallback(ImGuiTextEditCallbackData* data) - { - //AddLog("cursor: %d, selection: %d-%d", data->CursorPos, data->SelectionStart, data->SelectionEnd); - switch (data->EventFlag) - { - case ImGuiInputTextFlags_CallbackCompletion: - { - // Example of TEXT COMPLETION - - // Locate beginning of current word - const char* word_end = data->Buf + data->CursorPos; - const char* word_start = word_end; - while (word_start > data->Buf) - { - const char c = word_start[-1]; - if (c == ' ' || c == '\t' || c == ',' || c == ';') - break; - word_start--; - } - - // Build a list of candidates - ImVector candidates; - for (int i = 0; i < Commands.Size; i++) - if (Strnicmp(Commands[i], word_start, (int)(word_end-word_start)) == 0) - candidates.push_back(Commands[i]); - - if (candidates.Size == 0) - { - // No match - AddLog("No match for \"%.*s\"!\n", (int)(word_end-word_start), word_start); - } - else if (candidates.Size == 1) - { - // Single match. Delete the beginning of the word and replace it entirely so we've got nice casing - data->DeleteChars((int)(word_start-data->Buf), (int)(word_end-word_start)); - data->InsertChars(data->CursorPos, candidates[0]); - data->InsertChars(data->CursorPos, " "); - } - else - { - // Multiple matches. Complete as much as we can, so inputing "C" will complete to "CL" and display "CLEAR" and "CLASSIFY" - int match_len = (int)(word_end - word_start); - for (;;) - { - int c = 0; - bool all_candidates_matches = true; - for (int i = 0; i < candidates.Size && all_candidates_matches; i++) - if (i == 0) - c = toupper(candidates[i][match_len]); - else if (c == 0 || c != toupper(candidates[i][match_len])) - all_candidates_matches = false; - if (!all_candidates_matches) - break; - match_len++; - } - - if (match_len > 0) - { - data->DeleteChars((int)(word_start - data->Buf), (int)(word_end-word_start)); - data->InsertChars(data->CursorPos, candidates[0], candidates[0] + match_len); - } - - // List matches - AddLog("Possible matches:\n"); - for (int i = 0; i < candidates.Size; i++) - AddLog("- %s\n", candidates[i]); - } - - break; - } - case ImGuiInputTextFlags_CallbackHistory: - { - // Example of HISTORY - const int prev_history_pos = HistoryPos; - if (data->EventKey == ImGuiKey_UpArrow) - { - if (HistoryPos == -1) - HistoryPos = History.Size - 1; - else if (HistoryPos > 0) - HistoryPos--; - } - else if (data->EventKey == ImGuiKey_DownArrow) - { - if (HistoryPos != -1) - if (++HistoryPos >= History.Size) - HistoryPos = -1; - } - - // A better implementation would preserve the data on the current input line along with cursor position. - if (prev_history_pos != HistoryPos) - { - data->CursorPos = data->SelectionStart = data->SelectionEnd = data->BufTextLen = (int)snprintf(data->Buf, (size_t)data->BufSize, "%s", (HistoryPos >= 0) ? History[HistoryPos] : ""); - data->BufDirty = true; - } - } - } - return 0; - } -}; - - -static void ShowExampleAppConsole(bool* p_open) -{ - static ExampleAppConsole console; - console.Draw("Example: Console", p_open); -} - -// Usage: -// static ExampleAppLog my_log; -// my_log.AddLog("Hello %d world\n", 123); -// my_log.Draw("title"); -struct ExampleAppLog -{ - ImGuiTextBuffer Buf; - ImGuiTextFilter Filter; - ImVector LineOffsets; // Index to lines offset - bool ScrollToBottom; - - void Clear() { Buf.clear(); LineOffsets.clear(); } - - void AddLog(const char* fmt, ...) IM_FMTARGS(2) - { - int old_size = Buf.size(); - va_list args; - va_start(args, fmt); - Buf.appendfv(fmt, args); - va_end(args); - for (int new_size = Buf.size(); old_size < new_size; old_size++) - if (Buf[old_size] == '\n') - LineOffsets.push_back(old_size); - ScrollToBottom = true; - } - - void Draw(const char* title, bool* p_open = NULL) - { - ImGui::SetNextWindowSize(ImVec2(900,900), ImGuiCond_FirstUseEver); - ImGui::Begin(title, p_open); - if (ImGui::Button("Clear")) Clear(); - ImGui::SameLine(); - bool copy = ImGui::Button("Copy"); - ImGui::SameLine(); - Filter.Draw("Filter", -100.0f); - ImGui::Separator(); - ImGui::BeginChild("scrolling", ImVec2(0,0), false, ImGuiWindowFlags_HorizontalScrollbar); - if (copy) ImGui::LogToClipboard(); - - if (Filter.IsActive()) - { - const char* buf_begin = Buf.begin(); - const char* line = buf_begin; - for (int line_no = 0; line != NULL; line_no++) - { - const char* line_end = (line_no < LineOffsets.Size) ? buf_begin + LineOffsets[line_no] : NULL; - if (Filter.PassFilter(line, line_end)) - ImGui::TextUnformatted(line, line_end); - line = line_end && line_end[1] ? line_end + 1 : NULL; - } - } - else - { - ImGui::TextUnformatted(Buf.begin()); - } - - if (ScrollToBottom) - ImGui::SetScrollHere(1.0f); - ScrollToBottom = false; - ImGui::EndChild(); - ImGui::End(); - } -}; - - -// [Bruno] -static ExampleAppLog the_log; -namespace ImGui { - void debug_printf(const char* fmt, ...) { - int old_size = the_log.Buf.size(); - va_list args; - va_start(args, fmt); - the_log.Buf.appendfv(fmt, args); - va_end(args); - for (int new_size = the_log.Buf.size(); old_size < new_size; old_size++) - if (the_log.Buf[old_size] == '\n') - the_log.LineOffsets.push_back(old_size); - the_log.ScrollToBottom = true; - } - void show_debug_console() { - show_app_log = true; - } - void hide_debug_console() { - show_app_log = false; - } - void draw_debug_console() { - if(show_app_log) { - ShowExampleAppLog(&show_app_log); - } - } -} -// [End Bruno] - - -// Demonstrate creating a simple log window with basic filtering. -static void ShowExampleAppLog(bool* p_open) -{ - // Demo: add random items (unless Ctrl is held) - // [Bruno] removed, I'm using this one for my own logging. - /* - static float last_time = -1.0f; - float time = ImGui::GetTime(); - if (time - last_time >= 0.20f && !ImGui::GetIO().KeyCtrl) - { - const char* random_words[] = { "system", "info", "warning", "error", "fatal", "notice", "log" }; - the_log.AddLog("[%s] Hello, time is %.1f, frame count is %d\n", random_words[rand() % IM_ARRAYSIZE(random_words)], time, ImGui::GetFrameCount()); - last_time = time; - } - */ - the_log.Draw("Example: Log", p_open); // [Bruno] using the_log global variable (used also by debug_printf). -} - -// Demonstrate create a window with multiple child windows. -static void ShowExampleAppLayout(bool* p_open) -{ - ImGui::SetNextWindowSize(ImVec2(500, 440), ImGuiCond_FirstUseEver); - if (ImGui::Begin("Example: Layout", p_open, ImGuiWindowFlags_MenuBar)) - { - if (ImGui::BeginMenuBar()) - { - if (ImGui::BeginMenu("File")) - { - if (ImGui::MenuItem("Close")) *p_open = false; - ImGui::EndMenu(); - } - ImGui::EndMenuBar(); - } - - // left - static int selected = 0; - ImGui::BeginChild("left pane", ImVec2(150, 0), true); - for (int i = 0; i < 100; i++) - { - char label[128]; - sprintf(label, "MyObject %d", i); - if (ImGui::Selectable(label, selected == i)) - selected = i; - } - ImGui::EndChild(); - ImGui::SameLine(); - - // right - ImGui::BeginGroup(); - ImGui::BeginChild("item view", ImVec2(0, -ImGui::GetFrameHeightWithSpacing())); // Leave room for 1 line below us - ImGui::Text("MyObject: %d", selected); - ImGui::Separator(); - ImGui::TextWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. "); - ImGui::EndChild(); - if (ImGui::Button("Revert")) {} - ImGui::SameLine(); - if (ImGui::Button("Save")) {} - ImGui::EndGroup(); - } - ImGui::End(); -} - -// Demonstrate create a simple property editor. -static void ShowExampleAppPropertyEditor(bool* p_open) -{ - ImGui::SetNextWindowSize(ImVec2(430,450), ImGuiCond_FirstUseEver); - if (!ImGui::Begin("Example: Property editor", p_open)) - { - ImGui::End(); - return; - } - - ShowHelpMarker("This example shows how you may implement a property editor using two columns.\nAll objects/fields data are dummies here.\nRemember that in many simple cases, you can use ImGui::SameLine(xxx) to position\nyour cursor horizontally instead of using the Columns() API."); - - ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(2,2)); - ImGui::Columns(2); - ImGui::Separator(); - - struct funcs - { - static void ShowDummyObject(const char* prefix, int uid) - { - ImGui::PushID(uid); // Use object uid as identifier. Most commonly you could also use the object pointer as a base ID. - ImGui::AlignTextToFramePadding(); // Text and Tree nodes are less high than regular widgets, here we add vertical spacing to make the tree lines equal high. - bool node_open = ImGui::TreeNode("Object", "%s_%u", prefix, uid); - ImGui::NextColumn(); - ImGui::AlignTextToFramePadding(); - ImGui::Text("my sailor is rich"); - ImGui::NextColumn(); - if (node_open) - { - static float dummy_members[8] = { 0.0f,0.0f,1.0f,3.1416f,100.0f,999.0f }; - for (int i = 0; i < 8; i++) - { - ImGui::PushID(i); // Use field index as identifier. - if (i < 2) - { - ShowDummyObject("Child", 424242); - } - else - { - // Here we use a TreeNode to highlight on hover (we could use e.g. Selectable as well) - ImGui::AlignTextToFramePadding(); - ImGui::TreeNodeEx("Field", ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_Bullet, "Field_%d", i); - ImGui::NextColumn(); - ImGui::PushItemWidth(-1); - if (i >= 5) - ImGui::InputFloat("##value", &dummy_members[i], 1.0f); - else - ImGui::DragFloat("##value", &dummy_members[i], 0.01f); - ImGui::PopItemWidth(); - ImGui::NextColumn(); - } - ImGui::PopID(); - } - ImGui::TreePop(); - } - ImGui::PopID(); - } - }; - - // Iterate dummy objects with dummy members (all the same data) - for (int obj_i = 0; obj_i < 3; obj_i++) - funcs::ShowDummyObject("Object", obj_i); - - ImGui::Columns(1); - ImGui::Separator(); - ImGui::PopStyleVar(); - ImGui::End(); -} - -// Demonstrate/test rendering huge amount of text, and the incidence of clipping. -static void ShowExampleAppLongText(bool* p_open) -{ - ImGui::SetNextWindowSize(ImVec2(520,600), ImGuiCond_FirstUseEver); - if (!ImGui::Begin("Example: Long text display", p_open)) - { - ImGui::End(); - return; - } - - static int test_type = 0; - static ImGuiTextBuffer log; - static int lines = 0; - ImGui::Text("Printing unusually long amount of text."); - ImGui::Combo("Test type", &test_type, "Single call to TextUnformatted()\0Multiple calls to Text(), clipped manually\0Multiple calls to Text(), not clipped (slow)\0"); - ImGui::Text("Buffer contents: %d lines, %d bytes", lines, log.size()); - if (ImGui::Button("Clear")) { log.clear(); lines = 0; } - ImGui::SameLine(); - if (ImGui::Button("Add 1000 lines")) - { - for (int i = 0; i < 1000; i++) - log.appendf("%i The quick brown fox jumps over the lazy dog\n", lines+i); - lines += 1000; - } - ImGui::BeginChild("Log"); - switch (test_type) - { - case 0: - // Single call to TextUnformatted() with a big buffer - ImGui::TextUnformatted(log.begin(), log.end()); - break; - case 1: - { - // Multiple calls to Text(), manually coarsely clipped - demonstrate how to use the ImGuiListClipper helper. - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0,0)); - ImGuiListClipper clipper(lines); - while (clipper.Step()) - for (int i = clipper.DisplayStart; i < clipper.DisplayEnd; i++) - ImGui::Text("%i The quick brown fox jumps over the lazy dog", i); - ImGui::PopStyleVar(); - break; - } - case 2: - // Multiple calls to Text(), not clipped (slow) - ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0,0)); - for (int i = 0; i < lines; i++) - ImGui::Text("%i The quick brown fox jumps over the lazy dog", i); - ImGui::PopStyleVar(); - break; - } - ImGui::EndChild(); - ImGui::End(); -} - -// End of Demo code -#else - -void ImGui::ShowDemoWindow(bool*) {} -void ImGui::ShowUserGuide() {} -void ImGui::ShowStyleEditor(ImGuiStyle*) {} - -#endif diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_draw.cpp b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_draw.cpp deleted file mode 100644 index c6471939..00000000 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_draw.cpp +++ /dev/null @@ -1,3086 +0,0 @@ -// dear imgui, v1.62 -// (drawing and font code) - -// Contains implementation for -// - Default styles -// - ImDrawList -// - ImDrawData -// - ImFontAtlas -// - ImFont -// - Default font data - -#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) -#define _CRT_SECURE_NO_WARNINGS -#endif - -#include "imgui.h" -#define IMGUI_DEFINE_MATH_OPERATORS -#include "imgui_internal.h" - -#include // vsnprintf, sscanf, printf -#if !defined(alloca) -#ifdef _WIN32 -#include // alloca -#if !defined(alloca) -#define alloca _alloca // for clang with MS Codegen -#endif -#elif defined(__GLIBC__) || defined(__sun) -#include // alloca -#else -#include // alloca -#endif -#endif - -#ifdef _MSC_VER -#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff) -#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen -#endif - -#ifdef __clang__ -#pragma clang diagnostic ignored "-Wunknown-pragmas" // [Bruno Levy] 05/23/2016: so that it does not complain on older compilers -#pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse. -#pragma clang diagnostic ignored "-Wfloat-equal" // warning : comparing floating point with == or != is unsafe // storing and comparing against same constants ok. -#pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference it. -#pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness // -#if __has_warning("-Wcomma") -#pragma clang diagnostic ignored "-Wcomma" // warning : possible misuse of comma operator here // -#endif -#if __has_warning("-Wreserved-id-macro") -#pragma clang diagnostic ignored "-Wreserved-id-macro" // warning : macro name is a reserved identifier // -#endif -#if __has_warning("-Wdouble-promotion") -#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function -#endif -#elif defined(__GNUC__) -#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used -#pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function -#pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value -#if __GNUC__ >= 8 -#pragma GCC diagnostic ignored "-Wclass-memaccess" // warning: 'memset/memcpy' clearing/writing an object of type 'xxxx' with no trivial copy-assignment; use assignment or value-initialization instead -#endif -#endif - -//[Bruno Levy 01/30/17] -// 177: warning: function was declared but never referenced -// 593: warning: variable was set but never used -#ifdef __ICC -#pragma warning disable 177 593 -#endif - -//------------------------------------------------------------------------- -// STB libraries implementation -//------------------------------------------------------------------------- - -// Compile time options: -//#define IMGUI_STB_NAMESPACE ImGuiStb -//#define IMGUI_STB_TRUETYPE_FILENAME "my_folder/stb_truetype.h" -//#define IMGUI_STB_RECT_PACK_FILENAME "my_folder/stb_rect_pack.h" -//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION -//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION - -#ifdef IMGUI_STB_NAMESPACE -namespace IMGUI_STB_NAMESPACE -{ -#endif - -#ifdef _MSC_VER -#pragma warning (push) -#pragma warning (disable: 4456) // declaration of 'xx' hides previous local declaration -#endif - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-function" -#pragma clang diagnostic ignored "-Wmissing-prototypes" -#pragma clang diagnostic ignored "-Wimplicit-fallthrough" -#pragma clang diagnostic ignored "-Wcast-qual" // warning : cast from 'const xxxx *' to 'xxx *' drops const qualifier // -#endif - -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wtype-limits" // warning: comparison is always true due to limited range of data type [-Wtype-limits] -#pragma GCC diagnostic ignored "-Wcast-qual" // warning: cast from type 'const xxxx *' to type 'xxxx *' casts away qualifiers -#endif - -#ifndef STB_RECT_PACK_IMPLEMENTATION // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds) -#ifndef IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION -#define STBRP_STATIC -#define STBRP_ASSERT(x) IM_ASSERT(x) -#define STB_RECT_PACK_IMPLEMENTATION -#endif -#ifdef IMGUI_STB_RECT_PACK_FILENAME -#include IMGUI_STB_RECT_PACK_FILENAME -#else -#include "stb_rect_pack.h" -#endif -#endif - -#ifndef STB_TRUETYPE_IMPLEMENTATION // in case the user already have an implementation in the _same_ compilation unit (e.g. unity builds) -#ifndef IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION -#define STBTT_malloc(x,u) ((void)(u), ImGui::MemAlloc(x)) -#define STBTT_free(x,u) ((void)(u), ImGui::MemFree(x)) -#define STBTT_assert(x) IM_ASSERT(x) -#define STBTT_fmod(x,y) ImFmod(x,y) -#define STBTT_sqrt(x) ImSqrt(x) -#define STBTT_pow(x,y) ImPow(x,y) -#define STBTT_fabs(x) ImFabs(x) -#define STBTT_ifloor(x) ((int)ImFloorStd(x)) -#define STBTT_iceil(x) ((int)ImCeil(x)) -#define STBTT_STATIC -#define STB_TRUETYPE_IMPLEMENTATION -#else -#define STBTT_DEF extern -#endif -#ifdef IMGUI_STB_TRUETYPE_FILENAME -#include IMGUI_STB_TRUETYPE_FILENAME -#else -#include "stb_truetype.h" -#endif -#endif - -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -#ifdef _MSC_VER -#pragma warning (pop) -#endif - -#ifdef IMGUI_STB_NAMESPACE -} // namespace ImGuiStb -using namespace IMGUI_STB_NAMESPACE; -#endif - -//----------------------------------------------------------------------------- -// Style functions -//----------------------------------------------------------------------------- - -void ImGui::StyleColorsDark(ImGuiStyle* dst) -{ - ImGuiStyle* style = dst ? dst : &ImGui::GetStyle(); - ImVec4* colors = style->Colors; - - colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); - colors[ImGuiCol_TextDisabled] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f); - colors[ImGuiCol_WindowBg] = ImVec4(0.06f, 0.06f, 0.06f, 0.94f); - colors[ImGuiCol_ChildBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.00f); - colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.94f); - colors[ImGuiCol_Border] = ImVec4(0.43f, 0.43f, 0.50f, 0.50f); - colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); - colors[ImGuiCol_FrameBg] = ImVec4(0.16f, 0.29f, 0.48f, 0.54f); - colors[ImGuiCol_FrameBgHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.40f); - colors[ImGuiCol_FrameBgActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); - colors[ImGuiCol_TitleBg] = ImVec4(0.04f, 0.04f, 0.04f, 1.00f); - colors[ImGuiCol_TitleBgActive] = ImVec4(0.16f, 0.29f, 0.48f, 1.00f); - colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 0.51f); - colors[ImGuiCol_MenuBarBg] = ImVec4(0.14f, 0.14f, 0.14f, 1.00f); - colors[ImGuiCol_ScrollbarBg] = ImVec4(0.02f, 0.02f, 0.02f, 0.53f); - colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.31f, 0.31f, 0.31f, 1.00f); - colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f); - colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.51f, 0.51f, 0.51f, 1.00f); - colors[ImGuiCol_CheckMark] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); - colors[ImGuiCol_SliderGrab] = ImVec4(0.24f, 0.52f, 0.88f, 1.00f); - colors[ImGuiCol_SliderGrabActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); - colors[ImGuiCol_Button] = ImVec4(0.26f, 0.59f, 0.98f, 0.40f); - colors[ImGuiCol_ButtonHovered] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); - colors[ImGuiCol_ButtonActive] = ImVec4(0.06f, 0.53f, 0.98f, 1.00f); - colors[ImGuiCol_Header] = ImVec4(0.26f, 0.59f, 0.98f, 0.31f); - colors[ImGuiCol_HeaderHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.80f); - colors[ImGuiCol_HeaderActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); - colors[ImGuiCol_Separator] = colors[ImGuiCol_Border]; - colors[ImGuiCol_SeparatorHovered] = ImVec4(0.10f, 0.40f, 0.75f, 0.78f); - colors[ImGuiCol_SeparatorActive] = ImVec4(0.10f, 0.40f, 0.75f, 1.00f); - colors[ImGuiCol_ResizeGrip] = ImVec4(0.26f, 0.59f, 0.98f, 0.25f); - colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); - colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); - colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f); - colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); - colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); - colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f); - colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f); - colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f); - colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f); - colors[ImGuiCol_NavHighlight] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); - colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); -} - -void ImGui::StyleColorsClassic(ImGuiStyle* dst) -{ - ImGuiStyle* style = dst ? dst : &ImGui::GetStyle(); - ImVec4* colors = style->Colors; - - colors[ImGuiCol_Text] = ImVec4(0.90f, 0.90f, 0.90f, 1.00f); - colors[ImGuiCol_TextDisabled] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f); - colors[ImGuiCol_WindowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.70f); - colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); - colors[ImGuiCol_PopupBg] = ImVec4(0.11f, 0.11f, 0.14f, 0.92f); - colors[ImGuiCol_Border] = ImVec4(0.50f, 0.50f, 0.50f, 0.50f); - colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); - colors[ImGuiCol_FrameBg] = ImVec4(0.43f, 0.43f, 0.43f, 0.39f); - colors[ImGuiCol_FrameBgHovered] = ImVec4(0.47f, 0.47f, 0.69f, 0.40f); - colors[ImGuiCol_FrameBgActive] = ImVec4(0.42f, 0.41f, 0.64f, 0.69f); - colors[ImGuiCol_TitleBg] = ImVec4(0.27f, 0.27f, 0.54f, 0.83f); - colors[ImGuiCol_TitleBgActive] = ImVec4(0.32f, 0.32f, 0.63f, 0.87f); - colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.40f, 0.40f, 0.80f, 0.20f); - colors[ImGuiCol_MenuBarBg] = ImVec4(0.40f, 0.40f, 0.55f, 0.80f); - colors[ImGuiCol_ScrollbarBg] = ImVec4(0.20f, 0.25f, 0.30f, 0.60f); - colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.40f, 0.40f, 0.80f, 0.30f); - colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.40f, 0.40f, 0.80f, 0.40f); - colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.41f, 0.39f, 0.80f, 0.60f); - colors[ImGuiCol_CheckMark] = ImVec4(0.90f, 0.90f, 0.90f, 0.50f); - colors[ImGuiCol_SliderGrab] = ImVec4(1.00f, 1.00f, 1.00f, 0.30f); - colors[ImGuiCol_SliderGrabActive] = ImVec4(0.41f, 0.39f, 0.80f, 0.60f); - colors[ImGuiCol_Button] = ImVec4(0.35f, 0.40f, 0.61f, 0.62f); - colors[ImGuiCol_ButtonHovered] = ImVec4(0.40f, 0.48f, 0.71f, 0.79f); - colors[ImGuiCol_ButtonActive] = ImVec4(0.46f, 0.54f, 0.80f, 1.00f); - colors[ImGuiCol_Header] = ImVec4(0.40f, 0.40f, 0.90f, 0.45f); - colors[ImGuiCol_HeaderHovered] = ImVec4(0.45f, 0.45f, 0.90f, 0.80f); - colors[ImGuiCol_HeaderActive] = ImVec4(0.53f, 0.53f, 0.87f, 0.80f); - colors[ImGuiCol_Separator] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f); - colors[ImGuiCol_SeparatorHovered] = ImVec4(0.60f, 0.60f, 0.70f, 1.00f); - colors[ImGuiCol_SeparatorActive] = ImVec4(0.70f, 0.70f, 0.90f, 1.00f); - colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.16f); - colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.78f, 0.82f, 1.00f, 0.60f); - colors[ImGuiCol_ResizeGripActive] = ImVec4(0.78f, 0.82f, 1.00f, 0.90f); - colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); - colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); - colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); - colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f); - colors[ImGuiCol_TextSelectedBg] = ImVec4(0.00f, 0.00f, 1.00f, 0.35f); - colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f); - colors[ImGuiCol_DragDropTarget] = ImVec4(1.00f, 1.00f, 0.00f, 0.90f); - colors[ImGuiCol_NavHighlight] = colors[ImGuiCol_HeaderHovered]; - colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); -} - -// Those light colors are better suited with a thicker font than the default one + FrameBorder -void ImGui::StyleColorsLight(ImGuiStyle* dst) -{ - ImGuiStyle* style = dst ? dst : &ImGui::GetStyle(); - ImVec4* colors = style->Colors; - - colors[ImGuiCol_Text] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); - colors[ImGuiCol_TextDisabled] = ImVec4(0.60f, 0.60f, 0.60f, 1.00f); - colors[ImGuiCol_WindowBg] = ImVec4(0.94f, 0.94f, 0.94f, 1.00f); - colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); - colors[ImGuiCol_PopupBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.98f); - colors[ImGuiCol_Border] = ImVec4(0.00f, 0.00f, 0.00f, 0.30f); - colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); - colors[ImGuiCol_FrameBg] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); - colors[ImGuiCol_FrameBgHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.40f); - colors[ImGuiCol_FrameBgActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); - colors[ImGuiCol_TitleBg] = ImVec4(0.96f, 0.96f, 0.96f, 1.00f); - colors[ImGuiCol_TitleBgActive] = ImVec4(0.82f, 0.82f, 0.82f, 1.00f); - colors[ImGuiCol_TitleBgCollapsed] = ImVec4(1.00f, 1.00f, 1.00f, 0.51f); - colors[ImGuiCol_MenuBarBg] = ImVec4(0.86f, 0.86f, 0.86f, 1.00f); - colors[ImGuiCol_ScrollbarBg] = ImVec4(0.98f, 0.98f, 0.98f, 0.53f); - colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.69f, 0.69f, 0.69f, 0.80f); - colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.49f, 0.49f, 0.49f, 0.80f); - colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.49f, 0.49f, 0.49f, 1.00f); - colors[ImGuiCol_CheckMark] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); - colors[ImGuiCol_SliderGrab] = ImVec4(0.26f, 0.59f, 0.98f, 0.78f); - colors[ImGuiCol_SliderGrabActive] = ImVec4(0.46f, 0.54f, 0.80f, 0.60f); - colors[ImGuiCol_Button] = ImVec4(0.26f, 0.59f, 0.98f, 0.40f); - colors[ImGuiCol_ButtonHovered] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); - colors[ImGuiCol_ButtonActive] = ImVec4(0.06f, 0.53f, 0.98f, 1.00f); - colors[ImGuiCol_Header] = ImVec4(0.26f, 0.59f, 0.98f, 0.31f); - colors[ImGuiCol_HeaderHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.80f); - colors[ImGuiCol_HeaderActive] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); - colors[ImGuiCol_Separator] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f); - colors[ImGuiCol_SeparatorHovered] = ImVec4(0.14f, 0.44f, 0.80f, 0.78f); - colors[ImGuiCol_SeparatorActive] = ImVec4(0.14f, 0.44f, 0.80f, 1.00f); - colors[ImGuiCol_ResizeGrip] = ImVec4(0.80f, 0.80f, 0.80f, 0.56f); - colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f); - colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); - colors[ImGuiCol_PlotLines] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f); - colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); - colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); - colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.45f, 0.00f, 1.00f); - colors[ImGuiCol_TextSelectedBg] = ImVec4(0.26f, 0.59f, 0.98f, 0.35f); - colors[ImGuiCol_ModalWindowDarkening] = ImVec4(0.20f, 0.20f, 0.20f, 0.35f); - colors[ImGuiCol_DragDropTarget] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f); - colors[ImGuiCol_NavHighlight] = colors[ImGuiCol_HeaderHovered]; - colors[ImGuiCol_NavWindowingHighlight] = ImVec4(0.70f, 0.70f, 0.70f, 0.70f); -} - -//----------------------------------------------------------------------------- -// ImDrawListData -//----------------------------------------------------------------------------- - -ImDrawListSharedData::ImDrawListSharedData() -{ - Font = NULL; - FontSize = 0.0f; - CurveTessellationTol = 0.0f; - ClipRectFullscreen = ImVec4(-8192.0f, -8192.0f, +8192.0f, +8192.0f); - - // Const data - for (int i = 0; i < IM_ARRAYSIZE(CircleVtx12); i++) - { - const float a = ((float)i * 2 * IM_PI) / (float)IM_ARRAYSIZE(CircleVtx12); - CircleVtx12[i] = ImVec2(ImCos(a), ImSin(a)); - } -} - -//----------------------------------------------------------------------------- -// ImDrawList -//----------------------------------------------------------------------------- - -void ImDrawList::Clear() -{ - CmdBuffer.resize(0); - IdxBuffer.resize(0); - VtxBuffer.resize(0); - Flags = ImDrawListFlags_AntiAliasedLines | ImDrawListFlags_AntiAliasedFill; - _VtxCurrentIdx = 0; - _VtxWritePtr = NULL; - _IdxWritePtr = NULL; - _ClipRectStack.resize(0); - _TextureIdStack.resize(0); - _Path.resize(0); - _ChannelsCurrent = 0; - _ChannelsCount = 1; - // NB: Do not clear channels so our allocations are re-used after the first frame. -} - -void ImDrawList::ClearFreeMemory() -{ - CmdBuffer.clear(); - IdxBuffer.clear(); - VtxBuffer.clear(); - _VtxCurrentIdx = 0; - _VtxWritePtr = NULL; - _IdxWritePtr = NULL; - _ClipRectStack.clear(); - _TextureIdStack.clear(); - _Path.clear(); - _ChannelsCurrent = 0; - _ChannelsCount = 1; - for (int i = 0; i < _Channels.Size; i++) - { - if (i == 0) memset(&_Channels[0], 0, sizeof(_Channels[0])); // channel 0 is a copy of CmdBuffer/IdxBuffer, don't destruct again - _Channels[i].CmdBuffer.clear(); - _Channels[i].IdxBuffer.clear(); - } - _Channels.clear(); -} - -ImDrawList* ImDrawList::CloneOutput() const -{ - ImDrawList* dst = IM_NEW(ImDrawList(NULL)); - dst->CmdBuffer = CmdBuffer; - dst->IdxBuffer = IdxBuffer; - dst->VtxBuffer = VtxBuffer; - dst->Flags = Flags; - return dst; -} - -// Using macros because C++ is a terrible language, we want guaranteed inline, no code in header, and no overhead in Debug builds -#define GetCurrentClipRect() (_ClipRectStack.Size ? _ClipRectStack.Data[_ClipRectStack.Size-1] : _Data->ClipRectFullscreen) -#define GetCurrentTextureId() (_TextureIdStack.Size ? _TextureIdStack.Data[_TextureIdStack.Size-1] : NULL) - -void ImDrawList::AddDrawCmd() -{ - ImDrawCmd draw_cmd; - draw_cmd.ClipRect = GetCurrentClipRect(); - draw_cmd.TextureId = GetCurrentTextureId(); - - IM_ASSERT(draw_cmd.ClipRect.x <= draw_cmd.ClipRect.z && draw_cmd.ClipRect.y <= draw_cmd.ClipRect.w); - CmdBuffer.push_back(draw_cmd); -} - -void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data) -{ - ImDrawCmd* current_cmd = CmdBuffer.Size ? &CmdBuffer.back() : NULL; - if (!current_cmd || current_cmd->ElemCount != 0 || current_cmd->UserCallback != NULL) - { - AddDrawCmd(); - current_cmd = &CmdBuffer.back(); - } - current_cmd->UserCallback = callback; - current_cmd->UserCallbackData = callback_data; - - AddDrawCmd(); // Force a new command after us (see comment below) -} - -// Our scheme may appears a bit unusual, basically we want the most-common calls AddLine AddRect etc. to not have to perform any check so we always have a command ready in the stack. -// The cost of figuring out if a new command has to be added or if we can merge is paid in those Update** functions only. -void ImDrawList::UpdateClipRect() -{ - // If current command is used with different settings we need to add a new command - const ImVec4 curr_clip_rect = GetCurrentClipRect(); - ImDrawCmd* curr_cmd = CmdBuffer.Size > 0 ? &CmdBuffer.Data[CmdBuffer.Size-1] : NULL; - if (!curr_cmd || (curr_cmd->ElemCount != 0 && memcmp(&curr_cmd->ClipRect, &curr_clip_rect, sizeof(ImVec4)) != 0) || curr_cmd->UserCallback != NULL) - { - AddDrawCmd(); - return; - } - - // Try to merge with previous command if it matches, else use current command - ImDrawCmd* prev_cmd = CmdBuffer.Size > 1 ? curr_cmd - 1 : NULL; - if (curr_cmd->ElemCount == 0 && prev_cmd && memcmp(&prev_cmd->ClipRect, &curr_clip_rect, sizeof(ImVec4)) == 0 && prev_cmd->TextureId == GetCurrentTextureId() && prev_cmd->UserCallback == NULL) - CmdBuffer.pop_back(); - else - curr_cmd->ClipRect = curr_clip_rect; -} - -void ImDrawList::UpdateTextureID() -{ - // If current command is used with different settings we need to add a new command - const ImTextureID curr_texture_id = GetCurrentTextureId(); - ImDrawCmd* curr_cmd = CmdBuffer.Size ? &CmdBuffer.back() : NULL; - if (!curr_cmd || (curr_cmd->ElemCount != 0 && curr_cmd->TextureId != curr_texture_id) || curr_cmd->UserCallback != NULL) - { - AddDrawCmd(); - return; - } - - // Try to merge with previous command if it matches, else use current command - ImDrawCmd* prev_cmd = CmdBuffer.Size > 1 ? curr_cmd - 1 : NULL; - if (curr_cmd->ElemCount == 0 && prev_cmd && prev_cmd->TextureId == curr_texture_id && memcmp(&prev_cmd->ClipRect, &GetCurrentClipRect(), sizeof(ImVec4)) == 0 && prev_cmd->UserCallback == NULL) - CmdBuffer.pop_back(); - else - curr_cmd->TextureId = curr_texture_id; -} - -#undef GetCurrentClipRect -#undef GetCurrentTextureId - -// Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling) -void ImDrawList::PushClipRect(ImVec2 cr_min, ImVec2 cr_max, bool intersect_with_current_clip_rect) -{ - ImVec4 cr(cr_min.x, cr_min.y, cr_max.x, cr_max.y); - if (intersect_with_current_clip_rect && _ClipRectStack.Size) - { - ImVec4 current = _ClipRectStack.Data[_ClipRectStack.Size-1]; - if (cr.x < current.x) cr.x = current.x; - if (cr.y < current.y) cr.y = current.y; - if (cr.z > current.z) cr.z = current.z; - if (cr.w > current.w) cr.w = current.w; - } - cr.z = ImMax(cr.x, cr.z); - cr.w = ImMax(cr.y, cr.w); - - _ClipRectStack.push_back(cr); - UpdateClipRect(); -} - -void ImDrawList::PushClipRectFullScreen() -{ - PushClipRect(ImVec2(_Data->ClipRectFullscreen.x, _Data->ClipRectFullscreen.y), ImVec2(_Data->ClipRectFullscreen.z, _Data->ClipRectFullscreen.w)); -} - -void ImDrawList::PopClipRect() -{ - IM_ASSERT(_ClipRectStack.Size > 0); - _ClipRectStack.pop_back(); - UpdateClipRect(); -} - -void ImDrawList::PushTextureID(ImTextureID texture_id) -{ - _TextureIdStack.push_back(texture_id); - UpdateTextureID(); -} - -void ImDrawList::PopTextureID() -{ - IM_ASSERT(_TextureIdStack.Size > 0); - _TextureIdStack.pop_back(); - UpdateTextureID(); -} - -void ImDrawList::ChannelsSplit(int channels_count) -{ - IM_ASSERT(_ChannelsCurrent == 0 && _ChannelsCount == 1); - int old_channels_count = _Channels.Size; - if (old_channels_count < channels_count) - _Channels.resize(channels_count); - _ChannelsCount = channels_count; - - // _Channels[] (24/32 bytes each) hold storage that we'll swap with this->_CmdBuffer/_IdxBuffer - // The content of _Channels[0] at this point doesn't matter. We clear it to make state tidy in a debugger but we don't strictly need to. - // When we switch to the next channel, we'll copy _CmdBuffer/_IdxBuffer into _Channels[0] and then _Channels[1] into _CmdBuffer/_IdxBuffer - memset(&_Channels[0], 0, sizeof(ImDrawChannel)); - for (int i = 1; i < channels_count; i++) - { - if (i >= old_channels_count) - { - IM_PLACEMENT_NEW(&_Channels[i]) ImDrawChannel(); - } - else - { - _Channels[i].CmdBuffer.resize(0); - _Channels[i].IdxBuffer.resize(0); - } - if (_Channels[i].CmdBuffer.Size == 0) - { - ImDrawCmd draw_cmd; - draw_cmd.ClipRect = _ClipRectStack.back(); - draw_cmd.TextureId = _TextureIdStack.back(); - _Channels[i].CmdBuffer.push_back(draw_cmd); - } - } -} - -void ImDrawList::ChannelsMerge() -{ - // Note that we never use or rely on channels.Size because it is merely a buffer that we never shrink back to 0 to keep all sub-buffers ready for use. - if (_ChannelsCount <= 1) - return; - - ChannelsSetCurrent(0); - if (CmdBuffer.Size && CmdBuffer.back().ElemCount == 0) - CmdBuffer.pop_back(); - - int new_cmd_buffer_count = 0, new_idx_buffer_count = 0; - for (int i = 1; i < _ChannelsCount; i++) - { - ImDrawChannel& ch = _Channels[i]; - if (ch.CmdBuffer.Size && ch.CmdBuffer.back().ElemCount == 0) - ch.CmdBuffer.pop_back(); - new_cmd_buffer_count += ch.CmdBuffer.Size; - new_idx_buffer_count += ch.IdxBuffer.Size; - } - CmdBuffer.resize(CmdBuffer.Size + new_cmd_buffer_count); - IdxBuffer.resize(IdxBuffer.Size + new_idx_buffer_count); - - ImDrawCmd* cmd_write = CmdBuffer.Data + CmdBuffer.Size - new_cmd_buffer_count; - _IdxWritePtr = IdxBuffer.Data + IdxBuffer.Size - new_idx_buffer_count; - for (int i = 1; i < _ChannelsCount; i++) - { - ImDrawChannel& ch = _Channels[i]; - if (int sz = ch.CmdBuffer.Size) { memcpy(cmd_write, ch.CmdBuffer.Data, sz * sizeof(ImDrawCmd)); cmd_write += sz; } - if (int sz = ch.IdxBuffer.Size) { memcpy(_IdxWritePtr, ch.IdxBuffer.Data, sz * sizeof(ImDrawIdx)); _IdxWritePtr += sz; } - } - UpdateClipRect(); // We call this instead of AddDrawCmd(), so that empty channels won't produce an extra draw call. - _ChannelsCount = 1; -} - -void ImDrawList::ChannelsSetCurrent(int idx) -{ - IM_ASSERT(idx < _ChannelsCount); - if (_ChannelsCurrent == idx) return; - memcpy(&_Channels.Data[_ChannelsCurrent].CmdBuffer, &CmdBuffer, sizeof(CmdBuffer)); // copy 12 bytes, four times - memcpy(&_Channels.Data[_ChannelsCurrent].IdxBuffer, &IdxBuffer, sizeof(IdxBuffer)); - _ChannelsCurrent = idx; - memcpy(&CmdBuffer, &_Channels.Data[_ChannelsCurrent].CmdBuffer, sizeof(CmdBuffer)); - memcpy(&IdxBuffer, &_Channels.Data[_ChannelsCurrent].IdxBuffer, sizeof(IdxBuffer)); - _IdxWritePtr = IdxBuffer.Data + IdxBuffer.Size; -} - -// NB: this can be called with negative count for removing primitives (as long as the result does not underflow) -void ImDrawList::PrimReserve(int idx_count, int vtx_count) -{ - ImDrawCmd& draw_cmd = CmdBuffer.Data[CmdBuffer.Size-1]; - draw_cmd.ElemCount += idx_count; - - int vtx_buffer_old_size = VtxBuffer.Size; - VtxBuffer.resize(vtx_buffer_old_size + vtx_count); - _VtxWritePtr = VtxBuffer.Data + vtx_buffer_old_size; - - int idx_buffer_old_size = IdxBuffer.Size; - IdxBuffer.resize(idx_buffer_old_size + idx_count); - _IdxWritePtr = IdxBuffer.Data + idx_buffer_old_size; -} - -// Fully unrolled with inline call to keep our debug builds decently fast. -void ImDrawList::PrimRect(const ImVec2& a, const ImVec2& c, ImU32 col) -{ - ImVec2 b(c.x, a.y), d(a.x, c.y), uv(_Data->TexUvWhitePixel); - ImDrawIdx idx = (ImDrawIdx)_VtxCurrentIdx; - _IdxWritePtr[0] = idx; _IdxWritePtr[1] = (ImDrawIdx)(idx+1); _IdxWritePtr[2] = (ImDrawIdx)(idx+2); - _IdxWritePtr[3] = idx; _IdxWritePtr[4] = (ImDrawIdx)(idx+2); _IdxWritePtr[5] = (ImDrawIdx)(idx+3); - _VtxWritePtr[0].pos = a; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; - _VtxWritePtr[1].pos = b; _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col; - _VtxWritePtr[2].pos = c; _VtxWritePtr[2].uv = uv; _VtxWritePtr[2].col = col; - _VtxWritePtr[3].pos = d; _VtxWritePtr[3].uv = uv; _VtxWritePtr[3].col = col; - _VtxWritePtr += 4; - _VtxCurrentIdx += 4; - _IdxWritePtr += 6; -} - -void ImDrawList::PrimRectUV(const ImVec2& a, const ImVec2& c, const ImVec2& uv_a, const ImVec2& uv_c, ImU32 col) -{ - ImVec2 b(c.x, a.y), d(a.x, c.y), uv_b(uv_c.x, uv_a.y), uv_d(uv_a.x, uv_c.y); - ImDrawIdx idx = (ImDrawIdx)_VtxCurrentIdx; - _IdxWritePtr[0] = idx; _IdxWritePtr[1] = (ImDrawIdx)(idx+1); _IdxWritePtr[2] = (ImDrawIdx)(idx+2); - _IdxWritePtr[3] = idx; _IdxWritePtr[4] = (ImDrawIdx)(idx+2); _IdxWritePtr[5] = (ImDrawIdx)(idx+3); - _VtxWritePtr[0].pos = a; _VtxWritePtr[0].uv = uv_a; _VtxWritePtr[0].col = col; - _VtxWritePtr[1].pos = b; _VtxWritePtr[1].uv = uv_b; _VtxWritePtr[1].col = col; - _VtxWritePtr[2].pos = c; _VtxWritePtr[2].uv = uv_c; _VtxWritePtr[2].col = col; - _VtxWritePtr[3].pos = d; _VtxWritePtr[3].uv = uv_d; _VtxWritePtr[3].col = col; - _VtxWritePtr += 4; - _VtxCurrentIdx += 4; - _IdxWritePtr += 6; -} - -void ImDrawList::PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, const ImVec2& uv_a, const ImVec2& uv_b, const ImVec2& uv_c, const ImVec2& uv_d, ImU32 col) -{ - ImDrawIdx idx = (ImDrawIdx)_VtxCurrentIdx; - _IdxWritePtr[0] = idx; _IdxWritePtr[1] = (ImDrawIdx)(idx+1); _IdxWritePtr[2] = (ImDrawIdx)(idx+2); - _IdxWritePtr[3] = idx; _IdxWritePtr[4] = (ImDrawIdx)(idx+2); _IdxWritePtr[5] = (ImDrawIdx)(idx+3); - _VtxWritePtr[0].pos = a; _VtxWritePtr[0].uv = uv_a; _VtxWritePtr[0].col = col; - _VtxWritePtr[1].pos = b; _VtxWritePtr[1].uv = uv_b; _VtxWritePtr[1].col = col; - _VtxWritePtr[2].pos = c; _VtxWritePtr[2].uv = uv_c; _VtxWritePtr[2].col = col; - _VtxWritePtr[3].pos = d; _VtxWritePtr[3].uv = uv_d; _VtxWritePtr[3].col = col; - _VtxWritePtr += 4; - _VtxCurrentIdx += 4; - _IdxWritePtr += 6; -} - -// TODO: Thickness anti-aliased lines cap are missing their AA fringe. -void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 col, bool closed, float thickness) -{ - if (points_count < 2) - return; - - const ImVec2 uv = _Data->TexUvWhitePixel; - - int count = points_count; - if (!closed) - count = points_count-1; - - const bool thick_line = thickness > 1.0f; - if (Flags & ImDrawListFlags_AntiAliasedLines) - { - // Anti-aliased stroke - const float AA_SIZE = 1.0f; - const ImU32 col_trans = col & ~IM_COL32_A_MASK; - - const int idx_count = thick_line ? count*18 : count*12; - const int vtx_count = thick_line ? points_count*4 : points_count*3; - PrimReserve(idx_count, vtx_count); - - // Temporary buffer - ImVec2* temp_normals = (ImVec2*)alloca(points_count * (thick_line ? 5 : 3) * sizeof(ImVec2)); - ImVec2* temp_points = temp_normals + points_count; - - for (int i1 = 0; i1 < count; i1++) - { - const int i2 = (i1+1) == points_count ? 0 : i1+1; - ImVec2 diff = points[i2] - points[i1]; - diff *= ImInvLength(diff, 1.0f); - temp_normals[i1].x = diff.y; - temp_normals[i1].y = -diff.x; - } - if (!closed) - temp_normals[points_count-1] = temp_normals[points_count-2]; - - if (!thick_line) - { - if (!closed) - { - temp_points[0] = points[0] + temp_normals[0] * AA_SIZE; - temp_points[1] = points[0] - temp_normals[0] * AA_SIZE; - temp_points[(points_count-1)*2+0] = points[points_count-1] + temp_normals[points_count-1] * AA_SIZE; - temp_points[(points_count-1)*2+1] = points[points_count-1] - temp_normals[points_count-1] * AA_SIZE; - } - - // FIXME-OPT: Merge the different loops, possibly remove the temporary buffer. - unsigned int idx1 = _VtxCurrentIdx; - for (int i1 = 0; i1 < count; i1++) - { - const int i2 = (i1+1) == points_count ? 0 : i1+1; - unsigned int idx2 = (i1+1) == points_count ? _VtxCurrentIdx : idx1+3; - - // Average normals - ImVec2 dm = (temp_normals[i1] + temp_normals[i2]) * 0.5f; - float dmr2 = dm.x*dm.x + dm.y*dm.y; - if (dmr2 > 0.000001f) - { - float scale = 1.0f / dmr2; - if (scale > 100.0f) scale = 100.0f; - dm *= scale; - } - dm *= AA_SIZE; - temp_points[i2*2+0] = points[i2] + dm; - temp_points[i2*2+1] = points[i2] - dm; - - // Add indexes - _IdxWritePtr[0] = (ImDrawIdx)(idx2+0); _IdxWritePtr[1] = (ImDrawIdx)(idx1+0); _IdxWritePtr[2] = (ImDrawIdx)(idx1+2); - _IdxWritePtr[3] = (ImDrawIdx)(idx1+2); _IdxWritePtr[4] = (ImDrawIdx)(idx2+2); _IdxWritePtr[5] = (ImDrawIdx)(idx2+0); - _IdxWritePtr[6] = (ImDrawIdx)(idx2+1); _IdxWritePtr[7] = (ImDrawIdx)(idx1+1); _IdxWritePtr[8] = (ImDrawIdx)(idx1+0); - _IdxWritePtr[9] = (ImDrawIdx)(idx1+0); _IdxWritePtr[10]= (ImDrawIdx)(idx2+0); _IdxWritePtr[11]= (ImDrawIdx)(idx2+1); - _IdxWritePtr += 12; - - idx1 = idx2; - } - - // Add vertexes - for (int i = 0; i < points_count; i++) - { - _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; - _VtxWritePtr[1].pos = temp_points[i*2+0]; _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col_trans; - _VtxWritePtr[2].pos = temp_points[i*2+1]; _VtxWritePtr[2].uv = uv; _VtxWritePtr[2].col = col_trans; - _VtxWritePtr += 3; - } - } - else - { - const float half_inner_thickness = (thickness - AA_SIZE) * 0.5f; - if (!closed) - { - temp_points[0] = points[0] + temp_normals[0] * (half_inner_thickness + AA_SIZE); - temp_points[1] = points[0] + temp_normals[0] * (half_inner_thickness); - temp_points[2] = points[0] - temp_normals[0] * (half_inner_thickness); - temp_points[3] = points[0] - temp_normals[0] * (half_inner_thickness + AA_SIZE); - temp_points[(points_count-1)*4+0] = points[points_count-1] + temp_normals[points_count-1] * (half_inner_thickness + AA_SIZE); - temp_points[(points_count-1)*4+1] = points[points_count-1] + temp_normals[points_count-1] * (half_inner_thickness); - temp_points[(points_count-1)*4+2] = points[points_count-1] - temp_normals[points_count-1] * (half_inner_thickness); - temp_points[(points_count-1)*4+3] = points[points_count-1] - temp_normals[points_count-1] * (half_inner_thickness + AA_SIZE); - } - - // FIXME-OPT: Merge the different loops, possibly remove the temporary buffer. - unsigned int idx1 = _VtxCurrentIdx; - for (int i1 = 0; i1 < count; i1++) - { - const int i2 = (i1+1) == points_count ? 0 : i1+1; - unsigned int idx2 = (i1+1) == points_count ? _VtxCurrentIdx : idx1+4; - - // Average normals - ImVec2 dm = (temp_normals[i1] + temp_normals[i2]) * 0.5f; - float dmr2 = dm.x*dm.x + dm.y*dm.y; - if (dmr2 > 0.000001f) - { - float scale = 1.0f / dmr2; - if (scale > 100.0f) scale = 100.0f; - dm *= scale; - } - ImVec2 dm_out = dm * (half_inner_thickness + AA_SIZE); - ImVec2 dm_in = dm * half_inner_thickness; - temp_points[i2*4+0] = points[i2] + dm_out; - temp_points[i2*4+1] = points[i2] + dm_in; - temp_points[i2*4+2] = points[i2] - dm_in; - temp_points[i2*4+3] = points[i2] - dm_out; - - // Add indexes - _IdxWritePtr[0] = (ImDrawIdx)(idx2+1); _IdxWritePtr[1] = (ImDrawIdx)(idx1+1); _IdxWritePtr[2] = (ImDrawIdx)(idx1+2); - _IdxWritePtr[3] = (ImDrawIdx)(idx1+2); _IdxWritePtr[4] = (ImDrawIdx)(idx2+2); _IdxWritePtr[5] = (ImDrawIdx)(idx2+1); - _IdxWritePtr[6] = (ImDrawIdx)(idx2+1); _IdxWritePtr[7] = (ImDrawIdx)(idx1+1); _IdxWritePtr[8] = (ImDrawIdx)(idx1+0); - _IdxWritePtr[9] = (ImDrawIdx)(idx1+0); _IdxWritePtr[10] = (ImDrawIdx)(idx2+0); _IdxWritePtr[11] = (ImDrawIdx)(idx2+1); - _IdxWritePtr[12] = (ImDrawIdx)(idx2+2); _IdxWritePtr[13] = (ImDrawIdx)(idx1+2); _IdxWritePtr[14] = (ImDrawIdx)(idx1+3); - _IdxWritePtr[15] = (ImDrawIdx)(idx1+3); _IdxWritePtr[16] = (ImDrawIdx)(idx2+3); _IdxWritePtr[17] = (ImDrawIdx)(idx2+2); - _IdxWritePtr += 18; - - idx1 = idx2; - } - - // Add vertexes - for (int i = 0; i < points_count; i++) - { - _VtxWritePtr[0].pos = temp_points[i*4+0]; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col_trans; - _VtxWritePtr[1].pos = temp_points[i*4+1]; _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col; - _VtxWritePtr[2].pos = temp_points[i*4+2]; _VtxWritePtr[2].uv = uv; _VtxWritePtr[2].col = col; - _VtxWritePtr[3].pos = temp_points[i*4+3]; _VtxWritePtr[3].uv = uv; _VtxWritePtr[3].col = col_trans; - _VtxWritePtr += 4; - } - } - _VtxCurrentIdx += (ImDrawIdx)vtx_count; - } - else - { - // Non Anti-aliased Stroke - const int idx_count = count*6; - const int vtx_count = count*4; // FIXME-OPT: Not sharing edges - PrimReserve(idx_count, vtx_count); - - for (int i1 = 0; i1 < count; i1++) - { - const int i2 = (i1+1) == points_count ? 0 : i1+1; - const ImVec2& p1 = points[i1]; - const ImVec2& p2 = points[i2]; - ImVec2 diff = p2 - p1; - diff *= ImInvLength(diff, 1.0f); - - const float dx = diff.x * (thickness * 0.5f); - const float dy = diff.y * (thickness * 0.5f); - _VtxWritePtr[0].pos.x = p1.x + dy; _VtxWritePtr[0].pos.y = p1.y - dx; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; - _VtxWritePtr[1].pos.x = p2.x + dy; _VtxWritePtr[1].pos.y = p2.y - dx; _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col; - _VtxWritePtr[2].pos.x = p2.x - dy; _VtxWritePtr[2].pos.y = p2.y + dx; _VtxWritePtr[2].uv = uv; _VtxWritePtr[2].col = col; - _VtxWritePtr[3].pos.x = p1.x - dy; _VtxWritePtr[3].pos.y = p1.y + dx; _VtxWritePtr[3].uv = uv; _VtxWritePtr[3].col = col; - _VtxWritePtr += 4; - - _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx+1); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx+2); - _IdxWritePtr[3] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[4] = (ImDrawIdx)(_VtxCurrentIdx+2); _IdxWritePtr[5] = (ImDrawIdx)(_VtxCurrentIdx+3); - _IdxWritePtr += 6; - _VtxCurrentIdx += 4; - } - } -} - -void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_count, ImU32 col) -{ - const ImVec2 uv = _Data->TexUvWhitePixel; - - if (Flags & ImDrawListFlags_AntiAliasedFill) - { - // Anti-aliased Fill - const float AA_SIZE = 1.0f; - const ImU32 col_trans = col & ~IM_COL32_A_MASK; - const int idx_count = (points_count-2)*3 + points_count*6; - const int vtx_count = (points_count*2); - PrimReserve(idx_count, vtx_count); - - // Add indexes for fill - unsigned int vtx_inner_idx = _VtxCurrentIdx; - unsigned int vtx_outer_idx = _VtxCurrentIdx+1; - for (int i = 2; i < points_count; i++) - { - _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx+((i-1)<<1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_inner_idx+(i<<1)); - _IdxWritePtr += 3; - } - - // Compute normals - ImVec2* temp_normals = (ImVec2*)alloca(points_count * sizeof(ImVec2)); - for (int i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++) - { - const ImVec2& p0 = points[i0]; - const ImVec2& p1 = points[i1]; - ImVec2 diff = p1 - p0; - diff *= ImInvLength(diff, 1.0f); - temp_normals[i0].x = diff.y; - temp_normals[i0].y = -diff.x; - } - - for (int i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++) - { - // Average normals - const ImVec2& n0 = temp_normals[i0]; - const ImVec2& n1 = temp_normals[i1]; - ImVec2 dm = (n0 + n1) * 0.5f; - float dmr2 = dm.x*dm.x + dm.y*dm.y; - if (dmr2 > 0.000001f) - { - float scale = 1.0f / dmr2; - if (scale > 100.0f) scale = 100.0f; - dm *= scale; - } - dm *= AA_SIZE * 0.5f; - - // Add vertices - _VtxWritePtr[0].pos = (points[i1] - dm); _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; // Inner - _VtxWritePtr[1].pos = (points[i1] + dm); _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col_trans; // Outer - _VtxWritePtr += 2; - - // Add indexes for fringes - _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx+(i1<<1)); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx+(i0<<1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_outer_idx+(i0<<1)); - _IdxWritePtr[3] = (ImDrawIdx)(vtx_outer_idx+(i0<<1)); _IdxWritePtr[4] = (ImDrawIdx)(vtx_outer_idx+(i1<<1)); _IdxWritePtr[5] = (ImDrawIdx)(vtx_inner_idx+(i1<<1)); - _IdxWritePtr += 6; - } - _VtxCurrentIdx += (ImDrawIdx)vtx_count; - } - else - { - // Non Anti-aliased Fill - const int idx_count = (points_count-2)*3; - const int vtx_count = points_count; - PrimReserve(idx_count, vtx_count); - for (int i = 0; i < vtx_count; i++) - { - _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; - _VtxWritePtr++; - } - for (int i = 2; i < points_count; i++) - { - _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx+i-1); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx+i); - _IdxWritePtr += 3; - } - _VtxCurrentIdx += (ImDrawIdx)vtx_count; - } -} - -void ImDrawList::PathArcToFast(const ImVec2& centre, float radius, int a_min_of_12, int a_max_of_12) -{ - if (radius == 0.0f || a_min_of_12 > a_max_of_12) - { - _Path.push_back(centre); - return; - } - _Path.reserve(_Path.Size + (a_max_of_12 - a_min_of_12 + 1)); - for (int a = a_min_of_12; a <= a_max_of_12; a++) - { - const ImVec2& c = _Data->CircleVtx12[a % IM_ARRAYSIZE(_Data->CircleVtx12)]; - _Path.push_back(ImVec2(centre.x + c.x * radius, centre.y + c.y * radius)); - } -} - -void ImDrawList::PathArcTo(const ImVec2& centre, float radius, float a_min, float a_max, int num_segments) -{ - if (radius == 0.0f) - { - _Path.push_back(centre); - return; - } - _Path.reserve(_Path.Size + (num_segments + 1)); - for (int i = 0; i <= num_segments; i++) - { - const float a = a_min + ((float)i / (float)num_segments) * (a_max - a_min); - _Path.push_back(ImVec2(centre.x + ImCos(a) * radius, centre.y + ImSin(a) * radius)); - } -} - -static void PathBezierToCasteljau(ImVector* path, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float tess_tol, int level) -{ - float dx = x4 - x1; - float dy = y4 - y1; - float d2 = ((x2 - x4) * dy - (y2 - y4) * dx); - float d3 = ((x3 - x4) * dy - (y3 - y4) * dx); - d2 = (d2 >= 0) ? d2 : -d2; - d3 = (d3 >= 0) ? d3 : -d3; - if ((d2+d3) * (d2+d3) < tess_tol * (dx*dx + dy*dy)) - { - path->push_back(ImVec2(x4, y4)); - } - else if (level < 10) - { - float x12 = (x1+x2)*0.5f, y12 = (y1+y2)*0.5f; - float x23 = (x2+x3)*0.5f, y23 = (y2+y3)*0.5f; - float x34 = (x3+x4)*0.5f, y34 = (y3+y4)*0.5f; - float x123 = (x12+x23)*0.5f, y123 = (y12+y23)*0.5f; - float x234 = (x23+x34)*0.5f, y234 = (y23+y34)*0.5f; - float x1234 = (x123+x234)*0.5f, y1234 = (y123+y234)*0.5f; - - PathBezierToCasteljau(path, x1,y1, x12,y12, x123,y123, x1234,y1234, tess_tol, level+1); - PathBezierToCasteljau(path, x1234,y1234, x234,y234, x34,y34, x4,y4, tess_tol, level+1); - } -} - -void ImDrawList::PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments) -{ - ImVec2 p1 = _Path.back(); - if (num_segments == 0) - { - // Auto-tessellated - PathBezierToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, _Data->CurveTessellationTol, 0); - } - else - { - float t_step = 1.0f / (float)num_segments; - for (int i_step = 1; i_step <= num_segments; i_step++) - { - float t = t_step * i_step; - float u = 1.0f - t; - float w1 = u*u*u; - float w2 = 3*u*u*t; - float w3 = 3*u*t*t; - float w4 = t*t*t; - _Path.push_back(ImVec2(w1*p1.x + w2*p2.x + w3*p3.x + w4*p4.x, w1*p1.y + w2*p2.y + w3*p3.y + w4*p4.y)); - } - } -} - -void ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, int rounding_corners) -{ - rounding = ImMin(rounding, ImFabs(b.x - a.x) * ( ((rounding_corners & ImDrawCornerFlags_Top) == ImDrawCornerFlags_Top) || ((rounding_corners & ImDrawCornerFlags_Bot) == ImDrawCornerFlags_Bot) ? 0.5f : 1.0f ) - 1.0f); - rounding = ImMin(rounding, ImFabs(b.y - a.y) * ( ((rounding_corners & ImDrawCornerFlags_Left) == ImDrawCornerFlags_Left) || ((rounding_corners & ImDrawCornerFlags_Right) == ImDrawCornerFlags_Right) ? 0.5f : 1.0f ) - 1.0f); - - if (rounding <= 0.0f || rounding_corners == 0) - { - PathLineTo(a); - PathLineTo(ImVec2(b.x, a.y)); - PathLineTo(b); - PathLineTo(ImVec2(a.x, b.y)); - } - else - { - const float rounding_tl = (rounding_corners & ImDrawCornerFlags_TopLeft) ? rounding : 0.0f; - const float rounding_tr = (rounding_corners & ImDrawCornerFlags_TopRight) ? rounding : 0.0f; - const float rounding_br = (rounding_corners & ImDrawCornerFlags_BotRight) ? rounding : 0.0f; - const float rounding_bl = (rounding_corners & ImDrawCornerFlags_BotLeft) ? rounding : 0.0f; - PathArcToFast(ImVec2(a.x + rounding_tl, a.y + rounding_tl), rounding_tl, 6, 9); - PathArcToFast(ImVec2(b.x - rounding_tr, a.y + rounding_tr), rounding_tr, 9, 12); - PathArcToFast(ImVec2(b.x - rounding_br, b.y - rounding_br), rounding_br, 0, 3); - PathArcToFast(ImVec2(a.x + rounding_bl, b.y - rounding_bl), rounding_bl, 3, 6); - } -} - -void ImDrawList::AddLine(const ImVec2& a, const ImVec2& b, ImU32 col, float thickness) -{ - if ((col & IM_COL32_A_MASK) == 0) - return; - PathLineTo(a + ImVec2(0.5f,0.5f)); - PathLineTo(b + ImVec2(0.5f,0.5f)); - PathStroke(col, false, thickness); -} - -// a: upper-left, b: lower-right. we don't render 1 px sized rectangles properly. -void ImDrawList::AddRect(const ImVec2& a, const ImVec2& b, ImU32 col, float rounding, int rounding_corners_flags, float thickness) -{ - if ((col & IM_COL32_A_MASK) == 0) - return; - if (Flags & ImDrawListFlags_AntiAliasedLines) - PathRect(a + ImVec2(0.5f,0.5f), b - ImVec2(0.50f,0.50f), rounding, rounding_corners_flags); - else - PathRect(a + ImVec2(0.5f,0.5f), b - ImVec2(0.49f,0.49f), rounding, rounding_corners_flags); // Better looking lower-right corner and rounded non-AA shapes. - PathStroke(col, true, thickness); -} - -void ImDrawList::AddRectFilled(const ImVec2& a, const ImVec2& b, ImU32 col, float rounding, int rounding_corners_flags) -{ - if ((col & IM_COL32_A_MASK) == 0) - return; - if (rounding > 0.0f) - { - PathRect(a, b, rounding, rounding_corners_flags); - PathFillConvex(col); - } - else - { - PrimReserve(6, 4); - PrimRect(a, b, col); - } -} - -void ImDrawList::AddRectFilledMultiColor(const ImVec2& a, const ImVec2& c, ImU32 col_upr_left, ImU32 col_upr_right, ImU32 col_bot_right, ImU32 col_bot_left) -{ - if (((col_upr_left | col_upr_right | col_bot_right | col_bot_left) & IM_COL32_A_MASK) == 0) - return; - - const ImVec2 uv = _Data->TexUvWhitePixel; - PrimReserve(6, 4); - PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx+1)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx+2)); - PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx+2)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx+3)); - PrimWriteVtx(a, uv, col_upr_left); - PrimWriteVtx(ImVec2(c.x, a.y), uv, col_upr_right); - PrimWriteVtx(c, uv, col_bot_right); - PrimWriteVtx(ImVec2(a.x, c.y), uv, col_bot_left); -} - -void ImDrawList::AddQuad(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, ImU32 col, float thickness) -{ - if ((col & IM_COL32_A_MASK) == 0) - return; - - PathLineTo(a); - PathLineTo(b); - PathLineTo(c); - PathLineTo(d); - PathStroke(col, true, thickness); -} - -void ImDrawList::AddQuadFilled(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, ImU32 col) -{ - if ((col & IM_COL32_A_MASK) == 0) - return; - - PathLineTo(a); - PathLineTo(b); - PathLineTo(c); - PathLineTo(d); - PathFillConvex(col); -} - -void ImDrawList::AddTriangle(const ImVec2& a, const ImVec2& b, const ImVec2& c, ImU32 col, float thickness) -{ - if ((col & IM_COL32_A_MASK) == 0) - return; - - PathLineTo(a); - PathLineTo(b); - PathLineTo(c); - PathStroke(col, true, thickness); -} - -void ImDrawList::AddTriangleFilled(const ImVec2& a, const ImVec2& b, const ImVec2& c, ImU32 col) -{ - if ((col & IM_COL32_A_MASK) == 0) - return; - - PathLineTo(a); - PathLineTo(b); - PathLineTo(c); - PathFillConvex(col); -} - -void ImDrawList::AddCircle(const ImVec2& centre, float radius, ImU32 col, int num_segments, float thickness) -{ - if ((col & IM_COL32_A_MASK) == 0) - return; - - const float a_max = IM_PI*2.0f * ((float)num_segments - 1.0f) / (float)num_segments; - PathArcTo(centre, radius-0.5f, 0.0f, a_max, num_segments); - PathStroke(col, true, thickness); -} - -void ImDrawList::AddCircleFilled(const ImVec2& centre, float radius, ImU32 col, int num_segments) -{ - if ((col & IM_COL32_A_MASK) == 0) - return; - - const float a_max = IM_PI*2.0f * ((float)num_segments - 1.0f) / (float)num_segments; - PathArcTo(centre, radius, 0.0f, a_max, num_segments); - PathFillConvex(col); -} - -void ImDrawList::AddBezierCurve(const ImVec2& pos0, const ImVec2& cp0, const ImVec2& cp1, const ImVec2& pos1, ImU32 col, float thickness, int num_segments) -{ - if ((col & IM_COL32_A_MASK) == 0) - return; - - PathLineTo(pos0); - PathBezierCurveTo(cp0, cp1, pos1, num_segments); - PathStroke(col, false, thickness); -} - -void ImDrawList::AddText(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end, float wrap_width, const ImVec4* cpu_fine_clip_rect) -{ - if ((col & IM_COL32_A_MASK) == 0) - return; - - if (text_end == NULL) - text_end = text_begin + strlen(text_begin); - if (text_begin == text_end) - return; - - // Pull default font/size from the shared ImDrawListSharedData instance - if (font == NULL) - font = _Data->Font; - if (font_size == 0.0f) - font_size = _Data->FontSize; - - IM_ASSERT(font->ContainerAtlas->TexID == _TextureIdStack.back()); // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font. - - ImVec4 clip_rect = _ClipRectStack.back(); - if (cpu_fine_clip_rect) - { - clip_rect.x = ImMax(clip_rect.x, cpu_fine_clip_rect->x); - clip_rect.y = ImMax(clip_rect.y, cpu_fine_clip_rect->y); - clip_rect.z = ImMin(clip_rect.z, cpu_fine_clip_rect->z); - clip_rect.w = ImMin(clip_rect.w, cpu_fine_clip_rect->w); - } - font->RenderText(this, font_size, pos, col, clip_rect, text_begin, text_end, wrap_width, cpu_fine_clip_rect != NULL); -} - -void ImDrawList::AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end) -{ - AddText(NULL, 0.0f, pos, col, text_begin, text_end); -} - -void ImDrawList::AddImage(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, ImU32 col) -{ - if ((col & IM_COL32_A_MASK) == 0) - return; - - const bool push_texture_id = _TextureIdStack.empty() || user_texture_id != _TextureIdStack.back(); - if (push_texture_id) - PushTextureID(user_texture_id); - - PrimReserve(6, 4); - PrimRectUV(a, b, uv_a, uv_b, col); - - if (push_texture_id) - PopTextureID(); -} - -void ImDrawList::AddImageQuad(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, const ImVec2& uv_a, const ImVec2& uv_b, const ImVec2& uv_c, const ImVec2& uv_d, ImU32 col) -{ - if ((col & IM_COL32_A_MASK) == 0) - return; - - const bool push_texture_id = _TextureIdStack.empty() || user_texture_id != _TextureIdStack.back(); - if (push_texture_id) - PushTextureID(user_texture_id); - - PrimReserve(6, 4); - PrimQuadUV(a, b, c, d, uv_a, uv_b, uv_c, uv_d, col); - - if (push_texture_id) - PopTextureID(); -} - -void ImDrawList::AddImageRounded(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, ImU32 col, float rounding, int rounding_corners) -{ - if ((col & IM_COL32_A_MASK) == 0) - return; - - if (rounding <= 0.0f || (rounding_corners & ImDrawCornerFlags_All) == 0) - { - AddImage(user_texture_id, a, b, uv_a, uv_b, col); - return; - } - - const bool push_texture_id = _TextureIdStack.empty() || user_texture_id != _TextureIdStack.back(); - if (push_texture_id) - PushTextureID(user_texture_id); - - int vert_start_idx = VtxBuffer.Size; - PathRect(a, b, rounding, rounding_corners); - PathFillConvex(col); - int vert_end_idx = VtxBuffer.Size; - ImGui::ShadeVertsLinearUV(VtxBuffer.Data + vert_start_idx, VtxBuffer.Data + vert_end_idx, a, b, uv_a, uv_b, true); - - if (push_texture_id) - PopTextureID(); -} - -//----------------------------------------------------------------------------- -// ImDrawData -//----------------------------------------------------------------------------- - -// For backward compatibility: convert all buffers from indexed to de-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering! -void ImDrawData::DeIndexAllBuffers() -{ - ImVector new_vtx_buffer; - TotalVtxCount = TotalIdxCount = 0; - for (int i = 0; i < CmdListsCount; i++) - { - ImDrawList* cmd_list = CmdLists[i]; - if (cmd_list->IdxBuffer.empty()) - continue; - new_vtx_buffer.resize(cmd_list->IdxBuffer.Size); - for (int j = 0; j < cmd_list->IdxBuffer.Size; j++) - new_vtx_buffer[j] = cmd_list->VtxBuffer[cmd_list->IdxBuffer[j]]; - cmd_list->VtxBuffer.swap(new_vtx_buffer); - cmd_list->IdxBuffer.resize(0); - TotalVtxCount += cmd_list->VtxBuffer.Size; - } -} - -// Helper to scale the ClipRect field of each ImDrawCmd. Use if your final output buffer is at a different scale than ImGui expects, or if there is a difference between your window resolution and framebuffer resolution. -void ImDrawData::ScaleClipRects(const ImVec2& scale) -{ - for (int i = 0; i < CmdListsCount; i++) - { - ImDrawList* cmd_list = CmdLists[i]; - for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) - { - ImDrawCmd* cmd = &cmd_list->CmdBuffer[cmd_i]; - cmd->ClipRect = ImVec4(cmd->ClipRect.x * scale.x, cmd->ClipRect.y * scale.y, cmd->ClipRect.z * scale.x, cmd->ClipRect.w * scale.y); - } - } -} - -//----------------------------------------------------------------------------- -// Shade functions -//----------------------------------------------------------------------------- - -// Generic linear color gradient, write to RGB fields, leave A untouched. -void ImGui::ShadeVertsLinearColorGradientKeepAlpha(ImDrawVert* vert_start, ImDrawVert* vert_end, ImVec2 gradient_p0, ImVec2 gradient_p1, ImU32 col0, ImU32 col1) -{ - ImVec2 gradient_extent = gradient_p1 - gradient_p0; - float gradient_inv_length2 = 1.0f / ImLengthSqr(gradient_extent); - for (ImDrawVert* vert = vert_start; vert < vert_end; vert++) - { - float d = ImDot(vert->pos - gradient_p0, gradient_extent); - float t = ImClamp(d * gradient_inv_length2, 0.0f, 1.0f); - int r = ImLerp((int)(col0 >> IM_COL32_R_SHIFT) & 0xFF, (int)(col1 >> IM_COL32_R_SHIFT) & 0xFF, t); - int g = ImLerp((int)(col0 >> IM_COL32_G_SHIFT) & 0xFF, (int)(col1 >> IM_COL32_G_SHIFT) & 0xFF, t); - int b = ImLerp((int)(col0 >> IM_COL32_B_SHIFT) & 0xFF, (int)(col1 >> IM_COL32_B_SHIFT) & 0xFF, t); - vert->col = (r << IM_COL32_R_SHIFT) | (g << IM_COL32_G_SHIFT) | (b << IM_COL32_B_SHIFT) | (vert->col & IM_COL32_A_MASK); - } -} - -// Scan and shade backward from the end of given vertices. Assume vertices are text only (= vert_start..vert_end going left to right) so we can break as soon as we are out the gradient bounds. -void ImGui::ShadeVertsLinearAlphaGradientForLeftToRightText(ImDrawVert* vert_start, ImDrawVert* vert_end, float gradient_p0_x, float gradient_p1_x) -{ - float gradient_extent_x = gradient_p1_x - gradient_p0_x; - float gradient_inv_length2 = 1.0f / (gradient_extent_x * gradient_extent_x); - int full_alpha_count = 0; - for (ImDrawVert* vert = vert_end - 1; vert >= vert_start; vert--) - { - float d = (vert->pos.x - gradient_p0_x) * (gradient_extent_x); - float alpha_mul = 1.0f - ImClamp(d * gradient_inv_length2, 0.0f, 1.0f); - if (alpha_mul >= 1.0f && ++full_alpha_count > 2) - return; // Early out - int a = (int)(((vert->col >> IM_COL32_A_SHIFT) & 0xFF) * alpha_mul); - vert->col = (vert->col & ~IM_COL32_A_MASK) | (a << IM_COL32_A_SHIFT); - } -} - -// Distribute UV over (a, b) rectangle -void ImGui::ShadeVertsLinearUV(ImDrawVert* vert_start, ImDrawVert* vert_end, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, bool clamp) -{ - const ImVec2 size = b - a; - const ImVec2 uv_size = uv_b - uv_a; - const ImVec2 scale = ImVec2( - size.x != 0.0f ? (uv_size.x / size.x) : 0.0f, - size.y != 0.0f ? (uv_size.y / size.y) : 0.0f); - - if (clamp) - { - const ImVec2 min = ImMin(uv_a, uv_b); - const ImVec2 max = ImMax(uv_a, uv_b); - - for (ImDrawVert* vertex = vert_start; vertex < vert_end; ++vertex) - vertex->uv = ImClamp(uv_a + ImMul(ImVec2(vertex->pos.x, vertex->pos.y) - a, scale), min, max); - } - else - { - for (ImDrawVert* vertex = vert_start; vertex < vert_end; ++vertex) - vertex->uv = uv_a + ImMul(ImVec2(vertex->pos.x, vertex->pos.y) - a, scale); - } -} - -//----------------------------------------------------------------------------- -// ImFontConfig -//----------------------------------------------------------------------------- - -ImFontConfig::ImFontConfig() -{ - FontData = NULL; - FontDataSize = 0; - FontDataOwnedByAtlas = true; - FontNo = 0; - SizePixels = 0.0f; - OversampleH = 3; - OversampleV = 1; - PixelSnapH = false; - GlyphExtraSpacing = ImVec2(0.0f, 0.0f); - GlyphOffset = ImVec2(0.0f, 0.0f); - GlyphRanges = NULL; - GlyphMinAdvanceX = 0.0f; - GlyphMaxAdvanceX = FLT_MAX; - MergeMode = false; - RasterizerFlags = 0x00; - RasterizerMultiply = 1.0f; - memset(Name, 0, sizeof(Name)); - DstFont = NULL; -} - -//----------------------------------------------------------------------------- -// ImFontAtlas -//----------------------------------------------------------------------------- - -// A work of art lies ahead! (. = white layer, X = black layer, others are blank) -// The white texels on the top left are the ones we'll use everywhere in ImGui to render filled shapes. -const int FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF = 90; -const int FONT_ATLAS_DEFAULT_TEX_DATA_H = 27; -const unsigned int FONT_ATLAS_DEFAULT_TEX_DATA_ID = 0x80000000; -static const char FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF * FONT_ATLAS_DEFAULT_TEX_DATA_H + 1] = -{ - "..- -XXXXXXX- X - X -XXXXXXX - XXXXXXX" - "..- -X.....X- X.X - X.X -X.....X - X.....X" - "--- -XXX.XXX- X...X - X...X -X....X - X....X" - "X - X.X - X.....X - X.....X -X...X - X...X" - "XX - X.X -X.......X- X.......X -X..X.X - X.X..X" - "X.X - X.X -XXXX.XXXX- XXXX.XXXX -X.X X.X - X.X X.X" - "X..X - X.X - X.X - X.X -XX X.X - X.X XX" - "X...X - X.X - X.X - XX X.X XX - X.X - X.X " - "X....X - X.X - X.X - X.X X.X X.X - X.X - X.X " - "X.....X - X.X - X.X - X..X X.X X..X - X.X - X.X " - "X......X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X XX-XX X.X " - "X.......X - X.X - X.X -X.....................X- X.X X.X-X.X X.X " - "X........X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X..X-X..X.X " - "X.........X -XXX.XXX- X.X - X..X X.X X..X - X...X-X...X " - "X..........X-X.....X- X.X - X.X X.X X.X - X....X-X....X " - "X......XXXXX-XXXXXXX- X.X - XX X.X XX - X.....X-X.....X " - "X...X..X --------- X.X - X.X - XXXXXXX-XXXXXXX " - "X..X X..X - -XXXX.XXXX- XXXX.XXXX ------------------------------------" - "X.X X..X - -X.......X- X.......X - XX XX - " - "XX X..X - - X.....X - X.....X - X.X X.X - " - " X..X - X...X - X...X - X..X X..X - " - " XX - X.X - X.X - X...XXXXXXXXXXXXX...X - " - "------------ - X - X -X.....................X- " - " ----------------------------------- X...XXXXXXXXXXXXX...X - " - " - X..X X..X - " - " - X.X X.X - " - " - XX XX - " -}; - -static const ImVec2 FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[ImGuiMouseCursor_COUNT][3] = -{ - // Pos ........ Size ......... Offset ...... - { ImVec2(0,3), ImVec2(12,19), ImVec2( 0, 0) }, // ImGuiMouseCursor_Arrow - { ImVec2(13,0), ImVec2(7,16), ImVec2( 4, 8) }, // ImGuiMouseCursor_TextInput - { ImVec2(31,0), ImVec2(23,23), ImVec2(11,11) }, // ImGuiMouseCursor_ResizeAll - { ImVec2(21,0), ImVec2( 9,23), ImVec2( 5,11) }, // ImGuiMouseCursor_ResizeNS - { ImVec2(55,18),ImVec2(23, 9), ImVec2(11, 5) }, // ImGuiMouseCursor_ResizeEW - { ImVec2(73,0), ImVec2(17,17), ImVec2( 9, 9) }, // ImGuiMouseCursor_ResizeNESW - { ImVec2(55,0), ImVec2(17,17), ImVec2( 9, 9) }, // ImGuiMouseCursor_ResizeNWSE -}; - -ImFontAtlas::ImFontAtlas() -{ - Flags = 0x00; - TexID = NULL; - TexDesiredWidth = 0; - TexGlyphPadding = 1; - - TexPixelsAlpha8 = NULL; - TexPixelsRGBA32 = NULL; - TexWidth = TexHeight = 0; - TexUvScale = ImVec2(0.0f, 0.0f); - TexUvWhitePixel = ImVec2(0.0f, 0.0f); - for (int n = 0; n < IM_ARRAYSIZE(CustomRectIds); n++) - CustomRectIds[n] = -1; -} - -ImFontAtlas::~ImFontAtlas() -{ - Clear(); -} - -void ImFontAtlas::ClearInputData() -{ - for (int i = 0; i < ConfigData.Size; i++) - if (ConfigData[i].FontData && ConfigData[i].FontDataOwnedByAtlas) - { - ImGui::MemFree(ConfigData[i].FontData); - ConfigData[i].FontData = NULL; - } - - // When clearing this we lose access to the font name and other information used to build the font. - for (int i = 0; i < Fonts.Size; i++) - if (Fonts[i]->ConfigData >= ConfigData.Data && Fonts[i]->ConfigData < ConfigData.Data + ConfigData.Size) - { - Fonts[i]->ConfigData = NULL; - Fonts[i]->ConfigDataCount = 0; - } - ConfigData.clear(); - CustomRects.clear(); - for (int n = 0; n < IM_ARRAYSIZE(CustomRectIds); n++) - CustomRectIds[n] = -1; -} - -void ImFontAtlas::ClearTexData() -{ - if (TexPixelsAlpha8) - ImGui::MemFree(TexPixelsAlpha8); - if (TexPixelsRGBA32) - ImGui::MemFree(TexPixelsRGBA32); - TexPixelsAlpha8 = NULL; - TexPixelsRGBA32 = NULL; -} - -void ImFontAtlas::ClearFonts() -{ - for (int i = 0; i < Fonts.Size; i++) - IM_DELETE(Fonts[i]); - Fonts.clear(); -} - -void ImFontAtlas::Clear() -{ - ClearInputData(); - ClearTexData(); - ClearFonts(); -} - -void ImFontAtlas::GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel) -{ - // Build atlas on demand - if (TexPixelsAlpha8 == NULL) - { - if (ConfigData.empty()) - AddFontDefault(); - Build(); - } - - *out_pixels = TexPixelsAlpha8; - if (out_width) *out_width = TexWidth; - if (out_height) *out_height = TexHeight; - if (out_bytes_per_pixel) *out_bytes_per_pixel = 1; -} - -void ImFontAtlas::GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel) -{ - // Convert to RGBA32 format on demand - // Although it is likely to be the most commonly used format, our font rendering is 1 channel / 8 bpp - if (!TexPixelsRGBA32) - { - unsigned char* pixels = NULL; - GetTexDataAsAlpha8(&pixels, NULL, NULL); - if (pixels) - { - TexPixelsRGBA32 = (unsigned int*)ImGui::MemAlloc((size_t)(TexWidth * TexHeight * 4)); - const unsigned char* src = pixels; - unsigned int* dst = TexPixelsRGBA32; - for (int n = TexWidth * TexHeight; n > 0; n--) - *dst++ = IM_COL32(255, 255, 255, (unsigned int)(*src++)); - } - } - - *out_pixels = (unsigned char*)TexPixelsRGBA32; - if (out_width) *out_width = TexWidth; - if (out_height) *out_height = TexHeight; - if (out_bytes_per_pixel) *out_bytes_per_pixel = 4; -} - -ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg) -{ - IM_ASSERT(font_cfg->FontData != NULL && font_cfg->FontDataSize > 0); - IM_ASSERT(font_cfg->SizePixels > 0.0f); - - // Create new font - if (!font_cfg->MergeMode) - Fonts.push_back(IM_NEW(ImFont)); - else - IM_ASSERT(!Fonts.empty()); // When using MergeMode make sure that a font has already been added before. You can use ImGui::GetIO().Fonts->AddFontDefault() to add the default imgui font. - - ConfigData.push_back(*font_cfg); - ImFontConfig& new_font_cfg = ConfigData.back(); - if (!new_font_cfg.DstFont) - new_font_cfg.DstFont = Fonts.back(); - if (!new_font_cfg.FontDataOwnedByAtlas) - { - new_font_cfg.FontData = ImGui::MemAlloc(new_font_cfg.FontDataSize); - new_font_cfg.FontDataOwnedByAtlas = true; - memcpy(new_font_cfg.FontData, font_cfg->FontData, (size_t)new_font_cfg.FontDataSize); - } - - // Invalidate texture - ClearTexData(); - return new_font_cfg.DstFont; -} - -// Default font TTF is compressed with stb_compress then base85 encoded (see misc/fonts/binary_to_compressed_c.cpp for encoder) -static unsigned int stb_decompress_length(const unsigned char *input); -static unsigned int stb_decompress(unsigned char *output, const unsigned char *input, unsigned int length); -static const char* GetDefaultCompressedFontDataTTFBase85(); -static unsigned int Decode85Byte(char c) { return c >= '\\' ? c-36 : c-35; } -static void Decode85(const unsigned char* src, unsigned char* dst) -{ - while (*src) - { - unsigned int tmp = Decode85Byte(src[0]) + 85*(Decode85Byte(src[1]) + 85*(Decode85Byte(src[2]) + 85*(Decode85Byte(src[3]) + 85*Decode85Byte(src[4])))); - dst[0] = ((tmp >> 0) & 0xFF); dst[1] = ((tmp >> 8) & 0xFF); dst[2] = ((tmp >> 16) & 0xFF); dst[3] = ((tmp >> 24) & 0xFF); // We can't assume little-endianness. - src += 5; - dst += 4; - } -} - -// Load embedded ProggyClean.ttf at size 13, disable oversampling -ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template) -{ - ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); - if (!font_cfg_template) - { - font_cfg.OversampleH = font_cfg.OversampleV = 1; - font_cfg.PixelSnapH = true; - } - if (font_cfg.Name[0] == '\0') strcpy(font_cfg.Name, "ProggyClean.ttf, 13px"); - if (font_cfg.SizePixels <= 0.0f) font_cfg.SizePixels = 13.0f; - - const char* ttf_compressed_base85 = GetDefaultCompressedFontDataTTFBase85(); - ImFont* font = AddFontFromMemoryCompressedBase85TTF(ttf_compressed_base85, font_cfg.SizePixels, &font_cfg, GetGlyphRangesDefault()); - font->DisplayOffset.y = 1.0f; - return font; -} - -ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges) -{ - size_t data_size = 0; - void* data = ImFileLoadToMemory(filename, "rb", &data_size, 0); - if (!data) - { - IM_ASSERT(0); // Could not load file. - return NULL; - } - ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); - if (font_cfg.Name[0] == '\0') - { - // Store a short copy of filename into into the font name for convenience - const char* p; - for (p = filename + strlen(filename); p > filename && p[-1] != '/' && p[-1] != '\\'; p--) {} - ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "%s, %.0fpx", p, size_pixels); - } - return AddFontFromMemoryTTF(data, (int)data_size, size_pixels, &font_cfg, glyph_ranges); -} - -// NB: Transfer ownership of 'ttf_data' to ImFontAtlas, unless font_cfg_template->FontDataOwnedByAtlas == false. Owned TTF buffer will be deleted after Build(). -ImFont* ImFontAtlas::AddFontFromMemoryTTF(void* ttf_data, int ttf_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges) -{ - ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); - IM_ASSERT(font_cfg.FontData == NULL); - font_cfg.FontData = ttf_data; - font_cfg.FontDataSize = ttf_size; - font_cfg.SizePixels = size_pixels; - if (glyph_ranges) - font_cfg.GlyphRanges = glyph_ranges; - return AddFont(&font_cfg); -} - -ImFont* ImFontAtlas::AddFontFromMemoryCompressedTTF(const void* compressed_ttf_data, int compressed_ttf_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges) -{ - const unsigned int buf_decompressed_size = stb_decompress_length((const unsigned char*)compressed_ttf_data); - unsigned char* buf_decompressed_data = (unsigned char *)ImGui::MemAlloc(buf_decompressed_size); - stb_decompress(buf_decompressed_data, (const unsigned char*)compressed_ttf_data, (unsigned int)compressed_ttf_size); - - ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig(); - IM_ASSERT(font_cfg.FontData == NULL); - font_cfg.FontDataOwnedByAtlas = true; - return AddFontFromMemoryTTF(buf_decompressed_data, (int)buf_decompressed_size, size_pixels, &font_cfg, glyph_ranges); -} - -ImFont* ImFontAtlas::AddFontFromMemoryCompressedBase85TTF(const char* compressed_ttf_data_base85, float size_pixels, const ImFontConfig* font_cfg, const ImWchar* glyph_ranges) -{ - int compressed_ttf_size = (((int)strlen(compressed_ttf_data_base85) + 4) / 5) * 4; - void* compressed_ttf = ImGui::MemAlloc((size_t)compressed_ttf_size); - Decode85((const unsigned char*)compressed_ttf_data_base85, (unsigned char*)compressed_ttf); - ImFont* font = AddFontFromMemoryCompressedTTF(compressed_ttf, compressed_ttf_size, size_pixels, font_cfg, glyph_ranges); - ImGui::MemFree(compressed_ttf); - return font; -} - -int ImFontAtlas::AddCustomRectRegular(unsigned int id, int width, int height) -{ - IM_ASSERT(id >= 0x10000); - IM_ASSERT(width > 0 && width <= 0xFFFF); - IM_ASSERT(height > 0 && height <= 0xFFFF); - CustomRect r; - r.ID = id; - r.Width = (unsigned short)width; - r.Height = (unsigned short)height; - CustomRects.push_back(r); - return CustomRects.Size - 1; // Return index -} - -int ImFontAtlas::AddCustomRectFontGlyph(ImFont* font, ImWchar id, int width, int height, float advance_x, const ImVec2& offset) -{ - IM_ASSERT(font != NULL); - IM_ASSERT(width > 0 && width <= 0xFFFF); - IM_ASSERT(height > 0 && height <= 0xFFFF); - CustomRect r; - r.ID = id; - r.Width = (unsigned short)width; - r.Height = (unsigned short)height; - r.GlyphAdvanceX = advance_x; - r.GlyphOffset = offset; - r.Font = font; - CustomRects.push_back(r); - return CustomRects.Size - 1; // Return index -} - -void ImFontAtlas::CalcCustomRectUV(const CustomRect* rect, ImVec2* out_uv_min, ImVec2* out_uv_max) -{ - IM_ASSERT(TexWidth > 0 && TexHeight > 0); // Font atlas needs to be built before we can calculate UV coordinates - IM_ASSERT(rect->IsPacked()); // Make sure the rectangle has been packed - *out_uv_min = ImVec2((float)rect->X * TexUvScale.x, (float)rect->Y * TexUvScale.y); - *out_uv_max = ImVec2((float)(rect->X + rect->Width) * TexUvScale.x, (float)(rect->Y + rect->Height) * TexUvScale.y); -} - -bool ImFontAtlas::GetMouseCursorTexData(ImGuiMouseCursor cursor_type, ImVec2* out_offset, ImVec2* out_size, ImVec2 out_uv_border[2], ImVec2 out_uv_fill[2]) -{ - if (cursor_type <= ImGuiMouseCursor_None || cursor_type >= ImGuiMouseCursor_COUNT) - return false; - if (Flags & ImFontAtlasFlags_NoMouseCursors) - return false; - - IM_ASSERT(CustomRectIds[0] != -1); - ImFontAtlas::CustomRect& r = CustomRects[CustomRectIds[0]]; - IM_ASSERT(r.ID == FONT_ATLAS_DEFAULT_TEX_DATA_ID); - ImVec2 pos = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[cursor_type][0] + ImVec2((float)r.X, (float)r.Y); - ImVec2 size = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[cursor_type][1]; - *out_size = size; - *out_offset = FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[cursor_type][2]; - out_uv_border[0] = (pos) * TexUvScale; - out_uv_border[1] = (pos + size) * TexUvScale; - pos.x += FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF + 1; - out_uv_fill[0] = (pos) * TexUvScale; - out_uv_fill[1] = (pos + size) * TexUvScale; - return true; -} - -bool ImFontAtlas::Build() -{ - return ImFontAtlasBuildWithStbTruetype(this); -} - -void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_brighten_factor) -{ - for (unsigned int i = 0; i < 256; i++) - { - unsigned int value = (unsigned int)(i * in_brighten_factor); - out_table[i] = value > 255 ? 255 : (value & 0xFF); - } -} - -void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride) -{ - unsigned char* data = pixels + x + y * stride; - for (int j = h; j > 0; j--, data += stride) - for (int i = 0; i < w; i++) - data[i] = table[data[i]]; -} - -bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas) -{ - IM_ASSERT(atlas->ConfigData.Size > 0); - - ImFontAtlasBuildRegisterDefaultCustomRects(atlas); - - atlas->TexID = NULL; - atlas->TexWidth = atlas->TexHeight = 0; - atlas->TexUvScale = ImVec2(0.0f, 0.0f); - atlas->TexUvWhitePixel = ImVec2(0.0f, 0.0f); - atlas->ClearTexData(); - - // Count glyphs/ranges - int total_glyphs_count = 0; - int total_ranges_count = 0; - for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++) - { - ImFontConfig& cfg = atlas->ConfigData[input_i]; - if (!cfg.GlyphRanges) - cfg.GlyphRanges = atlas->GetGlyphRangesDefault(); - for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2, total_ranges_count++) - total_glyphs_count += (in_range[1] - in_range[0]) + 1; - } - - // We need a width for the skyline algorithm. Using a dumb heuristic here to decide of width. User can override TexDesiredWidth and TexGlyphPadding if they wish. - // Width doesn't really matter much, but some API/GPU have texture size limitations and increasing width can decrease height. - atlas->TexWidth = (atlas->TexDesiredWidth > 0) ? atlas->TexDesiredWidth : (total_glyphs_count > 4000) ? 4096 : (total_glyphs_count > 2000) ? 2048 : (total_glyphs_count > 1000) ? 1024 : 512; - atlas->TexHeight = 0; - - // Start packing - const int max_tex_height = 1024*32; - - // [Bruno Levy]: replaced "={}" with memset() (={} generates a warning on MSVC). - stbtt_pack_context spc; - memset(&spc, 0, sizeof(spc)); - - if (!stbtt_PackBegin(&spc, NULL, atlas->TexWidth, max_tex_height, 0, atlas->TexGlyphPadding, NULL)) - return false; - stbtt_PackSetOversampling(&spc, 1, 1); - - // Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values). - ImFontAtlasBuildPackCustomRects(atlas, spc.pack_info); - - // Initialize font information (so we can error without any cleanup) - struct ImFontTempBuildData - { - stbtt_fontinfo FontInfo; - stbrp_rect* Rects; - int RectsCount; - stbtt_pack_range* Ranges; - int RangesCount; - }; - ImFontTempBuildData* tmp_array = (ImFontTempBuildData*)ImGui::MemAlloc((size_t)atlas->ConfigData.Size * sizeof(ImFontTempBuildData)); - for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++) - { - ImFontConfig& cfg = atlas->ConfigData[input_i]; - ImFontTempBuildData& tmp = tmp_array[input_i]; - IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == atlas)); - - const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)cfg.FontData, cfg.FontNo); - IM_ASSERT(font_offset >= 0 && "FontData is incorrect, or FontNo cannot be found."); - if (!stbtt_InitFont(&tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset)) - { - atlas->TexWidth = atlas->TexHeight = 0; // Reset output on failure - ImGui::MemFree(tmp_array); - return false; - } - } - - // Allocate packing character data and flag packed characters buffer as non-packed (x0=y0=x1=y1=0) - int buf_packedchars_n = 0, buf_rects_n = 0, buf_ranges_n = 0; - stbtt_packedchar* buf_packedchars = (stbtt_packedchar*)ImGui::MemAlloc(total_glyphs_count * sizeof(stbtt_packedchar)); - stbrp_rect* buf_rects = (stbrp_rect*)ImGui::MemAlloc(total_glyphs_count * sizeof(stbrp_rect)); - stbtt_pack_range* buf_ranges = (stbtt_pack_range*)ImGui::MemAlloc(total_ranges_count * sizeof(stbtt_pack_range)); - memset(buf_packedchars, 0, total_glyphs_count * sizeof(stbtt_packedchar)); - memset(buf_rects, 0, total_glyphs_count * sizeof(stbrp_rect)); // Unnecessary but let's clear this for the sake of sanity. - memset(buf_ranges, 0, total_ranges_count * sizeof(stbtt_pack_range)); - - // First font pass: pack all glyphs (no rendering at this point, we are working with rectangles in an infinitely tall texture at this point) - for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++) - { - ImFontConfig& cfg = atlas->ConfigData[input_i]; - ImFontTempBuildData& tmp = tmp_array[input_i]; - - // Setup ranges - int font_glyphs_count = 0; - int font_ranges_count = 0; - for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2, font_ranges_count++) - font_glyphs_count += (in_range[1] - in_range[0]) + 1; - tmp.Ranges = buf_ranges + buf_ranges_n; - tmp.RangesCount = font_ranges_count; - buf_ranges_n += font_ranges_count; - for (int i = 0; i < font_ranges_count; i++) - { - const ImWchar* in_range = &cfg.GlyphRanges[i * 2]; - stbtt_pack_range& range = tmp.Ranges[i]; - range.font_size = cfg.SizePixels; - range.first_unicode_codepoint_in_range = in_range[0]; - range.num_chars = (in_range[1] - in_range[0]) + 1; - range.chardata_for_range = buf_packedchars + buf_packedchars_n; - buf_packedchars_n += range.num_chars; - } - - // Gather the sizes of all rectangle we need - tmp.Rects = buf_rects + buf_rects_n; - tmp.RectsCount = font_glyphs_count; - buf_rects_n += font_glyphs_count; - stbtt_PackSetOversampling(&spc, cfg.OversampleH, cfg.OversampleV); - int n = stbtt_PackFontRangesGatherRects(&spc, &tmp.FontInfo, tmp.Ranges, tmp.RangesCount, tmp.Rects); - IM_ASSERT(n == font_glyphs_count); - - // Detect missing glyphs and replace them with a zero-sized box instead of relying on the default glyphs - // This allows us merging overlapping icon fonts more easily. - int rect_i = 0; - for (int range_i = 0; range_i < tmp.RangesCount; range_i++) - for (int char_i = 0; char_i < tmp.Ranges[range_i].num_chars; char_i++, rect_i++) - if (stbtt_FindGlyphIndex(&tmp.FontInfo, tmp.Ranges[range_i].first_unicode_codepoint_in_range + char_i) == 0) - tmp.Rects[rect_i].w = tmp.Rects[rect_i].h = 0; - - // Pack - stbrp_pack_rects((stbrp_context*)spc.pack_info, tmp.Rects, n); - - // Extend texture height - // Also mark missing glyphs as non-packed so we don't attempt to render into them - for (int i = 0; i < n; i++) - { - if (tmp.Rects[i].w == 0 && tmp.Rects[i].h == 0) - tmp.Rects[i].was_packed = 0; - if (tmp.Rects[i].was_packed) - atlas->TexHeight = ImMax(atlas->TexHeight, tmp.Rects[i].y + tmp.Rects[i].h); - } - } - IM_ASSERT(buf_rects_n == total_glyphs_count); - IM_ASSERT(buf_packedchars_n == total_glyphs_count); - IM_ASSERT(buf_ranges_n == total_ranges_count); - - // Create texture - atlas->TexHeight = (atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) ? (atlas->TexHeight + 1) : ImUpperPowerOfTwo(atlas->TexHeight); - atlas->TexUvScale = ImVec2(1.0f / atlas->TexWidth, 1.0f / atlas->TexHeight); - atlas->TexPixelsAlpha8 = (unsigned char*)ImGui::MemAlloc(atlas->TexWidth * atlas->TexHeight); - memset(atlas->TexPixelsAlpha8, 0, atlas->TexWidth * atlas->TexHeight); - spc.pixels = atlas->TexPixelsAlpha8; - spc.height = atlas->TexHeight; - - // Second pass: render font characters - for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++) - { - ImFontConfig& cfg = atlas->ConfigData[input_i]; - ImFontTempBuildData& tmp = tmp_array[input_i]; - stbtt_PackSetOversampling(&spc, cfg.OversampleH, cfg.OversampleV); - stbtt_PackFontRangesRenderIntoRects(&spc, &tmp.FontInfo, tmp.Ranges, tmp.RangesCount, tmp.Rects); - if (cfg.RasterizerMultiply != 1.0f) - { - unsigned char multiply_table[256]; - ImFontAtlasBuildMultiplyCalcLookupTable(multiply_table, cfg.RasterizerMultiply); - for (const stbrp_rect* r = tmp.Rects; r != tmp.Rects + tmp.RectsCount; r++) - if (r->was_packed) - ImFontAtlasBuildMultiplyRectAlpha8(multiply_table, spc.pixels, r->x, r->y, r->w, r->h, spc.stride_in_bytes); - } - tmp.Rects = NULL; - } - - // End packing - stbtt_PackEnd(&spc); - ImGui::MemFree(buf_rects); - buf_rects = NULL; - - // Third pass: setup ImFont and glyphs for runtime - for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++) - { - ImFontConfig& cfg = atlas->ConfigData[input_i]; - ImFontTempBuildData& tmp = tmp_array[input_i]; - ImFont* dst_font = cfg.DstFont; // We can have multiple input fonts writing into a same destination font (when using MergeMode=true) - if (cfg.MergeMode) - dst_font->BuildLookupTable(); - - const float font_scale = stbtt_ScaleForPixelHeight(&tmp.FontInfo, cfg.SizePixels); - int unscaled_ascent, unscaled_descent, unscaled_line_gap; - stbtt_GetFontVMetrics(&tmp.FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap); - - const float ascent = ImFloor(unscaled_ascent * font_scale + ((unscaled_ascent > 0.0f) ? +1 : -1)); - const float descent = ImFloor(unscaled_descent * font_scale + ((unscaled_descent > 0.0f) ? +1 : -1)); - ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent); - const float font_off_x = cfg.GlyphOffset.x; - const float font_off_y = cfg.GlyphOffset.y + (float)(int)(dst_font->Ascent + 0.5f); - - for (int i = 0; i < tmp.RangesCount; i++) - { - stbtt_pack_range& range = tmp.Ranges[i]; - for (int char_idx = 0; char_idx < range.num_chars; char_idx += 1) - { - const stbtt_packedchar& pc = range.chardata_for_range[char_idx]; - if (!pc.x0 && !pc.x1 && !pc.y0 && !pc.y1) - continue; - - const int codepoint = range.first_unicode_codepoint_in_range + char_idx; - if (cfg.MergeMode && dst_font->FindGlyphNoFallback((unsigned short)codepoint)) - continue; - - float char_advance_x_org = pc.xadvance; - float char_advance_x_mod = ImClamp(char_advance_x_org, cfg.GlyphMinAdvanceX, cfg.GlyphMaxAdvanceX); - float char_off_x = font_off_x; - if (char_advance_x_org != char_advance_x_mod) - char_off_x += cfg.PixelSnapH ? (float)(int)((char_advance_x_mod - char_advance_x_org) * 0.5f) : (char_advance_x_mod - char_advance_x_org) * 0.5f; - - stbtt_aligned_quad q; - float dummy_x = 0.0f, dummy_y = 0.0f; - stbtt_GetPackedQuad(range.chardata_for_range, atlas->TexWidth, atlas->TexHeight, char_idx, &dummy_x, &dummy_y, &q, 0); - dst_font->AddGlyph((ImWchar)codepoint, q.x0 + char_off_x, q.y0 + font_off_y, q.x1 + char_off_x, q.y1 + font_off_y, q.s0, q.t0, q.s1, q.t1, char_advance_x_mod); - } - } - } - - // Cleanup temporaries - ImGui::MemFree(buf_packedchars); - ImGui::MemFree(buf_ranges); - ImGui::MemFree(tmp_array); - - ImFontAtlasBuildFinish(atlas); - - return true; -} - -void ImFontAtlasBuildRegisterDefaultCustomRects(ImFontAtlas* atlas) -{ - if (atlas->CustomRectIds[0] >= 0) - return; - if (!(atlas->Flags & ImFontAtlasFlags_NoMouseCursors)) - atlas->CustomRectIds[0] = atlas->AddCustomRectRegular(FONT_ATLAS_DEFAULT_TEX_DATA_ID, FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF*2+1, FONT_ATLAS_DEFAULT_TEX_DATA_H); - else - atlas->CustomRectIds[0] = atlas->AddCustomRectRegular(FONT_ATLAS_DEFAULT_TEX_DATA_ID, 2, 2); -} - -void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent) -{ - if (!font_config->MergeMode) - { - font->ClearOutputData(); - font->FontSize = font_config->SizePixels; - font->ConfigData = font_config; - font->ContainerAtlas = atlas; - font->Ascent = ascent; - font->Descent = descent; - } - font->ConfigDataCount++; -} - -void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* pack_context_opaque) -{ - stbrp_context* pack_context = (stbrp_context*)pack_context_opaque; - - ImVector& user_rects = atlas->CustomRects; - IM_ASSERT(user_rects.Size >= 1); // We expect at least the default custom rects to be registered, else something went wrong. - - ImVector pack_rects; - pack_rects.resize(user_rects.Size); - memset(pack_rects.Data, 0, sizeof(stbrp_rect) * user_rects.Size); - for (int i = 0; i < user_rects.Size; i++) - { - pack_rects[i].w = user_rects[i].Width; - pack_rects[i].h = user_rects[i].Height; - } - stbrp_pack_rects(pack_context, &pack_rects[0], pack_rects.Size); - for (int i = 0; i < pack_rects.Size; i++) - if (pack_rects[i].was_packed) - { - user_rects[i].X = pack_rects[i].x; - user_rects[i].Y = pack_rects[i].y; - IM_ASSERT(pack_rects[i].w == user_rects[i].Width && pack_rects[i].h == user_rects[i].Height); - atlas->TexHeight = ImMax(atlas->TexHeight, pack_rects[i].y + pack_rects[i].h); - } -} - -static void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas) -{ - IM_ASSERT(atlas->CustomRectIds[0] >= 0); - IM_ASSERT(atlas->TexPixelsAlpha8 != NULL); - ImFontAtlas::CustomRect& r = atlas->CustomRects[atlas->CustomRectIds[0]]; - IM_ASSERT(r.ID == FONT_ATLAS_DEFAULT_TEX_DATA_ID); - IM_ASSERT(r.IsPacked()); - - const int w = atlas->TexWidth; - if (!(atlas->Flags & ImFontAtlasFlags_NoMouseCursors)) - { - // Render/copy pixels - IM_ASSERT(r.Width == FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF * 2 + 1 && r.Height == FONT_ATLAS_DEFAULT_TEX_DATA_H); - for (int y = 0, n = 0; y < FONT_ATLAS_DEFAULT_TEX_DATA_H; y++) - for (int x = 0; x < FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF; x++, n++) - { - const int offset0 = (int)(r.X + x) + (int)(r.Y + y) * w; - const int offset1 = offset0 + FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF + 1; - atlas->TexPixelsAlpha8[offset0] = FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[n] == '.' ? 0xFF : 0x00; - atlas->TexPixelsAlpha8[offset1] = FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[n] == 'X' ? 0xFF : 0x00; - } - } - else - { - IM_ASSERT(r.Width == 2 && r.Height == 2); - const int offset = (int)(r.X) + (int)(r.Y) * w; - atlas->TexPixelsAlpha8[offset] = atlas->TexPixelsAlpha8[offset + 1] = atlas->TexPixelsAlpha8[offset + w] = atlas->TexPixelsAlpha8[offset + w + 1] = 0xFF; - } - atlas->TexUvWhitePixel = ImVec2((r.X + 0.5f) * atlas->TexUvScale.x, (r.Y + 0.5f) * atlas->TexUvScale.y); -} - -void ImFontAtlasBuildFinish(ImFontAtlas* atlas) -{ - // Render into our custom data block - ImFontAtlasBuildRenderDefaultTexData(atlas); - - // Register custom rectangle glyphs - for (int i = 0; i < atlas->CustomRects.Size; i++) - { - const ImFontAtlas::CustomRect& r = atlas->CustomRects[i]; - if (r.Font == NULL || r.ID > 0x10000) - continue; - - IM_ASSERT(r.Font->ContainerAtlas == atlas); - ImVec2 uv0, uv1; - atlas->CalcCustomRectUV(&r, &uv0, &uv1); - r.Font->AddGlyph((ImWchar)r.ID, r.GlyphOffset.x, r.GlyphOffset.y, r.GlyphOffset.x + r.Width, r.GlyphOffset.y + r.Height, uv0.x, uv0.y, uv1.x, uv1.y, r.GlyphAdvanceX); - } - - // Build all fonts lookup tables - for (int i = 0; i < atlas->Fonts.Size; i++) - if (atlas->Fonts[i]->DirtyLookupTables) - atlas->Fonts[i]->BuildLookupTable(); -} - -// Retrieve list of range (2 int per range, values are inclusive) -const ImWchar* ImFontAtlas::GetGlyphRangesDefault() -{ - static const ImWchar ranges[] = - { - 0x0020, 0x00FF, // Basic Latin + Latin Supplement - 0, - }; - return &ranges[0]; -} - -const ImWchar* ImFontAtlas::GetGlyphRangesKorean() -{ - static const ImWchar ranges[] = - { - 0x0020, 0x00FF, // Basic Latin + Latin Supplement - 0x3131, 0x3163, // Korean alphabets - 0xAC00, 0xD79D, // Korean characters - 0, - }; - return &ranges[0]; -} - -const ImWchar* ImFontAtlas::GetGlyphRangesChineseFull() -{ - static const ImWchar ranges[] = - { - 0x0020, 0x00FF, // Basic Latin + Latin Supplement - 0x3000, 0x30FF, // Punctuations, Hiragana, Katakana - 0x31F0, 0x31FF, // Katakana Phonetic Extensions - 0xFF00, 0xFFEF, // Half-width characters - 0x4e00, 0x9FAF, // CJK Ideograms - 0, - }; - return &ranges[0]; -} - -static void UnpackAccumulativeOffsetsIntoRanges(int base_codepoint, const short* accumulative_offsets, int accumulative_offsets_count, ImWchar* out_ranges) -{ - for (int n = 0; n < accumulative_offsets_count; n++, out_ranges += 2) - { - out_ranges[0] = out_ranges[1] = (ImWchar)(base_codepoint + accumulative_offsets[n]); - base_codepoint += accumulative_offsets[n]; - } - out_ranges[0] = 0; -} - -const ImWchar* ImFontAtlas::GetGlyphRangesChineseSimplifiedCommon() -{ - // Store 2500 regularly used characters for Simplified Chinese. - // Sourced from https://zh.wiktionary.org/wiki/%E9%99%84%E5%BD%95:%E7%8E%B0%E4%BB%A3%E6%B1%89%E8%AF%AD%E5%B8%B8%E7%94%A8%E5%AD%97%E8%A1%A8 - // This table covers 97.97% of all characters used during the month in July, 1987. - // You can use ImFontAtlas::GlyphRangesBuilder to create your own ranges derived from this, by merging existing ranges or adding new characters. - // (Stored as accumulative offsets from the initial unicode codepoint 0x4E00. This encoding is designed to helps us compact the source code size.) - static const short accumulative_offsets_from_0x4E00[] = - { - 0,1,2,4,1,1,1,1,2,1,3,2,1,2,2,1,1,1,1,1,5,2,1,2,3,3,3,2,2,4,1,1,1,2,1,5,2,3,1,2,1,2,1,1,2,1,1,2,2,1,4,1,1,1,1,5,10,1,2,19,2,1,2,1,2,1,2,1,2, - 1,5,1,6,3,2,1,2,2,1,1,1,4,8,5,1,1,4,1,1,3,1,2,1,5,1,2,1,1,1,10,1,1,5,2,4,6,1,4,2,2,2,12,2,1,1,6,1,1,1,4,1,1,4,6,5,1,4,2,2,4,10,7,1,1,4,2,4, - 2,1,4,3,6,10,12,5,7,2,14,2,9,1,1,6,7,10,4,7,13,1,5,4,8,4,1,1,2,28,5,6,1,1,5,2,5,20,2,2,9,8,11,2,9,17,1,8,6,8,27,4,6,9,20,11,27,6,68,2,2,1,1, - 1,2,1,2,2,7,6,11,3,3,1,1,3,1,2,1,1,1,1,1,3,1,1,8,3,4,1,5,7,2,1,4,4,8,4,2,1,2,1,1,4,5,6,3,6,2,12,3,1,3,9,2,4,3,4,1,5,3,3,1,3,7,1,5,1,1,1,1,2, - 3,4,5,2,3,2,6,1,1,2,1,7,1,7,3,4,5,15,2,2,1,5,3,22,19,2,1,1,1,1,2,5,1,1,1,6,1,1,12,8,2,9,18,22,4,1,1,5,1,16,1,2,7,10,15,1,1,6,2,4,1,2,4,1,6, - 1,1,3,2,4,1,6,4,5,1,2,1,1,2,1,10,3,1,3,2,1,9,3,2,5,7,2,19,4,3,6,1,1,1,1,1,4,3,2,1,1,1,2,5,3,1,1,1,2,2,1,1,2,1,1,2,1,3,1,1,1,3,7,1,4,1,1,2,1, - 1,2,1,2,4,4,3,8,1,1,1,2,1,3,5,1,3,1,3,4,6,2,2,14,4,6,6,11,9,1,15,3,1,28,5,2,5,5,3,1,3,4,5,4,6,14,3,2,3,5,21,2,7,20,10,1,2,19,2,4,28,28,2,3, - 2,1,14,4,1,26,28,42,12,40,3,52,79,5,14,17,3,2,2,11,3,4,6,3,1,8,2,23,4,5,8,10,4,2,7,3,5,1,1,6,3,1,2,2,2,5,28,1,1,7,7,20,5,3,29,3,17,26,1,8,4, - 27,3,6,11,23,5,3,4,6,13,24,16,6,5,10,25,35,7,3,2,3,3,14,3,6,2,6,1,4,2,3,8,2,1,1,3,3,3,4,1,1,13,2,2,4,5,2,1,14,14,1,2,2,1,4,5,2,3,1,14,3,12, - 3,17,2,16,5,1,2,1,8,9,3,19,4,2,2,4,17,25,21,20,28,75,1,10,29,103,4,1,2,1,1,4,2,4,1,2,3,24,2,2,2,1,1,2,1,3,8,1,1,1,2,1,1,3,1,1,1,6,1,5,3,1,1, - 1,3,4,1,1,5,2,1,5,6,13,9,16,1,1,1,1,3,2,3,2,4,5,2,5,2,2,3,7,13,7,2,2,1,1,1,1,2,3,3,2,1,6,4,9,2,1,14,2,14,2,1,18,3,4,14,4,11,41,15,23,15,23, - 176,1,3,4,1,1,1,1,5,3,1,2,3,7,3,1,1,2,1,2,4,4,6,2,4,1,9,7,1,10,5,8,16,29,1,1,2,2,3,1,3,5,2,4,5,4,1,1,2,2,3,3,7,1,6,10,1,17,1,44,4,6,2,1,1,6, - 5,4,2,10,1,6,9,2,8,1,24,1,2,13,7,8,8,2,1,4,1,3,1,3,3,5,2,5,10,9,4,9,12,2,1,6,1,10,1,1,7,7,4,10,8,3,1,13,4,3,1,6,1,3,5,2,1,2,17,16,5,2,16,6, - 1,4,2,1,3,3,6,8,5,11,11,1,3,3,2,4,6,10,9,5,7,4,7,4,7,1,1,4,2,1,3,6,8,7,1,6,11,5,5,3,24,9,4,2,7,13,5,1,8,82,16,61,1,1,1,4,2,2,16,10,3,8,1,1, - 6,4,2,1,3,1,1,1,4,3,8,4,2,2,1,1,1,1,1,6,3,5,1,1,4,6,9,2,1,1,1,2,1,7,2,1,6,1,5,4,4,3,1,8,1,3,3,1,3,2,2,2,2,3,1,6,1,2,1,2,1,3,7,1,8,2,1,2,1,5, - 2,5,3,5,10,1,2,1,1,3,2,5,11,3,9,3,5,1,1,5,9,1,2,1,5,7,9,9,8,1,3,3,3,6,8,2,3,2,1,1,32,6,1,2,15,9,3,7,13,1,3,10,13,2,14,1,13,10,2,1,3,10,4,15, - 2,15,15,10,1,3,9,6,9,32,25,26,47,7,3,2,3,1,6,3,4,3,2,8,5,4,1,9,4,2,2,19,10,6,2,3,8,1,2,2,4,2,1,9,4,4,4,6,4,8,9,2,3,1,1,1,1,3,5,5,1,3,8,4,6, - 2,1,4,12,1,5,3,7,13,2,5,8,1,6,1,2,5,14,6,1,5,2,4,8,15,5,1,23,6,62,2,10,1,1,8,1,2,2,10,4,2,2,9,2,1,1,3,2,3,1,5,3,3,2,1,3,8,1,1,1,11,3,1,1,4, - 3,7,1,14,1,2,3,12,5,2,5,1,6,7,5,7,14,11,1,3,1,8,9,12,2,1,11,8,4,4,2,6,10,9,13,1,1,3,1,5,1,3,2,4,4,1,18,2,3,14,11,4,29,4,2,7,1,3,13,9,2,2,5, - 3,5,20,7,16,8,5,72,34,6,4,22,12,12,28,45,36,9,7,39,9,191,1,1,1,4,11,8,4,9,2,3,22,1,1,1,1,4,17,1,7,7,1,11,31,10,2,4,8,2,3,2,1,4,2,16,4,32,2, - 3,19,13,4,9,1,5,2,14,8,1,1,3,6,19,6,5,1,16,6,2,10,8,5,1,2,3,1,5,5,1,11,6,6,1,3,3,2,6,3,8,1,1,4,10,7,5,7,7,5,8,9,2,1,3,4,1,1,3,1,3,3,2,6,16, - 1,4,6,3,1,10,6,1,3,15,2,9,2,10,25,13,9,16,6,2,2,10,11,4,3,9,1,2,6,6,5,4,30,40,1,10,7,12,14,33,6,3,6,7,3,1,3,1,11,14,4,9,5,12,11,49,18,51,31, - 140,31,2,2,1,5,1,8,1,10,1,4,4,3,24,1,10,1,3,6,6,16,3,4,5,2,1,4,2,57,10,6,22,2,22,3,7,22,6,10,11,36,18,16,33,36,2,5,5,1,1,1,4,10,1,4,13,2,7, - 5,2,9,3,4,1,7,43,3,7,3,9,14,7,9,1,11,1,1,3,7,4,18,13,1,14,1,3,6,10,73,2,2,30,6,1,11,18,19,13,22,3,46,42,37,89,7,3,16,34,2,2,3,9,1,7,1,1,1,2, - 2,4,10,7,3,10,3,9,5,28,9,2,6,13,7,3,1,3,10,2,7,2,11,3,6,21,54,85,2,1,4,2,2,1,39,3,21,2,2,5,1,1,1,4,1,1,3,4,15,1,3,2,4,4,2,3,8,2,20,1,8,7,13, - 4,1,26,6,2,9,34,4,21,52,10,4,4,1,5,12,2,11,1,7,2,30,12,44,2,30,1,1,3,6,16,9,17,39,82,2,2,24,7,1,7,3,16,9,14,44,2,1,2,1,2,3,5,2,4,1,6,7,5,3, - 2,6,1,11,5,11,2,1,18,19,8,1,3,24,29,2,1,3,5,2,2,1,13,6,5,1,46,11,3,5,1,1,5,8,2,10,6,12,6,3,7,11,2,4,16,13,2,5,1,1,2,2,5,2,28,5,2,23,10,8,4, - 4,22,39,95,38,8,14,9,5,1,13,5,4,3,13,12,11,1,9,1,27,37,2,5,4,4,63,211,95,2,2,2,1,3,5,2,1,1,2,2,1,1,1,3,2,4,1,2,1,1,5,2,2,1,1,2,3,1,3,1,1,1, - 3,1,4,2,1,3,6,1,1,3,7,15,5,3,2,5,3,9,11,4,2,22,1,6,3,8,7,1,4,28,4,16,3,3,25,4,4,27,27,1,4,1,2,2,7,1,3,5,2,28,8,2,14,1,8,6,16,25,3,3,3,14,3, - 3,1,1,2,1,4,6,3,8,4,1,1,1,2,3,6,10,6,2,3,18,3,2,5,5,4,3,1,5,2,5,4,23,7,6,12,6,4,17,11,9,5,1,1,10,5,12,1,1,11,26,33,7,3,6,1,17,7,1,5,12,1,11, - 2,4,1,8,14,17,23,1,2,1,7,8,16,11,9,6,5,2,6,4,16,2,8,14,1,11,8,9,1,1,1,9,25,4,11,19,7,2,15,2,12,8,52,7,5,19,2,16,4,36,8,1,16,8,24,26,4,6,2,9, - 5,4,36,3,28,12,25,15,37,27,17,12,59,38,5,32,127,1,2,9,17,14,4,1,2,1,1,8,11,50,4,14,2,19,16,4,17,5,4,5,26,12,45,2,23,45,104,30,12,8,3,10,2,2, - 3,3,1,4,20,7,2,9,6,15,2,20,1,3,16,4,11,15,6,134,2,5,59,1,2,2,2,1,9,17,3,26,137,10,211,59,1,2,4,1,4,1,1,1,2,6,2,3,1,1,2,3,2,3,1,3,4,4,2,3,3, - 1,4,3,1,7,2,2,3,1,2,1,3,3,3,2,2,3,2,1,3,14,6,1,3,2,9,6,15,27,9,34,145,1,1,2,1,1,1,1,2,1,1,1,1,2,2,2,3,1,2,1,1,1,2,3,5,8,3,5,2,4,1,3,2,2,2,12, - 4,1,1,1,10,4,5,1,20,4,16,1,15,9,5,12,2,9,2,5,4,2,26,19,7,1,26,4,30,12,15,42,1,6,8,172,1,1,4,2,1,1,11,2,2,4,2,1,2,1,10,8,1,2,1,4,5,1,2,5,1,8, - 4,1,3,4,2,1,6,2,1,3,4,1,2,1,1,1,1,12,5,7,2,4,3,1,1,1,3,3,6,1,2,2,3,3,3,2,1,2,12,14,11,6,6,4,12,2,8,1,7,10,1,35,7,4,13,15,4,3,23,21,28,52,5, - 26,5,6,1,7,10,2,7,53,3,2,1,1,1,2,163,532,1,10,11,1,3,3,4,8,2,8,6,2,2,23,22,4,2,2,4,2,1,3,1,3,3,5,9,8,2,1,2,8,1,10,2,12,21,20,15,105,2,3,1,1, - 3,2,3,1,1,2,5,1,4,15,11,19,1,1,1,1,5,4,5,1,1,2,5,3,5,12,1,2,5,1,11,1,1,15,9,1,4,5,3,26,8,2,1,3,1,1,15,19,2,12,1,2,5,2,7,2,19,2,20,6,26,7,5, - 2,2,7,34,21,13,70,2,128,1,1,2,1,1,2,1,1,3,2,2,2,15,1,4,1,3,4,42,10,6,1,49,85,8,1,2,1,1,4,4,2,3,6,1,5,7,4,3,211,4,1,2,1,2,5,1,2,4,2,2,6,5,6, - 10,3,4,48,100,6,2,16,296,5,27,387,2,2,3,7,16,8,5,38,15,39,21,9,10,3,7,59,13,27,21,47,5,21,6 - }; - static ImWchar base_ranges[] = // not zero-terminated - { - 0x0020, 0x00FF, // Basic Latin + Latin Supplement - 0x3000, 0x30FF, // Punctuations, Hiragana, Katakana - 0x31F0, 0x31FF, // Katakana Phonetic Extensions - 0xFF00, 0xFFEF, // Half-width characters - }; - static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) + IM_ARRAYSIZE(accumulative_offsets_from_0x4E00) * 2 + 1] = { 0 }; - if (!full_ranges[0]) - { - memcpy(full_ranges, base_ranges, sizeof(base_ranges)); - UnpackAccumulativeOffsetsIntoRanges(0x4E00, accumulative_offsets_from_0x4E00, IM_ARRAYSIZE(accumulative_offsets_from_0x4E00), full_ranges + IM_ARRAYSIZE(base_ranges)); - } - return &full_ranges[0]; -} - -const ImWchar* ImFontAtlas::GetGlyphRangesJapanese() -{ - // 1946 common ideograms code points for Japanese - // Sourced from http://theinstructionlimit.com/common-kanji-character-ranges-for-xna-spritefont-rendering - // FIXME: Source a list of the revised 2136 Joyo Kanji list from 2010 and rebuild this. - // You can use ImFontAtlas::GlyphRangesBuilder to create your own ranges derived from this, by merging existing ranges or adding new characters. - // (Stored as accumulative offsets from the initial unicode codepoint 0x4E00. This encoding is designed to helps us compact the source code size.) - static const short accumulative_offsets_from_0x4E00[] = - { - 0,1,2,4,1,1,1,1,2,1,6,2,2,1,8,5,7,11,1,2,10,10,8,2,4,20,2,11,8,2,1,2,1,6,2,1,7,5,3,7,1,1,13,7,9,1,4,6,1,2,1,10,1,1,9,2,2,4,5,6,14,1,1,9,3,18, - 5,4,2,2,10,7,1,1,1,3,2,4,3,23,2,10,12,2,14,2,4,13,1,6,10,3,1,7,13,6,4,13,5,2,3,17,2,2,5,7,6,4,1,7,14,16,6,13,9,15,1,1,7,16,4,7,1,19,9,2,7,15, - 2,6,5,13,25,4,14,13,11,25,1,1,1,2,1,2,2,3,10,11,3,3,1,1,4,4,2,1,4,9,1,4,3,5,5,2,7,12,11,15,7,16,4,5,16,2,1,1,6,3,3,1,1,2,7,6,6,7,1,4,7,6,1,1, - 2,1,12,3,3,9,5,8,1,11,1,2,3,18,20,4,1,3,6,1,7,3,5,5,7,2,2,12,3,1,4,2,3,2,3,11,8,7,4,17,1,9,25,1,1,4,2,2,4,1,2,7,1,1,1,3,1,2,6,16,1,2,1,1,3,12, - 20,2,5,20,8,7,6,2,1,1,1,1,6,2,1,2,10,1,1,6,1,3,1,2,1,4,1,12,4,1,3,1,1,1,1,1,10,4,7,5,13,1,15,1,1,30,11,9,1,15,38,14,1,32,17,20,1,9,31,2,21,9, - 4,49,22,2,1,13,1,11,45,35,43,55,12,19,83,1,3,2,3,13,2,1,7,3,18,3,13,8,1,8,18,5,3,7,25,24,9,24,40,3,17,24,2,1,6,2,3,16,15,6,7,3,12,1,9,7,3,3, - 3,15,21,5,16,4,5,12,11,11,3,6,3,2,31,3,2,1,1,23,6,6,1,4,2,6,5,2,1,1,3,3,22,2,6,2,3,17,3,2,4,5,1,9,5,1,1,6,15,12,3,17,2,14,2,8,1,23,16,4,2,23, - 8,15,23,20,12,25,19,47,11,21,65,46,4,3,1,5,6,1,2,5,26,2,1,1,3,11,1,1,1,2,1,2,3,1,1,10,2,3,1,1,1,3,6,3,2,2,6,6,9,2,2,2,6,2,5,10,2,4,1,2,1,2,2, - 3,1,1,3,1,2,9,23,9,2,1,1,1,1,5,3,2,1,10,9,6,1,10,2,31,25,3,7,5,40,1,15,6,17,7,27,180,1,3,2,2,1,1,1,6,3,10,7,1,3,6,17,8,6,2,2,1,3,5,5,8,16,14, - 15,1,1,4,1,2,1,1,1,3,2,7,5,6,2,5,10,1,4,2,9,1,1,11,6,1,44,1,3,7,9,5,1,3,1,1,10,7,1,10,4,2,7,21,15,7,2,5,1,8,3,4,1,3,1,6,1,4,2,1,4,10,8,1,4,5, - 1,5,10,2,7,1,10,1,1,3,4,11,10,29,4,7,3,5,2,3,33,5,2,19,3,1,4,2,6,31,11,1,3,3,3,1,8,10,9,12,11,12,8,3,14,8,6,11,1,4,41,3,1,2,7,13,1,5,6,2,6,12, - 12,22,5,9,4,8,9,9,34,6,24,1,1,20,9,9,3,4,1,7,2,2,2,6,2,28,5,3,6,1,4,6,7,4,2,1,4,2,13,6,4,4,3,1,8,8,3,2,1,5,1,2,2,3,1,11,11,7,3,6,10,8,6,16,16, - 22,7,12,6,21,5,4,6,6,3,6,1,3,2,1,2,8,29,1,10,1,6,13,6,6,19,31,1,13,4,4,22,17,26,33,10,4,15,12,25,6,67,10,2,3,1,6,10,2,6,2,9,1,9,4,4,1,2,16,2, - 5,9,2,3,8,1,8,3,9,4,8,6,4,8,11,3,2,1,1,3,26,1,7,5,1,11,1,5,3,5,2,13,6,39,5,1,5,2,11,6,10,5,1,15,5,3,6,19,21,22,2,4,1,6,1,8,1,4,8,2,4,2,2,9,2, - 1,1,1,4,3,6,3,12,7,1,14,2,4,10,2,13,1,17,7,3,2,1,3,2,13,7,14,12,3,1,29,2,8,9,15,14,9,14,1,3,1,6,5,9,11,3,38,43,20,7,7,8,5,15,12,19,15,81,8,7, - 1,5,73,13,37,28,8,8,1,15,18,20,165,28,1,6,11,8,4,14,7,15,1,3,3,6,4,1,7,14,1,1,11,30,1,5,1,4,14,1,4,2,7,52,2,6,29,3,1,9,1,21,3,5,1,26,3,11,14, - 11,1,17,5,1,2,1,3,2,8,1,2,9,12,1,1,2,3,8,3,24,12,7,7,5,17,3,3,3,1,23,10,4,4,6,3,1,16,17,22,3,10,21,16,16,6,4,10,2,1,1,2,8,8,6,5,3,3,3,39,25, - 15,1,1,16,6,7,25,15,6,6,12,1,22,13,1,4,9,5,12,2,9,1,12,28,8,3,5,10,22,60,1,2,40,4,61,63,4,1,13,12,1,4,31,12,1,14,89,5,16,6,29,14,2,5,49,18,18, - 5,29,33,47,1,17,1,19,12,2,9,7,39,12,3,7,12,39,3,1,46,4,12,3,8,9,5,31,15,18,3,2,2,66,19,13,17,5,3,46,124,13,57,34,2,5,4,5,8,1,1,1,4,3,1,17,5, - 3,5,3,1,8,5,6,3,27,3,26,7,12,7,2,17,3,7,18,78,16,4,36,1,2,1,6,2,1,39,17,7,4,13,4,4,4,1,10,4,2,4,6,3,10,1,19,1,26,2,4,33,2,73,47,7,3,8,2,4,15, - 18,1,29,2,41,14,1,21,16,41,7,39,25,13,44,2,2,10,1,13,7,1,7,3,5,20,4,8,2,49,1,10,6,1,6,7,10,7,11,16,3,12,20,4,10,3,1,2,11,2,28,9,2,4,7,2,15,1, - 27,1,28,17,4,5,10,7,3,24,10,11,6,26,3,2,7,2,2,49,16,10,16,15,4,5,27,61,30,14,38,22,2,7,5,1,3,12,23,24,17,17,3,3,2,4,1,6,2,7,5,1,1,5,1,1,9,4, - 1,3,6,1,8,2,8,4,14,3,5,11,4,1,3,32,1,19,4,1,13,11,5,2,1,8,6,8,1,6,5,13,3,23,11,5,3,16,3,9,10,1,24,3,198,52,4,2,2,5,14,5,4,22,5,20,4,11,6,41, - 1,5,2,2,11,5,2,28,35,8,22,3,18,3,10,7,5,3,4,1,5,3,8,9,3,6,2,16,22,4,5,5,3,3,18,23,2,6,23,5,27,8,1,33,2,12,43,16,5,2,3,6,1,20,4,2,9,7,1,11,2, - 10,3,14,31,9,3,25,18,20,2,5,5,26,14,1,11,17,12,40,19,9,6,31,83,2,7,9,19,78,12,14,21,76,12,113,79,34,4,1,1,61,18,85,10,2,2,13,31,11,50,6,33,159, - 179,6,6,7,4,4,2,4,2,5,8,7,20,32,22,1,3,10,6,7,28,5,10,9,2,77,19,13,2,5,1,4,4,7,4,13,3,9,31,17,3,26,2,6,6,5,4,1,7,11,3,4,2,1,6,2,20,4,1,9,2,6, - 3,7,1,1,1,20,2,3,1,6,2,3,6,2,4,8,1,5,13,8,4,11,23,1,10,6,2,1,3,21,2,2,4,24,31,4,10,10,2,5,192,15,4,16,7,9,51,1,2,1,1,5,1,1,2,1,3,5,3,1,3,4,1, - 3,1,3,3,9,8,1,2,2,2,4,4,18,12,92,2,10,4,3,14,5,25,16,42,4,14,4,2,21,5,126,30,31,2,1,5,13,3,22,5,6,6,20,12,1,14,12,87,3,19,1,8,2,9,9,3,3,23,2, - 3,7,6,3,1,2,3,9,1,3,1,6,3,2,1,3,11,3,1,6,10,3,2,3,1,2,1,5,1,1,11,3,6,4,1,7,2,1,2,5,5,34,4,14,18,4,19,7,5,8,2,6,79,1,5,2,14,8,2,9,2,1,36,28,16, - 4,1,1,1,2,12,6,42,39,16,23,7,15,15,3,2,12,7,21,64,6,9,28,8,12,3,3,41,59,24,51,55,57,294,9,9,2,6,2,15,1,2,13,38,90,9,9,9,3,11,7,1,1,1,5,6,3,2, - 1,2,2,3,8,1,4,4,1,5,7,1,4,3,20,4,9,1,1,1,5,5,17,1,5,2,6,2,4,1,4,5,7,3,18,11,11,32,7,5,4,7,11,127,8,4,3,3,1,10,1,1,6,21,14,1,16,1,7,1,3,6,9,65, - 51,4,3,13,3,10,1,1,12,9,21,110,3,19,24,1,1,10,62,4,1,29,42,78,28,20,18,82,6,3,15,6,84,58,253,15,155,264,15,21,9,14,7,58,40,39, - }; - static ImWchar base_ranges[] = // not zero-terminated - { - 0x0020, 0x00FF, // Basic Latin + Latin Supplement - 0x3000, 0x30FF, // Punctuations, Hiragana, Katakana - 0x31F0, 0x31FF, // Katakana Phonetic Extensions - 0xFF00, 0xFFEF, // Half-width characters - }; - static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) + IM_ARRAYSIZE(accumulative_offsets_from_0x4E00)*2 + 1] = { 0 }; - if (!full_ranges[0]) - { - memcpy(full_ranges, base_ranges, sizeof(base_ranges)); - UnpackAccumulativeOffsetsIntoRanges(0x4E00, accumulative_offsets_from_0x4E00, IM_ARRAYSIZE(accumulative_offsets_from_0x4E00), full_ranges + IM_ARRAYSIZE(base_ranges)); - } - return &full_ranges[0]; -} - -const ImWchar* ImFontAtlas::GetGlyphRangesCyrillic() -{ - static const ImWchar ranges[] = - { - 0x0020, 0x00FF, // Basic Latin + Latin Supplement - 0x0400, 0x052F, // Cyrillic + Cyrillic Supplement - 0x2DE0, 0x2DFF, // Cyrillic Extended-A - 0xA640, 0xA69F, // Cyrillic Extended-B - 0, - }; - return &ranges[0]; -} - -const ImWchar* ImFontAtlas::GetGlyphRangesThai() -{ - static const ImWchar ranges[] = - { - 0x0020, 0x00FF, // Basic Latin - 0x2010, 0x205E, // Punctuations - 0x0E00, 0x0E7F, // Thai - 0, - }; - return &ranges[0]; -} - -//----------------------------------------------------------------------------- -// ImFontAtlas::GlyphRangesBuilder -//----------------------------------------------------------------------------- - -void ImFontAtlas::GlyphRangesBuilder::AddText(const char* text, const char* text_end) -{ - while (text_end ? (text < text_end) : *text) - { - unsigned int c = 0; - int c_len = ImTextCharFromUtf8(&c, text, text_end); - text += c_len; - if (c_len == 0) - break; - if (c < 0x10000) - AddChar((ImWchar)c); - } -} - -void ImFontAtlas::GlyphRangesBuilder::AddRanges(const ImWchar* ranges) -{ - for (; ranges[0]; ranges += 2) - for (ImWchar c = ranges[0]; c <= ranges[1]; c++) - AddChar(c); -} - -void ImFontAtlas::GlyphRangesBuilder::BuildRanges(ImVector* out_ranges) -{ - for (int n = 0; n < 0x10000; n++) - if (GetBit(n)) - { - out_ranges->push_back((ImWchar)n); - while (n < 0x10000 && GetBit(n + 1)) - n++; - out_ranges->push_back((ImWchar)n); - } - out_ranges->push_back(0); -} - -//----------------------------------------------------------------------------- -// ImFont -//----------------------------------------------------------------------------- - -ImFont::ImFont() -{ - Scale = 1.0f; - FallbackChar = (ImWchar)'?'; - DisplayOffset = ImVec2(0.0f, 0.0f); - ClearOutputData(); -} - -ImFont::~ImFont() -{ - // Invalidate active font so that the user gets a clear crash instead of a dangling pointer. - // If you want to delete fonts you need to do it between Render() and NewFrame(). - // FIXME-CLEANUP - /* - ImGuiContext& g = *GImGui; - if (g.Font == this) - g.Font = NULL; - */ - ClearOutputData(); -} - -void ImFont::ClearOutputData() -{ - FontSize = 0.0f; - Glyphs.clear(); - IndexAdvanceX.clear(); - IndexLookup.clear(); - FallbackGlyph = NULL; - FallbackAdvanceX = 0.0f; - ConfigDataCount = 0; - ConfigData = NULL; - ContainerAtlas = NULL; - Ascent = Descent = 0.0f; - DirtyLookupTables = true; - MetricsTotalSurface = 0; -} - -void ImFont::BuildLookupTable() -{ - int max_codepoint = 0; - for (int i = 0; i != Glyphs.Size; i++) - max_codepoint = ImMax(max_codepoint, (int)Glyphs[i].Codepoint); - - IM_ASSERT(Glyphs.Size < 0xFFFF); // -1 is reserved - IndexAdvanceX.clear(); - IndexLookup.clear(); - DirtyLookupTables = false; - GrowIndex(max_codepoint + 1); - for (int i = 0; i < Glyphs.Size; i++) - { - int codepoint = (int)Glyphs[i].Codepoint; - IndexAdvanceX[codepoint] = Glyphs[i].AdvanceX; - IndexLookup[codepoint] = (unsigned short)i; - } - - // Create a glyph to handle TAB - // FIXME: Needs proper TAB handling but it needs to be contextualized (or we could arbitrary say that each string starts at "column 0" ?) - if (FindGlyph((unsigned short)' ')) - { - if (Glyphs.back().Codepoint != '\t') // So we can call this function multiple times - Glyphs.resize(Glyphs.Size + 1); - ImFontGlyph& tab_glyph = Glyphs.back(); - tab_glyph = *FindGlyph((unsigned short)' '); - tab_glyph.Codepoint = '\t'; - tab_glyph.AdvanceX *= 4; - IndexAdvanceX[(int)tab_glyph.Codepoint] = (float)tab_glyph.AdvanceX; - IndexLookup[(int)tab_glyph.Codepoint] = (unsigned short)(Glyphs.Size-1); - } - - FallbackGlyph = FindGlyphNoFallback(FallbackChar); - FallbackAdvanceX = FallbackGlyph ? FallbackGlyph->AdvanceX : 0.0f; - for (int i = 0; i < max_codepoint + 1; i++) - if (IndexAdvanceX[i] < 0.0f) - IndexAdvanceX[i] = FallbackAdvanceX; -} - -void ImFont::SetFallbackChar(ImWchar c) -{ - FallbackChar = c; - BuildLookupTable(); -} - -void ImFont::GrowIndex(int new_size) -{ - IM_ASSERT(IndexAdvanceX.Size == IndexLookup.Size); - if (new_size <= IndexLookup.Size) - return; - IndexAdvanceX.resize(new_size, -1.0f); - IndexLookup.resize(new_size, (unsigned short)-1); -} - -void ImFont::AddGlyph(ImWchar codepoint, float x0, float y0, float x1, float y1, float u0, float v0, float u1, float v1, float advance_x) -{ - Glyphs.resize(Glyphs.Size + 1); - ImFontGlyph& glyph = Glyphs.back(); - glyph.Codepoint = (ImWchar)codepoint; - glyph.X0 = x0; - glyph.Y0 = y0; - glyph.X1 = x1; - glyph.Y1 = y1; - glyph.U0 = u0; - glyph.V0 = v0; - glyph.U1 = u1; - glyph.V1 = v1; - glyph.AdvanceX = advance_x + ConfigData->GlyphExtraSpacing.x; // Bake spacing into AdvanceX - - if (ConfigData->PixelSnapH) - glyph.AdvanceX = (float)(int)(glyph.AdvanceX + 0.5f); - - // Compute rough surface usage metrics (+1 to account for average padding, +0.99 to round) - DirtyLookupTables = true; - MetricsTotalSurface += (int)((glyph.U1 - glyph.U0) * ContainerAtlas->TexWidth + 1.99f) * (int)((glyph.V1 - glyph.V0) * ContainerAtlas->TexHeight + 1.99f); -} - -void ImFont::AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst) -{ - IM_ASSERT(IndexLookup.Size > 0); // Currently this can only be called AFTER the font has been built, aka after calling ImFontAtlas::GetTexDataAs*() function. - int index_size = IndexLookup.Size; - - if (dst < index_size && IndexLookup.Data[dst] == (unsigned short)-1 && !overwrite_dst) // 'dst' already exists - return; - if (src >= index_size && dst >= index_size) // both 'dst' and 'src' don't exist -> no-op - return; - - GrowIndex(dst + 1); - IndexLookup[dst] = (src < index_size) ? IndexLookup.Data[src] : (unsigned short)-1; - IndexAdvanceX[dst] = (src < index_size) ? IndexAdvanceX.Data[src] : 1.0f; -} - -const ImFontGlyph* ImFont::FindGlyph(ImWchar c) const -{ - if (c >= IndexLookup.Size) - return FallbackGlyph; - const unsigned short i = IndexLookup[c]; - if (i == (unsigned short)-1) - return FallbackGlyph; - return &Glyphs.Data[i]; -} - -const ImFontGlyph* ImFont::FindGlyphNoFallback(ImWchar c) const -{ - if (c >= IndexLookup.Size) - return NULL; - const unsigned short i = IndexLookup[c]; - if (i == (unsigned short)-1) - return NULL; - return &Glyphs.Data[i]; -} - -const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) const -{ - // Simple word-wrapping for English, not full-featured. Please submit failing cases! - // FIXME: Much possible improvements (don't cut things like "word !", "word!!!" but cut within "word,,,,", more sensible support for punctuations, support for Unicode punctuations, etc.) - - // For references, possible wrap point marked with ^ - // "aaa bbb, ccc,ddd. eee fff. ggg!" - // ^ ^ ^ ^ ^__ ^ ^ - - // List of hardcoded separators: .,;!?'" - - // Skip extra blanks after a line returns (that includes not counting them in width computation) - // e.g. "Hello world" --> "Hello" "World" - - // Cut words that cannot possibly fit within one line. - // e.g.: "The tropical fish" with ~5 characters worth of width --> "The tr" "opical" "fish" - - float line_width = 0.0f; - float word_width = 0.0f; - float blank_width = 0.0f; - wrap_width /= scale; // We work with unscaled widths to avoid scaling every characters - - const char* word_end = text; - const char* prev_word_end = NULL; - bool inside_word = true; - - const char* s = text; - while (s < text_end) - { - unsigned int c = (unsigned int)*s; - const char* next_s; - if (c < 0x80) - next_s = s + 1; - else - next_s = s + ImTextCharFromUtf8(&c, s, text_end); - if (c == 0) - break; - - if (c < 32) - { - if (c == '\n') - { - line_width = word_width = blank_width = 0.0f; - inside_word = true; - s = next_s; - continue; - } - if (c == '\r') - { - s = next_s; - continue; - } - } - - const float char_width = ((int)c < IndexAdvanceX.Size ? IndexAdvanceX[(int)c] : FallbackAdvanceX); - if (ImCharIsBlankW(c)) - { - if (inside_word) - { - line_width += blank_width; - blank_width = 0.0f; - word_end = s; - } - blank_width += char_width; - inside_word = false; - } - else - { - word_width += char_width; - if (inside_word) - { - word_end = next_s; - } - else - { - prev_word_end = word_end; - line_width += word_width + blank_width; - word_width = blank_width = 0.0f; - } - - // Allow wrapping after punctuation. - inside_word = !(c == '.' || c == ',' || c == ';' || c == '!' || c == '?' || c == '\"'); - } - - // We ignore blank width at the end of the line (they can be skipped) - if (line_width + word_width >= wrap_width) - { - // Words that cannot possibly fit within an entire line will be cut anywhere. - if (word_width < wrap_width) - s = prev_word_end ? prev_word_end : word_end; - break; - } - - s = next_s; - } - - return s; -} - -ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, const char* text_begin, const char* text_end, const char** remaining) const -{ - if (!text_end) - text_end = text_begin + strlen(text_begin); // FIXME-OPT: Need to avoid this. - - const float line_height = size; - const float scale = size / FontSize; - - ImVec2 text_size = ImVec2(0,0); - float line_width = 0.0f; - - const bool word_wrap_enabled = (wrap_width > 0.0f); - const char* word_wrap_eol = NULL; - - const char* s = text_begin; - while (s < text_end) - { - if (word_wrap_enabled) - { - // Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature. - if (!word_wrap_eol) - { - word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - line_width); - if (word_wrap_eol == s) // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity. - word_wrap_eol++; // +1 may not be a character start point in UTF-8 but it's ok because we use s >= word_wrap_eol below - } - - if (s >= word_wrap_eol) - { - if (text_size.x < line_width) - text_size.x = line_width; - text_size.y += line_height; - line_width = 0.0f; - word_wrap_eol = NULL; - - // Wrapping skips upcoming blanks - while (s < text_end) - { - const char c = *s; - if (ImCharIsBlankA(c)) { s++; } else if (c == '\n') { s++; break; } else { break; } - } - continue; - } - } - - // Decode and advance source - const char* prev_s = s; - unsigned int c = (unsigned int)*s; - if (c < 0x80) - { - s += 1; - } - else - { - s += ImTextCharFromUtf8(&c, s, text_end); - if (c == 0) // Malformed UTF-8? - break; - } - - if (c < 32) - { - if (c == '\n') - { - text_size.x = ImMax(text_size.x, line_width); - text_size.y += line_height; - line_width = 0.0f; - continue; - } - if (c == '\r') - continue; - } - - const float char_width = ((int)c < IndexAdvanceX.Size ? IndexAdvanceX[(int)c] : FallbackAdvanceX) * scale; - if (line_width + char_width >= max_width) - { - s = prev_s; - break; - } - - line_width += char_width; - } - - if (text_size.x < line_width) - text_size.x = line_width; - - if (line_width > 0 || text_size.y == 0.0f) - text_size.y += line_height; - - if (remaining) - *remaining = s; - - return text_size; -} - -void ImFont::RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, unsigned short c) const -{ - if (c == ' ' || c == '\t' || c == '\n' || c == '\r') // Match behavior of RenderText(), those 4 codepoints are hard-coded. - return; - if (const ImFontGlyph* glyph = FindGlyph(c)) - { - float scale = (size >= 0.0f) ? (size / FontSize) : 1.0f; - pos.x = (float)(int)pos.x + DisplayOffset.x; - pos.y = (float)(int)pos.y + DisplayOffset.y; - draw_list->PrimReserve(6, 4); - draw_list->PrimRectUV(ImVec2(pos.x + glyph->X0 * scale, pos.y + glyph->Y0 * scale), ImVec2(pos.x + glyph->X1 * scale, pos.y + glyph->Y1 * scale), ImVec2(glyph->U0, glyph->V0), ImVec2(glyph->U1, glyph->V1), col); - } -} - -void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, const ImVec4& clip_rect, const char* text_begin, const char* text_end, float wrap_width, bool cpu_fine_clip) const -{ - if (!text_end) - text_end = text_begin + strlen(text_begin); // ImGui functions generally already provides a valid text_end, so this is merely to handle direct calls. - - // Align to be pixel perfect - pos.x = (float)(int)pos.x + DisplayOffset.x; - pos.y = (float)(int)pos.y + DisplayOffset.y; - float x = pos.x; - float y = pos.y; - if (y > clip_rect.w) - return; - - const float scale = size / FontSize; - const float line_height = FontSize * scale; - const bool word_wrap_enabled = (wrap_width > 0.0f); - const char* word_wrap_eol = NULL; - - // Skip non-visible lines - const char* s = text_begin; - if (!word_wrap_enabled && y + line_height < clip_rect.y) - while (s < text_end && *s != '\n') // Fast-forward to next line - s++; - - // Reserve vertices for remaining worse case (over-reserving is useful and easily amortized) - const int vtx_count_max = (int)(text_end - s) * 4; - const int idx_count_max = (int)(text_end - s) * 6; - const int idx_expected_size = draw_list->IdxBuffer.Size + idx_count_max; - draw_list->PrimReserve(idx_count_max, vtx_count_max); - - ImDrawVert* vtx_write = draw_list->_VtxWritePtr; - ImDrawIdx* idx_write = draw_list->_IdxWritePtr; - unsigned int vtx_current_idx = draw_list->_VtxCurrentIdx; - - while (s < text_end) - { - if (word_wrap_enabled) - { - // Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature. - if (!word_wrap_eol) - { - word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - (x - pos.x)); - if (word_wrap_eol == s) // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity. - word_wrap_eol++; // +1 may not be a character start point in UTF-8 but it's ok because we use s >= word_wrap_eol below - } - - if (s >= word_wrap_eol) - { - x = pos.x; - y += line_height; - word_wrap_eol = NULL; - - // Wrapping skips upcoming blanks - while (s < text_end) - { - const char c = *s; - if (ImCharIsBlankA(c)) { s++; } else if (c == '\n') { s++; break; } else { break; } - } - continue; - } - } - - // Decode and advance source - unsigned int c = (unsigned int)*s; - if (c < 0x80) - { - s += 1; - } - else - { - s += ImTextCharFromUtf8(&c, s, text_end); - if (c == 0) // Malformed UTF-8? - break; - } - - if (c < 32) - { - if (c == '\n') - { - x = pos.x; - y += line_height; - - if (y > clip_rect.w) - break; - if (!word_wrap_enabled && y + line_height < clip_rect.y) - while (s < text_end && *s != '\n') // Fast-forward to next line - s++; - continue; - } - if (c == '\r') - continue; - } - - float char_width = 0.0f; - if (const ImFontGlyph* glyph = FindGlyph((unsigned short)c)) - { - char_width = glyph->AdvanceX * scale; - - // Arbitrarily assume that both space and tabs are empty glyphs as an optimization - if (c != ' ' && c != '\t') - { - // We don't do a second finer clipping test on the Y axis as we've already skipped anything before clip_rect.y and exit once we pass clip_rect.w - float x1 = x + glyph->X0 * scale; - float x2 = x + glyph->X1 * scale; - float y1 = y + glyph->Y0 * scale; - float y2 = y + glyph->Y1 * scale; - if (x1 <= clip_rect.z && x2 >= clip_rect.x) - { - // Render a character - float u1 = glyph->U0; - float v1 = glyph->V0; - float u2 = glyph->U1; - float v2 = glyph->V1; - - // CPU side clipping used to fit text in their frame when the frame is too small. Only does clipping for axis aligned quads. - if (cpu_fine_clip) - { - if (x1 < clip_rect.x) - { - u1 = u1 + (1.0f - (x2 - clip_rect.x) / (x2 - x1)) * (u2 - u1); - x1 = clip_rect.x; - } - if (y1 < clip_rect.y) - { - v1 = v1 + (1.0f - (y2 - clip_rect.y) / (y2 - y1)) * (v2 - v1); - y1 = clip_rect.y; - } - if (x2 > clip_rect.z) - { - u2 = u1 + ((clip_rect.z - x1) / (x2 - x1)) * (u2 - u1); - x2 = clip_rect.z; - } - if (y2 > clip_rect.w) - { - v2 = v1 + ((clip_rect.w - y1) / (y2 - y1)) * (v2 - v1); - y2 = clip_rect.w; - } - if (y1 >= y2) - { - x += char_width; - continue; - } - } - - // We are NOT calling PrimRectUV() here because non-inlined causes too much overhead in a debug builds. Inlined here: - { - idx_write[0] = (ImDrawIdx)(vtx_current_idx); idx_write[1] = (ImDrawIdx)(vtx_current_idx+1); idx_write[2] = (ImDrawIdx)(vtx_current_idx+2); - idx_write[3] = (ImDrawIdx)(vtx_current_idx); idx_write[4] = (ImDrawIdx)(vtx_current_idx+2); idx_write[5] = (ImDrawIdx)(vtx_current_idx+3); - vtx_write[0].pos.x = x1; vtx_write[0].pos.y = y1; vtx_write[0].col = col; vtx_write[0].uv.x = u1; vtx_write[0].uv.y = v1; - vtx_write[1].pos.x = x2; vtx_write[1].pos.y = y1; vtx_write[1].col = col; vtx_write[1].uv.x = u2; vtx_write[1].uv.y = v1; - vtx_write[2].pos.x = x2; vtx_write[2].pos.y = y2; vtx_write[2].col = col; vtx_write[2].uv.x = u2; vtx_write[2].uv.y = v2; - vtx_write[3].pos.x = x1; vtx_write[3].pos.y = y2; vtx_write[3].col = col; vtx_write[3].uv.x = u1; vtx_write[3].uv.y = v2; - vtx_write += 4; - vtx_current_idx += 4; - idx_write += 6; - } - } - } - } - - x += char_width; - } - - // Give back unused vertices - draw_list->VtxBuffer.resize((int)(vtx_write - draw_list->VtxBuffer.Data)); - draw_list->IdxBuffer.resize((int)(idx_write - draw_list->IdxBuffer.Data)); - draw_list->CmdBuffer[draw_list->CmdBuffer.Size-1].ElemCount -= (idx_expected_size - draw_list->IdxBuffer.Size); - draw_list->_VtxWritePtr = vtx_write; - draw_list->_IdxWritePtr = idx_write; - draw_list->_VtxCurrentIdx = (unsigned int)draw_list->VtxBuffer.Size; -} - -//----------------------------------------------------------------------------- -// Internals Drawing Helpers -//----------------------------------------------------------------------------- - -static inline float ImAcos01(float x) -{ - if (x <= 0.0f) return IM_PI * 0.5f; - if (x >= 1.0f) return 0.0f; - return ImAcos(x); - //return (-0.69813170079773212f * x * x - 0.87266462599716477f) * x + 1.5707963267948966f; // Cheap approximation, may be enough for what we do. -} - -// FIXME: Cleanup and move code to ImDrawList. -void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding) -{ - if (x_end_norm == x_start_norm) - return; - if (x_start_norm > x_end_norm) - ImSwap(x_start_norm, x_end_norm); - - ImVec2 p0 = ImVec2(ImLerp(rect.Min.x, rect.Max.x, x_start_norm), rect.Min.y); - ImVec2 p1 = ImVec2(ImLerp(rect.Min.x, rect.Max.x, x_end_norm), rect.Max.y); - if (rounding == 0.0f) - { - draw_list->AddRectFilled(p0, p1, col, 0.0f); - return; - } - - rounding = ImClamp(ImMin((rect.Max.x - rect.Min.x) * 0.5f, (rect.Max.y - rect.Min.y) * 0.5f) - 1.0f, 0.0f, rounding); - const float inv_rounding = 1.0f / rounding; - const float arc0_b = ImAcos01(1.0f - (p0.x - rect.Min.x) * inv_rounding); - const float arc0_e = ImAcos01(1.0f - (p1.x - rect.Min.x) * inv_rounding); - const float x0 = ImMax(p0.x, rect.Min.x + rounding); - if (arc0_b == arc0_e) - { - draw_list->PathLineTo(ImVec2(x0, p1.y)); - draw_list->PathLineTo(ImVec2(x0, p0.y)); - } - else if (arc0_b == 0.0f && arc0_e == IM_PI*0.5f) - { - draw_list->PathArcToFast(ImVec2(x0, p1.y - rounding), rounding, 3, 6); // BL - draw_list->PathArcToFast(ImVec2(x0, p0.y + rounding), rounding, 6, 9); // TR - } - else - { - draw_list->PathArcTo(ImVec2(x0, p1.y - rounding), rounding, IM_PI - arc0_e, IM_PI - arc0_b, 3); // BL - draw_list->PathArcTo(ImVec2(x0, p0.y + rounding), rounding, IM_PI + arc0_b, IM_PI + arc0_e, 3); // TR - } - if (p1.x > rect.Min.x + rounding) - { - const float arc1_b = ImAcos01(1.0f - (rect.Max.x - p1.x) * inv_rounding); - const float arc1_e = ImAcos01(1.0f - (rect.Max.x - p0.x) * inv_rounding); - const float x1 = ImMin(p1.x, rect.Max.x - rounding); - if (arc1_b == arc1_e) - { - draw_list->PathLineTo(ImVec2(x1, p0.y)); - draw_list->PathLineTo(ImVec2(x1, p1.y)); - } - else if (arc1_b == 0.0f && arc1_e == IM_PI*0.5f) - { - draw_list->PathArcToFast(ImVec2(x1, p0.y + rounding), rounding, 9, 12); // TR - draw_list->PathArcToFast(ImVec2(x1, p1.y - rounding), rounding, 0, 3); // BR - } - else - { - draw_list->PathArcTo(ImVec2(x1, p0.y + rounding), rounding, -arc1_e, -arc1_b, 3); // TR - draw_list->PathArcTo(ImVec2(x1, p1.y - rounding), rounding, +arc1_b, +arc1_e, 3); // BR - } - } - draw_list->PathFillConvex(col); -} - -//----------------------------------------------------------------------------- -// DEFAULT FONT DATA -//----------------------------------------------------------------------------- -// Compressed with stb_compress() then converted to a C array. -// Use the program in misc/fonts/binary_to_compressed_c.cpp to create the array from a TTF file. -// Decompression from stb.h (public domain) by Sean Barrett https://github.com/nothings/stb/blob/master/stb.h -//----------------------------------------------------------------------------- - -static unsigned int stb_decompress_length(const unsigned char *input) -{ - return (input[8] << 24) + (input[9] << 16) + (input[10] << 8) + input[11]; -} - -static unsigned char *stb__barrier_out_e, *stb__barrier_out_b; -static const unsigned char *stb__barrier_in_b; -static unsigned char *stb__dout; -static void stb__match(const unsigned char *data, unsigned int length) -{ - // INVERSE of memmove... write each byte before copying the next... - IM_ASSERT(stb__dout + length <= stb__barrier_out_e); - if (stb__dout + length > stb__barrier_out_e) { stb__dout += length; return; } - if (data < stb__barrier_out_b) { stb__dout = stb__barrier_out_e+1; return; } - while (length--) *stb__dout++ = *data++; -} - -static void stb__lit(const unsigned char *data, unsigned int length) -{ - IM_ASSERT(stb__dout + length <= stb__barrier_out_e); - if (stb__dout + length > stb__barrier_out_e) { stb__dout += length; return; } - if (data < stb__barrier_in_b) { stb__dout = stb__barrier_out_e+1; return; } - memcpy(stb__dout, data, length); - stb__dout += length; -} - -#define stb__in2(x) ((i[x] << 8) + i[(x)+1]) -#define stb__in3(x) ((i[x] << 16) + stb__in2((x)+1)) -#define stb__in4(x) ((i[x] << 24) + stb__in3((x)+1)) - -static const unsigned char *stb_decompress_token(const unsigned char *i) -{ - if (*i >= 0x20) { // use fewer if's for cases that expand small - if (*i >= 0x80) stb__match(stb__dout-i[1]-1, i[0] - 0x80 + 1), i += 2; - else if (*i >= 0x40) stb__match(stb__dout-(stb__in2(0) - 0x4000 + 1), i[2]+1), i += 3; - else /* *i >= 0x20 */ stb__lit(i+1, i[0] - 0x20 + 1), i += 1 + (i[0] - 0x20 + 1); - } else { // more ifs for cases that expand large, since overhead is amortized - if (*i >= 0x18) stb__match(stb__dout-(stb__in3(0) - 0x180000 + 1), i[3]+1), i += 4; - else if (*i >= 0x10) stb__match(stb__dout-(stb__in3(0) - 0x100000 + 1), stb__in2(3)+1), i += 5; - else if (*i >= 0x08) stb__lit(i+2, stb__in2(0) - 0x0800 + 1), i += 2 + (stb__in2(0) - 0x0800 + 1); - else if (*i == 0x07) stb__lit(i+3, stb__in2(1) + 1), i += 3 + (stb__in2(1) + 1); - else if (*i == 0x06) stb__match(stb__dout-(stb__in3(1)+1), i[4]+1), i += 5; - else if (*i == 0x04) stb__match(stb__dout-(stb__in3(1)+1), stb__in2(4)+1), i += 6; - } - return i; -} - -static unsigned int stb_adler32(unsigned int adler32, unsigned char *buffer, unsigned int buflen) -{ - const unsigned long ADLER_MOD = 65521; - unsigned long s1 = adler32 & 0xffff, s2 = adler32 >> 16; - unsigned long blocklen, i; - - blocklen = buflen % 5552; - while (buflen) { - for (i=0; i + 7 < blocklen; i += 8) { - s1 += buffer[0], s2 += s1; - s1 += buffer[1], s2 += s1; - s1 += buffer[2], s2 += s1; - s1 += buffer[3], s2 += s1; - s1 += buffer[4], s2 += s1; - s1 += buffer[5], s2 += s1; - s1 += buffer[6], s2 += s1; - s1 += buffer[7], s2 += s1; - - buffer += 8; - } - - for (; i < blocklen; ++i) - s1 += *buffer++, s2 += s1; - - s1 %= ADLER_MOD, s2 %= ADLER_MOD; - buflen -= blocklen; - blocklen = 5552; - } - return (unsigned int)(s2 << 16) + (unsigned int)s1; -} - -static unsigned int stb_decompress(unsigned char *output, const unsigned char *i, unsigned int /*length*/) -{ - unsigned int olen; - if (stb__in4(0) != 0x57bC0000) return 0; - if (stb__in4(4) != 0) return 0; // error! stream is > 4GB - olen = stb_decompress_length(i); - stb__barrier_in_b = i; - stb__barrier_out_e = output + olen; - stb__barrier_out_b = output; - i += 16; - - stb__dout = output; - for (;;) { - const unsigned char *old_i = i; - i = stb_decompress_token(i); - if (i == old_i) { - if (*i == 0x05 && i[1] == 0xfa) { - IM_ASSERT(stb__dout == output + olen); - if (stb__dout != output + olen) return 0; - if (stb_adler32(1, output, olen) != (unsigned int) stb__in4(2)) - return 0; - return olen; - } else { - IM_ASSERT(0); /* NOTREACHED */ - return 0; - } - } - IM_ASSERT(stb__dout <= output + olen); - if (stb__dout > output + olen) - return 0; - } -} - -//----------------------------------------------------------------------------- -// ProggyClean.ttf -// Copyright (c) 2004, 2005 Tristan Grimmer -// MIT license (see License.txt in http://www.upperbounds.net/download/ProggyClean.ttf.zip) -// Download and more information at http://upperbounds.net -//----------------------------------------------------------------------------- -// File: 'ProggyClean.ttf' (41208 bytes) -// Exported using binary_to_compressed_c.cpp -//----------------------------------------------------------------------------- -static const char proggy_clean_ttf_compressed_data_base85[11980+1] = - "7])#######hV0qs'/###[),##/l:$#Q6>##5[n42>c-TH`->>#/e>11NNV=Bv(*:.F?uu#(gRU.o0XGH`$vhLG1hxt9?W`#,5LsCp#-i>.r$<$6pD>Lb';9Crc6tgXmKVeU2cD4Eo3R/" - "2*>]b(MC;$jPfY.;h^`IWM9Qo#t'X#(v#Y9w0#1D$CIf;W'#pWUPXOuxXuU(H9M(1=Ke$$'5F%)]0^#0X@U.a$FBjVQTSDgEKnIS7EM9>ZY9w0#L;>>#Mx&4Mvt//L[MkA#W@lK.N'[0#7RL_&#w+F%HtG9M#XL`N&.,GM4Pg;--VsM.M0rJfLH2eTM`*oJMHRC`N" - "kfimM2J,W-jXS:)r0wK#@Fge$U>`w'N7G#$#fB#$E^$#:9:hk+eOe--6x)F7*E%?76%^GMHePW-Z5l'&GiF#$956:rS?dA#fiK:)Yr+`�j@'DbG&#^$PG.Ll+DNa&VZ>1i%h1S9u5o@YaaW$e+bROPOpxTO7Stwi1::iB1q)C_=dV26J;2,]7op$]uQr@_V7$q^%lQwtuHY]=DX,n3L#0PHDO4f9>dC@O>HBuKPpP*E,N+b3L#lpR/MrTEH.IAQk.a>D[.e;mc." - "x]Ip.PH^'/aqUO/$1WxLoW0[iLAw=4h(9.`G" - "CRUxHPeR`5Mjol(dUWxZa(>STrPkrJiWx`5U7F#.g*jrohGg`cg:lSTvEY/EV_7H4Q9[Z%cnv;JQYZ5q.l7Zeas:HOIZOB?Ggv:[7MI2k).'2($5FNP&EQ(,)" - "U]W]+fh18.vsai00);D3@4ku5P?DP8aJt+;qUM]=+b'8@;mViBKx0DE[-auGl8:PJ&Dj+M6OC]O^((##]`0i)drT;-7X`=-H3[igUnPG-NZlo.#k@h#=Ork$m>a>$-?Tm$UV(?#P6YY#" - "'/###xe7q.73rI3*pP/$1>s9)W,JrM7SN]'/4C#v$U`0#V.[0>xQsH$fEmPMgY2u7Kh(G%siIfLSoS+MK2eTM$=5,M8p`A.;_R%#u[K#$x4AG8.kK/HSB==-'Ie/QTtG?-.*^N-4B/ZM" - "_3YlQC7(p7q)&](`6_c)$/*JL(L-^(]$wIM`dPtOdGA,U3:w2M-0+WomX2u7lqM2iEumMTcsF?-aT=Z-97UEnXglEn1K-bnEO`gu" - "Ft(c%=;Am_Qs@jLooI&NX;]0#j4#F14;gl8-GQpgwhrq8'=l_f-b49'UOqkLu7-##oDY2L(te+Mch&gLYtJ,MEtJfLh'x'M=$CS-ZZ%P]8bZ>#S?YY#%Q&q'3^Fw&?D)UDNrocM3A76/" - "/oL?#h7gl85[qW/NDOk%16ij;+:1a'iNIdb-ou8.P*w,v5#EI$TWS>Pot-R*H'-SEpA:g)f+O$%%`kA#G=8RMmG1&O`>to8bC]T&$,n.LoO>29sp3dt-52U%VM#q7'DHpg+#Z9%H[Ket`e;)f#Km8&+DC$I46>#Kr]]u-[=99tts1.qb#q72g1WJO81q+eN'03'eM>&1XxY-caEnO" - "j%2n8)),?ILR5^.Ibn<-X-Mq7[a82Lq:F&#ce+S9wsCK*x`569E8ew'He]h:sI[2LM$[guka3ZRd6:t%IG:;$%YiJ:Nq=?eAw;/:nnDq0(CYcMpG)qLN4$##&J-XTt,%OVU4)S1+R-#dg0/Nn?Ku1^0f$B*P:Rowwm-`0PKjYDDM'3]d39VZHEl4,.j']Pk-M.h^&:0FACm$maq-&sgw0t7/6(^xtk%" - "LuH88Fj-ekm>GA#_>568x6(OFRl-IZp`&b,_P'$MhLbxfc$mj`,O;&%W2m`Zh:/)Uetw:aJ%]K9h:TcF]u_-Sj9,VK3M.*'&0D[Ca]J9gp8,kAW]" - "%(?A%R$f<->Zts'^kn=-^@c4%-pY6qI%J%1IGxfLU9CP8cbPlXv);C=b),<2mOvP8up,UVf3839acAWAW-W?#ao/^#%KYo8fRULNd2.>%m]UK:n%r$'sw]J;5pAoO_#2mO3n,'=H5(et" - "Hg*`+RLgv>=4U8guD$I%D:W>-r5V*%j*W:Kvej.Lp$'?;++O'>()jLR-^u68PHm8ZFWe+ej8h:9r6L*0//c&iH&R8pRbA#Kjm%upV1g:" - "a_#Ur7FuA#(tRh#.Y5K+@?3<-8m0$PEn;J:rh6?I6uG<-`wMU'ircp0LaE_OtlMb&1#6T.#FDKu#1Lw%u%+GM+X'e?YLfjM[VO0MbuFp7;>Q&#WIo)0@F%q7c#4XAXN-U&VBpqB>0ie&jhZ[?iLR@@_AvA-iQC(=ksRZRVp7`.=+NpBC%rh&3]R:8XDmE5^V8O(x<-+k?'(^](H.aREZSi,#1:[IXaZFOm<-ui#qUq2$##Ri;u75OK#(RtaW-K-F`S+cF]uN`-KMQ%rP/Xri.LRcB##=YL3BgM/3M" - "D?@f&1'BW-)Ju#bmmWCMkk&#TR`C,5d>g)F;t,4:@_l8G/5h4vUd%&%950:VXD'QdWoY-F$BtUwmfe$YqL'8(PWX(" - "P?^@Po3$##`MSs?DWBZ/S>+4%>fX,VWv/w'KD`LP5IbH;rTV>n3cEK8U#bX]l-/V+^lj3;vlMb&[5YQ8#pekX9JP3XUC72L,,?+Ni&co7ApnO*5NK,((W-i:$,kp'UDAO(G0Sq7MVjJs" - "bIu)'Z,*[>br5fX^:FPAWr-m2KgLQ_nN6'8uTGT5g)uLv:873UpTLgH+#FgpH'_o1780Ph8KmxQJ8#H72L4@768@Tm&Q" - "h4CB/5OvmA&,Q&QbUoi$a_%3M01H)4x7I^&KQVgtFnV+;[Pc>[m4k//,]1?#`VY[Jr*3&&slRfLiVZJ:]?=K3Sw=[$=uRB?3xk48@aege0jT6'N#(q%.O=?2S]u*(m<-" - "V8J'(1)G][68hW$5'q[GC&5j`TE?m'esFGNRM)j,ffZ?-qx8;->g4t*:CIP/[Qap7/9'#(1sao7w-.qNUdkJ)tCF&#B^;xGvn2r9FEPFFFcL@.iFNkTve$m%#QvQS8U@)2Z+3K:AKM5i" - "sZ88+dKQ)W6>J%CL`.d*(B`-n8D9oK-XV1q['-5k'cAZ69e;D_?$ZPP&s^+7])$*$#@QYi9,5P r+$%CE=68>K8r0=dSC%%(@p7" - ".m7jilQ02'0-VWAgTlGW'b)Tq7VT9q^*^$$.:&N@@" - "$&)WHtPm*5_rO0&e%K&#-30j(E4#'Zb.o/(Tpm$>K'f@[PvFl,hfINTNU6u'0pao7%XUp9]5.>%h`8_=VYbxuel.NTSsJfLacFu3B'lQSu/m6-Oqem8T+oE--$0a/k]uj9EwsG>%veR*" - "hv^BFpQj:K'#SJ,sB-'#](j.Lg92rTw-*n%@/;39rrJF,l#qV%OrtBeC6/,;qB3ebNW[?,Hqj2L.1NP&GjUR=1D8QaS3Up&@*9wP?+lo7b?@%'k4`p0Z$22%K3+iCZj?XJN4Nm&+YF]u" - "@-W$U%VEQ/,,>>#)D#%8cY#YZ?=,`Wdxu/ae&#" - "w6)R89tI#6@s'(6Bf7a&?S=^ZI_kS&ai`&=tE72L_D,;^R)7[$so8lKN%5/$(vdfq7+ebA#" - "u1p]ovUKW&Y%q]'>$1@-[xfn$7ZTp7mM,G,Ko7a&Gu%G[RMxJs[0MM%wci.LFDK)(%:_i2B5CsR8&9Z&#=mPEnm0f`<&c)QL5uJ#%u%lJj+D-r;BoFDoS97h5g)E#o:&S4weDF,9^Hoe`h*L+_a*NrLW-1pG_&2UdB8" - "6e%B/:=>)N4xeW.*wft-;$'58-ESqr#U`'6AQ]m&6/`Z>#S?YY#Vc;r7U2&326d=w&H####?TZ`*4?&.MK?LP8Vxg>$[QXc%QJv92.(Db*B)gb*BM9dM*hJMAo*c&#" - "b0v=Pjer]$gG&JXDf->'StvU7505l9$AFvgYRI^&<^b68?j#q9QX4SM'RO#&sL1IM.rJfLUAj221]d##DW=m83u5;'bYx,*Sl0hL(W;;$doB&O/TQ:(Z^xBdLjLV#*8U_72Lh+2Q8Cj0i:6hp&$C/:p(HK>T8Y[gHQ4`4)'$Ab(Nof%V'8hL&#SfD07&6D@M.*J:;$-rv29'M]8qMv-tLp,'886iaC=Hb*YJoKJ,(j%K=H`K.v9HggqBIiZu'QvBT.#=)0ukruV&.)3=(^1`o*Pj4<-#MJ+gLq9-##@HuZPN0]u:h7.T..G:;$/Usj(T7`Q8tT72LnYl<-qx8;-HV7Q-&Xdx%1a,hC=0u+HlsV>nuIQL-5" - "_>@kXQtMacfD.m-VAb8;IReM3$wf0''hra*so568'Ip&vRs849'MRYSp%:t:h5qSgwpEr$B>Q,;s(C#$)`svQuF$##-D,##,g68@2[T;.XSdN9Qe)rpt._K-#5wF)sP'##p#C0c%-Gb%" - "hd+<-j'Ai*x&&HMkT]C'OSl##5RG[JXaHN;d'uA#x._U;.`PU@(Z3dt4r152@:v,'R.Sj'w#0<-;kPI)FfJ&#AYJ&#//)>-k=m=*XnK$>=)72L]0I%>.G690a:$##<,);?;72#?x9+d;" - "^V'9;jY@;)br#q^YQpx:X#Te$Z^'=-=bGhLf:D6&bNwZ9-ZD#n^9HhLMr5G;']d&6'wYmTFmLq9wI>P(9mI[>kC-ekLC/R&CH+s'B;K-M6$EB%is00:" - "+A4[7xks.LrNk0&E)wILYF@2L'0Nb$+pv<(2.768/FrY&h$^3i&@+G%JT'<-,v`3;_)I9M^AE]CN?Cl2AZg+%4iTpT3$U4O]GKx'm9)b@p7YsvK3w^YR-" - "CdQ*:Ir<($u&)#(&?L9Rg3H)4fiEp^iI9O8KnTj,]H?D*r7'M;PwZ9K0E^k&-cpI;.p/6_vwoFMV<->#%Xi.LxVnrU(4&8/P+:hLSKj$#U%]49t'I:rgMi'FL@a:0Y-uA[39',(vbma*" - "hU%<-SRF`Tt:542R_VV$p@[p8DV[A,?1839FWdFTi1O*H&#(AL8[_P%.M>v^-))qOT*F5Cq0`Ye%+$B6i:7@0IXSsDiWP,##P`%/L-" - "S(qw%sf/@%#B6;/U7K]uZbi^Oc^2n%t<)'mEVE''n`WnJra$^TKvX5B>;_aSEK',(hwa0:i4G?.Bci.(X[?b*($,=-n<.Q%`(X=?+@Am*Js0&=3bh8K]mL69=Lb,OcZV/);TTm8VI;?%OtJ<(b4mq7M6:u?KRdFl*:xP?Yb.5)%w_I?7uk5JC+FS(m#i'k.'a0i)9<7b'fs'59hq$*5Uhv##pi^8+hIEBF`nvo`;'l0.^S1<-wUK2/Coh58KKhLj" - "M=SO*rfO`+qC`W-On.=AJ56>>i2@2LH6A:&5q`?9I3@@'04&p2/LVa*T-4<-i3;M9UvZd+N7>b*eIwg:CC)c<>nO&#$(>.Z-I&J(Q0Hd5Q%7Co-b`-cP)hI;*_F]u`Rb[.j8_Q/<&>uu+VsH$sM9TA%?)(vmJ80),P7E>)tjD%2L=-t#fK[%`v=Q8WlA2);Sa" - ">gXm8YB`1d@K#n]76-a$U,mF%Ul:#/'xoFM9QX-$.QN'>" - "[%$Z$uF6pA6Ki2O5:8w*vP1<-1`[G,)-m#>0`P&#eb#.3i)rtB61(o'$?X3B2Qft^ae_5tKL9MUe9b*sLEQ95C&`=G?@Mj=wh*'3E>=-<)Gt*Iw)'QG:`@I" - "wOf7&]1i'S01B+Ev/Nac#9S;=;YQpg_6U`*kVY39xK,[/6Aj7:'1Bm-_1EYfa1+o&o4hp7KN_Q(OlIo@S%;jVdn0'1h19w,WQhLI)3S#f$2(eb,jr*b;3Vw]*7NH%$c4Vs,eD9>XW8?N]o+(*pgC%/72LV-uW%iewS8W6m2rtCpo'RS1R84=@paTKt)>=%&1[)*vp'u+x,VrwN;&]kuO9JDbg=pO$J*.jVe;u'm0dr9l,<*wMK*Oe=g8lV_KEBFkO'oU]^=[-792#ok,)" - "i]lR8qQ2oA8wcRCZ^7w/Njh;?.stX?Q1>S1q4Bn$)K1<-rGdO'$Wr.Lc.CG)$/*JL4tNR/,SVO3,aUw'DJN:)Ss;wGn9A32ijw%FL+Z0Fn.U9;reSq)bmI32U==5ALuG&#Vf1398/pVo" - "1*c-(aY168o<`JsSbk-,1N;$>0:OUas(3:8Z972LSfF8eb=c-;>SPw7.6hn3m`9^Xkn(r.qS[0;T%&Qc=+STRxX'q1BNk3&*eu2;&8q$&x>Q#Q7^Tf+6<(d%ZVmj2bDi%.3L2n+4W'$P" - "iDDG)g,r%+?,$@?uou5tSe2aN_AQU*'IAO" - "URQ##V^Fv-XFbGM7Fl(N<3DhLGF%q.1rC$#:T__&Pi68%0xi_&[qFJ(77j_&JWoF.V735&T,[R*:xFR*K5>>#`bW-?4Ne_&6Ne_&6Ne_&n`kr-#GJcM6X;uM6X;uM(.a..^2TkL%oR(#" - ";u.T%fAr%4tJ8&><1=GHZ_+m9/#H1F^R#SC#*N=BA9(D?v[UiFY>>^8p,KKF.W]L29uLkLlu/+4T" - "w$)F./^n3+rlo+DB;5sIYGNk+i1t-69Jg--0pao7Sm#K)pdHW&;LuDNH@H>#/X-TI(;P>#,Gc>#0Su>#4`1?#8lC?#xL$#B.`$#F:r$#JF.%#NR@%#R_R%#Vke%#Zww%#_-4^Rh%Sflr-k'MS.o?.5/sWel/wpEM0%3'/1)K^f1-d>G21&v(35>V`39V7A4=onx4" - "A1OY5EI0;6Ibgr6M$HS7Q<)58C5w,;WoA*#[%T*#`1g*#d=#+#hI5+#lUG+#pbY+#tnl+#x$),#&1;,#*=M,#.I`,#2Ur,#6b.-#;w[H#iQtA#m^0B#qjBB#uvTB##-hB#'9$C#+E6C#" - "/QHC#3^ZC#7jmC#;v)D#?,)4kMYD4lVu`4m`:&5niUA5@(A5BA1]PBB:xlBCC=2CDLXMCEUtiCf&0g2'tN?PGT4CPGT4CPGT4CPGT4CPGT4CPGT4CPGT4CP" - "GT4CPGT4CPGT4CPGT4CPGT4CPGT4CP-qekC`.9kEg^+F$kwViFJTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5o,^<-28ZI'O?;xp" - "O?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xp;7q-#lLYI:xvD=#"; - -static const char* GetDefaultCompressedFontDataTTFBase85() -{ - return proggy_clean_ttf_compressed_data_base85; -} diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_android.cpp b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_android.cpp deleted file mode 100644 index 4dc3ec7a..00000000 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_android.cpp +++ /dev/null @@ -1,576 +0,0 @@ -// ImGui Platform Binding for: Android -// [Bruno Levy] Sun Aug 19 08:01:39 CEST 2018 -// Note: not part (yet) of the official ImGui distribution -// -// Note: to use, include in the CMakeLists.txt that compiles this file: -// if(ANDROID) -// target_include_directories(geogram_gfx_third_party PRIVATE -// ${ANDROID_NDK}/sources/android/native_app_glue -// ) -// endif() - -// What works: -// Rendering with OpenGL ES 2.x -// Fingers/Stylus/Mouse interaction -// Virtual and physical keyboard interaction - -// TODO (Bugs to be fixed): -// ------------------------ -// - soft keyboard directional keys do not always work (it depends on the used keyboard, -// for some keyboards, they work a little bit, randomly, for some others they work...) -// -// - app is restarted when connecting/disconnecting a physical keyboard -// while application is running (I do not understand, I have: -// android:configChanges="orientation|keyboardHidden|keyboard" -// in AndroidManifest.xml) -> this one is more related to android_main.cpp - -// TODO (Improvements): -// -------------------- -// - UTF8 text input (probably not very difficult to add). -// -// - mouse cursors (https://developer.android.com/about/versions/nougat/android-7.0#custom_pointer_api) -// (need to overload Java function, cannot do that with native_glue I think, unless we can change -// methods of an existing Java object with JNI) -// -// - setMousePos - - - -#ifdef __ANDROID__ - -#include "imgui.h" -#include "imgui_impl_android.h" - -#include -#include -#include -#include -#include -#include - -namespace { - double g_Time = 0.0; - float g_mouseX = 0.0f; - float g_mouseY = 0.0f; - bool g_mousePressed[5] = {false, false, false, false, false}; - bool g_resetKeys = false; -} - -// Some utilities functions that interact with Android. -namespace AndroidUtils { - - // Shows or hides the software keyboard. - void set_soft_keyboard_visibility(struct android_app* app, bool show); - - // Converts a keycode to a unicode. - // deviceId, keyCode, metaState can be obtained from the InputEvent. - jint keycode_to_unicode( - struct android_app* app, int32_t deviceId, int32_t keyCode, int32_t metaState - ); -} - - -bool ImGui_ImplAndroid_Init(struct android_app* app) { - g_Time = 0.0; - g_mouseX = 0.0f; - g_mouseY = 0.0f; - for (int i = 0; i < IM_ARRAYSIZE(g_mousePressed); i++) { - g_mousePressed[i] = false; - } - // TODO: mouse cursor - // TODO: setmousepos ? - - ImGuiIO& io = ImGui::GetIO(); - io.KeyMap[ImGuiKey_Tab] = AKEYCODE_TAB; - io.KeyMap[ImGuiKey_LeftArrow] = AKEYCODE_DPAD_LEFT; - io.KeyMap[ImGuiKey_RightArrow] = AKEYCODE_DPAD_RIGHT; - io.KeyMap[ImGuiKey_UpArrow] = AKEYCODE_DPAD_UP; - io.KeyMap[ImGuiKey_DownArrow] = AKEYCODE_DPAD_DOWN; - io.KeyMap[ImGuiKey_PageUp] = AKEYCODE_PAGE_UP; - io.KeyMap[ImGuiKey_PageDown] = AKEYCODE_PAGE_DOWN; - io.KeyMap[ImGuiKey_Home] = AKEYCODE_MOVE_HOME; - io.KeyMap[ImGuiKey_End] = AKEYCODE_MOVE_END; - io.KeyMap[ImGuiKey_Insert] = AKEYCODE_INSERT; - io.KeyMap[ImGuiKey_Delete] = AKEYCODE_FORWARD_DEL; - io.KeyMap[ImGuiKey_Backspace] = AKEYCODE_DEL; - io.KeyMap[ImGuiKey_Space] = AKEYCODE_SPACE; - io.KeyMap[ImGuiKey_Enter] = AKEYCODE_ENTER; - io.KeyMap[ImGuiKey_Escape] = AKEYCODE_ESCAPE; - io.KeyMap[ImGuiKey_A] = AKEYCODE_A; - io.KeyMap[ImGuiKey_C] = AKEYCODE_C; - io.KeyMap[ImGuiKey_V] = AKEYCODE_V; - io.KeyMap[ImGuiKey_X] = AKEYCODE_X; - io.KeyMap[ImGuiKey_Y] = AKEYCODE_Y; - io.KeyMap[ImGuiKey_Z] = AKEYCODE_Z; - - - // Install callbacks - if(app != nullptr) { - app->onInputEvent = ImGui_ImplAndroid_InputEvent; - } - - return true; -} - -void ImGui_ImplAndroid_Shutdown() { - // TODO: destroy mouse cursors. -} - -static void ImGui_ImplAndroid_UpdateMousePosAndButtons() -{ - ImGuiIO& io = ImGui::GetIO(); - - for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) { - io.MouseDown[i] = g_mousePressed[i]; - } - io.MousePos = ImVec2(g_mouseX, g_mouseY); -} - -static void ImGui_ImplAndroid_UpdateMouseCursor() { - // TODO... -} - -void ImGui_ImplAndroid_NewFrame() { - ImGuiIO& io = ImGui::GetIO(); - // Font atlas needs to be built, call renderer _NewFrame() function - // e.g. ImGui_ImplOpenGL3_NewFrame() - IM_ASSERT(io.Fonts->IsBuilt()); - - // Get current display size - EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); - EGLSurface surface = eglGetCurrentSurface(EGL_DRAW); - int w=0; - int h=0; - eglQuerySurface(display, surface, EGL_WIDTH, &w); - eglQuerySurface(display, surface, EGL_HEIGHT, &h); - int display_w = w; - int display_h = h; - - io.DisplaySize = ImVec2((float)w, (float)h); - io.DisplayFramebufferScale = - ImVec2( - w > 0 ?((float)display_w / w) : 0, - h > 0 ? ((float)display_h / h) : 0 - ); - - // Setup time step - timespec now; - clock_gettime(CLOCK_MONOTONIC, &now); - double current_time = double(now.tv_sec) + double(now.tv_nsec) * 1e-9; - - io.DeltaTime = g_Time > 0.0 ? float(current_time - g_Time) : 1.0f/60.0f; - g_Time = current_time; - - ImGui_ImplAndroid_UpdateMousePosAndButtons(); - ImGui_ImplAndroid_UpdateMouseCursor(); - - // TODO: Gamepad navigation mapping ? -} - - -void ImGui_ImplAndroid_EndFrame() { - // g_resetKeys is set when the latest key event came from the soft keyboard, - // then we need to reset the keys. - if(g_resetKeys) { - ImGuiIO& io = ImGui::GetIO(); - for(int key = 0; key < IM_ARRAYSIZE(io.KeysDown); ++key) { - io.KeysDown[key] = false; - } - io.KeyShift = false; - io.KeyCtrl = false; - io.KeyAlt = false; - io.KeySuper = false; - g_resetKeys = false; - } -} - -// Emulates mouse buttons using multiple fingers: -// emulated mouse button is determined by number of fingers -// coordinates are defined by last finger -int32_t ImGui_ImplAndroid_FingerEvent( - struct android_app* app, AInputEvent* event -) { - int32_t action = AMotionEvent_getAction(event); - bool down_or_move = (action == AMOTION_EVENT_ACTION_DOWN || - action == AMOTION_EVENT_ACTION_MOVE ); - - int nb_fingers = int(AMotionEvent_getPointerCount(event)); - int btn = nb_fingers-1; - for(int i=0; i= 0 && key < IM_ARRAYSIZE(io.KeysDown)) { - if((AKeyEvent_getFlags(event) & AKEY_EVENT_FLAG_SOFT_KEYBOARD)) { - // The soft keyboard generates Push/Release events when the - // key is released. Thus we mark the key as pushed, and - // set g_resetKeys so that ImGui_ImplAndroid_EndFrame() - // will mark the key as released after ImGui could do what - // it has to do with the key. - io.KeysDown[key] = true; - g_resetKeys = true; - } else { - io.KeysDown[key] = (action == AKEY_EVENT_ACTION_DOWN); - g_resetKeys = false; - } - io.KeyShift = ((modifiers & AMETA_SHIFT_ON) != 0); - io.KeyCtrl = ((modifiers & AMETA_CTRL_ON) != 0); - io.KeyAlt = ((modifiers & AMETA_ALT_ON) != 0); - io.KeySuper = ((modifiers & AMETA_META_ON) != 0); - } - - if(action == AKEY_EVENT_ACTION_DOWN) { - if(key == AKEYCODE_BACK) { - ImGui::debug_printf("Show virtual keyboard\n"); - AndroidUtils::set_soft_keyboard_visibility(app, true); - } else { - jint unicode = AndroidUtils::keycode_to_unicode( - app, device, key, modifiers - ); - // TODO: use AddInputCharactersUTF8() - char c = char(unicode); - if(isprint(c)) { - io.AddInputCharacter(c); - } - } - } - - return 1; -} - -int32_t ImGui_ImplAndroid_InputEvent( - struct android_app* app, AInputEvent* event -) { - int32_t result = 0; - switch(AInputEvent_getType(event)) { - case AINPUT_EVENT_TYPE_MOTION: - result = ImGui_ImplAndroid_MotionEvent(app, event); - break; - case AINPUT_EVENT_TYPE_KEY: - result = ImGui_ImplAndroid_KeyEvent(app, event); - break; - default: - break; - } - return result; -} - -/*****************************************************************/ - -// Functions that interact with Java. - -namespace AndroidUtils { - - // Display soft keyboard programmatically - //https://groups.google.com/forum/?fromgroups=#!topic/android-ndk/Tk3g00wLKhk - // (see alto messages about how to attach/detach thread). - // There is a function supposed to do that: - // ANativeActivity_showSoftInput( - // mApplication->activity,ANATIVEACTIVITY_SHOW_SOFT_INPUT_FORCED - // ); - // (or ANATIVEACTIVITY_SHOW_SOFT_INPUT_IMPLICIT) - // but I did not manage to make it work. - - void set_soft_keyboard_visibility(struct android_app* app, bool pShow) { - - JavaVM* lJavaVM = app->activity->vm; - JNIEnv* lJNIEnv = nullptr; - bool lThreadAttached = false; - - // Get JNIEnv from lJavaVM using GetEnv to test whether - // thread is attached or not to the VM. If not, attach it - // (and note that it will need to be detached at the end - // of the function). - switch (lJavaVM->GetEnv((void**)&lJNIEnv, JNI_VERSION_1_6)) { - case JNI_OK: - break; - case JNI_EDETACHED: { - jint lResult = lJavaVM->AttachCurrentThread(&lJNIEnv, nullptr); - if(lResult == JNI_ERR) { - throw std::runtime_error("Could not attach current thread"); - } - lThreadAttached = true; - } break; - case JNI_EVERSION: - throw std::runtime_error("Invalid java version"); - } - - // Retrieves NativeActivity. - jobject lNativeActivity = app->activity->clazz; - jclass ClassNativeActivity = lJNIEnv->GetObjectClass(lNativeActivity); - - // Retrieves Context.INPUT_METHOD_SERVICE. - jclass ClassContext = lJNIEnv->FindClass("android/content/Context"); - jfieldID FieldINPUT_METHOD_SERVICE = - lJNIEnv->GetStaticFieldID( - ClassContext,"INPUT_METHOD_SERVICE", "Ljava/lang/String;" - ); - jobject INPUT_METHOD_SERVICE = - lJNIEnv->GetStaticObjectField( - ClassContext, FieldINPUT_METHOD_SERVICE - ); - - // Runs getSystemService(Context.INPUT_METHOD_SERVICE). - jclass ClassInputMethodManager = lJNIEnv->FindClass( - "android/view/inputmethod/InputMethodManager" - ); - jmethodID MethodGetSystemService = lJNIEnv->GetMethodID( - ClassNativeActivity, "getSystemService", - "(Ljava/lang/String;)Ljava/lang/Object;" - ); - jobject lInputMethodManager = lJNIEnv->CallObjectMethod( - lNativeActivity, MethodGetSystemService, - INPUT_METHOD_SERVICE - ); - - // Runs getWindow().getDecorView(). - jmethodID MethodGetWindow = lJNIEnv->GetMethodID( - ClassNativeActivity, "getWindow", - "()Landroid/view/Window;" - ); - jobject lWindow = lJNIEnv->CallObjectMethod( - lNativeActivity, MethodGetWindow - ); - jclass ClassWindow = lJNIEnv->FindClass("android/view/Window"); - jmethodID MethodGetDecorView = lJNIEnv->GetMethodID( - ClassWindow, "getDecorView", "()Landroid/view/View;" - ); - jobject lDecorView = lJNIEnv->CallObjectMethod( - lWindow, MethodGetDecorView - ); - - if (pShow) { - // Runs lInputMethodManager.showSoftInput(...). - jmethodID MethodShowSoftInput = lJNIEnv->GetMethodID( - ClassInputMethodManager, "showSoftInput", - "(Landroid/view/View;I)Z" - ); - lJNIEnv->CallBooleanMethod( - lInputMethodManager, MethodShowSoftInput, - lDecorView, 0 - ); - } else { - // Runs lWindow.getViewToken() - jclass ClassView = lJNIEnv->FindClass( - "android/view/View" - ); - jmethodID MethodGetWindowToken = lJNIEnv->GetMethodID( - ClassView, "getWindowToken", "()Landroid/os/IBinder;" - ); - jobject lBinder = lJNIEnv->CallObjectMethod( - lDecorView, MethodGetWindowToken - ); - - // lInputMethodManager.hideSoftInput(...). - jmethodID MethodHideSoftInput = lJNIEnv->GetMethodID( - ClassInputMethodManager, "hideSoftInputFromWindow", - "(Landroid/os/IBinder;I)Z" - ); - lJNIEnv->CallBooleanMethod( - lInputMethodManager, MethodHideSoftInput, - lBinder, 0 - ); - } - - if(lThreadAttached) { - lJavaVM->DetachCurrentThread(); - } - } - - jint keycode_to_unicode( - struct android_app* app, - int32_t pDeviceId, int32_t pKeyCode, int32_t pMetaState - ) { - jint result = 0; - - // Early exit for special keys - // (works without it, but well, why calling all that - // Java stuff if we now in advance that we do not need - // to ?). - if( - pKeyCode == AKEYCODE_TAB || - pKeyCode == AKEYCODE_DPAD_LEFT || - pKeyCode == AKEYCODE_DPAD_RIGHT || - pKeyCode == AKEYCODE_DPAD_UP || - pKeyCode == AKEYCODE_DPAD_DOWN || - pKeyCode == AKEYCODE_PAGE_UP || - pKeyCode == AKEYCODE_PAGE_DOWN || - pKeyCode == AKEYCODE_MOVE_HOME || - pKeyCode == AKEYCODE_MOVE_END || - pKeyCode == AKEYCODE_INSERT || - pKeyCode == AKEYCODE_FORWARD_DEL || - pKeyCode == AKEYCODE_DEL || - pKeyCode == AKEYCODE_ENTER || - pKeyCode == AKEYCODE_ESCAPE - ) { - return result; - } - - - JavaVM* lJavaVM = app->activity->vm; - JNIEnv* lJNIEnv = nullptr; - bool lThreadAttached = false; - - // Get JNIEnv from lJavaVM using GetEnv to test whether - // thread is attached or not to the VM. If not, attach it - // (and note that it will need to be detached at the end - // of the function). - switch (lJavaVM->GetEnv((void**)&lJNIEnv, JNI_VERSION_1_6)) { - case JNI_OK: - break; - case JNI_EDETACHED: { - jint lResult = lJavaVM->AttachCurrentThread(&lJNIEnv, nullptr); - if(lResult == JNI_ERR) { - throw std::runtime_error("Could not attach current thread"); - } - lThreadAttached = true; - } break; - case JNI_EVERSION: - throw std::runtime_error("Invalid java version"); - } - - jclass ClassKeyCharacterMap = lJNIEnv->FindClass( - "android/view/KeyCharacterMap" - ); - - jmethodID MethodLoad = lJNIEnv->GetStaticMethodID( - ClassKeyCharacterMap, "load", - "(I)Landroid/view/KeyCharacterMap;" - ); - - jobject lKeyCharacterMap = lJNIEnv->CallStaticObjectMethod( - ClassKeyCharacterMap, MethodLoad, jint(pDeviceId) - ); - - jmethodID MethodGet = lJNIEnv->GetMethodID( - ClassKeyCharacterMap, "get", - "(II)I" - ); - - result = lJNIEnv->CallIntMethod( - lKeyCharacterMap, MethodGet, - jint(pKeyCode), jint(pMetaState) - ); - - if(lThreadAttached) { - lJavaVM->DetachCurrentThread(); - } - - return result; - } -} - -#endif - -/********************************************************************/ - -/* - - Notes, links etc... - =================== - - In pre-4.3 Androids, there was a bug on some devices making the - app. crash when hiding the soft keyboard. Normally it was fixed in Android 4.3. - - https://stackoverflow.com/questions/15913080/crash-when-closing-soft-keyboard-while-using-native-activity - -*/ - diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_android.h b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_android.h deleted file mode 100644 index cb2391ab..00000000 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_android.h +++ /dev/null @@ -1,27 +0,0 @@ -/* - * ImGui Platform Binding for: Android - * Author: Bruno Levy Sun Aug 19 08:01:39 CEST 2018 - * Note: not part (yet) of the official ImGui distribution - */ - -#ifdef __ANDROID__ -#include - -// param app: if non-null, registers input handler to specified app. -IMGUI_IMPL_API bool ImGui_ImplAndroid_Init(struct android_app* app = nullptr); - -IMGUI_IMPL_API void ImGui_ImplAndroid_Shutdown(); - -// Needs to be called at the beginning of each frame, -// before ImGui::NewFrame(). -IMGUI_IMPL_API void ImGui_ImplAndroid_NewFrame(); - -// Needs to be called at the end of each frame, -// after all other ImGui functions. -IMGUI_IMPL_API void ImGui_ImplAndroid_EndFrame(); - -// The event handler, to be used if not registered by ImGui_ImplAndroid_Init(). -IMGUI_IMPL_API int32_t ImGui_ImplAndroid_InputEvent( - struct android_app* app, AInputEvent* event -); -#endif \ No newline at end of file diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_glfw.cpp b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_glfw.cpp deleted file mode 100644 index 82ca77d6..00000000 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_glfw.cpp +++ /dev/null @@ -1,332 +0,0 @@ -// ImGui Platform Binding for: GLFW -// This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan..) -// (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) - -// Implemented features: -// [X] Platform: Clipboard support. -// [X] Platform: Gamepad navigation mapping. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. -// [x] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: 3 cursors types are missing from GLFW. - -// You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. -// If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). -// If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. -// https://github.com/ocornut/imgui - -// CHANGELOG -// (minor and older changes stripped away, please see git history for details) -// 2018-06-08: Misc: Extracted imgui_impl_glfw.cpp/.h away from the old combined GLFW+OpenGL/Vulkan examples. -// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors flag + honor ImGuiConfigFlags_NoMouseCursorChange flag. -// 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value, passed to glfwSetCursor()). -// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. -// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. -// 2018-01-25: Inputs: Added gamepad support if ImGuiConfigFlags_NavEnableGamepad is set. -// 2018-01-25: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set). -// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. -// 2018-01-18: Inputs: Added mapping for ImGuiKey_Insert. -// 2017-08-25: Inputs: MousePos set to -FLT_MAX,-FLT_MAX when mouse is unavailable/missing (instead of -1,-1). -// 2016-10-15: Misc: Added a void* user_data parameter to Clipboard function handlers. - -#ifndef __ANDROID__ /* [Bruno] */ - -#include "imgui.h" -#include "imgui_impl_glfw.h" - -// GLFW -// [Bruno] adapted to geogram glfw compile -#ifdef __EMSCRIPTEN__ -#include -#include -#else -#ifdef GEO_USE_SYSTEM_GLFW3 -#include -#else -#include -#endif -#endif - -// Win32 / glfwGetWin32Window -// [Bruno] adapted to geogram glfw compile -#ifdef _WIN32 -#undef APIENTRY -#define GLFW_EXPOSE_NATIVE_WIN32 -#ifdef GEO_USE_SYSTEM_GLFW3 -#include -#else -#include -#endif -#endif - -#define GLFW_HAS_WINDOW_TOPMOST (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ GLFW_FLOATING -#define GLFW_HAS_WINDOW_HOVERED (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ GLFW_HOVERED -#define GLFW_HAS_WINDOW_ALPHA (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwSetWindowOpacity -#define GLFW_HAS_PER_MONITOR_DPI (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3300) // 3.3+ glfwGetMonitorContentScale -#define GLFW_HAS_VULKAN (GLFW_VERSION_MAJOR * 1000 + GLFW_VERSION_MINOR * 100 >= 3200) // 3.2+ glfwCreateWindowSurface - -// Data -enum GlfwClientApi -{ - GlfwClientApi_Unknown, - GlfwClientApi_OpenGL, - GlfwClientApi_Vulkan -}; -static GLFWwindow* g_Window = NULL; -static GlfwClientApi g_ClientApi = GlfwClientApi_Unknown; -static double g_Time = 0.0; -static bool g_MouseJustPressed[5] = { false, false, false, false, false }; -static GLFWcursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = { 0 }; - -// [Bruno Levy] 01/06/2017 Do not use GLFW3 clipboard under emscripten, use built-in -// ImGUI clipboard instead. -#ifndef __EMSCRIPTEN__ - -static const char* ImGui_ImplGlfw_GetClipboardText(void* user_data) -{ - return glfwGetClipboardString((GLFWwindow*)user_data); -} - -static void ImGui_ImplGlfw_SetClipboardText(void* user_data, const char* text) -{ - glfwSetClipboardString((GLFWwindow*)user_data, text); -} - -#endif - -void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow*, int button, int action, int /*mods*/) -{ - if (action == GLFW_PRESS && button >= 0 && button < IM_ARRAYSIZE(g_MouseJustPressed)) - g_MouseJustPressed[button] = true; -} - -void ImGui_ImplGlfw_ScrollCallback(GLFWwindow*, double xoffset, double yoffset) -{ - // [Bruno Levy] 01/06/2017 - // Under emscripten and apple, mouse wheel is inversed - // as compared to the other platforms. -#if defined(__EMSCRIPTEN__) || defined(__APPLE__) - yoffset *= -1.0; -#endif - ImGuiIO& io = ImGui::GetIO(); - io.MouseWheelH += (float)xoffset; - io.MouseWheel += (float)yoffset; -} - -void ImGui_ImplGlfw_KeyCallback(GLFWwindow*, int key, int, int action, int mods) -{ - ImGuiIO& io = ImGui::GetIO(); - if (action == GLFW_PRESS) - io.KeysDown[key] = true; - if (action == GLFW_RELEASE) - io.KeysDown[key] = false; - - (void)mods; // Modifiers are not reliable across systems - io.KeyCtrl = io.KeysDown[GLFW_KEY_LEFT_CONTROL] || io.KeysDown[GLFW_KEY_RIGHT_CONTROL]; - io.KeyShift = io.KeysDown[GLFW_KEY_LEFT_SHIFT] || io.KeysDown[GLFW_KEY_RIGHT_SHIFT]; - io.KeyAlt = io.KeysDown[GLFW_KEY_LEFT_ALT] || io.KeysDown[GLFW_KEY_RIGHT_ALT]; - io.KeySuper = io.KeysDown[GLFW_KEY_LEFT_SUPER] || io.KeysDown[GLFW_KEY_RIGHT_SUPER]; -} - -void ImGui_ImplGlfw_CharCallback(GLFWwindow*, unsigned int c) -{ - ImGuiIO& io = ImGui::GetIO(); - if (c > 0 && c < 0x10000) - io.AddInputCharacter((unsigned short)c); -} - -void ImGui_ImplGlfw_InstallCallbacks(GLFWwindow* window) -{ - glfwSetMouseButtonCallback(window, ImGui_ImplGlfw_MouseButtonCallback); - glfwSetScrollCallback(window, ImGui_ImplGlfw_ScrollCallback); - glfwSetKeyCallback(window, ImGui_ImplGlfw_KeyCallback); - glfwSetCharCallback(window, ImGui_ImplGlfw_CharCallback); -} - -static bool ImGui_ImplGlfw_Init(GLFWwindow* window, bool install_callbacks, GlfwClientApi client_api) -{ - g_Window = window; - g_Time = 0.0; - - // Setup back-end capabilities flags - ImGuiIO& io = ImGui::GetIO(); - io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) - io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) - - // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array. - io.KeyMap[ImGuiKey_Tab] = GLFW_KEY_TAB; - io.KeyMap[ImGuiKey_LeftArrow] = GLFW_KEY_LEFT; - io.KeyMap[ImGuiKey_RightArrow] = GLFW_KEY_RIGHT; - io.KeyMap[ImGuiKey_UpArrow] = GLFW_KEY_UP; - io.KeyMap[ImGuiKey_DownArrow] = GLFW_KEY_DOWN; - io.KeyMap[ImGuiKey_PageUp] = GLFW_KEY_PAGE_UP; - io.KeyMap[ImGuiKey_PageDown] = GLFW_KEY_PAGE_DOWN; - io.KeyMap[ImGuiKey_Home] = GLFW_KEY_HOME; - io.KeyMap[ImGuiKey_End] = GLFW_KEY_END; - io.KeyMap[ImGuiKey_Insert] = GLFW_KEY_INSERT; - io.KeyMap[ImGuiKey_Delete] = GLFW_KEY_DELETE; - io.KeyMap[ImGuiKey_Backspace] = GLFW_KEY_BACKSPACE; - io.KeyMap[ImGuiKey_Space] = GLFW_KEY_SPACE; - io.KeyMap[ImGuiKey_Enter] = GLFW_KEY_ENTER; - io.KeyMap[ImGuiKey_Escape] = GLFW_KEY_ESCAPE; - io.KeyMap[ImGuiKey_A] = GLFW_KEY_A; - io.KeyMap[ImGuiKey_C] = GLFW_KEY_C; - io.KeyMap[ImGuiKey_V] = GLFW_KEY_V; - io.KeyMap[ImGuiKey_X] = GLFW_KEY_X; - io.KeyMap[ImGuiKey_Y] = GLFW_KEY_Y; - io.KeyMap[ImGuiKey_Z] = GLFW_KEY_Z; - -// [Bruno Levy] 01/06/2017 Do not use GLFW3 clipboard under emscripten, use built-in -// ImGUI clipboard instead. -#ifndef __EMSCRIPTEN__ - io.SetClipboardTextFn = ImGui_ImplGlfw_SetClipboardText; - io.GetClipboardTextFn = ImGui_ImplGlfw_GetClipboardText; - io.ClipboardUserData = g_Window; -#endif - -#if defined(_WIN32) - io.ImeWindowHandle = (void*)glfwGetWin32Window(g_Window); -#endif - - g_MouseCursors[ImGuiMouseCursor_Arrow] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); - g_MouseCursors[ImGuiMouseCursor_TextInput] = glfwCreateStandardCursor(GLFW_IBEAM_CURSOR); - g_MouseCursors[ImGuiMouseCursor_ResizeAll] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); // FIXME: GLFW doesn't have this. - g_MouseCursors[ImGuiMouseCursor_ResizeNS] = glfwCreateStandardCursor(GLFW_VRESIZE_CURSOR); - g_MouseCursors[ImGuiMouseCursor_ResizeEW] = glfwCreateStandardCursor(GLFW_HRESIZE_CURSOR); - g_MouseCursors[ImGuiMouseCursor_ResizeNESW] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); // FIXME: GLFW doesn't have this. - g_MouseCursors[ImGuiMouseCursor_ResizeNWSE] = glfwCreateStandardCursor(GLFW_ARROW_CURSOR); // FIXME: GLFW doesn't have this. - - if (install_callbacks) - ImGui_ImplGlfw_InstallCallbacks(window); - - g_ClientApi = client_api; - return true; -} - -bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks) -{ - return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_OpenGL); -} - -bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks) -{ - return ImGui_ImplGlfw_Init(window, install_callbacks, GlfwClientApi_Vulkan); -} - -void ImGui_ImplGlfw_Shutdown() -{ - for (ImGuiMouseCursor cursor_n = 0; cursor_n < ImGuiMouseCursor_COUNT; cursor_n++) - { - glfwDestroyCursor(g_MouseCursors[cursor_n]); - g_MouseCursors[cursor_n] = NULL; - } - g_ClientApi = GlfwClientApi_Unknown; -} - -static void ImGui_ImplGlfw_UpdateMousePosAndButtons() -{ - // Update buttons - ImGuiIO& io = ImGui::GetIO(); - for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) - { - // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. - io.MouseDown[i] = g_MouseJustPressed[i] || glfwGetMouseButton(g_Window, i) != 0; - g_MouseJustPressed[i] = false; - } - - // Update mouse position - const ImVec2 mouse_pos_backup = io.MousePos; - io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); - //[Bruno Levy] 05/16/2016 Under emscripten, it seems that the window is never focused (so I bypass the test). -#ifndef __EMSCRIPTEN__ - if (glfwGetWindowAttrib(g_Window, GLFW_FOCUSED)) -#endif - { - if (io.WantSetMousePos) - { - glfwSetCursorPos(g_Window, (double)mouse_pos_backup.x, (double)mouse_pos_backup.y); - } - else - { - double mouse_x, mouse_y; - glfwGetCursorPos(g_Window, &mouse_x, &mouse_y); - io.MousePos = ImVec2((float)mouse_x, (float)mouse_y); - } - } -} - -static void ImGui_ImplGlfw_UpdateMouseCursor() -{ - ImGuiIO& io = ImGui::GetIO(); - if ((io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) || glfwGetInputMode(g_Window, GLFW_CURSOR) == GLFW_CURSOR_DISABLED) - return; - - ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); - if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor) - { - // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor - glfwSetInputMode(g_Window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); - } - else - { - // Show OS mouse cursor - // FIXME-PLATFORM: Unfocused windows seems to fail changing the mouse cursor with GLFW 3.2, but 3.3 works here. - glfwSetCursor(g_Window, g_MouseCursors[imgui_cursor] ? g_MouseCursors[imgui_cursor] : g_MouseCursors[ImGuiMouseCursor_Arrow]); - glfwSetInputMode(g_Window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); - } -} - -void ImGui_ImplGlfw_NewFrame() -{ - ImGuiIO& io = ImGui::GetIO(); - IM_ASSERT(io.Fonts->IsBuilt()); // Font atlas needs to be built, call renderer _NewFrame() function e.g. ImGui_ImplOpenGL3_NewFrame() - - // Setup display size - int w, h; - int display_w, display_h; - glfwGetWindowSize(g_Window, &w, &h); - glfwGetFramebufferSize(g_Window, &display_w, &display_h); - io.DisplaySize = ImVec2((float)w, (float)h); - io.DisplayFramebufferScale = ImVec2(w > 0 ? ((float)display_w / w) : 0, h > 0 ? ((float)display_h / h) : 0); - - // Setup time step - double current_time = glfwGetTime(); - io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f/60.0f); - g_Time = current_time; - - ImGui_ImplGlfw_UpdateMousePosAndButtons(); - ImGui_ImplGlfw_UpdateMouseCursor(); - - // Gamepad navigation mapping [BETA] - memset(io.NavInputs, 0, sizeof(io.NavInputs)); - if (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) - { - // Update gamepad inputs - #define MAP_BUTTON(NAV_NO, BUTTON_NO) { if (buttons_count > BUTTON_NO && buttons[BUTTON_NO] == GLFW_PRESS) io.NavInputs[NAV_NO] = 1.0f; } - #define MAP_ANALOG(NAV_NO, AXIS_NO, V0, V1) { float v = (axes_count > AXIS_NO) ? axes[AXIS_NO] : V0; v = (v - V0) / (V1 - V0); if (v > 1.0f) v = 1.0f; if (io.NavInputs[NAV_NO] < v) io.NavInputs[NAV_NO] = v; } - int axes_count = 0, buttons_count = 0; - const float* axes = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &axes_count); - const unsigned char* buttons = glfwGetJoystickButtons(GLFW_JOYSTICK_1, &buttons_count); - MAP_BUTTON(ImGuiNavInput_Activate, 0); // Cross / A - MAP_BUTTON(ImGuiNavInput_Cancel, 1); // Circle / B - MAP_BUTTON(ImGuiNavInput_Menu, 2); // Square / X - MAP_BUTTON(ImGuiNavInput_Input, 3); // Triangle / Y - MAP_BUTTON(ImGuiNavInput_DpadLeft, 13); // D-Pad Left - MAP_BUTTON(ImGuiNavInput_DpadRight, 11); // D-Pad Right - MAP_BUTTON(ImGuiNavInput_DpadUp, 10); // D-Pad Up - MAP_BUTTON(ImGuiNavInput_DpadDown, 12); // D-Pad Down - MAP_BUTTON(ImGuiNavInput_FocusPrev, 4); // L1 / LB - MAP_BUTTON(ImGuiNavInput_FocusNext, 5); // R1 / RB - MAP_BUTTON(ImGuiNavInput_TweakSlow, 4); // L1 / LB - MAP_BUTTON(ImGuiNavInput_TweakFast, 5); // R1 / RB - MAP_ANALOG(ImGuiNavInput_LStickLeft, 0, -0.3f, -0.9f); - MAP_ANALOG(ImGuiNavInput_LStickRight,0, +0.3f, +0.9f); - MAP_ANALOG(ImGuiNavInput_LStickUp, 1, +0.3f, +0.9f); - MAP_ANALOG(ImGuiNavInput_LStickDown, 1, -0.3f, -0.9f); - #undef MAP_BUTTON - #undef MAP_ANALOG - if (axes_count > 0 && buttons_count > 0) - io.BackendFlags |= ImGuiBackendFlags_HasGamepad; - else - io.BackendFlags &= ~ImGuiBackendFlags_HasGamepad; - } -} - -#endif /* __ANDROID__ [Bruno] */ diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_glfw.h b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_glfw.h deleted file mode 100644 index eddd7c95..00000000 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_glfw.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * ImGui Platform Binding for: GLFW - * This needs to be used along with a Renderer (e.g. OpenGL3, Vulkan..) - * (Info: GLFW is a cross-platform general purpose library for handling windows, inputs, OpenGL/Vulkan graphics context creation, etc.) - * - * Implemented features: - * [X] Platform: Clipboard support. - * [X] Platform: Gamepad navigation mapping. Enable with 'io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad'. - * [x] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. FIXME: 3 cursors types are missing from GLFW. - * - * You can copy and use unmodified imgui_impl_* files in your project. See main.cpp for an example of using this. - * If you use this binding you'll need to call 4 functions: ImGui_ImplXXXX_Init(), ImGui_ImplXXXX_NewFrame(), ImGui::Render() and ImGui_ImplXXXX_Shutdown(). - * If you are new to ImGui, see examples/README.txt and documentation at the top of imgui.cpp. - * https://github.com/ocornut/imgui - * - * About GLSL version: - * The 'glsl_version' initialization parameter defaults to "#version 150" if NULL. - * Only override if your GL version doesn't handle this GLSL version. Keep NULL if unsure! - */ - -/* [Bruno] C-style comment */ - -#ifndef __ANDROID__ /* [Bruno] */ - -/* [Bruno] */ -#ifdef __cplusplus -extern "C" { -#endif - -struct GLFWwindow; - -IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForOpenGL(GLFWwindow* window, bool install_callbacks); -IMGUI_IMPL_API bool ImGui_ImplGlfw_InitForVulkan(GLFWwindow* window, bool install_callbacks); -IMGUI_IMPL_API void ImGui_ImplGlfw_Shutdown(); -IMGUI_IMPL_API void ImGui_ImplGlfw_NewFrame(); - -/* - * GLFW callbacks (installed by default if you enable 'install_callbacks' during initialization) - * Provided here if you want to chain callbacks. - * You can also handle inputs yourself and use those as a reference. - */ -IMGUI_IMPL_API void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods); -IMGUI_IMPL_API void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset); -IMGUI_IMPL_API void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int key, int scancode, int action, int mods); -IMGUI_IMPL_API void ImGui_ImplGlfw_CharCallback(GLFWwindow* window, unsigned int c); - -/* [Bruno] */ -#ifdef __cplusplus -} -#endif - -#endif /* __ANDROID__ [Bruno] */ diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_opengl2.cpp b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_opengl2.cpp deleted file mode 100644 index db56c135..00000000 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_opengl2.cpp +++ /dev/null @@ -1,221 +0,0 @@ -// ImGui Renderer for: OpenGL2 (legacy OpenGL, fixed pipeline) -// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) - -// Implemented features: -// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. - -// **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** -// **Prefer using the code in imgui_impl_opengl3.cpp** -// This code is mostly provided as a reference to learn how ImGui integration works, because it is shorter to read. -// If your code is using GL3+ context or any semi modern OpenGL calls, using this is likely to make everything more -// complicated, will require your code to reset every single OpenGL attributes to their initial state, and might -// confuse your GPU driver. -// The GL2 code is unable to reset attributes or even call e.g. "glUseProgram(0)" because they don't exist in that API. - -// CHANGELOG -// (minor and older changes stripped away, please see git history for details) -// 2018-06-08: Misc: Extracted imgui_impl_opengl2.cpp/.h away from the old combined GLFW/SDL+OpenGL2 examples. -// 2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle. -// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplGlfwGL2_RenderDrawData() in the .h file so you can call it yourself. -// 2017-09-01: OpenGL: Save and restore current polygon mode. -// 2016-09-10: OpenGL: Uploading font texture as RGBA32 to increase compatibility with users shaders (not ideal). -// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle. - - -#include "imgui.h" -#include "imgui_impl_opengl2.h" - -// [Bruno 05/16/2016] conditional compilation -#ifdef GEO_GL_LEGACY - -// Include OpenGL header (without an OpenGL loader) requires a bit of fiddling -#if defined(_WIN32) && !defined(APIENTRY) -#define APIENTRY __stdcall // It is customary to use APIENTRY for OpenGL function pointer declarations on all platforms. Additionally, the Windows OpenGL header needs APIENTRY. -#endif -#if defined(_WIN32) && !defined(WINGDIAPI) -#define WINGDIAPI __declspec(dllimport) // Some Windows OpenGL headers need this -#endif -#if defined(__APPLE__) -#include -#else -#include -#endif - -// OpenGL Data -static GLuint g_FontTexture = 0; - -// Functions -bool ImGui_ImplOpenGL2_Init() -{ - return true; -} - -void ImGui_ImplOpenGL2_Shutdown() -{ - ImGui_ImplOpenGL2_DestroyDeviceObjects(); -} - -void ImGui_ImplOpenGL2_NewFrame() -{ - if (!g_FontTexture) - ImGui_ImplOpenGL2_CreateDeviceObjects(); -} - -// OpenGL2 Render function. -// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop) -// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so. -void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data) -{ - // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) - ImGuiIO& io = ImGui::GetIO(); - int fb_width = (int)(draw_data->DisplaySize.x * io.DisplayFramebufferScale.x); - int fb_height = (int)(draw_data->DisplaySize.y * io.DisplayFramebufferScale.y); - if (fb_width == 0 || fb_height == 0) - return; - draw_data->ScaleClipRects(io.DisplayFramebufferScale); - - // We are using the OpenGL fixed pipeline to make the example code simpler to read! - // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, vertex/texcoord/color pointers, polygon fill. - GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); - GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport); - GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box); - glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_TRANSFORM_BIT); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDisable(GL_CULL_FACE); - glDisable(GL_DEPTH_TEST); - glEnable(GL_SCISSOR_TEST); - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - - // [Bruno Levy] 05/18/2016 Added these missing state variables. - glDisable(GL_CLIP_PLANE0); - glActiveTexture(GL_TEXTURE0); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - glDisable(GL_LIGHTING); - glMatrixMode(GL_TEXTURE); - glPushMatrix(); - glLoadIdentity(); - - - glEnable(GL_TEXTURE_2D); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - //glUseProgram(0); // You may want this if using this code in an OpenGL 3+ context where shaders may be bound - - // Setup viewport, orthographic projection matrix - // Our visible imgui space lies from draw_data->DisplayPps (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is typically (0,0) for single viewport apps. - glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glOrtho(draw_data->DisplayPos.x, draw_data->DisplayPos.x + draw_data->DisplaySize.x, draw_data->DisplayPos.y + draw_data->DisplaySize.y, draw_data->DisplayPos.y, -1.0f, +1.0f); - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glLoadIdentity(); - - // Render command lists - ImVec2 pos = draw_data->DisplayPos; - for (int n = 0; n < draw_data->CmdListsCount; n++) - { - const ImDrawList* cmd_list = draw_data->CmdLists[n]; - const ImDrawVert* vtx_buffer = cmd_list->VtxBuffer.Data; - const ImDrawIdx* idx_buffer = cmd_list->IdxBuffer.Data; - glVertexPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, pos))); - glTexCoordPointer(2, GL_FLOAT, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, uv))); - glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(ImDrawVert), (const GLvoid*)((const char*)vtx_buffer + IM_OFFSETOF(ImDrawVert, col))); - - for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) - { - const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; - if (pcmd->UserCallback) - { - // User callback (registered via ImDrawList::AddCallback) - pcmd->UserCallback(cmd_list, pcmd); - } - else - { - ImVec4 clip_rect = ImVec4(pcmd->ClipRect.x - pos.x, pcmd->ClipRect.y - pos.y, pcmd->ClipRect.z - pos.x, pcmd->ClipRect.w - pos.y); - if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) - { - // Apply scissor/clipping rectangle - glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y)); - - // Bind texture, Draw - glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId); - glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer); - } - } - idx_buffer += pcmd->ElemCount; - } - } - - // Restore modified state - glDisableClientState(GL_COLOR_ARRAY); - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glDisableClientState(GL_VERTEX_ARRAY); - glBindTexture(GL_TEXTURE_2D, (GLuint)last_texture); - - glMatrixMode(GL_TEXTURE); // [Bruno Levy] 05/18/2016 Restore texture matrix. - glPopMatrix(); - - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - glMatrixMode(GL_PROJECTION); - glPopMatrix(); - glPopAttrib(); - glPolygonMode(GL_FRONT, (GLenum)last_polygon_mode[0]); glPolygonMode(GL_BACK, (GLenum)last_polygon_mode[1]); - glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]); - glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]); -} - -bool ImGui_ImplOpenGL2_CreateFontsTexture() -{ - // Build texture atlas - ImGuiIO& io = ImGui::GetIO(); - unsigned char* pixels; - int width, height; - io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. - - // Upload texture to graphics system - GLint last_texture; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - glGenTextures(1, &g_FontTexture); - glBindTexture(GL_TEXTURE_2D, g_FontTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); - - // Store our identifier - io.Fonts->TexID = (void *)(intptr_t)g_FontTexture; - - // Restore state - glBindTexture(GL_TEXTURE_2D, last_texture); - - return true; -} - -void ImGui_ImplOpenGL2_DestroyFontsTexture() -{ - if (g_FontTexture) - { - ImGuiIO& io = ImGui::GetIO(); - glDeleteTextures(1, &g_FontTexture); - io.Fonts->TexID = 0; - g_FontTexture = 0; - } -} - -bool ImGui_ImplOpenGL2_CreateDeviceObjects() -{ - return ImGui_ImplOpenGL2_CreateFontsTexture(); -} - -void ImGui_ImplOpenGL2_DestroyDeviceObjects() -{ - ImGui_ImplOpenGL2_DestroyFontsTexture(); -} - -#endif diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_opengl2.h b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_opengl2.h deleted file mode 100644 index b2f67580..00000000 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_opengl2.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * ImGui Renderer for: OpenGL2 (legacy OpenGL, fixed pipeline) - * This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) - * - * Implemented features: - * [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void* / ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. - * - * **DO NOT USE THIS CODE IF YOUR CODE/ENGINE IS USING MODERN OPENGL (SHADERS, VBO, VAO, etc.)** - * **Prefer using the code in imgui_impl_opengl3.cpp** - * This code is mostly provided as a reference to learn how ImGui integration works, because it is shorter to read. - * If your code is using GL3+ context or any semi modern OpenGL calls, using this is likely to make everything more - * complicated, will require your code to reset every single OpenGL attributes to their initial state, and might - * confuse your GPU driver. - * The GL2 code is unable to reset attributes or even call e.g. "glUseProgram(0)" because they don't exist in that API. - */ - -/* [Bruno] C-style comment */ - -// [Bruno 05/16/2016] conditional compilation -#include -#ifdef GEO_GL_LEGACY - -/* [Bruno] */ -#ifdef __cplusplus -extern "C" { -#endif - -IMGUI_IMPL_API bool ImGui_ImplOpenGL2_Init(); -IMGUI_IMPL_API void ImGui_ImplOpenGL2_Shutdown(); -IMGUI_IMPL_API void ImGui_ImplOpenGL2_NewFrame(); -IMGUI_IMPL_API void ImGui_ImplOpenGL2_RenderDrawData(ImDrawData* draw_data); - -/* Called by Init/NewFrame/Shutdown */ -IMGUI_IMPL_API bool ImGui_ImplOpenGL2_CreateFontsTexture(); -IMGUI_IMPL_API void ImGui_ImplOpenGL2_DestroyFontsTexture(); -IMGUI_IMPL_API bool ImGui_ImplOpenGL2_CreateDeviceObjects(); -IMGUI_IMPL_API void ImGui_ImplOpenGL2_DestroyDeviceObjects(); - -/* [Bruno] */ -#ifdef __cplusplus -} -#endif - -#endif - diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_opengl3.cpp b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_opengl3.cpp deleted file mode 100644 index 96babba4..00000000 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_opengl3.cpp +++ /dev/null @@ -1,370 +0,0 @@ -// ImGui Renderer for: OpenGL3 (modern OpenGL with shaders / programmatic pipeline) -// This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) -// (Note: We are using GL3W as a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. Alternatives are GLEW, Glad, etc..) - -// Implemented features: -// [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void*/ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. - -// CHANGELOG -// (minor and older changes stripped away, please see git history for details) -// 2018-06-08: Misc: Extracted imgui_impl_opengl3.cpp/.h away from the old combined GLFW/SDL+OpenGL3 examples. -// 2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle. -// 2018-05-25: OpenGL: Removed unnecessary backup/restore of GL_ELEMENT_ARRAY_BUFFER_BINDING since this is part of the VAO state. -// 2018-05-14: OpenGL: Making the call to glBindSampler() optional so 3.2 context won't fail if the function is a NULL pointer. -// 2018-03-06: OpenGL: Added const char* glsl_version parameter to ImGui_ImplOpenGL3_Init() so user can override the GLSL version e.g. "#version 150". -// 2018-02-23: OpenGL: Create the VAO in the render function so the setup can more easily be used with multiple shared GL context. -// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplSdlGL3_RenderDrawData() in the .h file so you can call it yourself. -// 2018-01-07: OpenGL: Changed GLSL shader version from 330 to 150. -// 2017-09-01: OpenGL: Save and restore current bound sampler. Save and restore current polygon mode. -// 2017-05-01: OpenGL: Fixed save and restore of current blend func state. -// 2017-05-01: OpenGL: Fixed save and restore of current GL_ACTIVE_TEXTURE. -// 2016-09-05: OpenGL: Fixed save and restore of current scissor rectangle. -// 2016-07-29: OpenGL: Explicitly setting GL_UNPACK_ROW_LENGTH to reduce issues because SDL changes it. (#752) - -#ifdef __clang__ // [Bruno] -# pragma GCC diagnostic ignored "-Wpointer-bool-conversion" -#endif - -#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) -#define _CRT_SECURE_NO_WARNINGS -#endif - -#include "imgui.h" -#include "imgui_impl_opengl3.h" - -// [Bruno] -#include -#include -#include -#include "glup_compat.h" -// End [Bruno] - - - -// OpenGL Data -static char g_GlslVersion[32] = "#version 150"; -static GLuint g_FontTexture = 0; -static int g_ShaderHandle = 0, g_VertHandle = 0, g_FragHandle = 0; -static int g_AttribLocationTex = 0, g_AttribLocationProjMtx = 0; -static int g_AttribLocationPosition = 0, g_AttribLocationUV = 0, g_AttribLocationColor = 0; -static unsigned int g_VboHandle = 0, g_ElementsHandle = 0; - -// Functions -bool ImGui_ImplOpenGL3_Init(const char* glsl_version) -{ - // Store GLSL version string so we can refer to it later in case we recreate shaders. Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure. - if (glsl_version == NULL) - glsl_version = "#version 150"; - IM_ASSERT((int)strlen(glsl_version) + 2 < IM_ARRAYSIZE(g_GlslVersion)); - strcpy(g_GlslVersion, glsl_version); - strcat(g_GlslVersion, "\n"); - return true; -} - -void ImGui_ImplOpenGL3_Shutdown() -{ - ImGui_ImplOpenGL3_DestroyDeviceObjects(); -} - -void ImGui_ImplOpenGL3_NewFrame() -{ - if (!g_FontTexture) - ImGui_ImplOpenGL3_CreateDeviceObjects(); -} - -// OpenGL3 Render function. -// (this used to be set in io.RenderDrawListsFn and called by ImGui::Render(), but you can now call this directly from your main loop) -// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so. -void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) -{ - // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) - ImGuiIO& io = ImGui::GetIO(); - int fb_width = (int)(draw_data->DisplaySize.x * io.DisplayFramebufferScale.x); - int fb_height = (int)(draw_data->DisplaySize.y * io.DisplayFramebufferScale.y); - if (fb_width <= 0 || fb_height <= 0) - return; - draw_data->ScaleClipRects(io.DisplayFramebufferScale); - - // Backup GL state - GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture); - glActiveTexture(GL_TEXTURE0); - GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program); - GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - GLint last_sampler; glGetIntegerv(GL_SAMPLER_BINDING, &last_sampler); - GLint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); - GLint last_vertex_array; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); - GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); - GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport); - GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box); - GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, (GLint*)&last_blend_src_rgb); - GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, (GLint*)&last_blend_dst_rgb); - GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint*)&last_blend_src_alpha); - GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint*)&last_blend_dst_alpha); - GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, (GLint*)&last_blend_equation_rgb); - GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, (GLint*)&last_blend_equation_alpha); - GLboolean last_enable_blend = glIsEnabled(GL_BLEND); - GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE); - GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST); - GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST); - - // Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, polygon fill - glEnable(GL_BLEND); - glBlendEquation(GL_FUNC_ADD); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glDisable(GL_CULL_FACE); - glDisable(GL_DEPTH_TEST); - glEnable(GL_SCISSOR_TEST); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - - // Setup viewport, orthographic projection matrix - // Our visible imgui space lies from draw_data->DisplayPps (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is typically (0,0) for single viewport apps. - glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); - float L = draw_data->DisplayPos.x; - float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; - float T = draw_data->DisplayPos.y; - float B = draw_data->DisplayPos.y + draw_data->DisplaySize.y; - const float ortho_projection[4][4] = - { - { 2.0f/(R-L), 0.0f, 0.0f, 0.0f }, - { 0.0f, 2.0f/(T-B), 0.0f, 0.0f }, - { 0.0f, 0.0f, -1.0f, 0.0f }, - { (R+L)/(L-R), (T+B)/(B-T), 0.0f, 1.0f }, - }; - glUseProgram(g_ShaderHandle); - glUniform1i(g_AttribLocationTex, 0); - glUniformMatrix4fv(g_AttribLocationProjMtx, 1, GL_FALSE, &ortho_projection[0][0]); - if (glBindSampler) glBindSampler(0, 0); // We use combined texture/sampler state. Applications using GL 3.3 may set that otherwise. - - // Recreate the VAO every time - // (This is to easily allow multiple GL contexts. VAO are not shared among GL contexts, and we don't track creation/deletion of windows so we don't have an obvious key to use to cache them.) - GLuint vao_handle = 0; - glGenVertexArrays(1, &vao_handle); - glBindVertexArray(vao_handle); - glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle); - glEnableVertexAttribArray(g_AttribLocationPosition); - glEnableVertexAttribArray(g_AttribLocationUV); - glEnableVertexAttribArray(g_AttribLocationColor); - glVertexAttribPointer(g_AttribLocationPosition, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos)); - glVertexAttribPointer(g_AttribLocationUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv)); - glVertexAttribPointer(g_AttribLocationColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col)); - - // Draw - ImVec2 pos = draw_data->DisplayPos; - for (int n = 0; n < draw_data->CmdListsCount; n++) - { - const ImDrawList* cmd_list = draw_data->CmdLists[n]; - const ImDrawIdx* idx_buffer_offset = 0; - - glBindBuffer(GL_ARRAY_BUFFER, g_VboHandle); - glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)cmd_list->VtxBuffer.Size * sizeof(ImDrawVert), (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW); - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, g_ElementsHandle); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx), (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW); - - for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) - { - const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; - if (pcmd->UserCallback) - { - // User callback (registered via ImDrawList::AddCallback) - pcmd->UserCallback(cmd_list, pcmd); - } - else - { - ImVec4 clip_rect = ImVec4(pcmd->ClipRect.x - pos.x, pcmd->ClipRect.y - pos.y, pcmd->ClipRect.z - pos.x, pcmd->ClipRect.w - pos.y); - if (clip_rect.x < fb_width && clip_rect.y < fb_height && clip_rect.z >= 0.0f && clip_rect.w >= 0.0f) - { - // Apply scissor/clipping rectangle - glScissor((int)clip_rect.x, (int)(fb_height - clip_rect.w), (int)(clip_rect.z - clip_rect.x), (int)(clip_rect.w - clip_rect.y)); - - // Bind texture, Draw - glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->TextureId); - glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, idx_buffer_offset); - } - } - idx_buffer_offset += pcmd->ElemCount; - } - } - glDeleteVertexArrays(1, &vao_handle); - - // Restore modified GL state - glUseProgram(last_program); - glBindTexture(GL_TEXTURE_2D, last_texture); - if (glBindSampler) glBindSampler(0, last_sampler); - glActiveTexture(last_active_texture); - glBindVertexArray(last_vertex_array); - glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); - glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha); - glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha); - if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND); - if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); - if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST); - if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST); - glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]); - glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]); - glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]); -} - -bool ImGui_ImplOpenGL3_CreateFontsTexture() -{ - // Build texture atlas - ImGuiIO& io = ImGui::GetIO(); - unsigned char* pixels; - int width, height; - io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); // Load as RGBA 32-bits (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory. - - // Upload texture to graphics system - GLint last_texture; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - glGenTextures(1, &g_FontTexture); - glBindTexture(GL_TEXTURE_2D, g_FontTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); - - // Store our identifier - io.Fonts->TexID = (void *)(intptr_t)g_FontTexture; - - // Restore state - glBindTexture(GL_TEXTURE_2D, last_texture); - - return true; -} - -void ImGui_ImplOpenGL3_DestroyFontsTexture() -{ - if (g_FontTexture) - { - ImGuiIO& io = ImGui::GetIO(); - glDeleteTextures(1, &g_FontTexture); - io.Fonts->TexID = 0; - g_FontTexture = 0; - } -} - -bool ImGui_ImplOpenGL3_CreateDeviceObjects() -{ - // Backup GL state - GLint last_texture, last_array_buffer, last_vertex_array; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); - glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); - - // Create shaders - - const GLchar* vertex_shader = - "uniform mat4 ProjMtx;\n" - "in vec2 Position;\n" - "in vec2 UV;\n" - "in vec4 Color;\n" - "out vec2 Frag_UV;\n" - "out vec4 Frag_Color;\n" - "void main()\n" - "{\n" - " Frag_UV = UV;\n" - " Frag_Color = Color;\n" - " gl_Position = ProjMtx * vec4(Position.xy,0,1);\n" - "}\n"; - - const GLchar* fragment_shader = - "precision mediump float;\n" - "uniform sampler2D Texture;\n" - "in vec2 Frag_UV;\n" - "in vec4 Frag_Color;\n" - "out vec4 Out_Color;\n" - "void main()\n" - "{\n" - " Out_Color = Frag_Color * texture( Texture, Frag_UV.st);\n" - "}\n"; - - - // [Bruno] Added shaders for older GL APIs (e.g. OpenGL ES 2.0) - - const GLchar* vertex_shader_old = - "uniform mat4 ProjMtx; \n" - "attribute vec2 Position; \n" - "attribute vec2 UV; \n" - "attribute vec4 Color; \n" - "varying vec2 Frag_UV; \n" - "varying vec4 Frag_Color; \n" - "void main() { \n" - " Frag_UV = UV; \n" - " Frag_Color = Color; \n" - " gl_Position = ProjMtx * vec4(Position.xy,0,1); \n" - "} \n"; - - const GLchar* fragment_shader_old = - "precision mediump float; \n" - "uniform sampler2D Texture; \n" - "varying vec2 Frag_UV; \n" - "varying vec4 Frag_Color; \n" - "void main() { \n" - " gl_FragColor = Frag_Color * \n" - " texture2D(Texture, Frag_UV.st); \n" - "} \n"; - - - bool use_old = (strncmp(g_GlslVersion,"#version 100", 12) == 0); - - const GLchar* vertex_shader_with_version[2] = { - g_GlslVersion, use_old ? vertex_shader_old : vertex_shader - }; - - const GLchar* fragment_shader_with_version[2] = { - g_GlslVersion, use_old ? fragment_shader_old : fragment_shader - }; - - // [Bruno] 05/16/2016 Using Geogram_gfx's shader creation functions, so that - // we can have error reporting when compiling the shaders. - g_VertHandle = GEO::GLSL::compile_shader( - GL_VERTEX_SHADER, - vertex_shader_with_version[0], vertex_shader_with_version[1], - NULL - ); - g_FragHandle = GEO::GLSL::compile_shader( - GL_FRAGMENT_SHADER, - fragment_shader_with_version[0], fragment_shader_with_version[1], - NULL - ); - g_ShaderHandle = GEO::GLSL::create_program_from_shaders(g_VertHandle, g_FragHandle, 0); - // End [Bruno Levy] - - g_AttribLocationTex = glGetUniformLocation(g_ShaderHandle, "Texture"); - g_AttribLocationProjMtx = glGetUniformLocation(g_ShaderHandle, "ProjMtx"); - g_AttribLocationPosition = glGetAttribLocation(g_ShaderHandle, "Position"); - g_AttribLocationUV = glGetAttribLocation(g_ShaderHandle, "UV"); - g_AttribLocationColor = glGetAttribLocation(g_ShaderHandle, "Color"); - - glGenBuffers(1, &g_VboHandle); - glGenBuffers(1, &g_ElementsHandle); - - ImGui_ImplOpenGL3_CreateFontsTexture(); - - // Restore modified GL state - glBindTexture(GL_TEXTURE_2D, last_texture); - glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); - glBindVertexArray(last_vertex_array); - - return true; -} - -void ImGui_ImplOpenGL3_DestroyDeviceObjects() -{ - if (g_VboHandle) glDeleteBuffers(1, &g_VboHandle); - if (g_ElementsHandle) glDeleteBuffers(1, &g_ElementsHandle); - g_VboHandle = g_ElementsHandle = 0; - - if (g_ShaderHandle && g_VertHandle) glDetachShader(g_ShaderHandle, g_VertHandle); - if (g_VertHandle) glDeleteShader(g_VertHandle); - g_VertHandle = 0; - - if (g_ShaderHandle && g_FragHandle) glDetachShader(g_ShaderHandle, g_FragHandle); - if (g_FragHandle) glDeleteShader(g_FragHandle); - g_FragHandle = 0; - - if (g_ShaderHandle) glDeleteProgram(g_ShaderHandle); - g_ShaderHandle = 0; - - ImGui_ImplOpenGL3_DestroyFontsTexture(); -} diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_opengl3.h b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_opengl3.h deleted file mode 100644 index 3f7a50ca..00000000 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_opengl3.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * ImGui Renderer for: OpenGL3 (modern OpenGL with shaders / programmatic pipeline) - * This needs to be used along with a Platform Binding (e.g. GLFW, SDL, Win32, custom..) - * (Note: We are using GL3W as a helper library to access OpenGL functions since there is no standard header to access modern OpenGL functions easily. - * Alternatives are GLEW, Glad, etc..) - * - * Implemented features: - * [X] Renderer: User texture binding. Use 'GLuint' OpenGL texture identifier as void* / ImTextureID. Read the FAQ about ImTextureID in imgui.cpp. - * - * About GLSL version: - * The 'glsl_version' initialization parameter defaults to "#version 150" if NULL. - * Only override if your GL version doesn't handle this GLSL version. Keep NULL if unsure! - */ - -/* [Bruno] C-style comment */ - -/* [Bruno] */ -#ifdef __cplusplus -extern "C" { -#endif - -IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = "#version 150"); -IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown(); -IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame(); -IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data); - -/* Called by Init/NewFrame/Shutdown */ -IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateFontsTexture(); -IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyFontsTexture(); -IMGUI_IMPL_API bool ImGui_ImplOpenGL3_CreateDeviceObjects(); -IMGUI_IMPL_API void ImGui_ImplOpenGL3_DestroyDeviceObjects(); - -/* [Bruno] */ -#ifdef __cplusplus -} -#endif - diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_win32.cpp b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_win32.cpp deleted file mode 100644 index 926b7b69..00000000 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_win32.cpp +++ /dev/null @@ -1,247 +0,0 @@ -// ImGui Platform Binding for: Windows (standard windows API for 32 and 64 bits applications) -// This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) - -// Implemented features: -// [X] Platform: Clipboard support (for Win32 this is actually part of core imgui) -// [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. - -#ifdef _WIN32 /* [Bruno] */ - -#include "imgui.h" -#include "imgui_impl_win32.h" -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include -#include - -// CHANGELOG -// (minor and older changes stripped away, please see git history for details) -// 2018-06-10: Inputs: Fixed handling of mouse wheel messages to support fine position messages (typically sent by track-pads). -// 2018-06-08: Misc: Extracted imgui_impl_win32.cpp/.h away from the old combined DX9/DX10/DX11/DX12 examples. -// 2018-03-20: Misc: Setup io.BackendFlags ImGuiBackendFlags_HasMouseCursors and ImGuiBackendFlags_HasSetMousePos flags + honor ImGuiConfigFlags_NoMouseCursorChange flag. -// 2018-02-20: Inputs: Added support for mouse cursors (ImGui::GetMouseCursor() value and WM_SETCURSOR message handling). -// 2018-02-06: Inputs: Added mapping for ImGuiKey_Space. -// 2018-02-06: Inputs: Honoring the io.WantSetMousePos by repositioning the mouse (when using navigation and ImGuiConfigFlags_NavMoveMouse is set). -// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. -// 2018-01-20: Inputs: Added Horizontal Mouse Wheel support. -// 2018-01-08: Inputs: Added mapping for ImGuiKey_Insert. -// 2018-01-05: Inputs: Added WM_LBUTTONDBLCLK double-click handlers for window classes with the CS_DBLCLKS flag. -// 2017-10-23: Inputs: Added WM_SYSKEYDOWN / WM_SYSKEYUP handlers so e.g. the VK_MENU key can be read. -// 2017-10-23: Inputs: Using Win32 ::SetCapture/::GetCapture() to retrieve mouse positions outside the client area when dragging. -// 2016-11-12: Inputs: Only call Win32 ::SetCursor(NULL) when io.MouseDrawCursor is set. - -// Win32 Data -static HWND g_hWnd = 0; -static INT64 g_Time = 0; -static INT64 g_TicksPerSecond = 0; -static ImGuiMouseCursor g_LastMouseCursor = ImGuiMouseCursor_COUNT; - -// Functions -bool ImGui_ImplWin32_Init(void* hwnd) -{ - if (!::QueryPerformanceFrequency((LARGE_INTEGER *)&g_TicksPerSecond)) - return false; - if (!::QueryPerformanceCounter((LARGE_INTEGER *)&g_Time)) - return false; - - // Setup back-end capabilities flags - g_hWnd = (HWND)hwnd; - ImGuiIO& io = ImGui::GetIO(); - io.BackendFlags |= ImGuiBackendFlags_HasMouseCursors; // We can honor GetMouseCursor() values (optional) - io.BackendFlags |= ImGuiBackendFlags_HasSetMousePos; // We can honor io.WantSetMousePos requests (optional, rarely used) - io.ImeWindowHandle = hwnd; - - // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array that we will update during the application lifetime. - io.KeyMap[ImGuiKey_Tab] = VK_TAB; - io.KeyMap[ImGuiKey_LeftArrow] = VK_LEFT; - io.KeyMap[ImGuiKey_RightArrow] = VK_RIGHT; - io.KeyMap[ImGuiKey_UpArrow] = VK_UP; - io.KeyMap[ImGuiKey_DownArrow] = VK_DOWN; - io.KeyMap[ImGuiKey_PageUp] = VK_PRIOR; - io.KeyMap[ImGuiKey_PageDown] = VK_NEXT; - io.KeyMap[ImGuiKey_Home] = VK_HOME; - io.KeyMap[ImGuiKey_End] = VK_END; - io.KeyMap[ImGuiKey_Insert] = VK_INSERT; - io.KeyMap[ImGuiKey_Delete] = VK_DELETE; - io.KeyMap[ImGuiKey_Backspace] = VK_BACK; - io.KeyMap[ImGuiKey_Space] = VK_SPACE; - io.KeyMap[ImGuiKey_Enter] = VK_RETURN; - io.KeyMap[ImGuiKey_Escape] = VK_ESCAPE; - io.KeyMap[ImGuiKey_A] = 'A'; - io.KeyMap[ImGuiKey_C] = 'C'; - io.KeyMap[ImGuiKey_V] = 'V'; - io.KeyMap[ImGuiKey_X] = 'X'; - io.KeyMap[ImGuiKey_Y] = 'Y'; - io.KeyMap[ImGuiKey_Z] = 'Z'; - - return true; -} - -void ImGui_ImplWin32_Shutdown() -{ - g_hWnd = (HWND)0; -} - -static bool ImGui_ImplWin32_UpdateMouseCursor() -{ - ImGuiIO& io = ImGui::GetIO(); - if (io.ConfigFlags & ImGuiConfigFlags_NoMouseCursorChange) - return false; - - ImGuiMouseCursor imgui_cursor = ImGui::GetMouseCursor(); - if (imgui_cursor == ImGuiMouseCursor_None || io.MouseDrawCursor) - { - // Hide OS mouse cursor if imgui is drawing it or if it wants no cursor - ::SetCursor(NULL); - } - else - { - // Show OS mouse cursor - LPTSTR win32_cursor = IDC_ARROW; - switch (imgui_cursor) - { - case ImGuiMouseCursor_Arrow: win32_cursor = IDC_ARROW; break; - case ImGuiMouseCursor_TextInput: win32_cursor = IDC_IBEAM; break; - case ImGuiMouseCursor_ResizeAll: win32_cursor = IDC_SIZEALL; break; - case ImGuiMouseCursor_ResizeEW: win32_cursor = IDC_SIZEWE; break; - case ImGuiMouseCursor_ResizeNS: win32_cursor = IDC_SIZENS; break; - case ImGuiMouseCursor_ResizeNESW: win32_cursor = IDC_SIZENESW; break; - case ImGuiMouseCursor_ResizeNWSE: win32_cursor = IDC_SIZENWSE; break; - } - ::SetCursor(::LoadCursor(NULL, win32_cursor)); - } - return true; -} - -static void ImGui_ImplWin32_UpdateMousePos() -{ - ImGuiIO& io = ImGui::GetIO(); - - // Set OS mouse position if requested (rarely used, only when ImGuiConfigFlags_NavEnableSetMousePos is enabled by user) - if (io.WantSetMousePos) - { - POINT pos = { (int)io.MousePos.x, (int)io.MousePos.y }; - ::ClientToScreen(g_hWnd, &pos); - ::SetCursorPos(pos.x, pos.y); - } - - // Set mouse position - io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); - POINT pos; - if (::GetActiveWindow() == g_hWnd && ::GetCursorPos(&pos)) - if (::ScreenToClient(g_hWnd, &pos)) - io.MousePos = ImVec2((float)pos.x, (float)pos.y); -} - -void ImGui_ImplWin32_NewFrame() -{ - ImGuiIO& io = ImGui::GetIO(); - - // Setup display size (every frame to accommodate for window resizing) - RECT rect; - ::GetClientRect(g_hWnd, &rect); - io.DisplaySize = ImVec2((float)(rect.right - rect.left), (float)(rect.bottom - rect.top)); - - // Setup time step - INT64 current_time; - ::QueryPerformanceCounter((LARGE_INTEGER *)¤t_time); - io.DeltaTime = (float)(current_time - g_Time) / g_TicksPerSecond; - g_Time = current_time; - - // Read keyboard modifiers inputs - io.KeyCtrl = (::GetKeyState(VK_CONTROL) & 0x8000) != 0; - io.KeyShift = (::GetKeyState(VK_SHIFT) & 0x8000) != 0; - io.KeyAlt = (::GetKeyState(VK_MENU) & 0x8000) != 0; - io.KeySuper = false; - // io.KeysDown[], io.MousePos, io.MouseDown[], io.MouseWheel: filled by the WndProc handler below. - - // Update OS mouse position - ImGui_ImplWin32_UpdateMousePos(); - - // Update OS mouse cursor with the cursor requested by imgui - ImGuiMouseCursor mouse_cursor = io.MouseDrawCursor ? ImGuiMouseCursor_None : ImGui::GetMouseCursor(); - if (g_LastMouseCursor != mouse_cursor) - { - g_LastMouseCursor = mouse_cursor; - ImGui_ImplWin32_UpdateMouseCursor(); - } -} - -// Allow compilation with old Windows SDK. MinGW doesn't have default _WIN32_WINNT/WINVER versions. -#ifndef WM_MOUSEHWHEEL -#define WM_MOUSEHWHEEL 0x020E -#endif - -// Process Win32 mouse/keyboard inputs. -// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. -// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application. -// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application. -// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. -// PS: In this Win32 handler, we use the capture API (GetCapture/SetCapture/ReleaseCapture) to be able to read mouse coordinations when dragging mouse outside of our window bounds. -// PS: We treat DBLCLK messages as regular mouse down messages, so this code will work on windows classes that have the CS_DBLCLKS flag set. Our own example app code doesn't set this flag. -IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) -{ - if (ImGui::GetCurrentContext() == NULL) - return 0; - - ImGuiIO& io = ImGui::GetIO(); - switch (msg) - { - case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: - case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: - case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: - { - int button = 0; - if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) button = 0; - if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) button = 1; - if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK) button = 2; - if (!ImGui::IsAnyMouseDown() && ::GetCapture() == NULL) - ::SetCapture(hwnd); - io.MouseDown[button] = true; - return 0; - } - case WM_LBUTTONUP: - case WM_RBUTTONUP: - case WM_MBUTTONUP: - { - int button = 0; - if (msg == WM_LBUTTONUP) button = 0; - if (msg == WM_RBUTTONUP) button = 1; - if (msg == WM_MBUTTONUP) button = 2; - io.MouseDown[button] = false; - if (!ImGui::IsAnyMouseDown() && ::GetCapture() == hwnd) - ::ReleaseCapture(); - return 0; - } - case WM_MOUSEWHEEL: - io.MouseWheel += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA; - return 0; - case WM_MOUSEHWHEEL: - io.MouseWheelH += (float)GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA; - return 0; - case WM_KEYDOWN: - case WM_SYSKEYDOWN: - if (wParam < 256) - io.KeysDown[wParam] = 1; - return 0; - case WM_KEYUP: - case WM_SYSKEYUP: - if (wParam < 256) - io.KeysDown[wParam] = 0; - return 0; - case WM_CHAR: - // You can also use ToAscii()+GetKeyboardState() to retrieve characters. - if (wParam > 0 && wParam < 0x10000) - io.AddInputCharacter((unsigned short)wParam); - return 0; - case WM_SETCURSOR: - if (LOWORD(lParam) == HTCLIENT && ImGui_ImplWin32_UpdateMouseCursor()) - return 1; - return 0; - } - return 0; -} - -#endif /* [Bruno] */ - diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_win32.h b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_win32.h deleted file mode 100644 index 275d7709..00000000 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_impl_win32.h +++ /dev/null @@ -1,36 +0,0 @@ -/* - * ImGui Platform Binding for: Windows (standard windows API for 32 and 64 bits applications) - * This needs to be used along with a Renderer (e.g. DirectX11, OpenGL3, Vulkan..) - * - * Implemented features: - * [X] Platform: Clipboard support (for Win32 this is actually part of core imgui) - * [X] Platform: Mouse cursor shape and visibility. Disable with 'io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange'. - */ - -/* [Bruno] C-style comment */ - -/* [Bruno] */ -#ifdef _WIN32 -#ifdef __cplusplus -extern "C" { -#endif - - -IMGUI_IMPL_API bool ImGui_ImplWin32_Init(void* hwnd); -IMGUI_IMPL_API void ImGui_ImplWin32_Shutdown(); -IMGUI_IMPL_API void ImGui_ImplWin32_NewFrame(); - -/* - * Handler for Win32 messages, update mouse/keyboard data. - * You may or not need this for your implementation, but it can serve as reference for handling inputs. - * Intentionally commented out to avoid dragging dependencies on types. You can copy the extern declaration in your code. - */ -/* -IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); -*/ - -/* [Bruno] */ -#ifdef __cplusplus -} -#endif -#endif diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_internal.h b/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_internal.h deleted file mode 100644 index bd129ae6..00000000 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/imgui_internal.h +++ /dev/null @@ -1,1195 +0,0 @@ -// dear imgui, v1.62 -// (internals) - -// You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility! -// Set: -// #define IMGUI_DEFINE_MATH_OPERATORS -// To implement maths operators for ImVec2 (disabled by default to not collide with using IM_VEC2_CLASS_EXTRA along with your own math types+operators) - -#pragma once - -#ifndef IMGUI_VERSION -#error Must include imgui.h before imgui_internal.h -#endif - -#include // FILE* -#include // NULL, malloc, free, qsort, atoi, atof -#include // sqrtf, fabsf, fmodf, powf, floorf, ceilf, cosf, sinf -#include // INT_MIN, INT_MAX - -#ifdef _MSC_VER -#pragma warning (push) -#pragma warning (disable: 4251) // class 'xxx' needs to have dll-interface to be used by clients of struct 'xxx' // when IMGUI_API is set to__declspec(dllexport) -#endif - -#ifdef __clang__ -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-function" // for stb_textedit.h -#pragma clang diagnostic ignored "-Wmissing-prototypes" // for stb_textedit.h -#pragma clang diagnostic ignored "-Wold-style-cast" -#endif - -//----------------------------------------------------------------------------- -// Forward Declarations -//----------------------------------------------------------------------------- - -struct ImRect; // An axis-aligned rectangle (2 points) -struct ImDrawDataBuilder; // Helper to build a ImDrawData instance -struct ImDrawListSharedData; // Data shared between all ImDrawList instances -struct ImGuiColMod; // Stacked color modifier, backup of modified data so we can restore it -struct ImGuiColumnData; // Storage data for a single column -struct ImGuiColumnsSet; // Storage data for a columns set -struct ImGuiContext; // Main imgui context -struct ImGuiGroupData; // Stacked storage data for BeginGroup()/EndGroup() -struct ImGuiItemHoveredDataBackup; // Backup and restore IsItemHovered() internal data -struct ImGuiMenuColumns; // Simple column measurement, currently used for MenuItem() only -struct ImGuiNavMoveResult; // Result of a directional navigation move query result -struct ImGuiNextWindowData; // Storage for SetNexWindow** functions -struct ImGuiPopupRef; // Storage for current popup stack -struct ImGuiSettingsHandler; -struct ImGuiStyleMod; // Stacked style modifier, backup of modified data so we can restore it -struct ImGuiTextEditState; // Internal state of the currently focused/edited text input box -struct ImGuiWindow; // Storage for one window -struct ImGuiWindowTempData; // Temporary storage for one, that's the data which in theory we could ditch at the end of the frame -struct ImGuiWindowSettings; // Storage for window settings stored in .ini file (we keep one of those even if the actual window wasn't instanced during this session) - -typedef int ImGuiLayoutType; // enum: horizontal or vertical // enum ImGuiLayoutType_ -typedef int ImGuiButtonFlags; // flags: for ButtonEx(), ButtonBehavior() // enum ImGuiButtonFlags_ -typedef int ImGuiItemFlags; // flags: for PushItemFlag() // enum ImGuiItemFlags_ -typedef int ImGuiItemStatusFlags; // flags: storage for DC.LastItemXXX // enum ImGuiItemStatusFlags_ -typedef int ImGuiNavHighlightFlags; // flags: for RenderNavHighlight() // enum ImGuiNavHighlightFlags_ -typedef int ImGuiNavDirSourceFlags; // flags: for GetNavInputAmount2d() // enum ImGuiNavDirSourceFlags_ -typedef int ImGuiNavMoveFlags; // flags: for navigation requests // enum ImGuiNavMoveFlags_ -typedef int ImGuiSeparatorFlags; // flags: for Separator() - internal // enum ImGuiSeparatorFlags_ -typedef int ImGuiSliderFlags; // flags: for SliderBehavior() // enum ImGuiSliderFlags_ - -//------------------------------------------------------------------------- -// STB libraries -//------------------------------------------------------------------------- - -namespace ImGuiStb -{ - -#undef STB_TEXTEDIT_STRING -#undef STB_TEXTEDIT_CHARTYPE -#define STB_TEXTEDIT_STRING ImGuiTextEditState -#define STB_TEXTEDIT_CHARTYPE ImWchar -#define STB_TEXTEDIT_GETWIDTH_NEWLINE -1.0f -#include "stb_textedit.h" - -} // namespace ImGuiStb - -//----------------------------------------------------------------------------- -// Context -//----------------------------------------------------------------------------- - -#ifndef GImGui -extern IMGUI_API ImGuiContext* GImGui; // Current implicit ImGui context pointer -#endif - -//----------------------------------------------------------------------------- -// Helpers -//----------------------------------------------------------------------------- - -#define IM_PI 3.14159265358979323846f -#ifdef _WIN32 -#define IM_NEWLINE "\r\n" // Play it nice with Windows users (2018/05 news: Microsoft announced that Notepad will finally display Unix-style carriage returns!) -#else -#define IM_NEWLINE "\n" -#endif - -// Helpers: UTF-8 <> wchar -IMGUI_API int ImTextStrToUtf8(char* buf, int buf_size, const ImWchar* in_text, const ImWchar* in_text_end); // return output UTF-8 bytes count -IMGUI_API int ImTextCharFromUtf8(unsigned int* out_char, const char* in_text, const char* in_text_end); // return input UTF-8 bytes count -IMGUI_API int ImTextStrFromUtf8(ImWchar* buf, int buf_size, const char* in_text, const char* in_text_end, const char** in_remaining = NULL); // return input UTF-8 bytes count -IMGUI_API int ImTextCountCharsFromUtf8(const char* in_text, const char* in_text_end); // return number of UTF-8 code-points (NOT bytes count) -IMGUI_API int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end); // return number of bytes to express string as UTF-8 code-points - -// Helpers: Misc -IMGUI_API ImU32 ImHash(const void* data, int data_size, ImU32 seed = 0); // Pass data_size==0 for zero-terminated strings -IMGUI_API void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, size_t* out_file_size = NULL, int padding_bytes = 0); -IMGUI_API FILE* ImFileOpen(const char* filename, const char* file_open_mode); -static inline bool ImCharIsBlankA(char c) { return c == ' ' || c == '\t'; } -static inline bool ImCharIsBlankW(unsigned int c) { return c == ' ' || c == '\t' || c == 0x3000; } -static inline bool ImIsPowerOfTwo(int v) { return v != 0 && (v & (v - 1)) == 0; } -static inline int ImUpperPowerOfTwo(int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; } - -// Helpers: Geometry -IMGUI_API ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p); -IMGUI_API bool ImTriangleContainsPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p); -IMGUI_API ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p); -IMGUI_API void ImTriangleBarycentricCoords(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& p, float& out_u, float& out_v, float& out_w); - -// Helpers: String -IMGUI_API int ImStricmp(const char* str1, const char* str2); -IMGUI_API int ImStrnicmp(const char* str1, const char* str2, size_t count); -IMGUI_API void ImStrncpy(char* dst, const char* src, size_t count); -IMGUI_API char* ImStrdup(const char* str); -IMGUI_API const char* ImStrchrRange(const char* str_begin, const char* str_end, char c); -IMGUI_API int ImStrlenW(const ImWchar* str); -IMGUI_API const ImWchar*ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* buf_begin); // Find beginning-of-line -IMGUI_API const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end); -IMGUI_API void ImStrTrimBlanks(char* str); -IMGUI_API int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...) IM_FMTARGS(3); -IMGUI_API int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args) IM_FMTLIST(3); -IMGUI_API const char* ImParseFormatFindStart(const char* format); -IMGUI_API const char* ImParseFormatFindEnd(const char* format); -IMGUI_API const char* ImParseFormatTrimDecorations(const char* format, char* buf, int buf_size); -IMGUI_API int ImParseFormatPrecision(const char* format, int default_value); - -// Helpers: ImVec2/ImVec4 operators -// We are keeping those disabled by default so they don't leak in user space, to allow user enabling implicit cast operators between ImVec2 and their own types (using IM_VEC2_CLASS_EXTRA etc.) -// We unfortunately don't have a unary- operator for ImVec2 because this would needs to be defined inside the class itself. -#ifdef IMGUI_DEFINE_MATH_OPERATORS -static inline ImVec2 operator*(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x*rhs, lhs.y*rhs); } -static inline ImVec2 operator/(const ImVec2& lhs, const float rhs) { return ImVec2(lhs.x/rhs, lhs.y/rhs); } -static inline ImVec2 operator+(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x+rhs.x, lhs.y+rhs.y); } -static inline ImVec2 operator-(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x-rhs.x, lhs.y-rhs.y); } -static inline ImVec2 operator*(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x*rhs.x, lhs.y*rhs.y); } -static inline ImVec2 operator/(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x/rhs.x, lhs.y/rhs.y); } -static inline ImVec2& operator+=(ImVec2& lhs, const ImVec2& rhs) { lhs.x += rhs.x; lhs.y += rhs.y; return lhs; } -static inline ImVec2& operator-=(ImVec2& lhs, const ImVec2& rhs) { lhs.x -= rhs.x; lhs.y -= rhs.y; return lhs; } -static inline ImVec2& operator*=(ImVec2& lhs, const float rhs) { lhs.x *= rhs; lhs.y *= rhs; return lhs; } -static inline ImVec2& operator/=(ImVec2& lhs, const float rhs) { lhs.x /= rhs; lhs.y /= rhs; return lhs; } -static inline ImVec4 operator+(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x+rhs.x, lhs.y+rhs.y, lhs.z+rhs.z, lhs.w+rhs.w); } -static inline ImVec4 operator-(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x-rhs.x, lhs.y-rhs.y, lhs.z-rhs.z, lhs.w-rhs.w); } -static inline ImVec4 operator*(const ImVec4& lhs, const ImVec4& rhs) { return ImVec4(lhs.x*rhs.x, lhs.y*rhs.y, lhs.z*rhs.z, lhs.w*rhs.w); } -#endif - -// Helpers: Maths -// - Wrapper for standard libs functions. (Note that imgui_demo.cpp does _not_ use them to keep the code easy to copy) -#ifndef IMGUI_DISABLE_MATH_FUNCTIONS -static inline float ImFabs(float x) { return fabsf(x); } -static inline float ImSqrt(float x) { return sqrtf(x); } -static inline float ImPow(float x, float y) { return powf(x, y); } -static inline double ImPow(double x, double y) { return pow(x, y); } -static inline float ImFmod(float x, float y) { return fmodf(x, y); } -static inline double ImFmod(double x, double y) { return fmod(x, y); } -static inline float ImCos(float x) { return cosf(x); } -static inline float ImSin(float x) { return sinf(x); } -static inline float ImAcos(float x) { return acosf(x); } -static inline float ImAtan2(float y, float x) { return atan2f(y, x); } -static inline double ImAtof(const char* s) { return atof(s); } -static inline float ImFloorStd(float x) { return floorf(x); } // we already uses our own ImFloor() { return (float)(int)v } internally so the standard one wrapper is named differently (it's used by stb_truetype) -static inline float ImCeil(float x) { return ceilf(x); } -#endif -// - ImMin/ImMax/ImClamp/ImLerp/ImSwap are used by widgets which support for variety of types: signed/unsigned int/long long float/double, using templates here but we could also redefine them 6 times -template static inline T ImMin(T lhs, T rhs) { return lhs < rhs ? lhs : rhs; } -template static inline T ImMax(T lhs, T rhs) { return lhs >= rhs ? lhs : rhs; } -template static inline T ImClamp(T v, T mn, T mx) { return (v < mn) ? mn : (v > mx) ? mx : v; } -template static inline T ImLerp(T a, T b, float t) { return (T)(a + (b - a) * t); } -template static inline void ImSwap(T& a, T& b) { T tmp = a; a = b; b = tmp; } -// - Misc maths helpers -static inline ImVec2 ImMin(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x < rhs.x ? lhs.x : rhs.x, lhs.y < rhs.y ? lhs.y : rhs.y); } -static inline ImVec2 ImMax(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x >= rhs.x ? lhs.x : rhs.x, lhs.y >= rhs.y ? lhs.y : rhs.y); } -static inline ImVec2 ImClamp(const ImVec2& v, const ImVec2& mn, ImVec2 mx) { return ImVec2((v.x < mn.x) ? mn.x : (v.x > mx.x) ? mx.x : v.x, (v.y < mn.y) ? mn.y : (v.y > mx.y) ? mx.y : v.y); } -static inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, float t) { return ImVec2(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t); } -static inline ImVec2 ImLerp(const ImVec2& a, const ImVec2& b, const ImVec2& t) { return ImVec2(a.x + (b.x - a.x) * t.x, a.y + (b.y - a.y) * t.y); } -static inline ImVec4 ImLerp(const ImVec4& a, const ImVec4& b, float t) { return ImVec4(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t, a.w + (b.w - a.w) * t); } -static inline float ImSaturate(float f) { return (f < 0.0f) ? 0.0f : (f > 1.0f) ? 1.0f : f; } -static inline float ImLengthSqr(const ImVec2& lhs) { return lhs.x*lhs.x + lhs.y*lhs.y; } -static inline float ImLengthSqr(const ImVec4& lhs) { return lhs.x*lhs.x + lhs.y*lhs.y + lhs.z*lhs.z + lhs.w*lhs.w; } -static inline float ImInvLength(const ImVec2& lhs, float fail_value) { float d = lhs.x*lhs.x + lhs.y*lhs.y; if (d > 0.0f) return 1.0f / ImSqrt(d); return fail_value; } -static inline float ImFloor(float f) { return (float)(int)f; } -static inline ImVec2 ImFloor(const ImVec2& v) { return ImVec2((float)(int)v.x, (float)(int)v.y); } -static inline float ImDot(const ImVec2& a, const ImVec2& b) { return a.x * b.x + a.y * b.y; } -static inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a) { return ImVec2(v.x * cos_a - v.y * sin_a, v.x * sin_a + v.y * cos_a); } -static inline float ImLinearSweep(float current, float target, float speed) { if (current < target) return ImMin(current + speed, target); if (current > target) return ImMax(current - speed, target); return current; } -static inline ImVec2 ImMul(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); } - -//----------------------------------------------------------------------------- -// Types -//----------------------------------------------------------------------------- - -enum ImGuiButtonFlags_ -{ - ImGuiButtonFlags_Repeat = 1 << 0, // hold to repeat - ImGuiButtonFlags_PressedOnClickRelease = 1 << 1, // return true on click + release on same item [DEFAULT if no PressedOn* flag is set] - ImGuiButtonFlags_PressedOnClick = 1 << 2, // return true on click (default requires click+release) - ImGuiButtonFlags_PressedOnRelease = 1 << 3, // return true on release (default requires click+release) - ImGuiButtonFlags_PressedOnDoubleClick = 1 << 4, // return true on double-click (default requires click+release) - ImGuiButtonFlags_FlattenChildren = 1 << 5, // allow interactions even if a child window is overlapping - ImGuiButtonFlags_AllowItemOverlap = 1 << 6, // require previous frame HoveredId to either match id or be null before being usable, use along with SetItemAllowOverlap() - ImGuiButtonFlags_DontClosePopups = 1 << 7, // disable automatically closing parent popup on press // [UNUSED] - ImGuiButtonFlags_Disabled = 1 << 8, // disable interactions - ImGuiButtonFlags_AlignTextBaseLine = 1 << 9, // vertically align button to match text baseline - ButtonEx() only // FIXME: Should be removed and handled by SmallButton(), not possible currently because of DC.CursorPosPrevLine - ImGuiButtonFlags_NoKeyModifiers = 1 << 10, // disable interaction if a key modifier is held - ImGuiButtonFlags_NoHoldingActiveID = 1 << 11, // don't set ActiveId while holding the mouse (ImGuiButtonFlags_PressedOnClick only) - ImGuiButtonFlags_PressedOnDragDropHold = 1 << 12, // press when held into while we are drag and dropping another item (used by e.g. tree nodes, collapsing headers) - ImGuiButtonFlags_NoNavFocus = 1 << 13 // don't override navigation focus when activated -}; - -enum ImGuiSliderFlags_ -{ - ImGuiSliderFlags_Vertical = 1 << 0 -}; - -enum ImGuiColumnsFlags_ -{ - // Default: 0 - ImGuiColumnsFlags_NoBorder = 1 << 0, // Disable column dividers - ImGuiColumnsFlags_NoResize = 1 << 1, // Disable resizing columns when clicking on the dividers - ImGuiColumnsFlags_NoPreserveWidths = 1 << 2, // Disable column width preservation when adjusting columns - ImGuiColumnsFlags_NoForceWithinWindow = 1 << 3, // Disable forcing columns to fit within window - ImGuiColumnsFlags_GrowParentContentsSize= 1 << 4 // (WIP) Restore pre-1.51 behavior of extending the parent window contents size but _without affecting the columns width at all_. Will eventually remove. -}; - -enum ImGuiSelectableFlagsPrivate_ -{ - // NB: need to be in sync with last value of ImGuiSelectableFlags_ - ImGuiSelectableFlags_NoHoldingActiveID = 1 << 3, - ImGuiSelectableFlags_PressedOnClick = 1 << 4, - ImGuiSelectableFlags_PressedOnRelease = 1 << 5, - ImGuiSelectableFlags_Disabled = 1 << 6, - ImGuiSelectableFlags_DrawFillAvailWidth = 1 << 7 -}; - -enum ImGuiSeparatorFlags_ -{ - ImGuiSeparatorFlags_Horizontal = 1 << 0, // Axis default to current layout type, so generally Horizontal unless e.g. in a menu bar - ImGuiSeparatorFlags_Vertical = 1 << 1 -}; - -// Storage for LastItem data -enum ImGuiItemStatusFlags_ -{ - ImGuiItemStatusFlags_HoveredRect = 1 << 0, - ImGuiItemStatusFlags_HasDisplayRect = 1 << 1 -}; - -// FIXME: this is in development, not exposed/functional as a generic feature yet. -enum ImGuiLayoutType_ -{ - ImGuiLayoutType_Vertical, - ImGuiLayoutType_Horizontal -}; - -enum ImGuiAxis -{ - ImGuiAxis_None = -1, - ImGuiAxis_X = 0, - ImGuiAxis_Y = 1 -}; - -enum ImGuiPlotType -{ - ImGuiPlotType_Lines, - ImGuiPlotType_Histogram -}; - -enum ImGuiInputSource -{ - ImGuiInputSource_None = 0, - ImGuiInputSource_Mouse, - ImGuiInputSource_Nav, - ImGuiInputSource_NavKeyboard, // Only used occasionally for storage, not tested/handled by most code - ImGuiInputSource_NavGamepad, // " - ImGuiInputSource_COUNT -}; - -// FIXME-NAV: Clarify/expose various repeat delay/rate -enum ImGuiInputReadMode -{ - ImGuiInputReadMode_Down, - ImGuiInputReadMode_Pressed, - ImGuiInputReadMode_Released, - ImGuiInputReadMode_Repeat, - ImGuiInputReadMode_RepeatSlow, - ImGuiInputReadMode_RepeatFast -}; - -enum ImGuiNavHighlightFlags_ -{ - ImGuiNavHighlightFlags_TypeDefault = 1 << 0, - ImGuiNavHighlightFlags_TypeThin = 1 << 1, - ImGuiNavHighlightFlags_AlwaysDraw = 1 << 2, - ImGuiNavHighlightFlags_NoRounding = 1 << 3 -}; - -enum ImGuiNavDirSourceFlags_ -{ - ImGuiNavDirSourceFlags_Keyboard = 1 << 0, - ImGuiNavDirSourceFlags_PadDPad = 1 << 1, - ImGuiNavDirSourceFlags_PadLStick = 1 << 2 -}; - -enum ImGuiNavMoveFlags_ -{ - ImGuiNavMoveFlags_LoopX = 1 << 0, // On failed request, restart from opposite side - ImGuiNavMoveFlags_LoopY = 1 << 1, - ImGuiNavMoveFlags_WrapX = 1 << 2, // On failed request, request from opposite side one line down (when NavDir==right) or one line up (when NavDir==left) - ImGuiNavMoveFlags_WrapY = 1 << 3, // This is not super useful for provided for completeness - ImGuiNavMoveFlags_AllowCurrentNavId = 1 << 4, // Allow scoring and considering the current NavId as a move target candidate. This is used when the move source is offset (e.g. pressing PageDown actually needs to send a Up move request, if we are pressing PageDown from the bottom-most item we need to stay in place) - ImGuiNavMoveFlags_AlsoScoreVisibleSet = 1 << 5 // Store alternate result in NavMoveResultLocalVisibleSet that only comprise elements that are already fully visible. -}; - -enum ImGuiNavForward -{ - ImGuiNavForward_None, - ImGuiNavForward_ForwardQueued, - ImGuiNavForward_ForwardActive -}; - -// 2D axis aligned bounding-box -// NB: we can't rely on ImVec2 math operators being available here -struct IMGUI_API ImRect -{ - ImVec2 Min; // Upper-left - ImVec2 Max; // Lower-right - - ImRect() : Min(FLT_MAX,FLT_MAX), Max(-FLT_MAX,-FLT_MAX) {} - ImRect(const ImVec2& min, const ImVec2& max) : Min(min), Max(max) {} - ImRect(const ImVec4& v) : Min(v.x, v.y), Max(v.z, v.w) {} - ImRect(float x1, float y1, float x2, float y2) : Min(x1, y1), Max(x2, y2) {} - - ImVec2 GetCenter() const { return ImVec2((Min.x + Max.x) * 0.5f, (Min.y + Max.y) * 0.5f); } - ImVec2 GetSize() const { return ImVec2(Max.x - Min.x, Max.y - Min.y); } - float GetWidth() const { return Max.x - Min.x; } - float GetHeight() const { return Max.y - Min.y; } - ImVec2 GetTL() const { return Min; } // Top-left - ImVec2 GetTR() const { return ImVec2(Max.x, Min.y); } // Top-right - ImVec2 GetBL() const { return ImVec2(Min.x, Max.y); } // Bottom-left - ImVec2 GetBR() const { return Max; } // Bottom-right - bool Contains(const ImVec2& p) const { return p.x >= Min.x && p.y >= Min.y && p.x < Max.x && p.y < Max.y; } - bool Contains(const ImRect& r) const { return r.Min.x >= Min.x && r.Min.y >= Min.y && r.Max.x <= Max.x && r.Max.y <= Max.y; } - bool Overlaps(const ImRect& r) const { return r.Min.y < Max.y && r.Max.y > Min.y && r.Min.x < Max.x && r.Max.x > Min.x; } - void Add(const ImVec2& p) { if (Min.x > p.x) Min.x = p.x; if (Min.y > p.y) Min.y = p.y; if (Max.x < p.x) Max.x = p.x; if (Max.y < p.y) Max.y = p.y; } - void Add(const ImRect& r) { if (Min.x > r.Min.x) Min.x = r.Min.x; if (Min.y > r.Min.y) Min.y = r.Min.y; if (Max.x < r.Max.x) Max.x = r.Max.x; if (Max.y < r.Max.y) Max.y = r.Max.y; } - void Expand(const float amount) { Min.x -= amount; Min.y -= amount; Max.x += amount; Max.y += amount; } - void Expand(const ImVec2& amount) { Min.x -= amount.x; Min.y -= amount.y; Max.x += amount.x; Max.y += amount.y; } - void Translate(const ImVec2& d) { Min.x += d.x; Min.y += d.y; Max.x += d.x; Max.y += d.y; } - void TranslateX(float dx) { Min.x += dx; Max.x += dx; } - void TranslateY(float dy) { Min.y += dy; Max.y += dy; } - void ClipWith(const ImRect& r) { Min = ImMax(Min, r.Min); Max = ImMin(Max, r.Max); } // Simple version, may lead to an inverted rectangle, which is fine for Contains/Overlaps test but not for display. - void ClipWithFull(const ImRect& r) { Min = ImClamp(Min, r.Min, r.Max); Max = ImClamp(Max, r.Min, r.Max); } // Full version, ensure both points are fully clipped. - void Floor() { Min.x = (float)(int)Min.x; Min.y = (float)(int)Min.y; Max.x = (float)(int)Max.x; Max.y = (float)(int)Max.y; } - bool IsInverted() const { return Min.x > Max.x || Min.y > Max.y; } -}; - -// Stacked color modifier, backup of modified data so we can restore it -struct ImGuiColMod -{ - ImGuiCol Col; - ImVec4 BackupValue; -}; - -// Stacked style modifier, backup of modified data so we can restore it. Data type inferred from the variable. -struct ImGuiStyleMod -{ - ImGuiStyleVar VarIdx; - union { int BackupInt[2]; float BackupFloat[2]; }; - ImGuiStyleMod(ImGuiStyleVar idx, int v) { VarIdx = idx; BackupInt[0] = v; } - ImGuiStyleMod(ImGuiStyleVar idx, float v) { VarIdx = idx; BackupFloat[0] = v; } - ImGuiStyleMod(ImGuiStyleVar idx, ImVec2 v) { VarIdx = idx; BackupFloat[0] = v.x; BackupFloat[1] = v.y; } -}; - -// Stacked storage data for BeginGroup()/EndGroup() -struct ImGuiGroupData -{ - ImVec2 BackupCursorPos; - ImVec2 BackupCursorMaxPos; - float BackupIndentX; - float BackupGroupOffsetX; - float BackupCurrentLineHeight; - float BackupCurrentLineTextBaseOffset; - float BackupLogLinePosY; - bool BackupActiveIdIsAlive; - bool BackupActiveIdPreviousFrameIsAlive; - bool AdvanceCursor; -}; - -// Simple column measurement, currently used for MenuItem() only.. This is very short-sighted/throw-away code and NOT a generic helper. -struct IMGUI_API ImGuiMenuColumns -{ - int Count; - float Spacing; - float Width, NextWidth; - float Pos[4], NextWidths[4]; - - ImGuiMenuColumns(); - void Update(int count, float spacing, bool clear); - float DeclColumns(float w0, float w1, float w2); - float CalcExtraSpace(float avail_w); -}; - -// Internal state of the currently focused/edited text input box -struct IMGUI_API ImGuiTextEditState -{ - ImGuiID Id; // widget id owning the text state - ImVector Text; // edit buffer, we need to persist but can't guarantee the persistence of the user-provided buffer. so we copy into own buffer. - ImVector InitialText; // backup of end-user buffer at the time of focus (in UTF-8, unaltered) - ImVector TempTextBuffer; - int CurLenA, CurLenW; // we need to maintain our buffer length in both UTF-8 and wchar format. - int BufSizeA; // end-user buffer size - float ScrollX; - ImGuiStb::STB_TexteditState StbState; - float CursorAnim; - bool CursorFollow; - bool SelectedAllMouseLock; - - ImGuiTextEditState() { memset(this, 0, sizeof(*this)); } - void CursorAnimReset() { CursorAnim = -0.30f; } // After a user-input the cursor stays on for a while without blinking - void CursorClamp() { StbState.cursor = ImMin(StbState.cursor, CurLenW); StbState.select_start = ImMin(StbState.select_start, CurLenW); StbState.select_end = ImMin(StbState.select_end, CurLenW); } - bool HasSelection() const { return StbState.select_start != StbState.select_end; } - void ClearSelection() { StbState.select_start = StbState.select_end = StbState.cursor; } - void SelectAll() { StbState.select_start = 0; StbState.cursor = StbState.select_end = CurLenW; StbState.has_preferred_x = false; } - void OnKeyPressed(int key); -}; - -// Windows data saved in imgui.ini file -struct ImGuiWindowSettings -{ - char* Name; - ImGuiID Id; - ImVec2 Pos; - ImVec2 Size; - bool Collapsed; - - ImGuiWindowSettings() { Name = NULL; Id = 0; Pos = Size = ImVec2(0,0); Collapsed = false; } -}; - -struct ImGuiSettingsHandler -{ - const char* TypeName; // Short description stored in .ini file. Disallowed characters: '[' ']' - ImGuiID TypeHash; // == ImHash(TypeName, 0, 0) - void* (*ReadOpenFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, const char* name); // Read: Called when entering into a new ini entry e.g. "[Window][Name]" - void (*ReadLineFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, void* entry, const char* line); // Read: Called for every line of text within an ini entry - void (*WriteAllFn)(ImGuiContext* ctx, ImGuiSettingsHandler* handler, ImGuiTextBuffer* out_buf); // Write: Output every entries into 'out_buf' - void* UserData; - - ImGuiSettingsHandler() { memset(this, 0, sizeof(*this)); } -}; - -// Storage for current popup stack -struct ImGuiPopupRef -{ - ImGuiID PopupId; // Set on OpenPopup() - ImGuiWindow* Window; // Resolved on BeginPopup() - may stay unresolved if user never calls OpenPopup() - ImGuiWindow* ParentWindow; // Set on OpenPopup() - int OpenFrameCount; // Set on OpenPopup() - ImGuiID OpenParentId; // Set on OpenPopup(), we need this to differenciate multiple menu sets from each others (e.g. inside menu bar vs loose menu items) - ImVec2 OpenPopupPos; // Set on OpenPopup(), preferred popup position (typically == OpenMousePos when using mouse) - ImVec2 OpenMousePos; // Set on OpenPopup(), copy of mouse position at the time of opening popup -}; - -struct ImGuiColumnData -{ - float OffsetNorm; // Column start offset, normalized 0.0 (far left) -> 1.0 (far right) - float OffsetNormBeforeResize; - ImGuiColumnsFlags Flags; // Not exposed - ImRect ClipRect; - - ImGuiColumnData() { OffsetNorm = OffsetNormBeforeResize = 0.0f; Flags = 0; } -}; - -struct ImGuiColumnsSet -{ - ImGuiID ID; - ImGuiColumnsFlags Flags; - bool IsFirstFrame; - bool IsBeingResized; - int Current; - int Count; - float MinX, MaxX; - float LineMinY, LineMaxY; - float StartPosY; // Copy of CursorPos - float StartMaxPosX; // Copy of CursorMaxPos - ImVector Columns; - - ImGuiColumnsSet() { Clear(); } - void Clear() - { - ID = 0; - Flags = 0; - IsFirstFrame = false; - IsBeingResized = false; - Current = 0; - Count = 1; - MinX = MaxX = 0.0f; - LineMinY = LineMaxY = 0.0f; - StartPosY = 0.0f; - StartMaxPosX = 0.0f; - Columns.clear(); - } -}; - -// Data shared between all ImDrawList instances -struct IMGUI_API ImDrawListSharedData -{ - ImVec2 TexUvWhitePixel; // UV of white pixel in the atlas - ImFont* Font; // Current/default font (optional, for simplified AddText overload) - float FontSize; // Current/default font size (optional, for simplified AddText overload) - float CurveTessellationTol; - ImVec4 ClipRectFullscreen; // Value for PushClipRectFullscreen() - - // Const data - // FIXME: Bake rounded corners fill/borders in atlas - ImVec2 CircleVtx12[12]; - - ImDrawListSharedData(); -}; - -struct ImDrawDataBuilder -{ - ImVector Layers[2]; // Global layers for: regular, tooltip - - void Clear() { for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n].resize(0); } - void ClearFreeMemory() { for (int n = 0; n < IM_ARRAYSIZE(Layers); n++) Layers[n].clear(); } - IMGUI_API void FlattenIntoSingleLayer(); -}; - -struct ImGuiNavMoveResult -{ - ImGuiID ID; // Best candidate - ImGuiWindow* Window; // Best candidate window - float DistBox; // Best candidate box distance to current NavId - float DistCenter; // Best candidate center distance to current NavId - float DistAxial; - ImRect RectRel; // Best candidate bounding box in window relative space - - ImGuiNavMoveResult() { Clear(); } - void Clear() { ID = 0; Window = NULL; DistBox = DistCenter = DistAxial = FLT_MAX; RectRel = ImRect(); } -}; - -// Storage for SetNexWindow** functions -struct ImGuiNextWindowData -{ - ImGuiCond PosCond; - ImGuiCond SizeCond; - ImGuiCond ContentSizeCond; - ImGuiCond CollapsedCond; - ImGuiCond SizeConstraintCond; - ImGuiCond FocusCond; - ImGuiCond BgAlphaCond; - ImVec2 PosVal; - ImVec2 PosPivotVal; - ImVec2 SizeVal; - ImVec2 ContentSizeVal; - bool CollapsedVal; - ImRect SizeConstraintRect; - ImGuiSizeCallback SizeCallback; - void* SizeCallbackUserData; - float BgAlphaVal; - ImVec2 MenuBarOffsetMinVal; // This is not exposed publicly, so we don't clear it. - - ImGuiNextWindowData() - { - PosCond = SizeCond = ContentSizeCond = CollapsedCond = SizeConstraintCond = FocusCond = BgAlphaCond = 0; - PosVal = PosPivotVal = SizeVal = ImVec2(0.0f, 0.0f); - ContentSizeVal = ImVec2(0.0f, 0.0f); - CollapsedVal = false; - SizeConstraintRect = ImRect(); - SizeCallback = NULL; - SizeCallbackUserData = NULL; - BgAlphaVal = FLT_MAX; - MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f); - } - - void Clear() - { - PosCond = SizeCond = ContentSizeCond = CollapsedCond = SizeConstraintCond = FocusCond = BgAlphaCond = 0; - } -}; - -// Main imgui context -struct ImGuiContext -{ - bool Initialized; - bool FontAtlasOwnedByContext; // Io.Fonts-> is owned by the ImGuiContext and will be destructed along with it. - ImGuiIO IO; - ImGuiStyle Style; - ImFont* Font; // (Shortcut) == FontStack.empty() ? IO.Font : FontStack.back() - float FontSize; // (Shortcut) == FontBaseSize * g.CurrentWindow->FontWindowScale == window->FontSize(). Text height for current window. - float FontBaseSize; // (Shortcut) == IO.FontGlobalScale * Font->Scale * Font->FontSize. Base text height. - ImDrawListSharedData DrawListSharedData; - - float Time; - int FrameCount; - int FrameCountEnded; - int FrameCountRendered; - ImVector Windows; - ImVector WindowsSortBuffer; - ImVector CurrentWindowStack; - ImGuiStorage WindowsById; - int WindowsActiveCount; - ImGuiWindow* CurrentWindow; // Being drawn into - ImGuiWindow* HoveredWindow; // Will catch mouse inputs - ImGuiWindow* HoveredRootWindow; // Will catch mouse inputs (for focus/move only) - ImGuiID HoveredId; // Hovered widget - bool HoveredIdAllowOverlap; - ImGuiID HoveredIdPreviousFrame; - float HoveredIdTimer; - ImGuiID ActiveId; // Active widget - ImGuiID ActiveIdPreviousFrame; - float ActiveIdTimer; - bool ActiveIdIsAlive; // Active widget has been seen this frame - bool ActiveIdIsJustActivated; // Set at the time of activation for one frame - bool ActiveIdAllowOverlap; // Active widget allows another widget to steal active id (generally for overlapping widgets, but not always) - bool ActiveIdValueChanged; - bool ActiveIdPreviousFrameIsAlive; - bool ActiveIdPreviousFrameValueChanged; - int ActiveIdAllowNavDirFlags; // Active widget allows using directional navigation (e.g. can activate a button and move away from it) - ImVec2 ActiveIdClickOffset; // Clicked offset from upper-left corner, if applicable (currently only set by ButtonBehavior) - ImGuiWindow* ActiveIdWindow; - ImGuiWindow* ActiveIdPreviousFrameWindow; - ImGuiInputSource ActiveIdSource; // Activating with mouse or nav (gamepad/keyboard) - ImGuiID LastActiveId; // Store the last non-zero ActiveId, useful for animation. - float LastActiveIdTimer; // Store the last non-zero ActiveId timer since the beginning of activation, useful for animation. - ImGuiWindow* MovingWindow; // Track the window we clicked on (in order to preserve focus). The actually window that is moved is generally MovingWindow->RootWindow. - ImVector ColorModifiers; // Stack for PushStyleColor()/PopStyleColor() - ImVector StyleModifiers; // Stack for PushStyleVar()/PopStyleVar() - ImVector FontStack; // Stack for PushFont()/PopFont() - ImVector OpenPopupStack; // Which popups are open (persistent) - ImVector CurrentPopupStack; // Which level of BeginPopup() we are in (reset every frame) - ImGuiNextWindowData NextWindowData; // Storage for SetNextWindow** functions - bool NextTreeNodeOpenVal; // Storage for SetNextTreeNode** functions - ImGuiCond NextTreeNodeOpenCond; - - // Navigation data (for gamepad/keyboard) - ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusWindow' - ImGuiID NavId; // Focused item for navigation - ImGuiID NavActivateId; // ~~ (g.ActiveId == 0) && IsNavInputPressed(ImGuiNavInput_Activate) ? NavId : 0, also set when calling ActivateItem() - ImGuiID NavActivateDownId; // ~~ IsNavInputDown(ImGuiNavInput_Activate) ? NavId : 0 - ImGuiID NavActivatePressedId; // ~~ IsNavInputPressed(ImGuiNavInput_Activate) ? NavId : 0 - ImGuiID NavInputId; // ~~ IsNavInputPressed(ImGuiNavInput_Input) ? NavId : 0 - ImGuiID NavJustTabbedId; // Just tabbed to this id. - ImGuiID NavJustMovedToId; // Just navigated to this id (result of a successfully MoveRequest) - ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame - ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? - ImRect NavScoringRectScreen; // Rectangle used for scoring, in screen space. Based of window->DC.NavRefRectRel[], modified for directional navigation scoring. - int NavScoringCount; // Metrics for debugging - ImGuiWindow* NavWindowingTarget; // When selecting a window (holding Menu+FocusPrev/Next, or equivalent of CTRL-TAB) this window is temporarily displayed front-most. - float NavWindowingHighlightTimer; - float NavWindowingHighlightAlpha; - bool NavWindowingToggleLayer; - int NavLayer; // Layer we are navigating on. For now the system is hard-coded for 0=main contents and 1=menu/title bar, may expose layers later. - int NavIdTabCounter; // == NavWindow->DC.FocusIdxTabCounter at time of NavId processing - bool NavIdIsAlive; // Nav widget has been seen this frame ~~ NavRefRectRel is valid - bool NavMousePosDirty; // When set we will update mouse position if (io.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos) if set (NB: this not enabled by default) - bool NavDisableHighlight; // When user starts using mouse, we hide gamepad/keyboard highlight (NB: but they are still available, which is why NavDisableHighlight isn't always != NavDisableMouseHover) - bool NavDisableMouseHover; // When user starts using gamepad/keyboard, we hide mouse hovering highlight until mouse is touched again. - bool NavAnyRequest; // ~~ NavMoveRequest || NavInitRequest - bool NavInitRequest; // Init request for appearing window to select first item - bool NavInitRequestFromMove; - ImGuiID NavInitResultId; - ImRect NavInitResultRectRel; - bool NavMoveFromClampedRefRect; // Set by manual scrolling, if we scroll to a point where NavId isn't visible we reset navigation from visible items - bool NavMoveRequest; // Move request for this frame - ImGuiNavMoveFlags NavMoveRequestFlags; - ImGuiNavForward NavMoveRequestForward; // None / ForwardQueued / ForwardActive (this is used to navigate sibling parent menus from a child menu) - ImGuiDir NavMoveDir, NavMoveDirLast; // Direction of the move request (left/right/up/down), direction of the previous move request - ImGuiDir NavMoveClipDir; - ImGuiNavMoveResult NavMoveResultLocal; // Best move request candidate within NavWindow - ImGuiNavMoveResult NavMoveResultLocalVisibleSet; // Best move request candidate within NavWindow that are mostly visible (when using ImGuiNavMoveFlags_AlsoScoreVisibleSet flag) - ImGuiNavMoveResult NavMoveResultOther; // Best move request candidate within NavWindow's flattened hierarchy (when using ImGuiWindowFlags_NavFlattened flag) - - // Render - ImDrawData DrawData; // Main ImDrawData instance to pass render information to the user - ImDrawDataBuilder DrawDataBuilder; - float ModalWindowDarkeningRatio; - ImDrawList OverlayDrawList; // Optional software render of mouse cursors, if io.MouseDrawCursor is set + a few debug overlays - ImGuiMouseCursor MouseCursor; - - // Drag and Drop - bool DragDropActive; - ImGuiDragDropFlags DragDropSourceFlags; - int DragDropMouseButton; - ImGuiPayload DragDropPayload; - ImRect DragDropTargetRect; - ImGuiID DragDropTargetId; - ImGuiDragDropFlags DragDropAcceptFlags; - float DragDropAcceptIdCurrRectSurface; // Target item surface (we resolve overlapping targets by prioritizing the smaller surface) - ImGuiID DragDropAcceptIdCurr; // Target item id (set at the time of accepting the payload) - ImGuiID DragDropAcceptIdPrev; // Target item id from previous frame (we need to store this to allow for overlapping drag and drop targets) - int DragDropAcceptFrameCount; // Last time a target expressed a desire to accept the source - ImVector DragDropPayloadBufHeap; // We don't expose the ImVector<> directly - unsigned char DragDropPayloadBufLocal[8]; // Local buffer for small payloads - - // Widget state - ImGuiTextEditState InputTextState; - ImFont InputTextPasswordFont; - ImGuiID ScalarAsInputTextId; // Temporary text input when CTRL+clicking on a slider, etc. - ImGuiColorEditFlags ColorEditOptions; // Store user options for color edit widgets - ImVec4 ColorPickerRef; - bool DragCurrentAccumDirty; - float DragCurrentAccum; // Accumulator for dragging modification. Always high-precision, not rounded by end-user precision settings - float DragSpeedDefaultRatio; // If speed == 0.0f, uses (max-min) * DragSpeedDefaultRatio - ImVec2 ScrollbarClickDeltaToGrabCenter; // Distance between mouse and center of grab box, normalized in parent space. Use storage? - int TooltipOverrideCount; - ImVector PrivateClipboard; // If no custom clipboard handler is defined - ImVec2 PlatformImePos, PlatformImeLastPos; // Cursor position request & last passed to the OS Input Method Editor - - // Settings - bool SettingsLoaded; - float SettingsDirtyTimer; // Save .ini Settings to memory when time reaches zero - ImGuiTextBuffer SettingsIniData; // In memory .ini settings - ImVector SettingsHandlers; // List of .ini settings handlers - ImVector SettingsWindows; // ImGuiWindow .ini settings entries (parsed from the last loaded .ini file and maintained on saving) - - // Logging - bool LogEnabled; - FILE* LogFile; // If != NULL log to stdout/ file - ImGuiTextBuffer LogClipboard; // Accumulation buffer when log to clipboard. This is pointer so our GImGui static constructor doesn't call heap allocators. - int LogStartDepth; - int LogAutoExpandMaxDepth; - - // Misc - float FramerateSecPerFrame[120]; // Calculate estimate of framerate for user over the last 2 seconds. - int FramerateSecPerFrameIdx; - float FramerateSecPerFrameAccum; - int WantCaptureMouseNextFrame; // Explicit capture via CaptureKeyboardFromApp()/CaptureMouseFromApp() sets those flags - int WantCaptureKeyboardNextFrame; - int WantTextInputNextFrame; - char TempBuffer[1024*3+1]; // Temporary text buffer - - ImGuiContext(ImFontAtlas* shared_font_atlas) : OverlayDrawList(NULL) - { - Initialized = false; - Font = NULL; - FontSize = FontBaseSize = 0.0f; - FontAtlasOwnedByContext = shared_font_atlas ? false : true; - IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)(); - - Time = 0.0f; - FrameCount = 0; - FrameCountEnded = FrameCountRendered = -1; - WindowsActiveCount = 0; - CurrentWindow = NULL; - HoveredWindow = NULL; - HoveredRootWindow = NULL; - HoveredId = 0; - HoveredIdAllowOverlap = false; - HoveredIdPreviousFrame = 0; - HoveredIdTimer = 0.0f; - ActiveId = 0; - ActiveIdPreviousFrame = 0; - ActiveIdTimer = 0.0f; - ActiveIdIsAlive = false; - ActiveIdIsJustActivated = false; - ActiveIdAllowOverlap = false; - ActiveIdValueChanged = false; - ActiveIdPreviousFrameIsAlive = false; - ActiveIdPreviousFrameValueChanged = false; - ActiveIdAllowNavDirFlags = 0; - ActiveIdClickOffset = ImVec2(-1,-1); - ActiveIdWindow = ActiveIdPreviousFrameWindow = NULL; - ActiveIdSource = ImGuiInputSource_None; - LastActiveId = 0; - LastActiveIdTimer = 0.0f; - MovingWindow = NULL; - NextTreeNodeOpenVal = false; - NextTreeNodeOpenCond = 0; - - NavWindow = NULL; - NavId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavInputId = 0; - NavJustTabbedId = NavJustMovedToId = NavNextActivateId = 0; - NavInputSource = ImGuiInputSource_None; - NavScoringRectScreen = ImRect(); - NavScoringCount = 0; - NavWindowingTarget = NULL; - NavWindowingHighlightTimer = NavWindowingHighlightAlpha = 0.0f; - NavWindowingToggleLayer = false; - NavLayer = 0; - NavIdTabCounter = INT_MAX; - NavIdIsAlive = false; - NavMousePosDirty = false; - NavDisableHighlight = true; - NavDisableMouseHover = false; - NavAnyRequest = false; - NavInitRequest = false; - NavInitRequestFromMove = false; - NavInitResultId = 0; - NavMoveFromClampedRefRect = false; - NavMoveRequest = false; - NavMoveRequestFlags = 0; - NavMoveRequestForward = ImGuiNavForward_None; - NavMoveDir = NavMoveDirLast = NavMoveClipDir = ImGuiDir_None; - - ModalWindowDarkeningRatio = 0.0f; - OverlayDrawList._Data = &DrawListSharedData; - OverlayDrawList._OwnerName = "##Overlay"; // Give it a name for debugging - MouseCursor = ImGuiMouseCursor_Arrow; - - DragDropActive = false; - DragDropSourceFlags = 0; - DragDropMouseButton = -1; - DragDropTargetId = 0; - DragDropAcceptFlags = 0; - DragDropAcceptIdCurrRectSurface = 0.0f; - DragDropAcceptIdPrev = DragDropAcceptIdCurr = 0; - DragDropAcceptFrameCount = -1; - memset(DragDropPayloadBufLocal, 0, sizeof(DragDropPayloadBufLocal)); - - ScalarAsInputTextId = 0; - ColorEditOptions = ImGuiColorEditFlags__OptionsDefault; - DragCurrentAccumDirty = false; - DragCurrentAccum = 0.0f; - DragSpeedDefaultRatio = 1.0f / 100.0f; - ScrollbarClickDeltaToGrabCenter = ImVec2(0.0f, 0.0f); - TooltipOverrideCount = 0; - PlatformImePos = PlatformImeLastPos = ImVec2(FLT_MAX, FLT_MAX); - - SettingsLoaded = false; - SettingsDirtyTimer = 0.0f; - - LogEnabled = false; - LogFile = NULL; - LogStartDepth = 0; - LogAutoExpandMaxDepth = 2; - - memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame)); - FramerateSecPerFrameIdx = 0; - FramerateSecPerFrameAccum = 0.0f; - WantCaptureMouseNextFrame = WantCaptureKeyboardNextFrame = WantTextInputNextFrame = -1; - memset(TempBuffer, 0, sizeof(TempBuffer)); - } -}; - -// Transient per-window flags, reset at the beginning of the frame. For child window, inherited from parent on first Begin(). -// This is going to be exposed in imgui.h when stabilized enough. -enum ImGuiItemFlags_ -{ - ImGuiItemFlags_AllowKeyboardFocus = 1 << 0, // true - ImGuiItemFlags_ButtonRepeat = 1 << 1, // false // Button() will return true multiple times based on io.KeyRepeatDelay and io.KeyRepeatRate settings. - ImGuiItemFlags_Disabled = 1 << 2, // false // FIXME-WIP: Disable interactions but doesn't affect visuals. Should be: grey out and disable interactions with widgets that affect data + view widgets (WIP) - ImGuiItemFlags_NoNav = 1 << 3, // false - ImGuiItemFlags_NoNavDefaultFocus = 1 << 4, // false - ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // MenuItem/Selectable() automatically closes current Popup window - ImGuiItemFlags_Default_ = ImGuiItemFlags_AllowKeyboardFocus -}; - -// Transient per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the DC variable name in ImGuiWindow. -// FIXME: That's theory, in practice the delimitation between ImGuiWindow and ImGuiWindowTempData is quite tenuous and could be reconsidered. -struct IMGUI_API ImGuiWindowTempData -{ - ImVec2 CursorPos; - ImVec2 CursorPosPrevLine; - ImVec2 CursorStartPos; - ImVec2 CursorMaxPos; // Used to implicitly calculate the size of our contents, always growing during the frame. Turned into window->SizeContents at the beginning of next frame - float CurrentLineHeight; - float CurrentLineTextBaseOffset; - float PrevLineHeight; - float PrevLineTextBaseOffset; - float LogLinePosY; - int TreeDepth; - ImU32 TreeDepthMayJumpToParentOnPop; // Store a copy of !g.NavIdIsAlive for TreeDepth 0..31 - ImGuiID LastItemId; - ImGuiItemStatusFlags LastItemStatusFlags; - ImRect LastItemRect; // Interaction rect - ImRect LastItemDisplayRect; // End-user display rect (only valid if LastItemStatusFlags & ImGuiItemStatusFlags_HasDisplayRect) - bool NavHideHighlightOneFrame; - bool NavHasScroll; // Set when scrolling can be used (ScrollMax > 0.0f) - int NavLayerCurrent; // Current layer, 0..31 (we currently only use 0..1) - int NavLayerCurrentMask; // = (1 << NavLayerCurrent) used by ItemAdd prior to clipping. - int NavLayerActiveMask; // Which layer have been written to (result from previous frame) - int NavLayerActiveMaskNext; // Which layer have been written to (buffer for current frame) - bool MenuBarAppending; // FIXME: Remove this - ImVec2 MenuBarOffset; // MenuBarOffset.x is sort of equivalent of a per-layer CursorPos.x, saved/restored as we switch to the menu bar. The only situation when MenuBarOffset.y is > 0 if when (SafeAreaPadding.y > FramePadding.y), often used on TVs. - ImVector ChildWindows; - ImGuiStorage* StateStorage; - ImGuiLayoutType LayoutType; - ImGuiLayoutType ParentLayoutType; // Layout type of parent window at the time of Begin() - - // We store the current settings outside of the vectors to increase memory locality (reduce cache misses). The vectors are rarely modified. Also it allows us to not heap allocate for short-lived windows which are not using those settings. - ImGuiItemFlags ItemFlags; // == ItemFlagsStack.back() [empty == ImGuiItemFlags_Default] - float ItemWidth; // == ItemWidthStack.back(). 0.0: default, >0.0: width in pixels, <0.0: align xx pixels to the right of window - float TextWrapPos; // == TextWrapPosStack.back() [empty == -1.0f] - ImVectorItemFlagsStack; - ImVector ItemWidthStack; - ImVector TextWrapPosStack; - ImVectorGroupStack; - int StackSizesBackup[6]; // Store size of various stacks for asserting - - float IndentX; // Indentation / start position from left of window (increased by TreePush/TreePop, etc.) - float GroupOffsetX; - float ColumnsOffsetX; // Offset to the current column (if ColumnsCurrent > 0). FIXME: This and the above should be a stack to allow use cases like Tree->Column->Tree. Need revamp columns API. - ImGuiColumnsSet* ColumnsSet; // Current columns set - - ImGuiWindowTempData() - { - CursorPos = CursorPosPrevLine = CursorStartPos = CursorMaxPos = ImVec2(0.0f, 0.0f); - CurrentLineHeight = PrevLineHeight = 0.0f; - CurrentLineTextBaseOffset = PrevLineTextBaseOffset = 0.0f; - LogLinePosY = -1.0f; - TreeDepth = 0; - TreeDepthMayJumpToParentOnPop = 0x00; - LastItemId = 0; - LastItemStatusFlags = 0; - LastItemRect = LastItemDisplayRect = ImRect(); - NavHideHighlightOneFrame = false; - NavHasScroll = false; - NavLayerActiveMask = NavLayerActiveMaskNext = 0x00; - NavLayerCurrent = 0; - NavLayerCurrentMask = 1 << 0; - MenuBarAppending = false; - MenuBarOffset = ImVec2(0.0f, 0.0f); - StateStorage = NULL; - LayoutType = ParentLayoutType = ImGuiLayoutType_Vertical; - ItemWidth = 0.0f; - ItemFlags = ImGuiItemFlags_Default_; - TextWrapPos = -1.0f; - memset(StackSizesBackup, 0, sizeof(StackSizesBackup)); - - IndentX = 0.0f; - GroupOffsetX = 0.0f; - ColumnsOffsetX = 0.0f; - ColumnsSet = NULL; - } -}; - -// Storage for one window -struct IMGUI_API ImGuiWindow -{ - char* Name; - ImGuiID ID; // == ImHash(Name) - ImGuiWindowFlags Flags; // See enum ImGuiWindowFlags_ - ImVec2 Pos; // Position (always rounded-up to nearest pixel) - ImVec2 Size; // Current size (==SizeFull or collapsed title bar size) - ImVec2 SizeFull; // Size when non collapsed - ImVec2 SizeFullAtLastBegin; // Copy of SizeFull at the end of Begin. This is the reference value we'll use on the next frame to decide if we need scrollbars. - ImVec2 SizeContents; // Size of contents (== extents reach of the drawing cursor) from previous frame. Include decoration, window title, border, menu, etc. - ImVec2 SizeContentsExplicit; // Size of contents explicitly set by the user via SetNextWindowContentSize() - ImVec2 WindowPadding; // Window padding at the time of begin. - float WindowRounding; // Window rounding at the time of begin. - float WindowBorderSize; // Window border size at the time of begin. - ImGuiID MoveId; // == window->GetID("#MOVE") - ImGuiID ChildId; // Id of corresponding item in parent window (for child windows) - ImVec2 Scroll; - ImVec2 ScrollTarget; // target scroll position. stored as cursor position with scrolling canceled out, so the highest point is always 0.0f. (FLT_MAX for no change) - ImVec2 ScrollTargetCenterRatio; // 0.0f = scroll so that target position is at top, 0.5f = scroll so that target position is centered - ImVec2 ScrollbarSizes; // Size taken by scrollbars on each axis - bool ScrollbarX, ScrollbarY; - bool Active; // Set to true on Begin(), unless Collapsed - bool WasActive; - bool WriteAccessed; // Set to true when any widget access the current window - bool Collapsed; // Set when collapsing window to become only title-bar - bool CollapseToggleWanted; - bool SkipItems; // Set when items can safely be all clipped (e.g. window not visible or collapsed) - bool Appearing; // Set during the frame where the window is appearing (or re-appearing) - bool CloseButton; // Set when the window has a close button (p_open != NULL) - int BeginOrderWithinParent; // Order within immediate parent window, if we are a child window. Otherwise 0. - int BeginOrderWithinContext; // Order within entire imgui context. This is mostly used for debugging submission order related issues. - int BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) - ImGuiID PopupId; // ID in the popup stack when this window is used as a popup/menu (because we use generic Name/ID for recycling) - int AutoFitFramesX, AutoFitFramesY; - bool AutoFitOnlyGrows; - int AutoFitChildAxises; - ImGuiDir AutoPosLastDirection; - int HiddenFrames; - ImGuiCond SetWindowPosAllowFlags; // store acceptable condition flags for SetNextWindowPos() use. - ImGuiCond SetWindowSizeAllowFlags; // store acceptable condition flags for SetNextWindowSize() use. - ImGuiCond SetWindowCollapsedAllowFlags; // store acceptable condition flags for SetNextWindowCollapsed() use. - ImVec2 SetWindowPosVal; // store window position when using a non-zero Pivot (position set needs to be processed when we know the window size) - ImVec2 SetWindowPosPivot; // store window pivot for positioning. ImVec2(0,0) when positioning from top-left corner; ImVec2(0.5f,0.5f) for centering; ImVec2(1,1) for bottom right. - - ImGuiWindowTempData DC; // Temporary per-window data, reset at the beginning of the frame. This used to be called ImGuiDrawContext, hence the "DC" variable name. - ImVector IDStack; // ID stack. ID are hashes seeded with the value at the top of the stack - ImRect ClipRect; // Current clipping rectangle. = DrawList->clip_rect_stack.back(). Scissoring / clipping rectangle. x1, y1, x2, y2. - ImRect OuterRectClipped; // = WindowRect just after setup in Begin(). == window->Rect() for root window. - ImRect InnerMainRect, InnerClipRect; - ImRect ContentsRegionRect; // FIXME: This is currently confusing/misleading. Maximum visible content position ~~ Pos + (SizeContentsExplicit ? SizeContentsExplicit : Size - ScrollbarSizes) - CursorStartPos, per axis - int LastFrameActive; // Last frame number the window was Active. - float ItemWidthDefault; - ImGuiMenuColumns MenuColumns; // Simplified columns storage for menu items - ImGuiStorage StateStorage; - ImVector ColumnsStorage; - float FontWindowScale; // User scale multiplier per-window - - ImDrawList* DrawList; // == &DrawListInst (for backward compatibility reason with code using imgui_internal.h we keep this a pointer) - ImDrawList DrawListInst; - ImGuiWindow* ParentWindow; // If we are a child _or_ popup window, this is pointing to our parent. Otherwise NULL. - ImGuiWindow* RootWindow; // Point to ourself or first ancestor that is not a child window. - ImGuiWindow* RootWindowForTitleBarHighlight; // Point to ourself or first ancestor which will display TitleBgActive color when this window is active. - ImGuiWindow* RootWindowForTabbing; // Point to ourself or first ancestor which can be CTRL-Tabbed into. - ImGuiWindow* RootWindowForNav; // Point to ourself or first ancestor which doesn't have the NavFlattened flag. - - ImGuiWindow* NavLastChildNavWindow; // When going to the menu bar, we remember the child window we came from. (This could probably be made implicit if we kept g.Windows sorted by last focused including child window.) - ImGuiID NavLastIds[2]; // Last known NavId for this window, per layer (0/1) - ImRect NavRectRel[2]; // Reference rectangle, in window relative space - - // Navigation / Focus - // FIXME-NAV: Merge all this with the new Nav system, at least the request variables should be moved to ImGuiContext - int FocusIdxAllCounter; // Start at -1 and increase as assigned via FocusItemRegister() - int FocusIdxTabCounter; // (same, but only count widgets which you can Tab through) - int FocusIdxAllRequestCurrent; // Item being requested for focus - int FocusIdxTabRequestCurrent; // Tab-able item being requested for focus - int FocusIdxAllRequestNext; // Item being requested for focus, for next update (relies on layout to be stable between the frame pressing TAB and the next frame) - int FocusIdxTabRequestNext; // " - -public: - ImGuiWindow(ImGuiContext* context, const char* name); - ~ImGuiWindow(); - - ImGuiID GetID(const char* str, const char* str_end = NULL); - ImGuiID GetID(const void* ptr); - ImGuiID GetIDNoKeepAlive(const char* str, const char* str_end = NULL); - ImGuiID GetIDFromRectangle(const ImRect& r_abs); - - // We don't use g.FontSize because the window may be != g.CurrentWidow. - ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x+Size.x, Pos.y+Size.y); } - float CalcFontSize() const { return GImGui->FontBaseSize * FontWindowScale; } - float TitleBarHeight() const { return (Flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : CalcFontSize() + GImGui->Style.FramePadding.y * 2.0f; } - ImRect TitleBarRect() const { return ImRect(Pos, ImVec2(Pos.x + SizeFull.x, Pos.y + TitleBarHeight())); } - float MenuBarHeight() const { return (Flags & ImGuiWindowFlags_MenuBar) ? DC.MenuBarOffset.y + CalcFontSize() + GImGui->Style.FramePadding.y * 2.0f : 0.0f; } - ImRect MenuBarRect() const { float y1 = Pos.y + TitleBarHeight(); return ImRect(Pos.x, y1, Pos.x + SizeFull.x, y1 + MenuBarHeight()); } -}; - -// Backup and restore just enough data to be able to use IsItemHovered() on item A after another B in the same window has overwritten the data. -struct ImGuiItemHoveredDataBackup -{ - ImGuiID LastItemId; - ImGuiItemStatusFlags LastItemStatusFlags; - ImRect LastItemRect; - ImRect LastItemDisplayRect; - - ImGuiItemHoveredDataBackup() { Backup(); } - void Backup() { ImGuiWindow* window = GImGui->CurrentWindow; LastItemId = window->DC.LastItemId; LastItemStatusFlags = window->DC.LastItemStatusFlags; LastItemRect = window->DC.LastItemRect; LastItemDisplayRect = window->DC.LastItemDisplayRect; } - void Restore() const { ImGuiWindow* window = GImGui->CurrentWindow; window->DC.LastItemId = LastItemId; window->DC.LastItemStatusFlags = LastItemStatusFlags; window->DC.LastItemRect = LastItemRect; window->DC.LastItemDisplayRect = LastItemDisplayRect; } -}; - -//----------------------------------------------------------------------------- -// Internal API -// No guarantee of forward compatibility here. -//----------------------------------------------------------------------------- - -namespace ImGui -{ - // We should always have a CurrentWindow in the stack (there is an implicit "Debug" window) - // If this ever crash because g.CurrentWindow is NULL it means that either - // - ImGui::NewFrame() has never been called, which is illegal. - // - You are calling ImGui functions after ImGui::EndFrame()/ImGui::Render() and before the next ImGui::NewFrame(), which is also illegal. - inline ImGuiWindow* GetCurrentWindowRead() { ImGuiContext& g = *GImGui; return g.CurrentWindow; } - inline ImGuiWindow* GetCurrentWindow() { ImGuiContext& g = *GImGui; g.CurrentWindow->WriteAccessed = true; return g.CurrentWindow; } - IMGUI_API ImGuiWindow* FindWindowByName(const char* name); - IMGUI_API void FocusWindow(ImGuiWindow* window); - IMGUI_API void BringWindowToFront(ImGuiWindow* window); - IMGUI_API void BringWindowToBack(ImGuiWindow* window); - IMGUI_API bool IsWindowChildOf(ImGuiWindow* window, ImGuiWindow* potential_parent); - IMGUI_API bool IsWindowNavFocusable(ImGuiWindow* window); - - IMGUI_API void Initialize(ImGuiContext* context); - IMGUI_API void Shutdown(ImGuiContext* context); // Since 1.60 this is a _private_ function. You can call DestroyContext() to destroy the context created by CreateContext(). - - IMGUI_API void NewFrameUpdateHoveredWindowAndCaptureFlags(); - - IMGUI_API void MarkIniSettingsDirty(); - IMGUI_API void MarkIniSettingsDirty(ImGuiWindow* window); - IMGUI_API ImGuiSettingsHandler* FindSettingsHandler(const char* type_name); - IMGUI_API ImGuiWindowSettings* FindWindowSettings(ImGuiID id); - - inline ImGuiID GetItemID() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DC.LastItemId; } - inline ImGuiID GetActiveID() { ImGuiContext& g = *GImGui; return g.ActiveId; } - inline ImGuiID GetFocusID() { ImGuiContext& g = *GImGui; return g.NavId; } - IMGUI_API void SetActiveID(ImGuiID id, ImGuiWindow* window); - IMGUI_API void SetFocusID(ImGuiID id, ImGuiWindow* window); - IMGUI_API void ClearActiveID(); - IMGUI_API ImGuiID GetHoveredID(); - IMGUI_API void SetHoveredID(ImGuiID id); - IMGUI_API void KeepAliveID(ImGuiID id); - IMGUI_API void MarkItemValueChanged(ImGuiID id); - - IMGUI_API void ItemSize(const ImVec2& size, float text_offset_y = 0.0f); - IMGUI_API void ItemSize(const ImRect& bb, float text_offset_y = 0.0f); - IMGUI_API bool ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb = NULL); - IMGUI_API bool ItemHoverable(const ImRect& bb, ImGuiID id); - IMGUI_API bool IsClippedEx(const ImRect& bb, ImGuiID id, bool clip_even_when_logged); - IMGUI_API bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id, bool tab_stop = true); // Return true if focus is requested - IMGUI_API void FocusableItemUnregister(ImGuiWindow* window); - IMGUI_API ImVec2 CalcItemSize(ImVec2 size, float default_x, float default_y); - IMGUI_API float CalcWrapWidthForPos(const ImVec2& pos, float wrap_pos_x); - IMGUI_API void PushMultiItemsWidths(int components, float width_full = 0.0f); - IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled); - IMGUI_API void PopItemFlag(); - - IMGUI_API void SetCurrentFont(ImFont* font); - - IMGUI_API void OpenPopupEx(ImGuiID id); - IMGUI_API void ClosePopup(ImGuiID id); - IMGUI_API void ClosePopupsOverWindow(ImGuiWindow* ref_window); - IMGUI_API bool IsPopupOpen(ImGuiID id); - IMGUI_API bool BeginPopupEx(ImGuiID id, ImGuiWindowFlags extra_flags); - IMGUI_API void BeginTooltipEx(ImGuiWindowFlags extra_flags, bool override_previous_tooltip = true); - IMGUI_API ImGuiWindow* GetFrontMostPopupModal(); - - IMGUI_API void NavInitWindow(ImGuiWindow* window, bool force_reinit); - IMGUI_API void NavMoveRequestCancel(); - IMGUI_API void NavMoveRequestForward(ImGuiDir move_dir, ImGuiDir clip_dir, const ImRect& bb_rel, ImGuiNavMoveFlags move_flags); - IMGUI_API void NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags); - IMGUI_API void ActivateItem(ImGuiID id); // Remotely activate a button, checkbox, tree node etc. given its unique ID. activation is queued and processed on the next frame when the item is encountered again. - - IMGUI_API float GetNavInputAmount(ImGuiNavInput n, ImGuiInputReadMode mode); - IMGUI_API ImVec2 GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiInputReadMode mode, float slow_factor = 0.0f, float fast_factor = 0.0f); - IMGUI_API int CalcTypematicPressedRepeatAmount(float t, float t_prev, float repeat_delay, float repeat_rate); - - IMGUI_API void Scrollbar(ImGuiLayoutType direction); - IMGUI_API void VerticalSeparator(); // Vertical separator, for menu bars (use current line height). not exposed because it is misleading what it doesn't have an effect on regular layout. - IMGUI_API bool SplitterBehavior(ImGuiID id, const ImRect& bb, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend = 0.0f); - - IMGUI_API bool BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id); - IMGUI_API void ClearDragDrop(); - IMGUI_API bool IsDragDropPayloadBeingAccepted(); - IMGUI_API void BeginDragDropTooltip(); - IMGUI_API void EndDragDropTooltip(); - - // FIXME-WIP: New Columns API - IMGUI_API void BeginColumns(const char* str_id, int count, ImGuiColumnsFlags flags = 0); // setup number of columns. use an identifier to distinguish multiple column sets. close with EndColumns(). - IMGUI_API void EndColumns(); // close columns - IMGUI_API void PushColumnClipRect(int column_index = -1); - - // NB: All position are in absolute pixels coordinates (never using window coordinates internally) - // AVOID USING OUTSIDE OF IMGUI.CPP! NOT FOR PUBLIC CONSUMPTION. THOSE FUNCTIONS ARE A MESS. THEIR SIGNATURE AND BEHAVIOR WILL CHANGE, THEY NEED TO BE REFACTORED INTO SOMETHING DECENT. - IMGUI_API void RenderText(ImVec2 pos, const char* text, const char* text_end = NULL, bool hide_text_after_hash = true); - IMGUI_API void RenderTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width); - IMGUI_API void RenderTextClipped(const ImVec2& pos_min, const ImVec2& pos_max, const char* text, const char* text_end, const ImVec2* text_size_if_known, const ImVec2& align = ImVec2(0,0), const ImRect* clip_rect = NULL); - IMGUI_API void RenderFrame(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, bool border = true, float rounding = 0.0f); - IMGUI_API void RenderFrameBorder(ImVec2 p_min, ImVec2 p_max, float rounding = 0.0f); - IMGUI_API void RenderColorRectWithAlphaCheckerboard(ImVec2 p_min, ImVec2 p_max, ImU32 fill_col, float grid_step, ImVec2 grid_off, float rounding = 0.0f, int rounding_corners_flags = ~0); - IMGUI_API void RenderArrow(ImVec2 pos, ImGuiDir dir, float scale = 1.0f); - IMGUI_API void RenderBullet(ImVec2 pos); - IMGUI_API void RenderCheckMark(ImVec2 pos, ImU32 col, float sz); - IMGUI_API void RenderNavHighlight(const ImRect& bb, ImGuiID id, ImGuiNavHighlightFlags flags = ImGuiNavHighlightFlags_TypeDefault); // Navigation highlight - IMGUI_API void RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding); - IMGUI_API const char* FindRenderedTextEnd(const char* text, const char* text_end = NULL); // Find the optional ## from which we stop displaying text. - - IMGUI_API bool ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags = 0); - IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0,0), ImGuiButtonFlags flags = 0); - IMGUI_API bool CloseButton(ImGuiID id, const ImVec2& pos, float radius); - - IMGUI_API bool DragBehavior(ImGuiID id, ImGuiDataType data_type, void* v, float v_speed, const void* v_min, const void* v_max, const char* format, float power); - IMGUI_API bool SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* v, const void* v_min, const void* v_max, const char* format, float power, ImGuiSliderFlags flags = 0); - - IMGUI_API bool InputTextEx(const char* label, char* buf, int buf_size, const ImVec2& size_arg, ImGuiInputTextFlags flags, ImGuiTextEditCallback callback = NULL, void* user_data = NULL); - IMGUI_API bool InputScalarAsWidgetReplacement(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* data_ptr, const char* format); - - IMGUI_API void ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags); - IMGUI_API void ColorEditOptionsPopup(const float* col, ImGuiColorEditFlags flags); - - IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL); - IMGUI_API bool TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0); // Consume previous SetNextTreeNodeOpened() data, if any. May return true when logging - IMGUI_API void TreePushRawID(ImGuiID id); - - IMGUI_API void PlotEx(ImGuiPlotType plot_type, const char* label, float (*values_getter)(void* data, int idx), void* data, int values_count, int values_offset, const char* overlay_text, float scale_min, float scale_max, ImVec2 graph_size); - - // Shade functions (write over already created vertices) - IMGUI_API void ShadeVertsLinearColorGradientKeepAlpha(ImDrawVert* vert_start, ImDrawVert* vert_end, ImVec2 gradient_p0, ImVec2 gradient_p1, ImU32 col0, ImU32 col1); - IMGUI_API void ShadeVertsLinearAlphaGradientForLeftToRightText(ImDrawVert* vert_start, ImDrawVert* vert_end, float gradient_p0_x, float gradient_p1_x); - IMGUI_API void ShadeVertsLinearUV(ImDrawVert* vert_start, ImDrawVert* vert_end, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, bool clamp); - -} // namespace ImGui - -// ImFontAtlas internals -IMGUI_API bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas); -IMGUI_API void ImFontAtlasBuildRegisterDefaultCustomRects(ImFontAtlas* atlas); -IMGUI_API void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent); -IMGUI_API void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* spc); -IMGUI_API void ImFontAtlasBuildFinish(ImFontAtlas* atlas); -IMGUI_API void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_multiply_factor); -IMGUI_API void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride); - -#ifdef __clang__ -#pragma clang diagnostic pop -#endif - -#ifdef _MSC_VER -#pragma warning (pop) -#endif diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/stb_rect_pack.h b/src/lib/geogram_gfx/third_party/ImGui/OLD/stb_rect_pack.h deleted file mode 100644 index 2b07dcc8..00000000 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/stb_rect_pack.h +++ /dev/null @@ -1,623 +0,0 @@ -// stb_rect_pack.h - v0.11 - public domain - rectangle packing -// Sean Barrett 2014 -// -// Useful for e.g. packing rectangular textures into an atlas. -// Does not do rotation. -// -// Not necessarily the awesomest packing method, but better than -// the totally naive one in stb_truetype (which is primarily what -// this is meant to replace). -// -// Has only had a few tests run, may have issues. -// -// More docs to come. -// -// No memory allocations; uses qsort() and assert() from stdlib. -// Can override those by defining STBRP_SORT and STBRP_ASSERT. -// -// This library currently uses the Skyline Bottom-Left algorithm. -// -// Please note: better rectangle packers are welcome! Please -// implement them to the same API, but with a different init -// function. -// -// Credits -// -// Library -// Sean Barrett -// Minor features -// Martins Mozeiko -// github:IntellectualKitty -// -// Bugfixes / warning fixes -// Jeremy Jaussaud -// -// Version history: -// -// 0.11 (2017-03-03) return packing success/fail result -// 0.10 (2016-10-25) remove cast-away-const to avoid warnings -// 0.09 (2016-08-27) fix compiler warnings -// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0) -// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0) -// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort -// 0.05: added STBRP_ASSERT to allow replacing assert -// 0.04: fixed minor bug in STBRP_LARGE_RECTS support -// 0.01: initial release -// -// LICENSE -// -// See end of file for license information. - -////////////////////////////////////////////////////////////////////////////// -// -// INCLUDE SECTION -// - -#ifndef STB_INCLUDE_STB_RECT_PACK_H -#define STB_INCLUDE_STB_RECT_PACK_H - -#define STB_RECT_PACK_VERSION 1 - -#ifdef STBRP_STATIC -#define STBRP_DEF static -#else -#define STBRP_DEF extern -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct stbrp_context stbrp_context; -typedef struct stbrp_node stbrp_node; -typedef struct stbrp_rect stbrp_rect; - -#ifdef STBRP_LARGE_RECTS -typedef int stbrp_coord; -#else -typedef unsigned short stbrp_coord; -#endif - -STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects); -// Assign packed locations to rectangles. The rectangles are of type -// 'stbrp_rect' defined below, stored in the array 'rects', and there -// are 'num_rects' many of them. -// -// Rectangles which are successfully packed have the 'was_packed' flag -// set to a non-zero value and 'x' and 'y' store the minimum location -// on each axis (i.e. bottom-left in cartesian coordinates, top-left -// if you imagine y increasing downwards). Rectangles which do not fit -// have the 'was_packed' flag set to 0. -// -// You should not try to access the 'rects' array from another thread -// while this function is running, as the function temporarily reorders -// the array while it executes. -// -// To pack into another rectangle, you need to call stbrp_init_target -// again. To continue packing into the same rectangle, you can call -// this function again. Calling this multiple times with multiple rect -// arrays will probably produce worse packing results than calling it -// a single time with the full rectangle array, but the option is -// available. -// -// The function returns 1 if all of the rectangles were successfully -// packed and 0 otherwise. - -struct stbrp_rect -{ - // reserved for your use: - int id; - - // input: - stbrp_coord w, h; - - // output: - stbrp_coord x, y; - int was_packed; // non-zero if valid packing - -}; // 16 bytes, nominally - - -STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes); -// Initialize a rectangle packer to: -// pack a rectangle that is 'width' by 'height' in dimensions -// using temporary storage provided by the array 'nodes', which is 'num_nodes' long -// -// You must call this function every time you start packing into a new target. -// -// There is no "shutdown" function. The 'nodes' memory must stay valid for -// the following stbrp_pack_rects() call (or calls), but can be freed after -// the call (or calls) finish. -// -// Note: to guarantee best results, either: -// 1. make sure 'num_nodes' >= 'width' -// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1' -// -// If you don't do either of the above things, widths will be quantized to multiples -// of small integers to guarantee the algorithm doesn't run out of temporary storage. -// -// If you do #2, then the non-quantized algorithm will be used, but the algorithm -// may run out of temporary storage and be unable to pack some rectangles. - -STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem); -// Optionally call this function after init but before doing any packing to -// change the handling of the out-of-temp-memory scenario, described above. -// If you call init again, this will be reset to the default (false). - - -STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic); -// Optionally select which packing heuristic the library should use. Different -// heuristics will produce better/worse results for different data sets. -// If you call init again, this will be reset to the default. - -enum -{ - STBRP_HEURISTIC_Skyline_default=0, - STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default, - STBRP_HEURISTIC_Skyline_BF_sortHeight -}; - - -////////////////////////////////////////////////////////////////////////////// -// -// the details of the following structures don't matter to you, but they must -// be visible so you can handle the memory allocations for them - -struct stbrp_node -{ - stbrp_coord x,y; - stbrp_node *next; -}; - -struct stbrp_context -{ - int width; - int height; - int align; - int init_mode; - int heuristic; - int num_nodes; - stbrp_node *active_head; - stbrp_node *free_head; - stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2' -}; - -#ifdef __cplusplus -} -#endif - -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// IMPLEMENTATION SECTION -// - -#ifdef STB_RECT_PACK_IMPLEMENTATION -#ifndef STBRP_SORT -#include -#define STBRP_SORT qsort -#endif - -#ifndef STBRP_ASSERT -#include -#define STBRP_ASSERT assert -#endif - -#ifdef _MSC_VER -#define STBRP__NOTUSED(v) (void)(v) -#define STBRP__CDECL __cdecl -#else -#define STBRP__NOTUSED(v) (void)sizeof(v) -#define STBRP__CDECL -#endif - -enum -{ - STBRP__INIT_skyline = 1 -}; - -STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic) -{ - switch (context->init_mode) { - case STBRP__INIT_skyline: - STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight); - context->heuristic = heuristic; - break; - default: - STBRP_ASSERT(0); - } -} - -STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem) -{ - if (allow_out_of_mem) - // if it's ok to run out of memory, then don't bother aligning them; - // this gives better packing, but may fail due to OOM (even though - // the rectangles easily fit). @TODO a smarter approach would be to only - // quantize once we've hit OOM, then we could get rid of this parameter. - context->align = 1; - else { - // if it's not ok to run out of memory, then quantize the widths - // so that num_nodes is always enough nodes. - // - // I.e. num_nodes * align >= width - // align >= width / num_nodes - // align = ceil(width/num_nodes) - - context->align = (context->width + context->num_nodes-1) / context->num_nodes; - } -} - -STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes) -{ - int i; -#ifndef STBRP_LARGE_RECTS - STBRP_ASSERT(width <= 0xffff && height <= 0xffff); -#endif - - for (i=0; i < num_nodes-1; ++i) - nodes[i].next = &nodes[i+1]; - nodes[i].next = NULL; - context->init_mode = STBRP__INIT_skyline; - context->heuristic = STBRP_HEURISTIC_Skyline_default; - context->free_head = &nodes[0]; - context->active_head = &context->extra[0]; - context->width = width; - context->height = height; - context->num_nodes = num_nodes; - stbrp_setup_allow_out_of_mem(context, 0); - - // node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly) - context->extra[0].x = 0; - context->extra[0].y = 0; - context->extra[0].next = &context->extra[1]; - context->extra[1].x = (stbrp_coord) width; -#ifdef STBRP_LARGE_RECTS - context->extra[1].y = (1<<30); -#else - context->extra[1].y = 65535; -#endif - context->extra[1].next = NULL; -} - -// find minimum y position if it starts at x1 -static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste) -{ - stbrp_node *node = first; - int x1 = x0 + width; - int min_y, visited_width, waste_area; - - STBRP__NOTUSED(c); - - STBRP_ASSERT(first->x <= x0); - - #if 0 - // skip in case we're past the node - while (node->next->x <= x0) - ++node; - #else - STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency - #endif - - STBRP_ASSERT(node->x <= x0); - - min_y = 0; - waste_area = 0; - visited_width = 0; - while (node->x < x1) { - if (node->y > min_y) { - // raise min_y higher. - // we've accounted for all waste up to min_y, - // but we'll now add more waste for everything we've visted - waste_area += visited_width * (node->y - min_y); - min_y = node->y; - // the first time through, visited_width might be reduced - if (node->x < x0) - visited_width += node->next->x - x0; - else - visited_width += node->next->x - node->x; - } else { - // add waste area - int under_width = node->next->x - node->x; - if (under_width + visited_width > width) - under_width = width - visited_width; - waste_area += under_width * (min_y - node->y); - visited_width += under_width; - } - node = node->next; - } - - *pwaste = waste_area; - return min_y; -} - -typedef struct -{ - int x,y; - stbrp_node **prev_link; -} stbrp__findresult; - -static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height) -{ - int best_waste = (1<<30), best_x, best_y = (1 << 30); - stbrp__findresult fr; - stbrp_node **prev, *node, *tail, **best = NULL; - - // align to multiple of c->align - width = (width + c->align - 1); - width -= width % c->align; - STBRP_ASSERT(width % c->align == 0); - - node = c->active_head; - prev = &c->active_head; - while (node->x + width <= c->width) { - int y,waste; - y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste); - if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL - // bottom left - if (y < best_y) { - best_y = y; - best = prev; - } - } else { - // best-fit - if (y + height <= c->height) { - // can only use it if it first vertically - if (y < best_y || (y == best_y && waste < best_waste)) { - best_y = y; - best_waste = waste; - best = prev; - } - } - } - prev = &node->next; - node = node->next; - } - - best_x = (best == NULL) ? 0 : (*best)->x; - - // if doing best-fit (BF), we also have to try aligning right edge to each node position - // - // e.g, if fitting - // - // ____________________ - // |____________________| - // - // into - // - // | | - // | ____________| - // |____________| - // - // then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned - // - // This makes BF take about 2x the time - - if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) { - tail = c->active_head; - node = c->active_head; - prev = &c->active_head; - // find first node that's admissible - while (tail->x < width) - tail = tail->next; - while (tail) { - int xpos = tail->x - width; - int y,waste; - STBRP_ASSERT(xpos >= 0); - // find the left position that matches this - while (node->next->x <= xpos) { - prev = &node->next; - node = node->next; - } - STBRP_ASSERT(node->next->x > xpos && node->x <= xpos); - y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste); - if (y + height < c->height) { - if (y <= best_y) { - if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) { - best_x = xpos; - STBRP_ASSERT(y <= best_y); - best_y = y; - best_waste = waste; - best = prev; - } - } - } - tail = tail->next; - } - } - - fr.prev_link = best; - fr.x = best_x; - fr.y = best_y; - return fr; -} - -static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height) -{ - // find best position according to heuristic - stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height); - stbrp_node *node, *cur; - - // bail if: - // 1. it failed - // 2. the best node doesn't fit (we don't always check this) - // 3. we're out of memory - if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) { - res.prev_link = NULL; - return res; - } - - // on success, create new node - node = context->free_head; - node->x = (stbrp_coord) res.x; - node->y = (stbrp_coord) (res.y + height); - - context->free_head = node->next; - - // insert the new node into the right starting point, and - // let 'cur' point to the remaining nodes needing to be - // stiched back in - - cur = *res.prev_link; - if (cur->x < res.x) { - // preserve the existing one, so start testing with the next one - stbrp_node *next = cur->next; - cur->next = node; - cur = next; - } else { - *res.prev_link = node; - } - - // from here, traverse cur and free the nodes, until we get to one - // that shouldn't be freed - while (cur->next && cur->next->x <= res.x + width) { - stbrp_node *next = cur->next; - // move the current node to the free list - cur->next = context->free_head; - context->free_head = cur; - cur = next; - } - - // stitch the list back in - node->next = cur; - - if (cur->x < res.x + width) - cur->x = (stbrp_coord) (res.x + width); - -#ifdef _DEBUG - cur = context->active_head; - while (cur->x < context->width) { - STBRP_ASSERT(cur->x < cur->next->x); - cur = cur->next; - } - STBRP_ASSERT(cur->next == NULL); - - { - int count=0; - cur = context->active_head; - while (cur) { - cur = cur->next; - ++count; - } - cur = context->free_head; - while (cur) { - cur = cur->next; - ++count; - } - STBRP_ASSERT(count == context->num_nodes+2); - } -#endif - - return res; -} - -static int STBRP__CDECL rect_height_compare(const void *a, const void *b) -{ - const stbrp_rect *p = (const stbrp_rect *) a; - const stbrp_rect *q = (const stbrp_rect *) b; - if (p->h > q->h) - return -1; - if (p->h < q->h) - return 1; - return (p->w > q->w) ? -1 : (p->w < q->w); -} - -static int STBRP__CDECL rect_original_order(const void *a, const void *b) -{ - const stbrp_rect *p = (const stbrp_rect *) a; - const stbrp_rect *q = (const stbrp_rect *) b; - return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed); -} - -#ifdef STBRP_LARGE_RECTS -#define STBRP__MAXVAL 0xffffffff -#else -#define STBRP__MAXVAL 0xffff -#endif - -STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects) -{ - int i, all_rects_packed = 1; - - // we use the 'was_packed' field internally to allow sorting/unsorting - for (i=0; i < num_rects; ++i) { - rects[i].was_packed = i; - #ifndef STBRP_LARGE_RECTS - STBRP_ASSERT(rects[i].w <= 0xffff && rects[i].h <= 0xffff); - #endif - } - - // sort according to heuristic - STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare); - - for (i=0; i < num_rects; ++i) { - if (rects[i].w == 0 || rects[i].h == 0) { - rects[i].x = rects[i].y = 0; // empty rect needs no space - } else { - stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h); - if (fr.prev_link) { - rects[i].x = (stbrp_coord) fr.x; - rects[i].y = (stbrp_coord) fr.y; - } else { - rects[i].x = rects[i].y = STBRP__MAXVAL; - } - } - } - - // unsort - STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order); - - // set was_packed flags and all_rects_packed status - for (i=0; i < num_rects; ++i) { - rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL); - if (!rects[i].was_packed) - all_rects_packed = 0; - } - - // return the all_rects_packed status - return all_rects_packed; -} -#endif - -/* ------------------------------------------------------------------------------- -This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------- -ALTERNATIVE A - MIT License -Copyright (c) 2017 Sean Barrett -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. ------------------------------------------------------------------------------- -ALTERNATIVE B - Public Domain (www.unlicense.org) -This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------- -*/ diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/stb_textedit.h b/src/lib/geogram_gfx/third_party/ImGui/OLD/stb_textedit.h deleted file mode 100644 index 9e12469b..00000000 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/stb_textedit.h +++ /dev/null @@ -1,1409 +0,0 @@ -// [ImGui] this is a slightly modified version of stb_textedit.h 1.12. Those changes would need to be pushed into nothings/stb -// [ImGui] - 2018-06: fixed undo/redo after pasting large amount of text (over 32 kb). Redo will still fail when undo buffers are exhausted, but text won't be corrupted (see nothings/stb issue #620) -// [ImGui] - 2018-06: fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321) -// [ImGui] - fixed some minor warnings - -// stb_textedit.h - v1.12 - public domain - Sean Barrett -// Development of this library was sponsored by RAD Game Tools -// -// This C header file implements the guts of a multi-line text-editing -// widget; you implement display, word-wrapping, and low-level string -// insertion/deletion, and stb_textedit will map user inputs into -// insertions & deletions, plus updates to the cursor position, -// selection state, and undo state. -// -// It is intended for use in games and other systems that need to build -// their own custom widgets and which do not have heavy text-editing -// requirements (this library is not recommended for use for editing large -// texts, as its performance does not scale and it has limited undo). -// -// Non-trivial behaviors are modelled after Windows text controls. -// -// -// LICENSE -// -// See end of file for license information. -// -// -// DEPENDENCIES -// -// Uses the C runtime function 'memmove', which you can override -// by defining STB_TEXTEDIT_memmove before the implementation. -// Uses no other functions. Performs no runtime allocations. -// -// -// VERSION HISTORY -// -// 1.12 (2018-01-29) user can change STB_TEXTEDIT_KEYTYPE, fix redo to avoid crash -// 1.11 (2017-03-03) fix HOME on last line, dragging off single-line textfield -// 1.10 (2016-10-25) supress warnings about casting away const with -Wcast-qual -// 1.9 (2016-08-27) customizable move-by-word -// 1.8 (2016-04-02) better keyboard handling when mouse button is down -// 1.7 (2015-09-13) change y range handling in case baseline is non-0 -// 1.6 (2015-04-15) allow STB_TEXTEDIT_memmove -// 1.5 (2014-09-10) add support for secondary keys for OS X -// 1.4 (2014-08-17) fix signed/unsigned warnings -// 1.3 (2014-06-19) fix mouse clicking to round to nearest char boundary -// 1.2 (2014-05-27) fix some RAD types that had crept into the new code -// 1.1 (2013-12-15) move-by-word (requires STB_TEXTEDIT_IS_SPACE ) -// 1.0 (2012-07-26) improve documentation, initial public release -// 0.3 (2012-02-24) bugfixes, single-line mode; insert mode -// 0.2 (2011-11-28) fixes to undo/redo -// 0.1 (2010-07-08) initial version -// -// ADDITIONAL CONTRIBUTORS -// -// Ulf Winklemann: move-by-word in 1.1 -// Fabian Giesen: secondary key inputs in 1.5 -// Martins Mozeiko: STB_TEXTEDIT_memmove in 1.6 -// -// Bugfixes: -// Scott Graham -// Daniel Keller -// Omar Cornut -// Dan Thompson -// -// USAGE -// -// This file behaves differently depending on what symbols you define -// before including it. -// -// -// Header-file mode: -// -// If you do not define STB_TEXTEDIT_IMPLEMENTATION before including this, -// it will operate in "header file" mode. In this mode, it declares a -// single public symbol, STB_TexteditState, which encapsulates the current -// state of a text widget (except for the string, which you will store -// separately). -// -// To compile in this mode, you must define STB_TEXTEDIT_CHARTYPE to a -// primitive type that defines a single character (e.g. char, wchar_t, etc). -// -// To save space or increase undo-ability, you can optionally define the -// following things that are used by the undo system: -// -// STB_TEXTEDIT_POSITIONTYPE small int type encoding a valid cursor position -// STB_TEXTEDIT_UNDOSTATECOUNT the number of undo states to allow -// STB_TEXTEDIT_UNDOCHARCOUNT the number of characters to store in the undo buffer -// -// If you don't define these, they are set to permissive types and -// moderate sizes. The undo system does no memory allocations, so -// it grows STB_TexteditState by the worst-case storage which is (in bytes): -// -// [4 + 3 * sizeof(STB_TEXTEDIT_POSITIONTYPE)] * STB_TEXTEDIT_UNDOSTATE_COUNT -// + sizeof(STB_TEXTEDIT_CHARTYPE) * STB_TEXTEDIT_UNDOCHAR_COUNT -// -// -// Implementation mode: -// -// If you define STB_TEXTEDIT_IMPLEMENTATION before including this, it -// will compile the implementation of the text edit widget, depending -// on a large number of symbols which must be defined before the include. -// -// The implementation is defined only as static functions. You will then -// need to provide your own APIs in the same file which will access the -// static functions. -// -// The basic concept is that you provide a "string" object which -// behaves like an array of characters. stb_textedit uses indices to -// refer to positions in the string, implicitly representing positions -// in the displayed textedit. This is true for both plain text and -// rich text; even with rich text stb_truetype interacts with your -// code as if there was an array of all the displayed characters. -// -// Symbols that must be the same in header-file and implementation mode: -// -// STB_TEXTEDIT_CHARTYPE the character type -// STB_TEXTEDIT_POSITIONTYPE small type that is a valid cursor position -// STB_TEXTEDIT_UNDOSTATECOUNT the number of undo states to allow -// STB_TEXTEDIT_UNDOCHARCOUNT the number of characters to store in the undo buffer -// -// Symbols you must define for implementation mode: -// -// STB_TEXTEDIT_STRING the type of object representing a string being edited, -// typically this is a wrapper object with other data you need -// -// STB_TEXTEDIT_STRINGLEN(obj) the length of the string (ideally O(1)) -// STB_TEXTEDIT_LAYOUTROW(&r,obj,n) returns the results of laying out a line of characters -// starting from character #n (see discussion below) -// STB_TEXTEDIT_GETWIDTH(obj,n,i) returns the pixel delta from the xpos of the i'th character -// to the xpos of the i+1'th char for a line of characters -// starting at character #n (i.e. accounts for kerning -// with previous char) -// STB_TEXTEDIT_KEYTOTEXT(k) maps a keyboard input to an insertable character -// (return type is int, -1 means not valid to insert) -// STB_TEXTEDIT_GETCHAR(obj,i) returns the i'th character of obj, 0-based -// STB_TEXTEDIT_NEWLINE the character returned by _GETCHAR() we recognize -// as manually wordwrapping for end-of-line positioning -// -// STB_TEXTEDIT_DELETECHARS(obj,i,n) delete n characters starting at i -// STB_TEXTEDIT_INSERTCHARS(obj,i,c*,n) insert n characters at i (pointed to by STB_TEXTEDIT_CHARTYPE*) -// -// STB_TEXTEDIT_K_SHIFT a power of two that is or'd in to a keyboard input to represent the shift key -// -// STB_TEXTEDIT_K_LEFT keyboard input to move cursor left -// STB_TEXTEDIT_K_RIGHT keyboard input to move cursor right -// STB_TEXTEDIT_K_UP keyboard input to move cursor up -// STB_TEXTEDIT_K_DOWN keyboard input to move cursor down -// STB_TEXTEDIT_K_LINESTART keyboard input to move cursor to start of line // e.g. HOME -// STB_TEXTEDIT_K_LINEEND keyboard input to move cursor to end of line // e.g. END -// STB_TEXTEDIT_K_TEXTSTART keyboard input to move cursor to start of text // e.g. ctrl-HOME -// STB_TEXTEDIT_K_TEXTEND keyboard input to move cursor to end of text // e.g. ctrl-END -// STB_TEXTEDIT_K_DELETE keyboard input to delete selection or character under cursor -// STB_TEXTEDIT_K_BACKSPACE keyboard input to delete selection or character left of cursor -// STB_TEXTEDIT_K_UNDO keyboard input to perform undo -// STB_TEXTEDIT_K_REDO keyboard input to perform redo -// -// Optional: -// STB_TEXTEDIT_K_INSERT keyboard input to toggle insert mode -// STB_TEXTEDIT_IS_SPACE(ch) true if character is whitespace (e.g. 'isspace'), -// required for default WORDLEFT/WORDRIGHT handlers -// STB_TEXTEDIT_MOVEWORDLEFT(obj,i) custom handler for WORDLEFT, returns index to move cursor to -// STB_TEXTEDIT_MOVEWORDRIGHT(obj,i) custom handler for WORDRIGHT, returns index to move cursor to -// STB_TEXTEDIT_K_WORDLEFT keyboard input to move cursor left one word // e.g. ctrl-LEFT -// STB_TEXTEDIT_K_WORDRIGHT keyboard input to move cursor right one word // e.g. ctrl-RIGHT -// STB_TEXTEDIT_K_LINESTART2 secondary keyboard input to move cursor to start of line -// STB_TEXTEDIT_K_LINEEND2 secondary keyboard input to move cursor to end of line -// STB_TEXTEDIT_K_TEXTSTART2 secondary keyboard input to move cursor to start of text -// STB_TEXTEDIT_K_TEXTEND2 secondary keyboard input to move cursor to end of text -// -// Todo: -// STB_TEXTEDIT_K_PGUP keyboard input to move cursor up a page -// STB_TEXTEDIT_K_PGDOWN keyboard input to move cursor down a page -// -// Keyboard input must be encoded as a single integer value; e.g. a character code -// and some bitflags that represent shift states. to simplify the interface, SHIFT must -// be a bitflag, so we can test the shifted state of cursor movements to allow selection, -// i.e. (STB_TEXTED_K_RIGHT|STB_TEXTEDIT_K_SHIFT) should be shifted right-arrow. -// -// You can encode other things, such as CONTROL or ALT, in additional bits, and -// then test for their presence in e.g. STB_TEXTEDIT_K_WORDLEFT. For example, -// my Windows implementations add an additional CONTROL bit, and an additional KEYDOWN -// bit. Then all of the STB_TEXTEDIT_K_ values bitwise-or in the KEYDOWN bit, -// and I pass both WM_KEYDOWN and WM_CHAR events to the "key" function in the -// API below. The control keys will only match WM_KEYDOWN events because of the -// keydown bit I add, and STB_TEXTEDIT_KEYTOTEXT only tests for the KEYDOWN -// bit so it only decodes WM_CHAR events. -// -// STB_TEXTEDIT_LAYOUTROW returns information about the shape of one displayed -// row of characters assuming they start on the i'th character--the width and -// the height and the number of characters consumed. This allows this library -// to traverse the entire layout incrementally. You need to compute word-wrapping -// here. -// -// Each textfield keeps its own insert mode state, which is not how normal -// applications work. To keep an app-wide insert mode, update/copy the -// "insert_mode" field of STB_TexteditState before/after calling API functions. -// -// API -// -// void stb_textedit_initialize_state(STB_TexteditState *state, int is_single_line) -// -// void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) -// void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) -// int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) -// int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len) -// void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXEDIT_KEYTYPE key) -// -// Each of these functions potentially updates the string and updates the -// state. -// -// initialize_state: -// set the textedit state to a known good default state when initially -// constructing the textedit. -// -// click: -// call this with the mouse x,y on a mouse down; it will update the cursor -// and reset the selection start/end to the cursor point. the x,y must -// be relative to the text widget, with (0,0) being the top left. -// -// drag: -// call this with the mouse x,y on a mouse drag/up; it will update the -// cursor and the selection end point -// -// cut: -// call this to delete the current selection; returns true if there was -// one. you should FIRST copy the current selection to the system paste buffer. -// (To copy, just copy the current selection out of the string yourself.) -// -// paste: -// call this to paste text at the current cursor point or over the current -// selection if there is one. -// -// key: -// call this for keyboard inputs sent to the textfield. you can use it -// for "key down" events or for "translated" key events. if you need to -// do both (as in Win32), or distinguish Unicode characters from control -// inputs, set a high bit to distinguish the two; then you can define the -// various definitions like STB_TEXTEDIT_K_LEFT have the is-key-event bit -// set, and make STB_TEXTEDIT_KEYTOCHAR check that the is-key-event bit is -// clear. STB_TEXTEDIT_KEYTYPE defaults to int, but you can #define it to -// anything other type you wante before including. -// -// -// When rendering, you can read the cursor position and selection state from -// the STB_TexteditState. -// -// -// Notes: -// -// This is designed to be usable in IMGUI, so it allows for the possibility of -// running in an IMGUI that has NOT cached the multi-line layout. For this -// reason, it provides an interface that is compatible with computing the -// layout incrementally--we try to make sure we make as few passes through -// as possible. (For example, to locate the mouse pointer in the text, we -// could define functions that return the X and Y positions of characters -// and binary search Y and then X, but if we're doing dynamic layout this -// will run the layout algorithm many times, so instead we manually search -// forward in one pass. Similar logic applies to e.g. up-arrow and -// down-arrow movement.) -// -// If it's run in a widget that *has* cached the layout, then this is less -// efficient, but it's not horrible on modern computers. But you wouldn't -// want to edit million-line files with it. - - -//////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////// -//// -//// Header-file mode -//// -//// - -#ifndef INCLUDE_STB_TEXTEDIT_H -#define INCLUDE_STB_TEXTEDIT_H - -//////////////////////////////////////////////////////////////////////// -// -// STB_TexteditState -// -// Definition of STB_TexteditState which you should store -// per-textfield; it includes cursor position, selection state, -// and undo state. -// - -#ifndef STB_TEXTEDIT_UNDOSTATECOUNT -#define STB_TEXTEDIT_UNDOSTATECOUNT 99 -#endif -#ifndef STB_TEXTEDIT_UNDOCHARCOUNT -#define STB_TEXTEDIT_UNDOCHARCOUNT 999 -#endif -#ifndef STB_TEXTEDIT_CHARTYPE -#define STB_TEXTEDIT_CHARTYPE int -#endif -#ifndef STB_TEXTEDIT_POSITIONTYPE -#define STB_TEXTEDIT_POSITIONTYPE int -#endif - -typedef struct -{ - // private data - STB_TEXTEDIT_POSITIONTYPE where; - STB_TEXTEDIT_POSITIONTYPE insert_length; - STB_TEXTEDIT_POSITIONTYPE delete_length; - int char_storage; -} StbUndoRecord; - -typedef struct -{ - // private data - StbUndoRecord undo_rec [STB_TEXTEDIT_UNDOSTATECOUNT]; - STB_TEXTEDIT_CHARTYPE undo_char[STB_TEXTEDIT_UNDOCHARCOUNT]; - short undo_point, redo_point; - int undo_char_point, redo_char_point; -} StbUndoState; - -typedef struct -{ - ///////////////////// - // - // public data - // - - int cursor; - // position of the text cursor within the string - - int select_start; // selection start point - int select_end; - // selection start and end point in characters; if equal, no selection. - // note that start may be less than or greater than end (e.g. when - // dragging the mouse, start is where the initial click was, and you - // can drag in either direction) - - unsigned char insert_mode; - // each textfield keeps its own insert mode state. to keep an app-wide - // insert mode, copy this value in/out of the app state - - ///////////////////// - // - // private data - // - unsigned char cursor_at_end_of_line; // not implemented yet - unsigned char initialized; - unsigned char has_preferred_x; - unsigned char single_line; - unsigned char padding1, padding2, padding3; - float preferred_x; // this determines where the cursor up/down tries to seek to along x - StbUndoState undostate; -} STB_TexteditState; - - -//////////////////////////////////////////////////////////////////////// -// -// StbTexteditRow -// -// Result of layout query, used by stb_textedit to determine where -// the text in each row is. - -// result of layout query -typedef struct -{ - float x0,x1; // starting x location, end x location (allows for align=right, etc) - float baseline_y_delta; // position of baseline relative to previous row's baseline - float ymin,ymax; // height of row above and below baseline - int num_chars; -} StbTexteditRow; -#endif //INCLUDE_STB_TEXTEDIT_H - - -//////////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////////// -//// -//// Implementation mode -//// -//// - - -// implementation isn't include-guarded, since it might have indirectly -// included just the "header" portion -#ifdef STB_TEXTEDIT_IMPLEMENTATION - -#ifndef STB_TEXTEDIT_memmove -#include -#define STB_TEXTEDIT_memmove memmove -#endif - - -///////////////////////////////////////////////////////////////////////////// -// -// Mouse input handling -// - -// traverse the layout to locate the nearest character to a display position -static int stb_text_locate_coord(STB_TEXTEDIT_STRING *str, float x, float y) -{ - StbTexteditRow r; - int n = STB_TEXTEDIT_STRINGLEN(str); - float base_y = 0, prev_x; - int i=0, k; - - r.x0 = r.x1 = 0; - r.ymin = r.ymax = 0; - r.num_chars = 0; - - // search rows to find one that straddles 'y' - while (i < n) { - STB_TEXTEDIT_LAYOUTROW(&r, str, i); - if (r.num_chars <= 0) - return n; - - if (i==0 && y < base_y + r.ymin) - return 0; - - if (y < base_y + r.ymax) - break; - - i += r.num_chars; - base_y += r.baseline_y_delta; - } - - // below all text, return 'after' last character - if (i >= n) - return n; - - // check if it's before the beginning of the line - if (x < r.x0) - return i; - - // check if it's before the end of the line - if (x < r.x1) { - // search characters in row for one that straddles 'x' - prev_x = r.x0; - for (k=0; k < r.num_chars; ++k) { - float w = STB_TEXTEDIT_GETWIDTH(str, i, k); - if (x < prev_x+w) { - if (x < prev_x+w/2) - return k+i; - else - return k+i+1; - } - prev_x += w; - } - // shouldn't happen, but if it does, fall through to end-of-line case - } - - // if the last character is a newline, return that. otherwise return 'after' the last character - if (STB_TEXTEDIT_GETCHAR(str, i+r.num_chars-1) == STB_TEXTEDIT_NEWLINE) - return i+r.num_chars-1; - else - return i+r.num_chars; -} - -// API click: on mouse down, move the cursor to the clicked location, and reset the selection -static void stb_textedit_click(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) -{ - // In single-line mode, just always make y = 0. This lets the drag keep working if the mouse - // goes off the top or bottom of the text - if( state->single_line ) - { - StbTexteditRow r; - STB_TEXTEDIT_LAYOUTROW(&r, str, 0); - y = r.ymin; - } - - state->cursor = stb_text_locate_coord(str, x, y); - state->select_start = state->cursor; - state->select_end = state->cursor; - state->has_preferred_x = 0; -} - -// API drag: on mouse drag, move the cursor and selection endpoint to the clicked location -static void stb_textedit_drag(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, float x, float y) -{ - int p = 0; - - // In single-line mode, just always make y = 0. This lets the drag keep working if the mouse - // goes off the top or bottom of the text - if( state->single_line ) - { - StbTexteditRow r; - STB_TEXTEDIT_LAYOUTROW(&r, str, 0); - y = r.ymin; - } - - if (state->select_start == state->select_end) - state->select_start = state->cursor; - - p = stb_text_locate_coord(str, x, y); - state->cursor = state->select_end = p; -} - -///////////////////////////////////////////////////////////////////////////// -// -// Keyboard input handling -// - -// forward declarations -static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state); -static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state); -static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length); -static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length); -static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length); - -typedef struct -{ - float x,y; // position of n'th character - float height; // height of line - int first_char, length; // first char of row, and length - int prev_first; // first char of previous row -} StbFindState; - -// find the x/y location of a character, and remember info about the previous row in -// case we get a move-up event (for page up, we'll have to rescan) -static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *str, int n, int single_line) -{ - StbTexteditRow r; - int prev_start = 0; - int z = STB_TEXTEDIT_STRINGLEN(str); - int i=0, first; - - if (n == z) { - // if it's at the end, then find the last line -- simpler than trying to - // explicitly handle this case in the regular code - if (single_line) { - STB_TEXTEDIT_LAYOUTROW(&r, str, 0); - find->y = 0; - find->first_char = 0; - find->length = z; - find->height = r.ymax - r.ymin; - find->x = r.x1; - } else { - find->y = 0; - find->x = 0; - find->height = 1; - while (i < z) { - STB_TEXTEDIT_LAYOUTROW(&r, str, i); - prev_start = i; - i += r.num_chars; - } - find->first_char = i; - find->length = 0; - find->prev_first = prev_start; - } - return; - } - - // search rows to find the one that straddles character n - find->y = 0; - - for(;;) { - STB_TEXTEDIT_LAYOUTROW(&r, str, i); - if (n < i + r.num_chars) - break; - prev_start = i; - i += r.num_chars; - find->y += r.baseline_y_delta; - } - - find->first_char = first = i; - find->length = r.num_chars; - find->height = r.ymax - r.ymin; - find->prev_first = prev_start; - - // now scan to find xpos - find->x = r.x0; - i = 0; - for (i=0; first+i < n; ++i) - find->x += STB_TEXTEDIT_GETWIDTH(str, first, i); -} - -#define STB_TEXT_HAS_SELECTION(s) ((s)->select_start != (s)->select_end) - -// make the selection/cursor state valid if client altered the string -static void stb_textedit_clamp(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) -{ - int n = STB_TEXTEDIT_STRINGLEN(str); - if (STB_TEXT_HAS_SELECTION(state)) { - if (state->select_start > n) state->select_start = n; - if (state->select_end > n) state->select_end = n; - // if clamping forced them to be equal, move the cursor to match - if (state->select_start == state->select_end) - state->cursor = state->select_start; - } - if (state->cursor > n) state->cursor = n; -} - -// delete characters while updating undo -static void stb_textedit_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int len) -{ - stb_text_makeundo_delete(str, state, where, len); - STB_TEXTEDIT_DELETECHARS(str, where, len); - state->has_preferred_x = 0; -} - -// delete the section -static void stb_textedit_delete_selection(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) -{ - stb_textedit_clamp(str, state); - if (STB_TEXT_HAS_SELECTION(state)) { - if (state->select_start < state->select_end) { - stb_textedit_delete(str, state, state->select_start, state->select_end - state->select_start); - state->select_end = state->cursor = state->select_start; - } else { - stb_textedit_delete(str, state, state->select_end, state->select_start - state->select_end); - state->select_start = state->cursor = state->select_end; - } - state->has_preferred_x = 0; - } -} - -// canoncialize the selection so start <= end -static void stb_textedit_sortselection(STB_TexteditState *state) -{ - if (state->select_end < state->select_start) { - int temp = state->select_end; - state->select_end = state->select_start; - state->select_start = temp; - } -} - -// move cursor to first character of selection -static void stb_textedit_move_to_first(STB_TexteditState *state) -{ - if (STB_TEXT_HAS_SELECTION(state)) { - stb_textedit_sortselection(state); - state->cursor = state->select_start; - state->select_end = state->select_start; - state->has_preferred_x = 0; - } -} - -// move cursor to last character of selection -static void stb_textedit_move_to_last(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) -{ - if (STB_TEXT_HAS_SELECTION(state)) { - stb_textedit_sortselection(state); - stb_textedit_clamp(str, state); - state->cursor = state->select_end; - state->select_start = state->select_end; - state->has_preferred_x = 0; - } -} - -#ifdef STB_TEXTEDIT_IS_SPACE -static int is_word_boundary( STB_TEXTEDIT_STRING *str, int idx ) -{ - return idx > 0 ? (STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str,idx-1) ) && !STB_TEXTEDIT_IS_SPACE( STB_TEXTEDIT_GETCHAR(str, idx) ) ) : 1; -} - -#ifndef STB_TEXTEDIT_MOVEWORDLEFT -static int stb_textedit_move_to_word_previous( STB_TEXTEDIT_STRING *str, int c ) -{ - --c; // always move at least one character - while( c >= 0 && !is_word_boundary( str, c ) ) - --c; - - if( c < 0 ) - c = 0; - - return c; -} -#define STB_TEXTEDIT_MOVEWORDLEFT stb_textedit_move_to_word_previous -#endif - -#ifndef STB_TEXTEDIT_MOVEWORDRIGHT -static int stb_textedit_move_to_word_next( STB_TEXTEDIT_STRING *str, int c ) -{ - const int len = STB_TEXTEDIT_STRINGLEN(str); - ++c; // always move at least one character - while( c < len && !is_word_boundary( str, c ) ) - ++c; - - if( c > len ) - c = len; - - return c; -} -#define STB_TEXTEDIT_MOVEWORDRIGHT stb_textedit_move_to_word_next -#endif - -#endif - -// update selection and cursor to match each other -static void stb_textedit_prep_selection_at_cursor(STB_TexteditState *state) -{ - if (!STB_TEXT_HAS_SELECTION(state)) - state->select_start = state->select_end = state->cursor; - else - state->cursor = state->select_end; -} - -// API cut: delete selection -static int stb_textedit_cut(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) -{ - if (STB_TEXT_HAS_SELECTION(state)) { - stb_textedit_delete_selection(str,state); // implicity clamps - state->has_preferred_x = 0; - return 1; - } - return 0; -} - -// API paste: replace existing selection with passed-in text -static int stb_textedit_paste_internal(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE *text, int len) -{ - // if there's a selection, the paste should delete it - stb_textedit_clamp(str, state); - stb_textedit_delete_selection(str,state); - // try to insert the characters - if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, text, len)) { - stb_text_makeundo_insert(state, state->cursor, len); - state->cursor += len; - state->has_preferred_x = 0; - return 1; - } - // remove the undo since we didn't actually insert the characters - if (state->undostate.undo_point) - --state->undostate.undo_point; - return 0; -} - -#ifndef STB_TEXTEDIT_KEYTYPE -#define STB_TEXTEDIT_KEYTYPE int -#endif - -// API key: process a keyboard input -static void stb_textedit_key(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_KEYTYPE key) -{ -retry: - switch (key) { - default: { - int c = STB_TEXTEDIT_KEYTOTEXT(key); - if (c > 0) { - STB_TEXTEDIT_CHARTYPE ch = (STB_TEXTEDIT_CHARTYPE) c; - - // can't add newline in single-line mode - if (c == '\n' && state->single_line) - break; - - if (state->insert_mode && !STB_TEXT_HAS_SELECTION(state) && state->cursor < STB_TEXTEDIT_STRINGLEN(str)) { - stb_text_makeundo_replace(str, state, state->cursor, 1, 1); - STB_TEXTEDIT_DELETECHARS(str, state->cursor, 1); - if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) { - ++state->cursor; - state->has_preferred_x = 0; - } - } else { - stb_textedit_delete_selection(str,state); // implicity clamps - if (STB_TEXTEDIT_INSERTCHARS(str, state->cursor, &ch, 1)) { - stb_text_makeundo_insert(state, state->cursor, 1); - ++state->cursor; - state->has_preferred_x = 0; - } - } - } - break; - } - -#ifdef STB_TEXTEDIT_K_INSERT - case STB_TEXTEDIT_K_INSERT: - state->insert_mode = !state->insert_mode; - break; -#endif - - case STB_TEXTEDIT_K_UNDO: - stb_text_undo(str, state); - state->has_preferred_x = 0; - break; - - case STB_TEXTEDIT_K_REDO: - stb_text_redo(str, state); - state->has_preferred_x = 0; - break; - - case STB_TEXTEDIT_K_LEFT: - // if currently there's a selection, move cursor to start of selection - if (STB_TEXT_HAS_SELECTION(state)) - stb_textedit_move_to_first(state); - else - if (state->cursor > 0) - --state->cursor; - state->has_preferred_x = 0; - break; - - case STB_TEXTEDIT_K_RIGHT: - // if currently there's a selection, move cursor to end of selection - if (STB_TEXT_HAS_SELECTION(state)) - stb_textedit_move_to_last(str, state); - else - ++state->cursor; - stb_textedit_clamp(str, state); - state->has_preferred_x = 0; - break; - - case STB_TEXTEDIT_K_LEFT | STB_TEXTEDIT_K_SHIFT: - stb_textedit_clamp(str, state); - stb_textedit_prep_selection_at_cursor(state); - // move selection left - if (state->select_end > 0) - --state->select_end; - state->cursor = state->select_end; - state->has_preferred_x = 0; - break; - -#ifdef STB_TEXTEDIT_MOVEWORDLEFT - case STB_TEXTEDIT_K_WORDLEFT: - if (STB_TEXT_HAS_SELECTION(state)) - stb_textedit_move_to_first(state); - else { - state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor); - stb_textedit_clamp( str, state ); - } - break; - - case STB_TEXTEDIT_K_WORDLEFT | STB_TEXTEDIT_K_SHIFT: - if( !STB_TEXT_HAS_SELECTION( state ) ) - stb_textedit_prep_selection_at_cursor(state); - - state->cursor = STB_TEXTEDIT_MOVEWORDLEFT(str, state->cursor); - state->select_end = state->cursor; - - stb_textedit_clamp( str, state ); - break; -#endif - -#ifdef STB_TEXTEDIT_MOVEWORDRIGHT - case STB_TEXTEDIT_K_WORDRIGHT: - if (STB_TEXT_HAS_SELECTION(state)) - stb_textedit_move_to_last(str, state); - else { - state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor); - stb_textedit_clamp( str, state ); - } - break; - - case STB_TEXTEDIT_K_WORDRIGHT | STB_TEXTEDIT_K_SHIFT: - if( !STB_TEXT_HAS_SELECTION( state ) ) - stb_textedit_prep_selection_at_cursor(state); - - state->cursor = STB_TEXTEDIT_MOVEWORDRIGHT(str, state->cursor); - state->select_end = state->cursor; - - stb_textedit_clamp( str, state ); - break; -#endif - - case STB_TEXTEDIT_K_RIGHT | STB_TEXTEDIT_K_SHIFT: - stb_textedit_prep_selection_at_cursor(state); - // move selection right - ++state->select_end; - stb_textedit_clamp(str, state); - state->cursor = state->select_end; - state->has_preferred_x = 0; - break; - - case STB_TEXTEDIT_K_DOWN: - case STB_TEXTEDIT_K_DOWN | STB_TEXTEDIT_K_SHIFT: { - StbFindState find; - StbTexteditRow row; - int i, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0; - - if (state->single_line) { - // on windows, up&down in single-line behave like left&right - key = STB_TEXTEDIT_K_RIGHT | (key & STB_TEXTEDIT_K_SHIFT); - goto retry; - } - - if (sel) - stb_textedit_prep_selection_at_cursor(state); - else if (STB_TEXT_HAS_SELECTION(state)) - stb_textedit_move_to_last(str,state); - - // compute current position of cursor point - stb_textedit_clamp(str, state); - stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); - - // now find character position down a row - if (find.length) { - float goal_x = state->has_preferred_x ? state->preferred_x : find.x; - float x; - int start = find.first_char + find.length; - state->cursor = start; - STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor); - x = row.x0; - for (i=0; i < row.num_chars; ++i) { - float dx = STB_TEXTEDIT_GETWIDTH(str, start, i); - #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE - if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE) - break; - #endif - x += dx; - if (x > goal_x) - break; - ++state->cursor; - } - stb_textedit_clamp(str, state); - - state->has_preferred_x = 1; - state->preferred_x = goal_x; - - if (sel) - state->select_end = state->cursor; - } - break; - } - - case STB_TEXTEDIT_K_UP: - case STB_TEXTEDIT_K_UP | STB_TEXTEDIT_K_SHIFT: { - StbFindState find; - StbTexteditRow row; - int i, sel = (key & STB_TEXTEDIT_K_SHIFT) != 0; - - if (state->single_line) { - // on windows, up&down become left&right - key = STB_TEXTEDIT_K_LEFT | (key & STB_TEXTEDIT_K_SHIFT); - goto retry; - } - - if (sel) - stb_textedit_prep_selection_at_cursor(state); - else if (STB_TEXT_HAS_SELECTION(state)) - stb_textedit_move_to_first(state); - - // compute current position of cursor point - stb_textedit_clamp(str, state); - stb_textedit_find_charpos(&find, str, state->cursor, state->single_line); - - // can only go up if there's a previous row - if (find.prev_first != find.first_char) { - // now find character position up a row - float goal_x = state->has_preferred_x ? state->preferred_x : find.x; - float x; - state->cursor = find.prev_first; - STB_TEXTEDIT_LAYOUTROW(&row, str, state->cursor); - x = row.x0; - for (i=0; i < row.num_chars; ++i) { - float dx = STB_TEXTEDIT_GETWIDTH(str, find.prev_first, i); - #ifdef STB_TEXTEDIT_GETWIDTH_NEWLINE - if (dx == STB_TEXTEDIT_GETWIDTH_NEWLINE) - break; - #endif - x += dx; - if (x > goal_x) - break; - ++state->cursor; - } - stb_textedit_clamp(str, state); - - state->has_preferred_x = 1; - state->preferred_x = goal_x; - - if (sel) - state->select_end = state->cursor; - } - break; - } - - case STB_TEXTEDIT_K_DELETE: - case STB_TEXTEDIT_K_DELETE | STB_TEXTEDIT_K_SHIFT: - if (STB_TEXT_HAS_SELECTION(state)) - stb_textedit_delete_selection(str, state); - else { - int n = STB_TEXTEDIT_STRINGLEN(str); - if (state->cursor < n) - stb_textedit_delete(str, state, state->cursor, 1); - } - state->has_preferred_x = 0; - break; - - case STB_TEXTEDIT_K_BACKSPACE: - case STB_TEXTEDIT_K_BACKSPACE | STB_TEXTEDIT_K_SHIFT: - if (STB_TEXT_HAS_SELECTION(state)) - stb_textedit_delete_selection(str, state); - else { - stb_textedit_clamp(str, state); - if (state->cursor > 0) { - stb_textedit_delete(str, state, state->cursor-1, 1); - --state->cursor; - } - } - state->has_preferred_x = 0; - break; - -#ifdef STB_TEXTEDIT_K_TEXTSTART2 - case STB_TEXTEDIT_K_TEXTSTART2: -#endif - case STB_TEXTEDIT_K_TEXTSTART: - state->cursor = state->select_start = state->select_end = 0; - state->has_preferred_x = 0; - break; - -#ifdef STB_TEXTEDIT_K_TEXTEND2 - case STB_TEXTEDIT_K_TEXTEND2: -#endif - case STB_TEXTEDIT_K_TEXTEND: - state->cursor = STB_TEXTEDIT_STRINGLEN(str); - state->select_start = state->select_end = 0; - state->has_preferred_x = 0; - break; - -#ifdef STB_TEXTEDIT_K_TEXTSTART2 - case STB_TEXTEDIT_K_TEXTSTART2 | STB_TEXTEDIT_K_SHIFT: -#endif - case STB_TEXTEDIT_K_TEXTSTART | STB_TEXTEDIT_K_SHIFT: - stb_textedit_prep_selection_at_cursor(state); - state->cursor = state->select_end = 0; - state->has_preferred_x = 0; - break; - -#ifdef STB_TEXTEDIT_K_TEXTEND2 - case STB_TEXTEDIT_K_TEXTEND2 | STB_TEXTEDIT_K_SHIFT: -#endif - case STB_TEXTEDIT_K_TEXTEND | STB_TEXTEDIT_K_SHIFT: - stb_textedit_prep_selection_at_cursor(state); - state->cursor = state->select_end = STB_TEXTEDIT_STRINGLEN(str); - state->has_preferred_x = 0; - break; - - -#ifdef STB_TEXTEDIT_K_LINESTART2 - case STB_TEXTEDIT_K_LINESTART2: -#endif - case STB_TEXTEDIT_K_LINESTART: - stb_textedit_clamp(str, state); - stb_textedit_move_to_first(state); - if (state->single_line) - state->cursor = 0; - else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE) - --state->cursor; - state->has_preferred_x = 0; - break; - -#ifdef STB_TEXTEDIT_K_LINEEND2 - case STB_TEXTEDIT_K_LINEEND2: -#endif - case STB_TEXTEDIT_K_LINEEND: { - int n = STB_TEXTEDIT_STRINGLEN(str); - stb_textedit_clamp(str, state); - stb_textedit_move_to_first(state); - if (state->single_line) - state->cursor = n; - else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE) - ++state->cursor; - state->has_preferred_x = 0; - break; - } - -#ifdef STB_TEXTEDIT_K_LINESTART2 - case STB_TEXTEDIT_K_LINESTART2 | STB_TEXTEDIT_K_SHIFT: -#endif - case STB_TEXTEDIT_K_LINESTART | STB_TEXTEDIT_K_SHIFT: - stb_textedit_clamp(str, state); - stb_textedit_prep_selection_at_cursor(state); - if (state->single_line) - state->cursor = 0; - else while (state->cursor > 0 && STB_TEXTEDIT_GETCHAR(str, state->cursor-1) != STB_TEXTEDIT_NEWLINE) - --state->cursor; - state->select_end = state->cursor; - state->has_preferred_x = 0; - break; - -#ifdef STB_TEXTEDIT_K_LINEEND2 - case STB_TEXTEDIT_K_LINEEND2 | STB_TEXTEDIT_K_SHIFT: -#endif - case STB_TEXTEDIT_K_LINEEND | STB_TEXTEDIT_K_SHIFT: { - int n = STB_TEXTEDIT_STRINGLEN(str); - stb_textedit_clamp(str, state); - stb_textedit_prep_selection_at_cursor(state); - if (state->single_line) - state->cursor = n; - else while (state->cursor < n && STB_TEXTEDIT_GETCHAR(str, state->cursor) != STB_TEXTEDIT_NEWLINE) - ++state->cursor; - state->select_end = state->cursor; - state->has_preferred_x = 0; - break; - } - -// @TODO: -// STB_TEXTEDIT_K_PGUP - move cursor up a page -// STB_TEXTEDIT_K_PGDOWN - move cursor down a page - } -} - -///////////////////////////////////////////////////////////////////////////// -// -// Undo processing -// -// @OPTIMIZE: the undo/redo buffer should be circular - -static void stb_textedit_flush_redo(StbUndoState *state) -{ - state->redo_point = STB_TEXTEDIT_UNDOSTATECOUNT; - state->redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT; -} - -// discard the oldest entry in the undo list -static void stb_textedit_discard_undo(StbUndoState *state) -{ - if (state->undo_point > 0) { - // if the 0th undo state has characters, clean those up - if (state->undo_rec[0].char_storage >= 0) { - int n = state->undo_rec[0].insert_length, i; - // delete n characters from all other records - state->undo_char_point -= n; - STB_TEXTEDIT_memmove(state->undo_char, state->undo_char + n, (size_t) (state->undo_char_point*sizeof(STB_TEXTEDIT_CHARTYPE))); - for (i=0; i < state->undo_point; ++i) - if (state->undo_rec[i].char_storage >= 0) - state->undo_rec[i].char_storage -= n; // @OPTIMIZE: get rid of char_storage and infer it - } - --state->undo_point; - STB_TEXTEDIT_memmove(state->undo_rec, state->undo_rec+1, (size_t) (state->undo_point*sizeof(state->undo_rec[0]))); - } -} - -// discard the oldest entry in the redo list--it's bad if this -// ever happens, but because undo & redo have to store the actual -// characters in different cases, the redo character buffer can -// fill up even though the undo buffer didn't -static void stb_textedit_discard_redo(StbUndoState *state) -{ - int k = STB_TEXTEDIT_UNDOSTATECOUNT-1; - - if (state->redo_point <= k) { - // if the k'th undo state has characters, clean those up - if (state->undo_rec[k].char_storage >= 0) { - int n = state->undo_rec[k].insert_length, i; - // move the remaining redo character data to the end of the buffer - state->redo_char_point += n; - STB_TEXTEDIT_memmove(state->undo_char + state->redo_char_point, state->undo_char + state->redo_char_point-n, (size_t) ((STB_TEXTEDIT_UNDOCHARCOUNT - state->redo_char_point)*sizeof(STB_TEXTEDIT_CHARTYPE))); - // adjust the position of all the other records to account for above memmove - for (i=state->redo_point; i < k; ++i) - if (state->undo_rec[i].char_storage >= 0) - state->undo_rec[i].char_storage += n; - } - // now move all the redo records towards the end of the buffer; the first one is at 'redo_point' - STB_TEXTEDIT_memmove(state->undo_rec + state->redo_point+1, state->undo_rec + state->redo_point, (size_t) ((STB_TEXTEDIT_UNDOSTATECOUNT - state->redo_point)*sizeof(state->undo_rec[0]))); - // now move redo_point to point to the new one - ++state->redo_point; - } -} - -static StbUndoRecord *stb_text_create_undo_record(StbUndoState *state, int numchars) -{ - // any time we create a new undo record, we discard redo - stb_textedit_flush_redo(state); - - // if we have no free records, we have to make room, by sliding the - // existing records down - if (state->undo_point == STB_TEXTEDIT_UNDOSTATECOUNT) - stb_textedit_discard_undo(state); - - // if the characters to store won't possibly fit in the buffer, we can't undo - if (numchars > STB_TEXTEDIT_UNDOCHARCOUNT) { - state->undo_point = 0; - state->undo_char_point = 0; - return NULL; - } - - // if we don't have enough free characters in the buffer, we have to make room - while (state->undo_char_point + numchars > STB_TEXTEDIT_UNDOCHARCOUNT) - stb_textedit_discard_undo(state); - - return &state->undo_rec[state->undo_point++]; -} - -static STB_TEXTEDIT_CHARTYPE *stb_text_createundo(StbUndoState *state, int pos, int insert_len, int delete_len) -{ - StbUndoRecord *r = stb_text_create_undo_record(state, insert_len); - if (r == NULL) - return NULL; - - r->where = pos; - r->insert_length = (STB_TEXTEDIT_POSITIONTYPE) insert_len; - r->delete_length = (STB_TEXTEDIT_POSITIONTYPE) delete_len; - - if (insert_len == 0) { - r->char_storage = -1; - return NULL; - } else { - r->char_storage = state->undo_char_point; - state->undo_char_point += insert_len; - return &state->undo_char[r->char_storage]; - } -} - -static void stb_text_undo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) -{ - StbUndoState *s = &state->undostate; - StbUndoRecord u, *r; - if (s->undo_point == 0) - return; - - // we need to do two things: apply the undo record, and create a redo record - u = s->undo_rec[s->undo_point-1]; - r = &s->undo_rec[s->redo_point-1]; - r->char_storage = -1; - - r->insert_length = u.delete_length; - r->delete_length = u.insert_length; - r->where = u.where; - - if (u.delete_length) { - // if the undo record says to delete characters, then the redo record will - // need to re-insert the characters that get deleted, so we need to store - // them. - - // there are three cases: - // there's enough room to store the characters - // characters stored for *redoing* don't leave room for redo - // characters stored for *undoing* don't leave room for redo - // if the last is true, we have to bail - - if (s->undo_char_point + u.delete_length >= STB_TEXTEDIT_UNDOCHARCOUNT) { - // the undo records take up too much character space; there's no space to store the redo characters - r->insert_length = 0; - } else { - int i; - - // there's definitely room to store the characters eventually - while (s->undo_char_point + u.delete_length > s->redo_char_point) { - // should never happen: - if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT) - return; - // there's currently not enough room, so discard a redo record - stb_textedit_discard_redo(s); - } - r = &s->undo_rec[s->redo_point-1]; - - r->char_storage = s->redo_char_point - u.delete_length; - s->redo_char_point = s->redo_char_point - u.delete_length; - - // now save the characters - for (i=0; i < u.delete_length; ++i) - s->undo_char[r->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u.where + i); - } - - // now we can carry out the deletion - STB_TEXTEDIT_DELETECHARS(str, u.where, u.delete_length); - } - - // check type of recorded action: - if (u.insert_length) { - // easy case: was a deletion, so we need to insert n characters - STB_TEXTEDIT_INSERTCHARS(str, u.where, &s->undo_char[u.char_storage], u.insert_length); - s->undo_char_point -= u.insert_length; - } - - state->cursor = u.where + u.insert_length; - - s->undo_point--; - s->redo_point--; -} - -static void stb_text_redo(STB_TEXTEDIT_STRING *str, STB_TexteditState *state) -{ - StbUndoState *s = &state->undostate; - StbUndoRecord *u, r; - if (s->redo_point == STB_TEXTEDIT_UNDOSTATECOUNT) - return; - - // we need to do two things: apply the redo record, and create an undo record - u = &s->undo_rec[s->undo_point]; - r = s->undo_rec[s->redo_point]; - - // we KNOW there must be room for the undo record, because the redo record - // was derived from an undo record - - u->delete_length = r.insert_length; - u->insert_length = r.delete_length; - u->where = r.where; - u->char_storage = -1; - - if (r.delete_length) { - // the redo record requires us to delete characters, so the undo record - // needs to store the characters - - if (s->undo_char_point + u->insert_length > s->redo_char_point) { - u->insert_length = 0; - u->delete_length = 0; - } else { - int i; - u->char_storage = s->undo_char_point; - s->undo_char_point = s->undo_char_point + u->insert_length; - - // now save the characters - for (i=0; i < u->insert_length; ++i) - s->undo_char[u->char_storage + i] = STB_TEXTEDIT_GETCHAR(str, u->where + i); - } - - STB_TEXTEDIT_DELETECHARS(str, r.where, r.delete_length); - } - - if (r.insert_length) { - // easy case: need to insert n characters - STB_TEXTEDIT_INSERTCHARS(str, r.where, &s->undo_char[r.char_storage], r.insert_length); - s->redo_char_point += r.insert_length; - } - - state->cursor = r.where + r.insert_length; - - s->undo_point++; - s->redo_point++; -} - -static void stb_text_makeundo_insert(STB_TexteditState *state, int where, int length) -{ - stb_text_createundo(&state->undostate, where, 0, length); -} - -static void stb_text_makeundo_delete(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int length) -{ - int i; - STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, length, 0); - if (p) { - for (i=0; i < length; ++i) - p[i] = STB_TEXTEDIT_GETCHAR(str, where+i); - } -} - -static void stb_text_makeundo_replace(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, int where, int old_length, int new_length) -{ - int i; - STB_TEXTEDIT_CHARTYPE *p = stb_text_createundo(&state->undostate, where, old_length, new_length); - if (p) { - for (i=0; i < old_length; ++i) - p[i] = STB_TEXTEDIT_GETCHAR(str, where+i); - } -} - -// reset the state to default -static void stb_textedit_clear_state(STB_TexteditState *state, int is_single_line) -{ - state->undostate.undo_point = 0; - state->undostate.undo_char_point = 0; - state->undostate.redo_point = STB_TEXTEDIT_UNDOSTATECOUNT; - state->undostate.redo_char_point = STB_TEXTEDIT_UNDOCHARCOUNT; - state->select_end = state->select_start = 0; - state->cursor = 0; - state->has_preferred_x = 0; - state->preferred_x = 0; - state->cursor_at_end_of_line = 0; - state->initialized = 1; - state->single_line = (unsigned char) is_single_line; - state->insert_mode = 0; -} - -// API initialize -static void stb_textedit_initialize_state(STB_TexteditState *state, int is_single_line) -{ - stb_textedit_clear_state(state, is_single_line); -} - -#if defined(__GNUC__) || defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wcast-qual" -#endif - -static int stb_textedit_paste(STB_TEXTEDIT_STRING *str, STB_TexteditState *state, STB_TEXTEDIT_CHARTYPE const *ctext, int len) -{ - return stb_textedit_paste_internal(str, state, (STB_TEXTEDIT_CHARTYPE *) ctext, len); -} - -#if defined(__GNUC__) || defined(__clang__) -#pragma GCC diagnostic pop -#endif - -#endif//STB_TEXTEDIT_IMPLEMENTATION - -/* ------------------------------------------------------------------------------- -This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------- -ALTERNATIVE A - MIT License -Copyright (c) 2017 Sean Barrett -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. ------------------------------------------------------------------------------- -ALTERNATIVE B - Public Domain (www.unlicense.org) -This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------- -*/ diff --git a/src/lib/geogram_gfx/third_party/ImGui/OLD/stb_truetype.h b/src/lib/geogram_gfx/third_party/ImGui/OLD/stb_truetype.h deleted file mode 100644 index f65deb50..00000000 --- a/src/lib/geogram_gfx/third_party/ImGui/OLD/stb_truetype.h +++ /dev/null @@ -1,4854 +0,0 @@ -// stb_truetype.h - v1.19 - public domain -// authored from 2009-2016 by Sean Barrett / RAD Game Tools -// -// This library processes TrueType files: -// parse files -// extract glyph metrics -// extract glyph shapes -// render glyphs to one-channel bitmaps with antialiasing (box filter) -// render glyphs to one-channel SDF bitmaps (signed-distance field/function) -// -// Todo: -// non-MS cmaps -// crashproof on bad data -// hinting? (no longer patented) -// cleartype-style AA? -// optimize: use simple memory allocator for intermediates -// optimize: build edge-list directly from curves -// optimize: rasterize directly from curves? -// -// ADDITIONAL CONTRIBUTORS -// -// Mikko Mononen: compound shape support, more cmap formats -// Tor Andersson: kerning, subpixel rendering -// Dougall Johnson: OpenType / Type 2 font handling -// Daniel Ribeiro Maciel: basic GPOS-based kerning -// -// Misc other: -// Ryan Gordon -// Simon Glass -// github:IntellectualKitty -// Imanol Celaya -// Daniel Ribeiro Maciel -// -// Bug/warning reports/fixes: -// "Zer" on mollyrocket Fabian "ryg" Giesen -// Cass Everitt Martins Mozeiko -// stoiko (Haemimont Games) Cap Petschulat -// Brian Hook Omar Cornut -// Walter van Niftrik github:aloucks -// David Gow Peter LaValle -// David Given Sergey Popov -// Ivan-Assen Ivanov Giumo X. Clanjor -// Anthony Pesch Higor Euripedes -// Johan Duparc Thomas Fields -// Hou Qiming Derek Vinyard -// Rob Loach Cort Stratton -// Kenney Phillis Jr. github:oyvindjam -// Brian Costabile github:vassvik -// -// VERSION HISTORY -// -// 1.19 (2018-02-11) GPOS kerning, STBTT_fmod -// 1.18 (2018-01-29) add missing function -// 1.17 (2017-07-23) make more arguments const; doc fix -// 1.16 (2017-07-12) SDF support -// 1.15 (2017-03-03) make more arguments const -// 1.14 (2017-01-16) num-fonts-in-TTC function -// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts -// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual -// 1.11 (2016-04-02) fix unused-variable warning -// 1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef -// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly -// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges -// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; -// variant PackFontRanges to pack and render in separate phases; -// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); -// fixed an assert() bug in the new rasterizer -// replace assert() with STBTT_assert() in new rasterizer -// -// Full history can be found at the end of this file. -// -// LICENSE -// -// See end of file for license information. -// -// USAGE -// -// Include this file in whatever places neeed to refer to it. In ONE C/C++ -// file, write: -// #define STB_TRUETYPE_IMPLEMENTATION -// before the #include of this file. This expands out the actual -// implementation into that C/C++ file. -// -// To make the implementation private to the file that generates the implementation, -// #define STBTT_STATIC -// -// Simple 3D API (don't ship this, but it's fine for tools and quick start) -// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture -// stbtt_GetBakedQuad() -- compute quad to draw for a given char -// -// Improved 3D API (more shippable): -// #include "stb_rect_pack.h" -- optional, but you really want it -// stbtt_PackBegin() -// stbtt_PackSetOversampling() -- for improved quality on small fonts -// stbtt_PackFontRanges() -- pack and renders -// stbtt_PackEnd() -// stbtt_GetPackedQuad() -// -// "Load" a font file from a memory buffer (you have to keep the buffer loaded) -// stbtt_InitFont() -// stbtt_GetFontOffsetForIndex() -- indexing for TTC font collections -// stbtt_GetNumberOfFonts() -- number of fonts for TTC font collections -// -// Render a unicode codepoint to a bitmap -// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap -// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide -// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be -// -// Character advance/positioning -// stbtt_GetCodepointHMetrics() -// stbtt_GetFontVMetrics() -// stbtt_GetFontVMetricsOS2() -// stbtt_GetCodepointKernAdvance() -// -// Starting with version 1.06, the rasterizer was replaced with a new, -// faster and generally-more-precise rasterizer. The new rasterizer more -// accurately measures pixel coverage for anti-aliasing, except in the case -// where multiple shapes overlap, in which case it overestimates the AA pixel -// coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If -// this turns out to be a problem, you can re-enable the old rasterizer with -// #define STBTT_RASTERIZER_VERSION 1 -// which will incur about a 15% speed hit. -// -// ADDITIONAL DOCUMENTATION -// -// Immediately after this block comment are a series of sample programs. -// -// After the sample programs is the "header file" section. This section -// includes documentation for each API function. -// -// Some important concepts to understand to use this library: -// -// Codepoint -// Characters are defined by unicode codepoints, e.g. 65 is -// uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is -// the hiragana for "ma". -// -// Glyph -// A visual character shape (every codepoint is rendered as -// some glyph) -// -// Glyph index -// A font-specific integer ID representing a glyph -// -// Baseline -// Glyph shapes are defined relative to a baseline, which is the -// bottom of uppercase characters. Characters extend both above -// and below the baseline. -// -// Current Point -// As you draw text to the screen, you keep track of a "current point" -// which is the origin of each character. The current point's vertical -// position is the baseline. Even "baked fonts" use this model. -// -// Vertical Font Metrics -// The vertical qualities of the font, used to vertically position -// and space the characters. See docs for stbtt_GetFontVMetrics. -// -// Font Size in Pixels or Points -// The preferred interface for specifying font sizes in stb_truetype -// is to specify how tall the font's vertical extent should be in pixels. -// If that sounds good enough, skip the next paragraph. -// -// Most font APIs instead use "points", which are a common typographic -// measurement for describing font size, defined as 72 points per inch. -// stb_truetype provides a point API for compatibility. However, true -// "per inch" conventions don't make much sense on computer displays -// since different monitors have different number of pixels per -// inch. For example, Windows traditionally uses a convention that -// there are 96 pixels per inch, thus making 'inch' measurements have -// nothing to do with inches, and thus effectively defining a point to -// be 1.333 pixels. Additionally, the TrueType font data provides -// an explicit scale factor to scale a given font's glyphs to points, -// but the author has observed that this scale factor is often wrong -// for non-commercial fonts, thus making fonts scaled in points -// according to the TrueType spec incoherently sized in practice. -// -// DETAILED USAGE: -// -// Scale: -// Select how high you want the font to be, in points or pixels. -// Call ScaleForPixelHeight or ScaleForMappingEmToPixels to compute -// a scale factor SF that will be used by all other functions. -// -// Baseline: -// You need to select a y-coordinate that is the baseline of where -// your text will appear. Call GetFontBoundingBox to get the baseline-relative -// bounding box for all characters. SF*-y0 will be the distance in pixels -// that the worst-case character could extend above the baseline, so if -// you want the top edge of characters to appear at the top of the -// screen where y=0, then you would set the baseline to SF*-y0. -// -// Current point: -// Set the current point where the first character will appear. The -// first character could extend left of the current point; this is font -// dependent. You can either choose a current point that is the leftmost -// point and hope, or add some padding, or check the bounding box or -// left-side-bearing of the first character to be displayed and set -// the current point based on that. -// -// Displaying a character: -// Compute the bounding box of the character. It will contain signed values -// relative to . I.e. if it returns x0,y0,x1,y1, -// then the character should be displayed in the rectangle from -// to = 32 && *text < 128) { - stbtt_aligned_quad q; - stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9 - glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y0); - glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y0); - glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y1); - glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y1); - } - ++text; - } - glEnd(); -} -#endif -// -// -////////////////////////////////////////////////////////////////////////////// -// -// Complete program (this compiles): get a single bitmap, print as ASCII art -// -#if 0 -#include -#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation -#include "stb_truetype.h" - -char ttf_buffer[1<<25]; - -int main(int argc, char **argv) -{ - stbtt_fontinfo font; - unsigned char *bitmap; - int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); - - fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); - - stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); - bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); - - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) - putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); - putchar('\n'); - } - return 0; -} -#endif -// -// Output: -// -// .ii. -// @@@@@@. -// V@Mio@@o -// :i. V@V -// :oM@@M -// :@@@MM@M -// @@o o@M -// :@@. M@M -// @@@o@@@@ -// :M@@V:@@. -// -////////////////////////////////////////////////////////////////////////////// -// -// Complete program: print "Hello World!" banner, with bugs -// -#if 0 -char buffer[24<<20]; -unsigned char screen[20][79]; - -int main(int arg, char **argv) -{ - stbtt_fontinfo font; - int i,j,ascent,baseline,ch=0; - float scale, xpos=2; // leave a little padding in case the character extends left - char *text = "Heljo World!"; // intentionally misspelled to show 'lj' brokenness - - fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); - stbtt_InitFont(&font, buffer, 0); - - scale = stbtt_ScaleForPixelHeight(&font, 15); - stbtt_GetFontVMetrics(&font, &ascent,0,0); - baseline = (int) (ascent*scale); - - while (text[ch]) { - int advance,lsb,x0,y0,x1,y1; - float x_shift = xpos - (float) floor(xpos); - stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb); - stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1); - stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]); - // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong - // because this API is really for baking character bitmaps into textures. if you want to render - // a sequence of characters, you really need to render each bitmap to a temp buffer, then - // "alpha blend" that into the working buffer - xpos += (advance * scale); - if (text[ch+1]) - xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]); - ++ch; - } - - for (j=0; j < 20; ++j) { - for (i=0; i < 78; ++i) - putchar(" .:ioVM@"[screen[j][i]>>5]); - putchar('\n'); - } - - return 0; -} -#endif - - -////////////////////////////////////////////////////////////////////////////// -////////////////////////////////////////////////////////////////////////////// -//// -//// INTEGRATION WITH YOUR CODEBASE -//// -//// The following sections allow you to supply alternate definitions -//// of C library functions used by stb_truetype, e.g. if you don't -//// link with the C runtime library. - -#ifdef STB_TRUETYPE_IMPLEMENTATION - // #define your own (u)stbtt_int8/16/32 before including to override this - #ifndef stbtt_uint8 - typedef unsigned char stbtt_uint8; - typedef signed char stbtt_int8; - typedef unsigned short stbtt_uint16; - typedef signed short stbtt_int16; - typedef unsigned int stbtt_uint32; - typedef signed int stbtt_int32; - #endif - - typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; - typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; - - // e.g. #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h - #ifndef STBTT_ifloor - #include - #define STBTT_ifloor(x) ((int) floor(x)) - #define STBTT_iceil(x) ((int) ceil(x)) - #endif - - #ifndef STBTT_sqrt - #include - #define STBTT_sqrt(x) sqrt(x) - #define STBTT_pow(x,y) pow(x,y) - #endif - - #ifndef STBTT_fmod - #include - #define STBTT_fmod(x,y) fmod(x,y) - #endif - - #ifndef STBTT_cos - #include - #define STBTT_cos(x) cos(x) - #define STBTT_acos(x) acos(x) - #endif - - #ifndef STBTT_fabs - #include - #define STBTT_fabs(x) fabs(x) - #endif - - // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h - #ifndef STBTT_malloc - #include - #define STBTT_malloc(x,u) ((void)(u),malloc(x)) - #define STBTT_free(x,u) ((void)(u),free(x)) - #endif - - #ifndef STBTT_assert - #include - #define STBTT_assert(x) assert(x) - #endif - - #ifndef STBTT_strlen - #include - #define STBTT_strlen(x) strlen(x) - #endif - - #ifndef STBTT_memcpy - #include - #define STBTT_memcpy memcpy - #define STBTT_memset memset - #endif -#endif - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -//// -//// INTERFACE -//// -//// - -#ifndef __STB_INCLUDE_STB_TRUETYPE_H__ -#define __STB_INCLUDE_STB_TRUETYPE_H__ - -#ifdef STBTT_STATIC -#define STBTT_DEF static -#else -#define STBTT_DEF extern -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -// private structure -typedef struct -{ - unsigned char *data; - int cursor; - int size; -} stbtt__buf; - -////////////////////////////////////////////////////////////////////////////// -// -// TEXTURE BAKING API -// -// If you use this API, you only have to call two functions ever. -// - -typedef struct -{ - unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap - float xoff,yoff,xadvance; -} stbtt_bakedchar; - -STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) - float pixel_height, // height of font in pixels - unsigned char *pixels, int pw, int ph, // bitmap to be filled in - int first_char, int num_chars, // characters to bake - stbtt_bakedchar *chardata); // you allocate this, it's num_chars long -// if return is positive, the first unused row of the bitmap -// if return is negative, returns the negative of the number of characters that fit -// if return is 0, no characters fit and no rows were used -// This uses a very crappy packing. - -typedef struct -{ - float x0,y0,s0,t0; // top-left - float x1,y1,s1,t1; // bottom-right -} stbtt_aligned_quad; - -STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, // same data as above - int char_index, // character to display - float *xpos, float *ypos, // pointers to current position in screen pixel space - stbtt_aligned_quad *q, // output: quad to draw - int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier -// Call GetBakedQuad with char_index = 'character - first_char', and it -// creates the quad you need to draw and advances the current position. -// -// The coordinate system used assumes y increases downwards. -// -// Characters will extend both above and below the current position; -// see discussion of "BASELINE" above. -// -// It's inefficient; you might want to c&p it and optimize it. - - - -////////////////////////////////////////////////////////////////////////////// -// -// NEW TEXTURE BAKING API -// -// This provides options for packing multiple fonts into one atlas, not -// perfectly but better than nothing. - -typedef struct -{ - unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap - float xoff,yoff,xadvance; - float xoff2,yoff2; -} stbtt_packedchar; - -typedef struct stbtt_pack_context stbtt_pack_context; -typedef struct stbtt_fontinfo stbtt_fontinfo; -#ifndef STB_RECT_PACK_VERSION -typedef struct stbrp_rect stbrp_rect; -#endif - -STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context); -// Initializes a packing context stored in the passed-in stbtt_pack_context. -// Future calls using this context will pack characters into the bitmap passed -// in here: a 1-channel bitmap that is width * height. stride_in_bytes is -// the distance from one row to the next (or 0 to mean they are packed tightly -// together). "padding" is the amount of padding to leave between each -// character (normally you want '1' for bitmaps you'll use as textures with -// bilinear filtering). -// -// Returns 0 on failure, 1 on success. - -STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc); -// Cleans up the packing context and frees all memory. - -#define STBTT_POINT_SIZE(x) (-(x)) - -STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, - int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range); -// Creates character bitmaps from the font_index'th font found in fontdata (use -// font_index=0 if you don't know what that is). It creates num_chars_in_range -// bitmaps for characters with unicode values starting at first_unicode_char_in_range -// and increasing. Data for how to render them is stored in chardata_for_range; -// pass these to stbtt_GetPackedQuad to get back renderable quads. -// -// font_size is the full height of the character from ascender to descender, -// as computed by stbtt_ScaleForPixelHeight. To use a point size as computed -// by stbtt_ScaleForMappingEmToPixels, wrap the point size in STBTT_POINT_SIZE() -// and pass that result as 'font_size': -// ..., 20 , ... // font max minus min y is 20 pixels tall -// ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall - -typedef struct -{ - float font_size; - int first_unicode_codepoint_in_range; // if non-zero, then the chars are continuous, and this is the first codepoint - int *array_of_unicode_codepoints; // if non-zero, then this is an array of unicode codepoints - int num_chars; - stbtt_packedchar *chardata_for_range; // output - unsigned char h_oversample, v_oversample; // don't set these, they're used internally -} stbtt_pack_range; - -STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); -// Creates character bitmaps from multiple ranges of characters stored in -// ranges. This will usually create a better-packed bitmap than multiple -// calls to stbtt_PackFontRange. Note that you can call this multiple -// times within a single PackBegin/PackEnd. - -STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample); -// Oversampling a font increases the quality by allowing higher-quality subpixel -// positioning, and is especially valuable at smaller text sizes. -// -// This function sets the amount of oversampling for all following calls to -// stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given -// pack context. The default (no oversampling) is achieved by h_oversample=1 -// and v_oversample=1. The total number of pixels required is -// h_oversample*v_oversample larger than the default; for example, 2x2 -// oversampling requires 4x the storage of 1x1. For best results, render -// oversampled textures with bilinear filtering. Look at the readme in -// stb/tests/oversample for information about oversampled fonts -// -// To use with PackFontRangesGather etc., you must set it before calls -// call to PackFontRangesGatherRects. - -STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above - int char_index, // character to display - float *xpos, float *ypos, // pointers to current position in screen pixel space - stbtt_aligned_quad *q, // output: quad to draw - int align_to_integer); - -STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); -STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects); -STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); -// Calling these functions in sequence is roughly equivalent to calling -// stbtt_PackFontRanges(). If you more control over the packing of multiple -// fonts, or if you want to pack custom data into a font texture, take a look -// at the source to of stbtt_PackFontRanges() and create a custom version -// using these functions, e.g. call GatherRects multiple times, -// building up a single array of rects, then call PackRects once, -// then call RenderIntoRects repeatedly. This may result in a -// better packing than calling PackFontRanges multiple times -// (or it may not). - -// this is an opaque structure that you shouldn't mess with which holds -// all the context needed from PackBegin to PackEnd. -struct stbtt_pack_context { - void *user_allocator_context; - void *pack_info; - int width; - int height; - int stride_in_bytes; - int padding; - unsigned int h_oversample, v_oversample; - unsigned char *pixels; - void *nodes; -}; - -////////////////////////////////////////////////////////////////////////////// -// -// FONT LOADING -// -// - -STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data); -// This function will determine the number of fonts in a font file. TrueType -// collection (.ttc) files may contain multiple fonts, while TrueType font -// (.ttf) files only contain one font. The number of fonts can be used for -// indexing with the previous function where the index is between zero and one -// less than the total fonts. If an error occurs, -1 is returned. - -STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); -// Each .ttf/.ttc file may have more than one font. Each font has a sequential -// index number starting from 0. Call this function to get the font offset for -// a given index; it returns -1 if the index is out of range. A regular .ttf -// file will only define one font and it always be at offset 0, so it will -// return '0' for index 0, and -1 for all other indices. - -// The following structure is defined publically so you can declare one on -// the stack or as a global or etc, but you should treat it as opaque. -struct stbtt_fontinfo -{ - void * userdata; - unsigned char * data; // pointer to .ttf file - int fontstart; // offset of start of font - - int numGlyphs; // number of glyphs, needed for range checking - - int loca,head,glyf,hhea,hmtx,kern,gpos; // table locations as offset from start of .ttf - int index_map; // a cmap mapping for our chosen character encoding - int indexToLocFormat; // format needed to map from glyph index to glyph - - stbtt__buf cff; // cff font data - stbtt__buf charstrings; // the charstring index - stbtt__buf gsubrs; // global charstring subroutines index - stbtt__buf subrs; // private charstring subroutines index - stbtt__buf fontdicts; // array of font dicts - stbtt__buf fdselect; // map from glyph to fontdict -}; - -STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); -// Given an offset into the file that defines a font, this function builds -// the necessary cached info for the rest of the system. You must allocate -// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't -// need to do anything special to free it, because the contents are pure -// value data with no additional data structures. Returns 0 on failure. - - -////////////////////////////////////////////////////////////////////////////// -// -// CHARACTER TO GLYPH-INDEX CONVERSIOn - -STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); -// If you're going to perform multiple operations on the same character -// and you want a speed-up, call this function with the character you're -// going to process, then use glyph-based functions instead of the -// codepoint-based functions. - - -////////////////////////////////////////////////////////////////////////////// -// -// CHARACTER PROPERTIES -// - -STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); -// computes a scale factor to produce a font whose "height" is 'pixels' tall. -// Height is measured as the distance from the highest ascender to the lowest -// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics -// and computing: -// scale = pixels / (ascent - descent) -// so if you prefer to measure height by the ascent only, use a similar calculation. - -STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels); -// computes a scale factor to produce a font whose EM size is mapped to -// 'pixels' tall. This is probably what traditional APIs compute, but -// I'm not positive. - -STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); -// ascent is the coordinate above the baseline the font extends; descent -// is the coordinate below the baseline the font extends (i.e. it is typically negative) -// lineGap is the spacing between one row's descent and the next row's ascent... -// so you should advance the vertical position by "*ascent - *descent + *lineGap" -// these are expressed in unscaled coordinates, so you must multiply by -// the scale factor for a given size - -STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap); -// analogous to GetFontVMetrics, but returns the "typographic" values from the OS/2 -// table (specific to MS/Windows TTF files). -// -// Returns 1 on success (table present), 0 on failure. - -STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1); -// the bounding box around all possible characters - -STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); -// leftSideBearing is the offset from the current horizontal position to the left edge of the character -// advanceWidth is the offset from the current horizontal position to the next horizontal position -// these are expressed in unscaled coordinates - -STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); -// an additional amount to add to the 'advance' value between ch1 and ch2 - -STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); -// Gets the bounding box of the visible part of the glyph, in unscaled coordinates - -STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); -STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); -STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); -// as above, but takes one or more glyph indices for greater efficiency - - -////////////////////////////////////////////////////////////////////////////// -// -// GLYPH SHAPES (you probably don't need these, but they have to go before -// the bitmaps for C declaration-order reasons) -// - -#ifndef STBTT_vmove // you can predefine these to use different values (but why?) - enum { - STBTT_vmove=1, - STBTT_vline, - STBTT_vcurve, - STBTT_vcubic - }; -#endif - -#ifndef stbtt_vertex // you can predefine this to use different values - // (we share this with other code at RAD) - #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file - typedef struct - { - stbtt_vertex_type x,y,cx,cy,cx1,cy1; - unsigned char type,padding; - } stbtt_vertex; -#endif - -STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index); -// returns non-zero if nothing is drawn for this glyph - -STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); -STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); -// returns # of vertices and fills *vertices with the pointer to them -// these are expressed in "unscaled" coordinates -// -// The shape is a series of countours. Each one starts with -// a STBTT_moveto, then consists of a series of mixed -// STBTT_lineto and STBTT_curveto segments. A lineto -// draws a line from previous endpoint to its x,y; a curveto -// draws a quadratic bezier from previous endpoint to -// its x,y, using cx,cy as the bezier control point. - -STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); -// frees the data allocated above - -////////////////////////////////////////////////////////////////////////////// -// -// BITMAP RENDERING -// - -STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); -// frees the bitmap allocated below - -STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); -// allocates a large-enough single-channel 8bpp bitmap and renders the -// specified character/glyph at the specified scale into it, with -// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque). -// *width & *height are filled out with the width & height of the bitmap, -// which is stored left-to-right, top-to-bottom. -// -// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap - -STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff); -// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel -// shift for the character - -STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); -// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap -// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap -// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the -// width and height and positioning info for it first. - -STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint); -// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel -// shift for the character - -STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint); -// same as stbtt_MakeCodepointBitmapSubpixel, but prefiltering -// is performed (see stbtt_PackSetOversampling) - -STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); -// get the bbox of the bitmap centered around the glyph origin; so the -// bitmap width is ix1-ix0, height is iy1-iy0, and location to place -// the bitmap top left is (leftSideBearing*scale,iy0). -// (Note that the bitmap uses y-increases-down, but the shape uses -// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.) - -STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); -// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel -// shift for the character - -// the following functions are equivalent to the above functions, but operate -// on glyph indices instead of Unicode codepoints (for efficiency) -STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); -STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff); -STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); -STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph); -STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int glyph); -STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); -STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); - - -// @TODO: don't expose this structure -typedef struct -{ - int w,h,stride; - unsigned char *pixels; -} stbtt__bitmap; - -// rasterize a shape with quadratic beziers into a bitmap -STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, // 1-channel bitmap to draw into - float flatness_in_pixels, // allowable error of curve in pixels - stbtt_vertex *vertices, // array of vertices defining shape - int num_verts, // number of vertices in above array - float scale_x, float scale_y, // scale applied to input vertices - float shift_x, float shift_y, // translation applied to input vertices - int x_off, int y_off, // another translation applied to input - int invert, // if non-zero, vertically flip shape - void *userdata); // context for to STBTT_MALLOC - -////////////////////////////////////////////////////////////////////////////// -// -// Signed Distance Function (or Field) rendering - -STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata); -// frees the SDF bitmap allocated below - -STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); -STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); -// These functions compute a discretized SDF field for a single character, suitable for storing -// in a single-channel texture, sampling with bilinear filtering, and testing against -// larger than some threshhold to produce scalable fonts. -// info -- the font -// scale -- controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap -// glyph/codepoint -- the character to generate the SDF for -// padding -- extra "pixels" around the character which are filled with the distance to the character (not 0), -// which allows effects like bit outlines -// onedge_value -- value 0-255 to test the SDF against to reconstruct the character (i.e. the isocontour of the character) -// pixel_dist_scale -- what value the SDF should increase by when moving one SDF "pixel" away from the edge (on the 0..255 scale) -// if positive, > onedge_value is inside; if negative, < onedge_value is inside -// width,height -- output height & width of the SDF bitmap (including padding) -// xoff,yoff -- output origin of the character -// return value -- a 2D array of bytes 0..255, width*height in size -// -// pixel_dist_scale & onedge_value are a scale & bias that allows you to make -// optimal use of the limited 0..255 for your application, trading off precision -// and special effects. SDF values outside the range 0..255 are clamped to 0..255. -// -// Example: -// scale = stbtt_ScaleForPixelHeight(22) -// padding = 5 -// onedge_value = 180 -// pixel_dist_scale = 180/5.0 = 36.0 -// -// This will create an SDF bitmap in which the character is about 22 pixels -// high but the whole bitmap is about 22+5+5=32 pixels high. To produce a filled -// shape, sample the SDF at each pixel and fill the pixel if the SDF value -// is greater than or equal to 180/255. (You'll actually want to antialias, -// which is beyond the scope of this example.) Additionally, you can compute -// offset outlines (e.g. to stroke the character border inside & outside, -// or only outside). For example, to fill outside the character up to 3 SDF -// pixels, you would compare against (180-36.0*3)/255 = 72/255. The above -// choice of variables maps a range from 5 pixels outside the shape to -// 2 pixels inside the shape to 0..255; this is intended primarily for apply -// outside effects only (the interior range is needed to allow proper -// antialiasing of the font at *smaller* sizes) -// -// The function computes the SDF analytically at each SDF pixel, not by e.g. -// building a higher-res bitmap and approximating it. In theory the quality -// should be as high as possible for an SDF of this size & representation, but -// unclear if this is true in practice (perhaps building a higher-res bitmap -// and computing from that can allow drop-out prevention). -// -// The algorithm has not been optimized at all, so expect it to be slow -// if computing lots of characters or very large sizes. - - - -////////////////////////////////////////////////////////////////////////////// -// -// Finding the right font... -// -// You should really just solve this offline, keep your own tables -// of what font is what, and don't try to get it out of the .ttf file. -// That's because getting it out of the .ttf file is really hard, because -// the names in the file can appear in many possible encodings, in many -// possible languages, and e.g. if you need a case-insensitive comparison, -// the details of that depend on the encoding & language in a complex way -// (actually underspecified in truetype, but also gigantic). -// -// But you can use the provided functions in two possible ways: -// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on -// unicode-encoded names to try to find the font you want; -// you can run this before calling stbtt_InitFont() -// -// stbtt_GetFontNameString() lets you get any of the various strings -// from the file yourself and do your own comparisons on them. -// You have to have called stbtt_InitFont() first. - - -STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); -// returns the offset (not index) of the font that matches, or -1 if none -// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold". -// if you use any other flag, use a font name like "Arial"; this checks -// the 'macStyle' header field; i don't know if fonts set this consistently -#define STBTT_MACSTYLE_DONTCARE 0 -#define STBTT_MACSTYLE_BOLD 1 -#define STBTT_MACSTYLE_ITALIC 2 -#define STBTT_MACSTYLE_UNDERSCORE 4 -#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0 - -STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); -// returns 1/0 whether the first string interpreted as utf8 is identical to -// the second string interpreted as big-endian utf16... useful for strings from next func - -STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); -// returns the string (which may be big-endian double byte, e.g. for unicode) -// and puts the length in bytes in *length. -// -// some of the values for the IDs are below; for more see the truetype spec: -// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html -// http://www.microsoft.com/typography/otspec/name.htm - -enum { // platformID - STBTT_PLATFORM_ID_UNICODE =0, - STBTT_PLATFORM_ID_MAC =1, - STBTT_PLATFORM_ID_ISO =2, - STBTT_PLATFORM_ID_MICROSOFT =3 -}; - -enum { // encodingID for STBTT_PLATFORM_ID_UNICODE - STBTT_UNICODE_EID_UNICODE_1_0 =0, - STBTT_UNICODE_EID_UNICODE_1_1 =1, - STBTT_UNICODE_EID_ISO_10646 =2, - STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, - STBTT_UNICODE_EID_UNICODE_2_0_FULL=4 -}; - -enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT - STBTT_MS_EID_SYMBOL =0, - STBTT_MS_EID_UNICODE_BMP =1, - STBTT_MS_EID_SHIFTJIS =2, - STBTT_MS_EID_UNICODE_FULL =10 -}; - -enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes - STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, - STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, - STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, - STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7 -}; - -enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... - // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs - STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, - STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, - STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, - STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, - STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, - STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D -}; - -enum { // languageID for STBTT_PLATFORM_ID_MAC - STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, - STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, - STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, - STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , - STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , - STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, - STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19 -}; - -#ifdef __cplusplus -} -#endif - -#endif // __STB_INCLUDE_STB_TRUETYPE_H__ - -/////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -//// -//// IMPLEMENTATION -//// -//// - -#ifdef STB_TRUETYPE_IMPLEMENTATION - -#ifndef STBTT_MAX_OVERSAMPLE -#define STBTT_MAX_OVERSAMPLE 8 -#endif - -#if STBTT_MAX_OVERSAMPLE > 255 -#error "STBTT_MAX_OVERSAMPLE cannot be > 255" -#endif - -typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1]; - -#ifndef STBTT_RASTERIZER_VERSION -#define STBTT_RASTERIZER_VERSION 2 -#endif - -#ifdef _MSC_VER -#define STBTT__NOTUSED(v) (void)(v) -#else -#define STBTT__NOTUSED(v) (void)sizeof(v) -#endif - -////////////////////////////////////////////////////////////////////////// -// -// stbtt__buf helpers to parse data from file -// - -static stbtt_uint8 stbtt__buf_get8(stbtt__buf *b) -{ - if (b->cursor >= b->size) - return 0; - return b->data[b->cursor++]; -} - -static stbtt_uint8 stbtt__buf_peek8(stbtt__buf *b) -{ - if (b->cursor >= b->size) - return 0; - return b->data[b->cursor]; -} - -static void stbtt__buf_seek(stbtt__buf *b, int o) -{ - STBTT_assert(!(o > b->size || o < 0)); - b->cursor = (o > b->size || o < 0) ? b->size : o; -} - -static void stbtt__buf_skip(stbtt__buf *b, int o) -{ - stbtt__buf_seek(b, b->cursor + o); -} - -static stbtt_uint32 stbtt__buf_get(stbtt__buf *b, int n) -{ - stbtt_uint32 v = 0; - int i; - STBTT_assert(n >= 1 && n <= 4); - for (i = 0; i < n; i++) - v = (v << 8) | stbtt__buf_get8(b); - return v; -} - -static stbtt__buf stbtt__new_buf(const void *p, size_t size) -{ - stbtt__buf r; - STBTT_assert(size < 0x40000000); - r.data = (stbtt_uint8*) p; - r.size = (int) size; - r.cursor = 0; - return r; -} - -#define stbtt__buf_get16(b) stbtt__buf_get((b), 2) -#define stbtt__buf_get32(b) stbtt__buf_get((b), 4) - -static stbtt__buf stbtt__buf_range(const stbtt__buf *b, int o, int s) -{ - stbtt__buf r = stbtt__new_buf(NULL, 0); - if (o < 0 || s < 0 || o > b->size || s > b->size - o) return r; - r.data = b->data + o; - r.size = s; - return r; -} - -static stbtt__buf stbtt__cff_get_index(stbtt__buf *b) -{ - int count, start, offsize; - start = b->cursor; - count = stbtt__buf_get16(b); - if (count) { - offsize = stbtt__buf_get8(b); - STBTT_assert(offsize >= 1 && offsize <= 4); - stbtt__buf_skip(b, offsize * count); - stbtt__buf_skip(b, stbtt__buf_get(b, offsize) - 1); - } - return stbtt__buf_range(b, start, b->cursor - start); -} - -static stbtt_uint32 stbtt__cff_int(stbtt__buf *b) -{ - int b0 = stbtt__buf_get8(b); - if (b0 >= 32 && b0 <= 246) return b0 - 139; - else if (b0 >= 247 && b0 <= 250) return (b0 - 247)*256 + stbtt__buf_get8(b) + 108; - else if (b0 >= 251 && b0 <= 254) return -(b0 - 251)*256 - stbtt__buf_get8(b) - 108; - else if (b0 == 28) return stbtt__buf_get16(b); - else if (b0 == 29) return stbtt__buf_get32(b); - STBTT_assert(0); - return 0; -} - -static void stbtt__cff_skip_operand(stbtt__buf *b) { - int v, b0 = stbtt__buf_peek8(b); - STBTT_assert(b0 >= 28); - if (b0 == 30) { - stbtt__buf_skip(b, 1); - while (b->cursor < b->size) { - v = stbtt__buf_get8(b); - if ((v & 0xF) == 0xF || (v >> 4) == 0xF) - break; - } - } else { - stbtt__cff_int(b); - } -} - -static stbtt__buf stbtt__dict_get(stbtt__buf *b, int key) -{ - stbtt__buf_seek(b, 0); - while (b->cursor < b->size) { - int start = b->cursor, end, op; - while (stbtt__buf_peek8(b) >= 28) - stbtt__cff_skip_operand(b); - end = b->cursor; - op = stbtt__buf_get8(b); - if (op == 12) op = stbtt__buf_get8(b) | 0x100; - if (op == key) return stbtt__buf_range(b, start, end-start); - } - return stbtt__buf_range(b, 0, 0); -} - -static void stbtt__dict_get_ints(stbtt__buf *b, int key, int outcount, stbtt_uint32 *out) -{ - int i; - stbtt__buf operands = stbtt__dict_get(b, key); - for (i = 0; i < outcount && operands.cursor < operands.size; i++) - out[i] = stbtt__cff_int(&operands); -} - -static int stbtt__cff_index_count(stbtt__buf *b) -{ - stbtt__buf_seek(b, 0); - return stbtt__buf_get16(b); -} - -static stbtt__buf stbtt__cff_index_get(stbtt__buf b, int i) -{ - int count, offsize, start, end; - stbtt__buf_seek(&b, 0); - count = stbtt__buf_get16(&b); - offsize = stbtt__buf_get8(&b); - STBTT_assert(i >= 0 && i < count); - STBTT_assert(offsize >= 1 && offsize <= 4); - stbtt__buf_skip(&b, i*offsize); - start = stbtt__buf_get(&b, offsize); - end = stbtt__buf_get(&b, offsize); - return stbtt__buf_range(&b, 2+(count+1)*offsize+start, end - start); -} - -////////////////////////////////////////////////////////////////////////// -// -// accessors to parse data from file -// - -// on platforms that don't allow misaligned reads, if we want to allow -// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE - -#define ttBYTE(p) (* (stbtt_uint8 *) (p)) -#define ttCHAR(p) (* (stbtt_int8 *) (p)) -#define ttFixed(p) ttLONG(p) - -static stbtt_uint16 ttUSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } -static stbtt_int16 ttSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } -static stbtt_uint32 ttULONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } -static stbtt_int32 ttLONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } - -#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) -#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) - -static int stbtt__isfont(stbtt_uint8 *font) -{ - // check the version number - if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1 - if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this! - if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF - if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 - if (stbtt_tag(font, "true")) return 1; // Apple specification for TrueType fonts - return 0; -} - -// @OPTIMIZE: binary search -static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag) -{ - stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); - stbtt_uint32 tabledir = fontstart + 12; - stbtt_int32 i; - for (i=0; i < num_tables; ++i) { - stbtt_uint32 loc = tabledir + 16*i; - if (stbtt_tag(data+loc+0, tag)) - return ttULONG(data+loc+8); - } - return 0; -} - -static int stbtt_GetFontOffsetForIndex_internal(unsigned char *font_collection, int index) -{ - // if it's just a font, there's only one valid index - if (stbtt__isfont(font_collection)) - return index == 0 ? 0 : -1; - - // check if it's a TTC - if (stbtt_tag(font_collection, "ttcf")) { - // version 1? - if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { - stbtt_int32 n = ttLONG(font_collection+8); - if (index >= n) - return -1; - return ttULONG(font_collection+12+index*4); - } - } - return -1; -} - -static int stbtt_GetNumberOfFonts_internal(unsigned char *font_collection) -{ - // if it's just a font, there's only one valid font - if (stbtt__isfont(font_collection)) - return 1; - - // check if it's a TTC - if (stbtt_tag(font_collection, "ttcf")) { - // version 1? - if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { - return ttLONG(font_collection+8); - } - } - return 0; -} - -static stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict) -{ - stbtt_uint32 subrsoff = 0, private_loc[2] = { 0, 0 }; - stbtt__buf pdict; - stbtt__dict_get_ints(&fontdict, 18, 2, private_loc); - if (!private_loc[1] || !private_loc[0]) return stbtt__new_buf(NULL, 0); - pdict = stbtt__buf_range(&cff, private_loc[1], private_loc[0]); - stbtt__dict_get_ints(&pdict, 19, 1, &subrsoff); - if (!subrsoff) return stbtt__new_buf(NULL, 0); - stbtt__buf_seek(&cff, private_loc[1]+subrsoff); - return stbtt__cff_get_index(&cff); -} - -static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart) -{ - stbtt_uint32 cmap, t; - stbtt_int32 i,numTables; - - info->data = data; - info->fontstart = fontstart; - info->cff = stbtt__new_buf(NULL, 0); - - cmap = stbtt__find_table(data, fontstart, "cmap"); // required - info->loca = stbtt__find_table(data, fontstart, "loca"); // required - info->head = stbtt__find_table(data, fontstart, "head"); // required - info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required - info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required - info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required - info->kern = stbtt__find_table(data, fontstart, "kern"); // not required - info->gpos = stbtt__find_table(data, fontstart, "GPOS"); // not required - - if (!cmap || !info->head || !info->hhea || !info->hmtx) - return 0; - if (info->glyf) { - // required for truetype - if (!info->loca) return 0; - } else { - // initialization for CFF / Type2 fonts (OTF) - stbtt__buf b, topdict, topdictidx; - stbtt_uint32 cstype = 2, charstrings = 0, fdarrayoff = 0, fdselectoff = 0; - stbtt_uint32 cff; - - cff = stbtt__find_table(data, fontstart, "CFF "); - if (!cff) return 0; - - info->fontdicts = stbtt__new_buf(NULL, 0); - info->fdselect = stbtt__new_buf(NULL, 0); - - // @TODO this should use size from table (not 512MB) - info->cff = stbtt__new_buf(data+cff, 512*1024*1024); - b = info->cff; - - // read the header - stbtt__buf_skip(&b, 2); - stbtt__buf_seek(&b, stbtt__buf_get8(&b)); // hdrsize - - // @TODO the name INDEX could list multiple fonts, - // but we just use the first one. - stbtt__cff_get_index(&b); // name INDEX - topdictidx = stbtt__cff_get_index(&b); - topdict = stbtt__cff_index_get(topdictidx, 0); - stbtt__cff_get_index(&b); // string INDEX - info->gsubrs = stbtt__cff_get_index(&b); - - stbtt__dict_get_ints(&topdict, 17, 1, &charstrings); - stbtt__dict_get_ints(&topdict, 0x100 | 6, 1, &cstype); - stbtt__dict_get_ints(&topdict, 0x100 | 36, 1, &fdarrayoff); - stbtt__dict_get_ints(&topdict, 0x100 | 37, 1, &fdselectoff); - info->subrs = stbtt__get_subrs(b, topdict); - - // we only support Type 2 charstrings - if (cstype != 2) return 0; - if (charstrings == 0) return 0; - - if (fdarrayoff) { - // looks like a CID font - if (!fdselectoff) return 0; - stbtt__buf_seek(&b, fdarrayoff); - info->fontdicts = stbtt__cff_get_index(&b); - info->fdselect = stbtt__buf_range(&b, fdselectoff, b.size-fdselectoff); - } - - stbtt__buf_seek(&b, charstrings); - info->charstrings = stbtt__cff_get_index(&b); - } - - t = stbtt__find_table(data, fontstart, "maxp"); - if (t) - info->numGlyphs = ttUSHORT(data+t+4); - else - info->numGlyphs = 0xffff; - - // find a cmap encoding table we understand *now* to avoid searching - // later. (todo: could make this installable) - // the same regardless of glyph. - numTables = ttUSHORT(data + cmap + 2); - info->index_map = 0; - for (i=0; i < numTables; ++i) { - stbtt_uint32 encoding_record = cmap + 4 + 8 * i; - // find an encoding we understand: - switch(ttUSHORT(data+encoding_record)) { - case STBTT_PLATFORM_ID_MICROSOFT: - switch (ttUSHORT(data+encoding_record+2)) { - case STBTT_MS_EID_UNICODE_BMP: - case STBTT_MS_EID_UNICODE_FULL: - // MS/Unicode - info->index_map = cmap + ttULONG(data+encoding_record+4); - break; - } - break; - case STBTT_PLATFORM_ID_UNICODE: - // Mac/iOS has these - // all the encodingIDs are unicode, so we don't bother to check it - info->index_map = cmap + ttULONG(data+encoding_record+4); - break; - } - } - if (info->index_map == 0) - return 0; - - info->indexToLocFormat = ttUSHORT(data+info->head + 50); - return 1; -} - -STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) -{ - stbtt_uint8 *data = info->data; - stbtt_uint32 index_map = info->index_map; - - stbtt_uint16 format = ttUSHORT(data + index_map + 0); - if (format == 0) { // apple byte encoding - stbtt_int32 bytes = ttUSHORT(data + index_map + 2); - if (unicode_codepoint < bytes-6) - return ttBYTE(data + index_map + 6 + unicode_codepoint); - return 0; - } else if (format == 6) { - stbtt_uint32 first = ttUSHORT(data + index_map + 6); - stbtt_uint32 count = ttUSHORT(data + index_map + 8); - if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) - return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); - return 0; - } else if (format == 2) { - STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean - return 0; - } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges - stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; - stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; - stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); - stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; - - // do a binary search of the segments - stbtt_uint32 endCount = index_map + 14; - stbtt_uint32 search = endCount; - - if (unicode_codepoint > 0xffff) - return 0; - - // they lie from endCount .. endCount + segCount - // but searchRange is the nearest power of two, so... - if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) - search += rangeShift*2; - - // now decrement to bias correctly to find smallest - search -= 2; - while (entrySelector) { - stbtt_uint16 end; - searchRange >>= 1; - end = ttUSHORT(data + search + searchRange*2); - if (unicode_codepoint > end) - search += searchRange*2; - --entrySelector; - } - search += 2; - - { - stbtt_uint16 offset, start; - stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1); - - STBTT_assert(unicode_codepoint <= ttUSHORT(data + endCount + 2*item)); - start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); - if (unicode_codepoint < start) - return 0; - - offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); - if (offset == 0) - return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); - - return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); - } - } else if (format == 12 || format == 13) { - stbtt_uint32 ngroups = ttULONG(data+index_map+12); - stbtt_int32 low,high; - low = 0; high = (stbtt_int32)ngroups; - // Binary search the right group. - while (low < high) { - stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high - stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); - stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); - if ((stbtt_uint32) unicode_codepoint < start_char) - high = mid; - else if ((stbtt_uint32) unicode_codepoint > end_char) - low = mid+1; - else { - stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); - if (format == 12) - return start_glyph + unicode_codepoint-start_char; - else // format == 13 - return start_glyph; - } - } - return 0; // not found - } - // @TODO - STBTT_assert(0); - return 0; -} - -STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) -{ - return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); -} - -static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy) -{ - v->type = type; - v->x = (stbtt_int16) x; - v->y = (stbtt_int16) y; - v->cx = (stbtt_int16) cx; - v->cy = (stbtt_int16) cy; -} - -static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) -{ - int g1,g2; - - STBTT_assert(!info->cff.size); - - if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range - if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format - - if (info->indexToLocFormat == 0) { - g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; - g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; - } else { - g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); - g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); - } - - return g1==g2 ? -1 : g1; // if length is 0, return -1 -} - -static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); - -STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) -{ - if (info->cff.size) { - stbtt__GetGlyphInfoT2(info, glyph_index, x0, y0, x1, y1); - } else { - int g = stbtt__GetGlyfOffset(info, glyph_index); - if (g < 0) return 0; - - if (x0) *x0 = ttSHORT(info->data + g + 2); - if (y0) *y0 = ttSHORT(info->data + g + 4); - if (x1) *x1 = ttSHORT(info->data + g + 6); - if (y1) *y1 = ttSHORT(info->data + g + 8); - } - return 1; -} - -STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) -{ - return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); -} - -STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) -{ - stbtt_int16 numberOfContours; - int g; - if (info->cff.size) - return stbtt__GetGlyphInfoT2(info, glyph_index, NULL, NULL, NULL, NULL) == 0; - g = stbtt__GetGlyfOffset(info, glyph_index); - if (g < 0) return 1; - numberOfContours = ttSHORT(info->data + g); - return numberOfContours == 0; -} - -static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off, - stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy) -{ - if (start_off) { - if (was_off) - stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); - stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy); - } else { - if (was_off) - stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); - else - stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); - } - return num_vertices; -} - -static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) -{ - stbtt_int16 numberOfContours; - stbtt_uint8 *endPtsOfContours; - stbtt_uint8 *data = info->data; - stbtt_vertex *vertices=0; - int num_vertices=0; - int g = stbtt__GetGlyfOffset(info, glyph_index); - - *pvertices = NULL; - - if (g < 0) return 0; - - numberOfContours = ttSHORT(data + g); - - if (numberOfContours > 0) { - stbtt_uint8 flags=0,flagcount; - stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; - stbtt_int32 x,y,cx,cy,sx,sy, scx,scy; - stbtt_uint8 *points; - endPtsOfContours = (data + g + 10); - ins = ttUSHORT(data + g + 10 + numberOfContours * 2); - points = data + g + 10 + numberOfContours * 2 + 2 + ins; - - n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); - - m = n + 2*numberOfContours; // a loose bound on how many vertices we might need - vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); - if (vertices == 0) - return 0; - - next_move = 0; - flagcount=0; - - // in first pass, we load uninterpreted data into the allocated array - // above, shifted to the end of the array so we won't overwrite it when - // we create our final data starting from the front - - off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated - - // first load flags - - for (i=0; i < n; ++i) { - if (flagcount == 0) { - flags = *points++; - if (flags & 8) - flagcount = *points++; - } else - --flagcount; - vertices[off+i].type = flags; - } - - // now load x coordinates - x=0; - for (i=0; i < n; ++i) { - flags = vertices[off+i].type; - if (flags & 2) { - stbtt_int16 dx = *points++; - x += (flags & 16) ? dx : -dx; // ??? - } else { - if (!(flags & 16)) { - x = x + (stbtt_int16) (points[0]*256 + points[1]); - points += 2; - } - } - vertices[off+i].x = (stbtt_int16) x; - } - - // now load y coordinates - y=0; - for (i=0; i < n; ++i) { - flags = vertices[off+i].type; - if (flags & 4) { - stbtt_int16 dy = *points++; - y += (flags & 32) ? dy : -dy; // ??? - } else { - if (!(flags & 32)) { - y = y + (stbtt_int16) (points[0]*256 + points[1]); - points += 2; - } - } - vertices[off+i].y = (stbtt_int16) y; - } - - // now convert them to our format - num_vertices=0; - sx = sy = cx = cy = scx = scy = 0; - for (i=0; i < n; ++i) { - flags = vertices[off+i].type; - x = (stbtt_int16) vertices[off+i].x; - y = (stbtt_int16) vertices[off+i].y; - - if (next_move == i) { - if (i != 0) - num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); - - // now start the new one - start_off = !(flags & 1); - if (start_off) { - // if we start off with an off-curve point, then when we need to find a point on the curve - // where we can start, and we need to save some state for when we wraparound. - scx = x; - scy = y; - if (!(vertices[off+i+1].type & 1)) { - // next point is also a curve point, so interpolate an on-point curve - sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1; - sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1; - } else { - // otherwise just use the next point as our start point - sx = (stbtt_int32) vertices[off+i+1].x; - sy = (stbtt_int32) vertices[off+i+1].y; - ++i; // we're using point i+1 as the starting point, so skip it - } - } else { - sx = x; - sy = y; - } - stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0); - was_off = 0; - next_move = 1 + ttUSHORT(endPtsOfContours+j*2); - ++j; - } else { - if (!(flags & 1)) { // if it's a curve - if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint - stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); - cx = x; - cy = y; - was_off = 1; - } else { - if (was_off) - stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); - else - stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); - was_off = 0; - } - } - } - num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); - } else if (numberOfContours == -1) { - // Compound shapes. - int more = 1; - stbtt_uint8 *comp = data + g + 10; - num_vertices = 0; - vertices = 0; - while (more) { - stbtt_uint16 flags, gidx; - int comp_num_verts = 0, i; - stbtt_vertex *comp_verts = 0, *tmp = 0; - float mtx[6] = {1,0,0,1,0,0}, m, n; - - flags = ttSHORT(comp); comp+=2; - gidx = ttSHORT(comp); comp+=2; - - if (flags & 2) { // XY values - if (flags & 1) { // shorts - mtx[4] = ttSHORT(comp); comp+=2; - mtx[5] = ttSHORT(comp); comp+=2; - } else { - mtx[4] = ttCHAR(comp); comp+=1; - mtx[5] = ttCHAR(comp); comp+=1; - } - } - else { - // @TODO handle matching point - STBTT_assert(0); - } - if (flags & (1<<3)) { // WE_HAVE_A_SCALE - mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; - mtx[1] = mtx[2] = 0; - } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE - mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; - mtx[1] = mtx[2] = 0; - mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; - } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO - mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; - mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; - mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; - mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; - } - - // Find transformation scales. - m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); - n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); - - // Get indexed glyph. - comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); - if (comp_num_verts > 0) { - // Transform vertices. - for (i = 0; i < comp_num_verts; ++i) { - stbtt_vertex* v = &comp_verts[i]; - stbtt_vertex_type x,y; - x=v->x; y=v->y; - v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); - v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); - x=v->cx; y=v->cy; - v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); - v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); - } - // Append vertices. - tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); - if (!tmp) { - if (vertices) STBTT_free(vertices, info->userdata); - if (comp_verts) STBTT_free(comp_verts, info->userdata); - return 0; - } - if (num_vertices > 0) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); - STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); - if (vertices) STBTT_free(vertices, info->userdata); - vertices = tmp; - STBTT_free(comp_verts, info->userdata); - num_vertices += comp_num_verts; - } - // More components ? - more = flags & (1<<5); - } - } else if (numberOfContours < 0) { - // @TODO other compound variations? - STBTT_assert(0); - } else { - // numberOfCounters == 0, do nothing - } - - *pvertices = vertices; - return num_vertices; -} - -typedef struct -{ - int bounds; - int started; - float first_x, first_y; - float x, y; - stbtt_int32 min_x, max_x, min_y, max_y; - - stbtt_vertex *pvertices; - int num_vertices; -} stbtt__csctx; - -#define STBTT__CSCTX_INIT(bounds) {bounds,0, 0,0, 0,0, 0,0,0,0, NULL, 0} - -static void stbtt__track_vertex(stbtt__csctx *c, stbtt_int32 x, stbtt_int32 y) -{ - if (x > c->max_x || !c->started) c->max_x = x; - if (y > c->max_y || !c->started) c->max_y = y; - if (x < c->min_x || !c->started) c->min_x = x; - if (y < c->min_y || !c->started) c->min_y = y; - c->started = 1; -} - -static void stbtt__csctx_v(stbtt__csctx *c, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy, stbtt_int32 cx1, stbtt_int32 cy1) -{ - if (c->bounds) { - stbtt__track_vertex(c, x, y); - if (type == STBTT_vcubic) { - stbtt__track_vertex(c, cx, cy); - stbtt__track_vertex(c, cx1, cy1); - } - } else { - stbtt_setvertex(&c->pvertices[c->num_vertices], type, x, y, cx, cy); - c->pvertices[c->num_vertices].cx1 = (stbtt_int16) cx1; - c->pvertices[c->num_vertices].cy1 = (stbtt_int16) cy1; - } - c->num_vertices++; -} - -static void stbtt__csctx_close_shape(stbtt__csctx *ctx) -{ - if (ctx->first_x != ctx->x || ctx->first_y != ctx->y) - stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->first_x, (int)ctx->first_y, 0, 0, 0, 0); -} - -static void stbtt__csctx_rmove_to(stbtt__csctx *ctx, float dx, float dy) -{ - stbtt__csctx_close_shape(ctx); - ctx->first_x = ctx->x = ctx->x + dx; - ctx->first_y = ctx->y = ctx->y + dy; - stbtt__csctx_v(ctx, STBTT_vmove, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); -} - -static void stbtt__csctx_rline_to(stbtt__csctx *ctx, float dx, float dy) -{ - ctx->x += dx; - ctx->y += dy; - stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); -} - -static void stbtt__csctx_rccurve_to(stbtt__csctx *ctx, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3) -{ - float cx1 = ctx->x + dx1; - float cy1 = ctx->y + dy1; - float cx2 = cx1 + dx2; - float cy2 = cy1 + dy2; - ctx->x = cx2 + dx3; - ctx->y = cy2 + dy3; - stbtt__csctx_v(ctx, STBTT_vcubic, (int)ctx->x, (int)ctx->y, (int)cx1, (int)cy1, (int)cx2, (int)cy2); -} - -static stbtt__buf stbtt__get_subr(stbtt__buf idx, int n) -{ - int count = stbtt__cff_index_count(&idx); - int bias = 107; - if (count >= 33900) - bias = 32768; - else if (count >= 1240) - bias = 1131; - n += bias; - if (n < 0 || n >= count) - return stbtt__new_buf(NULL, 0); - return stbtt__cff_index_get(idx, n); -} - -static stbtt__buf stbtt__cid_get_glyph_subrs(const stbtt_fontinfo *info, int glyph_index) -{ - stbtt__buf fdselect = info->fdselect; - int nranges, start, end, v, fmt, fdselector = -1, i; - - stbtt__buf_seek(&fdselect, 0); - fmt = stbtt__buf_get8(&fdselect); - if (fmt == 0) { - // untested - stbtt__buf_skip(&fdselect, glyph_index); - fdselector = stbtt__buf_get8(&fdselect); - } else if (fmt == 3) { - nranges = stbtt__buf_get16(&fdselect); - start = stbtt__buf_get16(&fdselect); - for (i = 0; i < nranges; i++) { - v = stbtt__buf_get8(&fdselect); - end = stbtt__buf_get16(&fdselect); - if (glyph_index >= start && glyph_index < end) { - fdselector = v; - break; - } - start = end; - } - } - if (fdselector == -1) stbtt__new_buf(NULL, 0); - return stbtt__get_subrs(info->cff, stbtt__cff_index_get(info->fontdicts, fdselector)); -} - -static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, stbtt__csctx *c) -{ - int in_header = 1, maskbits = 0, subr_stack_height = 0, sp = 0, v, i, b0; - int has_subrs = 0, clear_stack; - float s[48]; - stbtt__buf subr_stack[10], subrs = info->subrs, b; - float f; - -#define STBTT__CSERR(s) (0) - - // this currently ignores the initial width value, which isn't needed if we have hmtx - b = stbtt__cff_index_get(info->charstrings, glyph_index); - while (b.cursor < b.size) { - i = 0; - clear_stack = 1; - b0 = stbtt__buf_get8(&b); - switch (b0) { - // @TODO implement hinting - case 0x13: // hintmask - case 0x14: // cntrmask - if (in_header) - maskbits += (sp / 2); // implicit "vstem" - in_header = 0; - stbtt__buf_skip(&b, (maskbits + 7) / 8); - break; - - case 0x01: // hstem - case 0x03: // vstem - case 0x12: // hstemhm - case 0x17: // vstemhm - maskbits += (sp / 2); - break; - - case 0x15: // rmoveto - in_header = 0; - if (sp < 2) return STBTT__CSERR("rmoveto stack"); - stbtt__csctx_rmove_to(c, s[sp-2], s[sp-1]); - break; - case 0x04: // vmoveto - in_header = 0; - if (sp < 1) return STBTT__CSERR("vmoveto stack"); - stbtt__csctx_rmove_to(c, 0, s[sp-1]); - break; - case 0x16: // hmoveto - in_header = 0; - if (sp < 1) return STBTT__CSERR("hmoveto stack"); - stbtt__csctx_rmove_to(c, s[sp-1], 0); - break; - - case 0x05: // rlineto - if (sp < 2) return STBTT__CSERR("rlineto stack"); - for (; i + 1 < sp; i += 2) - stbtt__csctx_rline_to(c, s[i], s[i+1]); - break; - - // hlineto/vlineto and vhcurveto/hvcurveto alternate horizontal and vertical - // starting from a different place. - - case 0x07: // vlineto - if (sp < 1) return STBTT__CSERR("vlineto stack"); - goto vlineto; - case 0x06: // hlineto - if (sp < 1) return STBTT__CSERR("hlineto stack"); - for (;;) { - if (i >= sp) break; - stbtt__csctx_rline_to(c, s[i], 0); - i++; - vlineto: - if (i >= sp) break; - stbtt__csctx_rline_to(c, 0, s[i]); - i++; - } - break; - - case 0x1F: // hvcurveto - if (sp < 4) return STBTT__CSERR("hvcurveto stack"); - goto hvcurveto; - case 0x1E: // vhcurveto - if (sp < 4) return STBTT__CSERR("vhcurveto stack"); - for (;;) { - if (i + 3 >= sp) break; - stbtt__csctx_rccurve_to(c, 0, s[i], s[i+1], s[i+2], s[i+3], (sp - i == 5) ? s[i + 4] : 0.0f); - i += 4; - hvcurveto: - if (i + 3 >= sp) break; - stbtt__csctx_rccurve_to(c, s[i], 0, s[i+1], s[i+2], (sp - i == 5) ? s[i+4] : 0.0f, s[i+3]); - i += 4; - } - break; - - case 0x08: // rrcurveto - if (sp < 6) return STBTT__CSERR("rcurveline stack"); - for (; i + 5 < sp; i += 6) - stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); - break; - - case 0x18: // rcurveline - if (sp < 8) return STBTT__CSERR("rcurveline stack"); - for (; i + 5 < sp - 2; i += 6) - stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); - if (i + 1 >= sp) return STBTT__CSERR("rcurveline stack"); - stbtt__csctx_rline_to(c, s[i], s[i+1]); - break; - - case 0x19: // rlinecurve - if (sp < 8) return STBTT__CSERR("rlinecurve stack"); - for (; i + 1 < sp - 6; i += 2) - stbtt__csctx_rline_to(c, s[i], s[i+1]); - if (i + 5 >= sp) return STBTT__CSERR("rlinecurve stack"); - stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); - break; - - case 0x1A: // vvcurveto - case 0x1B: // hhcurveto - if (sp < 4) return STBTT__CSERR("(vv|hh)curveto stack"); - f = 0.0; - if (sp & 1) { f = s[i]; i++; } - for (; i + 3 < sp; i += 4) { - if (b0 == 0x1B) - stbtt__csctx_rccurve_to(c, s[i], f, s[i+1], s[i+2], s[i+3], 0.0); - else - stbtt__csctx_rccurve_to(c, f, s[i], s[i+1], s[i+2], 0.0, s[i+3]); - f = 0.0; - } - break; - - case 0x0A: // callsubr - if (!has_subrs) { - if (info->fdselect.size) - subrs = stbtt__cid_get_glyph_subrs(info, glyph_index); - has_subrs = 1; - } - // fallthrough - case 0x1D: // callgsubr - if (sp < 1) return STBTT__CSERR("call(g|)subr stack"); - v = (int) s[--sp]; - if (subr_stack_height >= 10) return STBTT__CSERR("recursion limit"); - subr_stack[subr_stack_height++] = b; - b = stbtt__get_subr(b0 == 0x0A ? subrs : info->gsubrs, v); - if (b.size == 0) return STBTT__CSERR("subr not found"); - b.cursor = 0; - clear_stack = 0; - break; - - case 0x0B: // return - if (subr_stack_height <= 0) return STBTT__CSERR("return outside subr"); - b = subr_stack[--subr_stack_height]; - clear_stack = 0; - break; - - case 0x0E: // endchar - stbtt__csctx_close_shape(c); - return 1; - - case 0x0C: { // two-byte escape - float dx1, dx2, dx3, dx4, dx5, dx6, dy1, dy2, dy3, dy4, dy5, dy6; - float dx, dy; - int b1 = stbtt__buf_get8(&b); - switch (b1) { - // @TODO These "flex" implementations ignore the flex-depth and resolution, - // and always draw beziers. - case 0x22: // hflex - if (sp < 7) return STBTT__CSERR("hflex stack"); - dx1 = s[0]; - dx2 = s[1]; - dy2 = s[2]; - dx3 = s[3]; - dx4 = s[4]; - dx5 = s[5]; - dx6 = s[6]; - stbtt__csctx_rccurve_to(c, dx1, 0, dx2, dy2, dx3, 0); - stbtt__csctx_rccurve_to(c, dx4, 0, dx5, -dy2, dx6, 0); - break; - - case 0x23: // flex - if (sp < 13) return STBTT__CSERR("flex stack"); - dx1 = s[0]; - dy1 = s[1]; - dx2 = s[2]; - dy2 = s[3]; - dx3 = s[4]; - dy3 = s[5]; - dx4 = s[6]; - dy4 = s[7]; - dx5 = s[8]; - dy5 = s[9]; - dx6 = s[10]; - dy6 = s[11]; - //fd is s[12] - stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); - stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); - break; - - case 0x24: // hflex1 - if (sp < 9) return STBTT__CSERR("hflex1 stack"); - dx1 = s[0]; - dy1 = s[1]; - dx2 = s[2]; - dy2 = s[3]; - dx3 = s[4]; - dx4 = s[5]; - dx5 = s[6]; - dy5 = s[7]; - dx6 = s[8]; - stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, 0); - stbtt__csctx_rccurve_to(c, dx4, 0, dx5, dy5, dx6, -(dy1+dy2+dy5)); - break; - - case 0x25: // flex1 - if (sp < 11) return STBTT__CSERR("flex1 stack"); - dx1 = s[0]; - dy1 = s[1]; - dx2 = s[2]; - dy2 = s[3]; - dx3 = s[4]; - dy3 = s[5]; - dx4 = s[6]; - dy4 = s[7]; - dx5 = s[8]; - dy5 = s[9]; - dx6 = dy6 = s[10]; - dx = dx1+dx2+dx3+dx4+dx5; - dy = dy1+dy2+dy3+dy4+dy5; - if (STBTT_fabs(dx) > STBTT_fabs(dy)) - dy6 = -dy; - else - dx6 = -dx; - stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); - stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); - break; - - default: - return STBTT__CSERR("unimplemented"); - } - } break; - - default: - if (b0 != 255 && b0 != 28 && (b0 < 32 || b0 > 254)) - return STBTT__CSERR("reserved operator"); - - // push immediate - if (b0 == 255) { - f = (float)(stbtt_int32)stbtt__buf_get32(&b) / 0x10000; - } else { - stbtt__buf_skip(&b, -1); - f = (float)(stbtt_int16)stbtt__cff_int(&b); - } - if (sp >= 48) return STBTT__CSERR("push stack overflow"); - s[sp++] = f; - clear_stack = 0; - break; - } - if (clear_stack) sp = 0; - } - return STBTT__CSERR("no endchar"); - -#undef STBTT__CSERR -} - -static int stbtt__GetGlyphShapeT2(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) -{ - // runs the charstring twice, once to count and once to output (to avoid realloc) - stbtt__csctx count_ctx = STBTT__CSCTX_INIT(1); - stbtt__csctx output_ctx = STBTT__CSCTX_INIT(0); - if (stbtt__run_charstring(info, glyph_index, &count_ctx)) { - *pvertices = (stbtt_vertex*)STBTT_malloc(count_ctx.num_vertices*sizeof(stbtt_vertex), info->userdata); - output_ctx.pvertices = *pvertices; - if (stbtt__run_charstring(info, glyph_index, &output_ctx)) { - STBTT_assert(output_ctx.num_vertices == count_ctx.num_vertices); - return output_ctx.num_vertices; - } - } - *pvertices = NULL; - return 0; -} - -static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) -{ - stbtt__csctx c = STBTT__CSCTX_INIT(1); - int r = stbtt__run_charstring(info, glyph_index, &c); - if (x0) *x0 = r ? c.min_x : 0; - if (y0) *y0 = r ? c.min_y : 0; - if (x1) *x1 = r ? c.max_x : 0; - if (y1) *y1 = r ? c.max_y : 0; - return r ? c.num_vertices : 0; -} - -STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) -{ - if (!info->cff.size) - return stbtt__GetGlyphShapeTT(info, glyph_index, pvertices); - else - return stbtt__GetGlyphShapeT2(info, glyph_index, pvertices); -} - -STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) -{ - stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); - if (glyph_index < numOfLongHorMetrics) { - if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); - if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); - } else { - if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); - if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); - } -} - -static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) -{ - stbtt_uint8 *data = info->data + info->kern; - stbtt_uint32 needle, straw; - int l, r, m; - - // we only look at the first table. it must be 'horizontal' and format 0. - if (!info->kern) - return 0; - if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 - return 0; - if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format - return 0; - - l = 0; - r = ttUSHORT(data+10) - 1; - needle = glyph1 << 16 | glyph2; - while (l <= r) { - m = (l + r) >> 1; - straw = ttULONG(data+18+(m*6)); // note: unaligned read - if (needle < straw) - r = m - 1; - else if (needle > straw) - l = m + 1; - else - return ttSHORT(data+22+(m*6)); - } - return 0; -} - -static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph) -{ - stbtt_uint16 coverageFormat = ttUSHORT(coverageTable); - switch(coverageFormat) { - case 1: { - stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2); - - // Binary search. - stbtt_int32 l=0, r=glyphCount-1, m; - int straw, needle=glyph; - while (l <= r) { - stbtt_uint8 *glyphArray = coverageTable + 4; - stbtt_uint16 glyphID; - m = (l + r) >> 1; - glyphID = ttUSHORT(glyphArray + 2 * m); - straw = glyphID; - if (needle < straw) - r = m - 1; - else if (needle > straw) - l = m + 1; - else { - return m; - } - } - } break; - - case 2: { - stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2); - stbtt_uint8 *rangeArray = coverageTable + 4; - - // Binary search. - stbtt_int32 l=0, r=rangeCount-1, m; - int strawStart, strawEnd, needle=glyph; - while (l <= r) { - stbtt_uint8 *rangeRecord; - m = (l + r) >> 1; - rangeRecord = rangeArray + 6 * m; - strawStart = ttUSHORT(rangeRecord); - strawEnd = ttUSHORT(rangeRecord + 2); - if (needle < strawStart) - r = m - 1; - else if (needle > strawEnd) - l = m + 1; - else { - stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4); - return startCoverageIndex + glyph - strawStart; - } - } - } break; - - default: { - // There are no other cases. - STBTT_assert(0); - } break; - } - - return -1; -} - -static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph) -{ - stbtt_uint16 classDefFormat = ttUSHORT(classDefTable); - switch(classDefFormat) - { - case 1: { - stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2); - stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4); - stbtt_uint8 *classDef1ValueArray = classDefTable + 6; - - if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount) - return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID)); - - classDefTable = classDef1ValueArray + 2 * glyphCount; - } break; - - case 2: { - stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2); - stbtt_uint8 *classRangeRecords = classDefTable + 4; - - // Binary search. - stbtt_int32 l=0, r=classRangeCount-1, m; - int strawStart, strawEnd, needle=glyph; - while (l <= r) { - stbtt_uint8 *classRangeRecord; - m = (l + r) >> 1; - classRangeRecord = classRangeRecords + 6 * m; - strawStart = ttUSHORT(classRangeRecord); - strawEnd = ttUSHORT(classRangeRecord + 2); - if (needle < strawStart) - r = m - 1; - else if (needle > strawEnd) - l = m + 1; - else - return (stbtt_int32)ttUSHORT(classRangeRecord + 4); - } - - classDefTable = classRangeRecords + 6 * classRangeCount; - } break; - - default: { - // There are no other cases. - STBTT_assert(0); - } break; - } - - return -1; -} - -// Define to STBTT_assert(x) if you want to break on unimplemented formats. -#define STBTT_GPOS_TODO_assert(x) - -static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) -{ - stbtt_uint16 lookupListOffset; - stbtt_uint8 *lookupList; - stbtt_uint16 lookupCount; - stbtt_uint8 *data; - stbtt_int32 i; - - if (!info->gpos) return 0; - - data = info->data + info->gpos; - - if (ttUSHORT(data+0) != 1) return 0; // Major version 1 - if (ttUSHORT(data+2) != 0) return 0; // Minor version 0 - - lookupListOffset = ttUSHORT(data+8); - lookupList = data + lookupListOffset; - lookupCount = ttUSHORT(lookupList); - - for (i=0; i> 1; - pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m; - secondGlyph = ttUSHORT(pairValue); - straw = secondGlyph; - if (needle < straw) - r = m - 1; - else if (needle > straw) - l = m + 1; - else { - stbtt_int16 xAdvance = ttSHORT(pairValue + 2); - return xAdvance; - } - } - } break; - - case 2: { - stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); - stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); - - stbtt_uint16 classDef1Offset = ttUSHORT(table + 8); - stbtt_uint16 classDef2Offset = ttUSHORT(table + 10); - int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1); - int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2); - - stbtt_uint16 class1Count = ttUSHORT(table + 12); - stbtt_uint16 class2Count = ttUSHORT(table + 14); - STBTT_assert(glyph1class < class1Count); - STBTT_assert(glyph2class < class2Count); - - // TODO: Support more formats. - STBTT_GPOS_TODO_assert(valueFormat1 == 4); - if (valueFormat1 != 4) return 0; - STBTT_GPOS_TODO_assert(valueFormat2 == 0); - if (valueFormat2 != 0) return 0; - - if (glyph1class >= 0 && glyph1class < class1Count && glyph2class >= 0 && glyph2class < class2Count) { - stbtt_uint8 *class1Records = table + 16; - stbtt_uint8 *class2Records = class1Records + 2 * (glyph1class * class2Count); - stbtt_int16 xAdvance = ttSHORT(class2Records + 2 * glyph2class); - return xAdvance; - } - } break; - - default: { - // There are no other cases. - STBTT_assert(0); - break; - }; - } - } - break; - }; - - default: - // TODO: Implement other stuff. - break; - } - } - - return 0; -} - -STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int g2) -{ - int xAdvance = 0; - - if (info->gpos) - xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2); - - if (info->kern) - xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2); - - return xAdvance; -} - -STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) -{ - if (!info->kern && !info->gpos) // if no kerning table, don't waste time looking up both codepoint->glyphs - return 0; - return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); -} - -STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) -{ - stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); -} - -STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) -{ - if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); - if (descent) *descent = ttSHORT(info->data+info->hhea + 6); - if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); -} - -STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap) -{ - int tab = stbtt__find_table(info->data, info->fontstart, "OS/2"); - if (!tab) - return 0; - if (typoAscent ) *typoAscent = ttSHORT(info->data+tab + 68); - if (typoDescent) *typoDescent = ttSHORT(info->data+tab + 70); - if (typoLineGap) *typoLineGap = ttSHORT(info->data+tab + 72); - return 1; -} - -STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) -{ - *x0 = ttSHORT(info->data + info->head + 36); - *y0 = ttSHORT(info->data + info->head + 38); - *x1 = ttSHORT(info->data + info->head + 40); - *y1 = ttSHORT(info->data + info->head + 42); -} - -STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) -{ - int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); - return (float) height / fheight; -} - -STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels) -{ - int unitsPerEm = ttUSHORT(info->data + info->head + 18); - return pixels / unitsPerEm; -} - -STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) -{ - STBTT_free(v, info->userdata); -} - -////////////////////////////////////////////////////////////////////////////// -// -// antialiasing software rasterizer -// - -STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) -{ - int x0=0,y0=0,x1,y1; // =0 suppresses compiler warning - if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { - // e.g. space character - if (ix0) *ix0 = 0; - if (iy0) *iy0 = 0; - if (ix1) *ix1 = 0; - if (iy1) *iy1 = 0; - } else { - // move to integral bboxes (treating pixels as little squares, what pixels get touched)? - if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x); - if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y); - if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x); - if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y); - } -} - -STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) -{ - stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); -} - -STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) -{ - stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1); -} - -STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) -{ - stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); -} - -////////////////////////////////////////////////////////////////////////////// -// -// Rasterizer - -typedef struct stbtt__hheap_chunk -{ - struct stbtt__hheap_chunk *next; -} stbtt__hheap_chunk; - -typedef struct stbtt__hheap -{ - struct stbtt__hheap_chunk *head; - void *first_free; - int num_remaining_in_head_chunk; -} stbtt__hheap; - -static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata) -{ - if (hh->first_free) { - void *p = hh->first_free; - hh->first_free = * (void **) p; - return p; - } else { - if (hh->num_remaining_in_head_chunk == 0) { - int count = (size < 32 ? 2000 : size < 128 ? 800 : 100); - stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata); - if (c == NULL) - return NULL; - c->next = hh->head; - hh->head = c; - hh->num_remaining_in_head_chunk = count; - } - --hh->num_remaining_in_head_chunk; - return (char *) (hh->head) + sizeof(stbtt__hheap_chunk) + size * hh->num_remaining_in_head_chunk; - } -} - -static void stbtt__hheap_free(stbtt__hheap *hh, void *p) -{ - *(void **) p = hh->first_free; - hh->first_free = p; -} - -static void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata) -{ - stbtt__hheap_chunk *c = hh->head; - while (c) { - stbtt__hheap_chunk *n = c->next; - STBTT_free(c, userdata); - c = n; - } -} - -typedef struct stbtt__edge { - float x0,y0, x1,y1; - int invert; -} stbtt__edge; - - -typedef struct stbtt__active_edge -{ - struct stbtt__active_edge *next; - #if STBTT_RASTERIZER_VERSION==1 - int x,dx; - float ey; - int direction; - #elif STBTT_RASTERIZER_VERSION==2 - float fx,fdx,fdy; - float direction; - float sy; - float ey; - #else - #error "Unrecognized value of STBTT_RASTERIZER_VERSION" - #endif -} stbtt__active_edge; - -#if STBTT_RASTERIZER_VERSION == 1 -#define STBTT_FIXSHIFT 10 -#define STBTT_FIX (1 << STBTT_FIXSHIFT) -#define STBTT_FIXMASK (STBTT_FIX-1) - -static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) -{ - stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); - float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); - STBTT_assert(z != NULL); - if (!z) return z; - - // round dx down to avoid overshooting - if (dxdy < 0) - z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy); - else - z->dx = STBTT_ifloor(STBTT_FIX * dxdy); - - z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); // use z->dx so when we offset later it's by the same amount - z->x -= off_x * STBTT_FIX; - - z->ey = e->y1; - z->next = 0; - z->direction = e->invert ? 1 : -1; - return z; -} -#elif STBTT_RASTERIZER_VERSION == 2 -static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) -{ - stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); - float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); - STBTT_assert(z != NULL); - //STBTT_assert(e->y0 <= start_point); - if (!z) return z; - z->fdx = dxdy; - z->fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f; - z->fx = e->x0 + dxdy * (start_point - e->y0); - z->fx -= off_x; - z->direction = e->invert ? 1.0f : -1.0f; - z->sy = e->y0; - z->ey = e->y1; - z->next = 0; - return z; -} -#else -#error "Unrecognized value of STBTT_RASTERIZER_VERSION" -#endif - -#if STBTT_RASTERIZER_VERSION == 1 -// note: this routine clips fills that extend off the edges... ideally this -// wouldn't happen, but it could happen if the truetype glyph bounding boxes -// are wrong, or if the user supplies a too-small bitmap -static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) -{ - // non-zero winding fill - int x0=0, w=0; - - while (e) { - if (w == 0) { - // if we're currently at zero, we need to record the edge start point - x0 = e->x; w += e->direction; - } else { - int x1 = e->x; w += e->direction; - // if we went to zero, we need to draw - if (w == 0) { - int i = x0 >> STBTT_FIXSHIFT; - int j = x1 >> STBTT_FIXSHIFT; - - if (i < len && j >= 0) { - if (i == j) { - // x0,x1 are the same pixel, so compute combined coverage - scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT); - } else { - if (i >= 0) // add antialiasing for x0 - scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT); - else - i = -1; // clip - - if (j < len) // add antialiasing for x1 - scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT); - else - j = len; // clip - - for (++i; i < j; ++i) // fill pixels between x0 and x1 - scanline[i] = scanline[i] + (stbtt_uint8) max_weight; - } - } - } - } - - e = e->next; - } -} - -static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) -{ - stbtt__hheap hh = { 0, 0, 0 }; - stbtt__active_edge *active = NULL; - int y,j=0; - int max_weight = (255 / vsubsample); // weight per vertical scanline - int s; // vertical subsample index - unsigned char scanline_data[512], *scanline; - - if (result->w > 512) - scanline = (unsigned char *) STBTT_malloc(result->w, userdata); - else - scanline = scanline_data; - - y = off_y * vsubsample; - e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; - - while (j < result->h) { - STBTT_memset(scanline, 0, result->w); - for (s=0; s < vsubsample; ++s) { - // find center of pixel for this scanline - float scan_y = y + 0.5f; - stbtt__active_edge **step = &active; - - // update all active edges; - // remove all active edges that terminate before the center of this scanline - while (*step) { - stbtt__active_edge * z = *step; - if (z->ey <= scan_y) { - *step = z->next; // delete from list - STBTT_assert(z->direction); - z->direction = 0; - stbtt__hheap_free(&hh, z); - } else { - z->x += z->dx; // advance to position for current scanline - step = &((*step)->next); // advance through list - } - } - - // resort the list if needed - for(;;) { - int changed=0; - step = &active; - while (*step && (*step)->next) { - if ((*step)->x > (*step)->next->x) { - stbtt__active_edge *t = *step; - stbtt__active_edge *q = t->next; - - t->next = q->next; - q->next = t; - *step = q; - changed = 1; - } - step = &(*step)->next; - } - if (!changed) break; - } - - // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline - while (e->y0 <= scan_y) { - if (e->y1 > scan_y) { - stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata); - if (z != NULL) { - // find insertion point - if (active == NULL) - active = z; - else if (z->x < active->x) { - // insert at front - z->next = active; - active = z; - } else { - // find thing to insert AFTER - stbtt__active_edge *p = active; - while (p->next && p->next->x < z->x) - p = p->next; - // at this point, p->next->x is NOT < z->x - z->next = p->next; - p->next = z; - } - } - } - ++e; - } - - // now process all active edges in XOR fashion - if (active) - stbtt__fill_active_edges(scanline, result->w, active, max_weight); - - ++y; - } - STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); - ++j; - } - - stbtt__hheap_cleanup(&hh, userdata); - - if (scanline != scanline_data) - STBTT_free(scanline, userdata); -} - -#elif STBTT_RASTERIZER_VERSION == 2 - -// the edge passed in here does not cross the vertical line at x or the vertical line at x+1 -// (i.e. it has already been clipped to those) -static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1) -{ - if (y0 == y1) return; - STBTT_assert(y0 < y1); - STBTT_assert(e->sy <= e->ey); - if (y0 > e->ey) return; - if (y1 < e->sy) return; - if (y0 < e->sy) { - x0 += (x1-x0) * (e->sy - y0) / (y1-y0); - y0 = e->sy; - } - if (y1 > e->ey) { - x1 += (x1-x0) * (e->ey - y1) / (y1-y0); - y1 = e->ey; - } - - if (x0 == x) - STBTT_assert(x1 <= x+1); - else if (x0 == x+1) - STBTT_assert(x1 >= x); - else if (x0 <= x) - STBTT_assert(x1 <= x); - else if (x0 >= x+1) - STBTT_assert(x1 >= x+1); - else - STBTT_assert(x1 >= x && x1 <= x+1); - - if (x0 <= x && x1 <= x) - scanline[x] += e->direction * (y1-y0); - else if (x0 >= x+1 && x1 >= x+1) - ; - else { - STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1); - scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position - } -} - -static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top) -{ - float y_bottom = y_top+1; - - while (e) { - // brute force every pixel - - // compute intersection points with top & bottom - STBTT_assert(e->ey >= y_top); - - if (e->fdx == 0) { - float x0 = e->fx; - if (x0 < len) { - if (x0 >= 0) { - stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom); - stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom); - } else { - stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom); - } - } - } else { - float x0 = e->fx; - float dx = e->fdx; - float xb = x0 + dx; - float x_top, x_bottom; - float sy0,sy1; - float dy = e->fdy; - STBTT_assert(e->sy <= y_bottom && e->ey >= y_top); - - // compute endpoints of line segment clipped to this scanline (if the - // line segment starts on this scanline. x0 is the intersection of the - // line with y_top, but that may be off the line segment. - if (e->sy > y_top) { - x_top = x0 + dx * (e->sy - y_top); - sy0 = e->sy; - } else { - x_top = x0; - sy0 = y_top; - } - if (e->ey < y_bottom) { - x_bottom = x0 + dx * (e->ey - y_top); - sy1 = e->ey; - } else { - x_bottom = xb; - sy1 = y_bottom; - } - - if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) { - // from here on, we don't have to range check x values - - if ((int) x_top == (int) x_bottom) { - float height; - // simple case, only spans one pixel - int x = (int) x_top; - height = sy1 - sy0; - STBTT_assert(x >= 0 && x < len); - scanline[x] += e->direction * (1-((x_top - x) + (x_bottom-x))/2) * height; - scanline_fill[x] += e->direction * height; // everything right of this pixel is filled - } else { - int x,x1,x2; - float y_crossing, step, sign, area; - // covers 2+ pixels - if (x_top > x_bottom) { - // flip scanline vertically; signed area is the same - float t; - sy0 = y_bottom - (sy0 - y_top); - sy1 = y_bottom - (sy1 - y_top); - t = sy0, sy0 = sy1, sy1 = t; - t = x_bottom, x_bottom = x_top, x_top = t; - dx = -dx; - dy = -dy; - t = x0, x0 = xb, xb = t; - } - - x1 = (int) x_top; - x2 = (int) x_bottom; - // compute intersection with y axis at x1+1 - y_crossing = (x1+1 - x0) * dy + y_top; - - sign = e->direction; - // area of the rectangle covered from y0..y_crossing - area = sign * (y_crossing-sy0); - // area of the triangle (x_top,y0), (x+1,y0), (x+1,y_crossing) - scanline[x1] += area * (1-((x_top - x1)+(x1+1-x1))/2); - - step = sign * dy; - for (x = x1+1; x < x2; ++x) { - scanline[x] += area + step/2; - area += step; - } - y_crossing += dy * (x2 - (x1+1)); - - STBTT_assert(STBTT_fabs(area) <= 1.01f); - - scanline[x2] += area + sign * (1-((x2-x2)+(x_bottom-x2))/2) * (sy1-y_crossing); - - scanline_fill[x2] += sign * (sy1-sy0); - } - } else { - // if edge goes outside of box we're drawing, we require - // clipping logic. since this does not match the intended use - // of this library, we use a different, very slow brute - // force implementation - int x; - for (x=0; x < len; ++x) { - // cases: - // - // there can be up to two intersections with the pixel. any intersection - // with left or right edges can be handled by splitting into two (or three) - // regions. intersections with top & bottom do not necessitate case-wise logic. - // - // the old way of doing this found the intersections with the left & right edges, - // then used some simple logic to produce up to three segments in sorted order - // from top-to-bottom. however, this had a problem: if an x edge was epsilon - // across the x border, then the corresponding y position might not be distinct - // from the other y segment, and it might ignored as an empty segment. to avoid - // that, we need to explicitly produce segments based on x positions. - - // rename variables to clearly-defined pairs - float y0 = y_top; - float x1 = (float) (x); - float x2 = (float) (x+1); - float x3 = xb; - float y3 = y_bottom; - - // x = e->x + e->dx * (y-y_top) - // (y-y_top) = (x - e->x) / e->dx - // y = (x - e->x) / e->dx + y_top - float y1 = (x - x0) / dx + y_top; - float y2 = (x+1 - x0) / dx + y_top; - - if (x0 < x1 && x3 > x2) { // three segments descending down-right - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); - stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2); - stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); - } else if (x3 < x1 && x0 > x2) { // three segments descending down-left - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); - stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1); - stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); - } else if (x0 < x1 && x3 > x1) { // two segments across x, down-right - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); - stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); - } else if (x3 < x1 && x0 > x1) { // two segments across x, down-left - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); - stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); - } else if (x0 < x2 && x3 > x2) { // two segments across x+1, down-right - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); - stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); - } else if (x3 < x2 && x0 > x2) { // two segments across x+1, down-left - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); - stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); - } else { // one segment - stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3); - } - } - } - } - e = e->next; - } -} - -// directly AA rasterize edges w/o supersampling -static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) -{ - stbtt__hheap hh = { 0, 0, 0 }; - stbtt__active_edge *active = NULL; - int y,j=0, i; - float scanline_data[129], *scanline, *scanline2; - - STBTT__NOTUSED(vsubsample); - - if (result->w > 64) - scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata); - else - scanline = scanline_data; - - scanline2 = scanline + result->w; - - y = off_y; - e[n].y0 = (float) (off_y + result->h) + 1; - - while (j < result->h) { - // find center of pixel for this scanline - float scan_y_top = y + 0.0f; - float scan_y_bottom = y + 1.0f; - stbtt__active_edge **step = &active; - - STBTT_memset(scanline , 0, result->w*sizeof(scanline[0])); - STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0])); - - // update all active edges; - // remove all active edges that terminate before the top of this scanline - while (*step) { - stbtt__active_edge * z = *step; - if (z->ey <= scan_y_top) { - *step = z->next; // delete from list - STBTT_assert(z->direction); - z->direction = 0; - stbtt__hheap_free(&hh, z); - } else { - step = &((*step)->next); // advance through list - } - } - - // insert all edges that start before the bottom of this scanline - while (e->y0 <= scan_y_bottom) { - if (e->y0 != e->y1) { - stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata); - if (z != NULL) { - STBTT_assert(z->ey >= scan_y_top); - // insert at front - z->next = active; - active = z; - } - } - ++e; - } - - // now process all active edges - if (active) - stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top); - - { - float sum = 0; - for (i=0; i < result->w; ++i) { - float k; - int m; - sum += scanline2[i]; - k = scanline[i] + sum; - k = (float) STBTT_fabs(k)*255 + 0.5f; - m = (int) k; - if (m > 255) m = 255; - result->pixels[j*result->stride + i] = (unsigned char) m; - } - } - // advance all the edges - step = &active; - while (*step) { - stbtt__active_edge *z = *step; - z->fx += z->fdx; // advance to position for current scanline - step = &((*step)->next); // advance through list - } - - ++y; - ++j; - } - - stbtt__hheap_cleanup(&hh, userdata); - - if (scanline != scanline_data) - STBTT_free(scanline, userdata); -} -#else -#error "Unrecognized value of STBTT_RASTERIZER_VERSION" -#endif - -#define STBTT__COMPARE(a,b) ((a)->y0 < (b)->y0) - -static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n) -{ - int i,j; - for (i=1; i < n; ++i) { - stbtt__edge t = p[i], *a = &t; - j = i; - while (j > 0) { - stbtt__edge *b = &p[j-1]; - int c = STBTT__COMPARE(a,b); - if (!c) break; - p[j] = p[j-1]; - --j; - } - if (i != j) - p[j] = t; - } -} - -static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n) -{ - /* threshhold for transitioning to insertion sort */ - while (n > 12) { - stbtt__edge t; - int c01,c12,c,m,i,j; - - /* compute median of three */ - m = n >> 1; - c01 = STBTT__COMPARE(&p[0],&p[m]); - c12 = STBTT__COMPARE(&p[m],&p[n-1]); - /* if 0 >= mid >= end, or 0 < mid < end, then use mid */ - if (c01 != c12) { - /* otherwise, we'll need to swap something else to middle */ - int z; - c = STBTT__COMPARE(&p[0],&p[n-1]); - /* 0>mid && midn => n; 0 0 */ - /* 0n: 0>n => 0; 0 n */ - z = (c == c12) ? 0 : n-1; - t = p[z]; - p[z] = p[m]; - p[m] = t; - } - /* now p[m] is the median-of-three */ - /* swap it to the beginning so it won't move around */ - t = p[0]; - p[0] = p[m]; - p[m] = t; - - /* partition loop */ - i=1; - j=n-1; - for(;;) { - /* handling of equality is crucial here */ - /* for sentinels & efficiency with duplicates */ - for (;;++i) { - if (!STBTT__COMPARE(&p[i], &p[0])) break; - } - for (;;--j) { - if (!STBTT__COMPARE(&p[0], &p[j])) break; - } - /* make sure we haven't crossed */ - if (i >= j) break; - t = p[i]; - p[i] = p[j]; - p[j] = t; - - ++i; - --j; - } - /* recurse on smaller side, iterate on larger */ - if (j < (n-i)) { - stbtt__sort_edges_quicksort(p,j); - p = p+i; - n = n-i; - } else { - stbtt__sort_edges_quicksort(p+i, n-i); - n = j; - } - } -} - -static void stbtt__sort_edges(stbtt__edge *p, int n) -{ - stbtt__sort_edges_quicksort(p, n); - stbtt__sort_edges_ins_sort(p, n); -} - -typedef struct -{ - float x,y; -} stbtt__point; - -static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata) -{ - float y_scale_inv = invert ? -scale_y : scale_y; - stbtt__edge *e; - int n,i,j,k,m; -#if STBTT_RASTERIZER_VERSION == 1 - int vsubsample = result->h < 8 ? 15 : 5; -#elif STBTT_RASTERIZER_VERSION == 2 - int vsubsample = 1; -#else - #error "Unrecognized value of STBTT_RASTERIZER_VERSION" -#endif - // vsubsample should divide 255 evenly; otherwise we won't reach full opacity - - // now we have to blow out the windings into explicit edge lists - n = 0; - for (i=0; i < windings; ++i) - n += wcount[i]; - - e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel - if (e == 0) return; - n = 0; - - m=0; - for (i=0; i < windings; ++i) { - stbtt__point *p = pts + m; - m += wcount[i]; - j = wcount[i]-1; - for (k=0; k < wcount[i]; j=k++) { - int a=k,b=j; - // skip the edge if horizontal - if (p[j].y == p[k].y) - continue; - // add edge from j to k to the list - e[n].invert = 0; - if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { - e[n].invert = 1; - a=j,b=k; - } - e[n].x0 = p[a].x * scale_x + shift_x; - e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample; - e[n].x1 = p[b].x * scale_x + shift_x; - e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample; - ++n; - } - } - - // now sort the edges by their highest point (should snap to integer, and then by x) - //STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); - stbtt__sort_edges(e, n); - - // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule - stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); - - STBTT_free(e, userdata); -} - -static void stbtt__add_point(stbtt__point *points, int n, float x, float y) -{ - if (!points) return; // during first pass, it's unallocated - points[n].x = x; - points[n].y = y; -} - -// tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching -static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) -{ - // midpoint - float mx = (x0 + 2*x1 + x2)/4; - float my = (y0 + 2*y1 + y2)/4; - // versus directly drawn line - float dx = (x0+x2)/2 - mx; - float dy = (y0+y2)/2 - my; - if (n > 16) // 65536 segments on one curve better be enough! - return 1; - if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA - stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); - stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); - } else { - stbtt__add_point(points, *num_points,x2,y2); - *num_points = *num_points+1; - } - return 1; -} - -static void stbtt__tesselate_cubic(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float objspace_flatness_squared, int n) -{ - // @TODO this "flatness" calculation is just made-up nonsense that seems to work well enough - float dx0 = x1-x0; - float dy0 = y1-y0; - float dx1 = x2-x1; - float dy1 = y2-y1; - float dx2 = x3-x2; - float dy2 = y3-y2; - float dx = x3-x0; - float dy = y3-y0; - float longlen = (float) (STBTT_sqrt(dx0*dx0+dy0*dy0)+STBTT_sqrt(dx1*dx1+dy1*dy1)+STBTT_sqrt(dx2*dx2+dy2*dy2)); - float shortlen = (float) STBTT_sqrt(dx*dx+dy*dy); - float flatness_squared = longlen*longlen-shortlen*shortlen; - - if (n > 16) // 65536 segments on one curve better be enough! - return; - - if (flatness_squared > objspace_flatness_squared) { - float x01 = (x0+x1)/2; - float y01 = (y0+y1)/2; - float x12 = (x1+x2)/2; - float y12 = (y1+y2)/2; - float x23 = (x2+x3)/2; - float y23 = (y2+y3)/2; - - float xa = (x01+x12)/2; - float ya = (y01+y12)/2; - float xb = (x12+x23)/2; - float yb = (y12+y23)/2; - - float mx = (xa+xb)/2; - float my = (ya+yb)/2; - - stbtt__tesselate_cubic(points, num_points, x0,y0, x01,y01, xa,ya, mx,my, objspace_flatness_squared,n+1); - stbtt__tesselate_cubic(points, num_points, mx,my, xb,yb, x23,y23, x3,y3, objspace_flatness_squared,n+1); - } else { - stbtt__add_point(points, *num_points,x3,y3); - *num_points = *num_points+1; - } -} - -// returns number of contours -static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) -{ - stbtt__point *points=0; - int num_points=0; - - float objspace_flatness_squared = objspace_flatness * objspace_flatness; - int i,n=0,start=0, pass; - - // count how many "moves" there are to get the contour count - for (i=0; i < num_verts; ++i) - if (vertices[i].type == STBTT_vmove) - ++n; - - *num_contours = n; - if (n == 0) return 0; - - *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); - - if (*contour_lengths == 0) { - *num_contours = 0; - return 0; - } - - // make two passes through the points so we don't need to realloc - for (pass=0; pass < 2; ++pass) { - float x=0,y=0; - if (pass == 1) { - points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); - if (points == NULL) goto error; - } - num_points = 0; - n= -1; - for (i=0; i < num_verts; ++i) { - switch (vertices[i].type) { - case STBTT_vmove: - // start the next contour - if (n >= 0) - (*contour_lengths)[n] = num_points - start; - ++n; - start = num_points; - - x = vertices[i].x, y = vertices[i].y; - stbtt__add_point(points, num_points++, x,y); - break; - case STBTT_vline: - x = vertices[i].x, y = vertices[i].y; - stbtt__add_point(points, num_points++, x, y); - break; - case STBTT_vcurve: - stbtt__tesselate_curve(points, &num_points, x,y, - vertices[i].cx, vertices[i].cy, - vertices[i].x, vertices[i].y, - objspace_flatness_squared, 0); - x = vertices[i].x, y = vertices[i].y; - break; - case STBTT_vcubic: - stbtt__tesselate_cubic(points, &num_points, x,y, - vertices[i].cx, vertices[i].cy, - vertices[i].cx1, vertices[i].cy1, - vertices[i].x, vertices[i].y, - objspace_flatness_squared, 0); - x = vertices[i].x, y = vertices[i].y; - break; - } - } - (*contour_lengths)[n] = num_points - start; - } - - return points; -error: - STBTT_free(points, userdata); - STBTT_free(*contour_lengths, userdata); - *contour_lengths = 0; - *num_contours = 0; - return NULL; -} - -STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata) -{ - float scale = scale_x > scale_y ? scale_y : scale_x; - int winding_count = 0; - int *winding_lengths = NULL; - stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); - if (windings) { - stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata); - STBTT_free(winding_lengths, userdata); - STBTT_free(windings, userdata); - } -} - -STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) -{ - STBTT_free(bitmap, userdata); -} - -STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff) -{ - int ix0,iy0,ix1,iy1; - stbtt__bitmap gbm; - stbtt_vertex *vertices; - int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); - - if (scale_x == 0) scale_x = scale_y; - if (scale_y == 0) { - if (scale_x == 0) { - STBTT_free(vertices, info->userdata); - return NULL; - } - scale_y = scale_x; - } - - stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1); - - // now we get the size - gbm.w = (ix1 - ix0); - gbm.h = (iy1 - iy0); - gbm.pixels = NULL; // in case we error - - if (width ) *width = gbm.w; - if (height) *height = gbm.h; - if (xoff ) *xoff = ix0; - if (yoff ) *yoff = iy0; - - if (gbm.w && gbm.h) { - gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); - if (gbm.pixels) { - gbm.stride = gbm.w; - - stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata); - } - } - STBTT_free(vertices, info->userdata); - return gbm.pixels; -} - -STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) -{ - return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff); -} - -STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) -{ - int ix0,iy0; - stbtt_vertex *vertices; - int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); - stbtt__bitmap gbm; - - stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); - gbm.pixels = output; - gbm.w = out_w; - gbm.h = out_h; - gbm.stride = out_stride; - - if (gbm.w && gbm.h) - stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata); - - STBTT_free(vertices, info->userdata); -} - -STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) -{ - stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph); -} - -STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) -{ - return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); -} - -STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint) -{ - stbtt_MakeGlyphBitmapSubpixelPrefilter(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, oversample_x, oversample_y, sub_x, sub_y, stbtt_FindGlyphIndex(info,codepoint)); -} - -STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) -{ - stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); -} - -STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) -{ - return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); -} - -STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) -{ - stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint); -} - -////////////////////////////////////////////////////////////////////////////// -// -// bitmap baking -// -// This is SUPER-CRAPPY packing to keep source code small - -static int stbtt_BakeFontBitmap_internal(unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) - float pixel_height, // height of font in pixels - unsigned char *pixels, int pw, int ph, // bitmap to be filled in - int first_char, int num_chars, // characters to bake - stbtt_bakedchar *chardata) -{ - float scale; - int x,y,bottom_y, i; - stbtt_fontinfo f; - f.userdata = NULL; - if (!stbtt_InitFont(&f, data, offset)) - return -1; - STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels - x=y=1; - bottom_y = 1; - - scale = stbtt_ScaleForPixelHeight(&f, pixel_height); - - for (i=0; i < num_chars; ++i) { - int advance, lsb, x0,y0,x1,y1,gw,gh; - int g = stbtt_FindGlyphIndex(&f, first_char + i); - stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); - stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); - gw = x1-x0; - gh = y1-y0; - if (x + gw + 1 >= pw) - y = bottom_y, x = 1; // advance to next row - if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row - return -i; - STBTT_assert(x+gw < pw); - STBTT_assert(y+gh < ph); - stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); - chardata[i].x0 = (stbtt_int16) x; - chardata[i].y0 = (stbtt_int16) y; - chardata[i].x1 = (stbtt_int16) (x + gw); - chardata[i].y1 = (stbtt_int16) (y + gh); - chardata[i].xadvance = scale * advance; - chardata[i].xoff = (float) x0; - chardata[i].yoff = (float) y0; - x = x + gw + 1; - if (y+gh+1 > bottom_y) - bottom_y = y+gh+1; - } - return bottom_y; -} - -STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) -{ - float d3d_bias = opengl_fillrule ? 0 : -0.5f; - float ipw = 1.0f / pw, iph = 1.0f / ph; - const stbtt_bakedchar *b = chardata + char_index; - int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f); - int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f); - - q->x0 = round_x + d3d_bias; - q->y0 = round_y + d3d_bias; - q->x1 = round_x + b->x1 - b->x0 + d3d_bias; - q->y1 = round_y + b->y1 - b->y0 + d3d_bias; - - q->s0 = b->x0 * ipw; - q->t0 = b->y0 * iph; - q->s1 = b->x1 * ipw; - q->t1 = b->y1 * iph; - - *xpos += b->xadvance; -} - -////////////////////////////////////////////////////////////////////////////// -// -// rectangle packing replacement routines if you don't have stb_rect_pack.h -// - -#ifndef STB_RECT_PACK_VERSION - -typedef int stbrp_coord; - -//////////////////////////////////////////////////////////////////////////////////// -// // -// // -// COMPILER WARNING ?!?!? // -// // -// // -// if you get a compile warning due to these symbols being defined more than // -// once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" // -// // -//////////////////////////////////////////////////////////////////////////////////// - -typedef struct -{ - int width,height; - int x,y,bottom_y; -} stbrp_context; - -typedef struct -{ - unsigned char x; -} stbrp_node; - -struct stbrp_rect -{ - stbrp_coord x,y; - int id,w,h,was_packed; -}; - -static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes) -{ - con->width = pw; - con->height = ph; - con->x = 0; - con->y = 0; - con->bottom_y = 0; - STBTT__NOTUSED(nodes); - STBTT__NOTUSED(num_nodes); -} - -static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) -{ - int i; - for (i=0; i < num_rects; ++i) { - if (con->x + rects[i].w > con->width) { - con->x = 0; - con->y = con->bottom_y; - } - if (con->y + rects[i].h > con->height) - break; - rects[i].x = con->x; - rects[i].y = con->y; - rects[i].was_packed = 1; - con->x += rects[i].w; - if (con->y + rects[i].h > con->bottom_y) - con->bottom_y = con->y + rects[i].h; - } - for ( ; i < num_rects; ++i) - rects[i].was_packed = 0; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// bitmap baking -// -// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If -// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy. - -STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context) -{ - stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context); - int num_nodes = pw - padding; - stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context); - - if (context == NULL || nodes == NULL) { - if (context != NULL) STBTT_free(context, alloc_context); - if (nodes != NULL) STBTT_free(nodes , alloc_context); - return 0; - } - - spc->user_allocator_context = alloc_context; - spc->width = pw; - spc->height = ph; - spc->pixels = pixels; - spc->pack_info = context; - spc->nodes = nodes; - spc->padding = padding; - spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; - spc->h_oversample = 1; - spc->v_oversample = 1; - - stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); - - if (pixels) - STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels - - return 1; -} - -STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc) -{ - STBTT_free(spc->nodes , spc->user_allocator_context); - STBTT_free(spc->pack_info, spc->user_allocator_context); -} - -STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample) -{ - STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE); - STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE); - if (h_oversample <= STBTT_MAX_OVERSAMPLE) - spc->h_oversample = h_oversample; - if (v_oversample <= STBTT_MAX_OVERSAMPLE) - spc->v_oversample = v_oversample; -} - -#define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1) - -static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) -{ - unsigned char buffer[STBTT_MAX_OVERSAMPLE]; - int safe_w = w - kernel_width; - int j; - STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze - for (j=0; j < h; ++j) { - int i; - unsigned int total; - STBTT_memset(buffer, 0, kernel_width); - - total = 0; - - // make kernel_width a constant in common cases so compiler can optimize out the divide - switch (kernel_width) { - case 2: - for (i=0; i <= safe_w; ++i) { - total += pixels[i] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; - pixels[i] = (unsigned char) (total / 2); - } - break; - case 3: - for (i=0; i <= safe_w; ++i) { - total += pixels[i] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; - pixels[i] = (unsigned char) (total / 3); - } - break; - case 4: - for (i=0; i <= safe_w; ++i) { - total += pixels[i] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; - pixels[i] = (unsigned char) (total / 4); - } - break; - case 5: - for (i=0; i <= safe_w; ++i) { - total += pixels[i] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; - pixels[i] = (unsigned char) (total / 5); - } - break; - default: - for (i=0; i <= safe_w; ++i) { - total += pixels[i] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; - pixels[i] = (unsigned char) (total / kernel_width); - } - break; - } - - for (; i < w; ++i) { - STBTT_assert(pixels[i] == 0); - total -= buffer[i & STBTT__OVER_MASK]; - pixels[i] = (unsigned char) (total / kernel_width); - } - - pixels += stride_in_bytes; - } -} - -static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) -{ - unsigned char buffer[STBTT_MAX_OVERSAMPLE]; - int safe_h = h - kernel_width; - int j; - STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze - for (j=0; j < w; ++j) { - int i; - unsigned int total; - STBTT_memset(buffer, 0, kernel_width); - - total = 0; - - // make kernel_width a constant in common cases so compiler can optimize out the divide - switch (kernel_width) { - case 2: - for (i=0; i <= safe_h; ++i) { - total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; - pixels[i*stride_in_bytes] = (unsigned char) (total / 2); - } - break; - case 3: - for (i=0; i <= safe_h; ++i) { - total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; - pixels[i*stride_in_bytes] = (unsigned char) (total / 3); - } - break; - case 4: - for (i=0; i <= safe_h; ++i) { - total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; - pixels[i*stride_in_bytes] = (unsigned char) (total / 4); - } - break; - case 5: - for (i=0; i <= safe_h; ++i) { - total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; - pixels[i*stride_in_bytes] = (unsigned char) (total / 5); - } - break; - default: - for (i=0; i <= safe_h; ++i) { - total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; - buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; - pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); - } - break; - } - - for (; i < h; ++i) { - STBTT_assert(pixels[i*stride_in_bytes] == 0); - total -= buffer[i & STBTT__OVER_MASK]; - pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); - } - - pixels += 1; - } -} - -static float stbtt__oversample_shift(int oversample) -{ - if (!oversample) - return 0.0f; - - // The prefilter is a box filter of width "oversample", - // which shifts phase by (oversample - 1)/2 pixels in - // oversampled space. We want to shift in the opposite - // direction to counter this. - return (float)-(oversample - 1) / (2.0f * (float)oversample); -} - -// rects array must be big enough to accommodate all characters in the given ranges -STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) -{ - int i,j,k; - - k=0; - for (i=0; i < num_ranges; ++i) { - float fh = ranges[i].font_size; - float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); - ranges[i].h_oversample = (unsigned char) spc->h_oversample; - ranges[i].v_oversample = (unsigned char) spc->v_oversample; - for (j=0; j < ranges[i].num_chars; ++j) { - int x0,y0,x1,y1; - int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; - int glyph = stbtt_FindGlyphIndex(info, codepoint); - stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, - scale * spc->h_oversample, - scale * spc->v_oversample, - 0,0, - &x0,&y0,&x1,&y1); - rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); - rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); - ++k; - } - } - - return k; -} - -STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int prefilter_x, int prefilter_y, float *sub_x, float *sub_y, int glyph) -{ - stbtt_MakeGlyphBitmapSubpixel(info, - output, - out_w - (prefilter_x - 1), - out_h - (prefilter_y - 1), - out_stride, - scale_x, - scale_y, - shift_x, - shift_y, - glyph); - - if (prefilter_x > 1) - stbtt__h_prefilter(output, out_w, out_h, out_stride, prefilter_x); - - if (prefilter_y > 1) - stbtt__v_prefilter(output, out_w, out_h, out_stride, prefilter_y); - - *sub_x = stbtt__oversample_shift(prefilter_x); - *sub_y = stbtt__oversample_shift(prefilter_y); -} - -// rects array must be big enough to accommodate all characters in the given ranges -STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) -{ - int i,j,k, return_value = 1; - - // save current values - int old_h_over = spc->h_oversample; - int old_v_over = spc->v_oversample; - - k = 0; - for (i=0; i < num_ranges; ++i) { - float fh = ranges[i].font_size; - float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); - float recip_h,recip_v,sub_x,sub_y; - spc->h_oversample = ranges[i].h_oversample; - spc->v_oversample = ranges[i].v_oversample; - recip_h = 1.0f / spc->h_oversample; - recip_v = 1.0f / spc->v_oversample; - sub_x = stbtt__oversample_shift(spc->h_oversample); - sub_y = stbtt__oversample_shift(spc->v_oversample); - for (j=0; j < ranges[i].num_chars; ++j) { - stbrp_rect *r = &rects[k]; - if (r->was_packed) { - stbtt_packedchar *bc = &ranges[i].chardata_for_range[j]; - int advance, lsb, x0,y0,x1,y1; - int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; - int glyph = stbtt_FindGlyphIndex(info, codepoint); - stbrp_coord pad = (stbrp_coord) spc->padding; - - // pad on left and top - r->x += pad; - r->y += pad; - r->w -= pad; - r->h -= pad; - stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb); - stbtt_GetGlyphBitmapBox(info, glyph, - scale * spc->h_oversample, - scale * spc->v_oversample, - &x0,&y0,&x1,&y1); - stbtt_MakeGlyphBitmapSubpixel(info, - spc->pixels + r->x + r->y*spc->stride_in_bytes, - r->w - spc->h_oversample+1, - r->h - spc->v_oversample+1, - spc->stride_in_bytes, - scale * spc->h_oversample, - scale * spc->v_oversample, - 0,0, - glyph); - - if (spc->h_oversample > 1) - stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, - r->w, r->h, spc->stride_in_bytes, - spc->h_oversample); - - if (spc->v_oversample > 1) - stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, - r->w, r->h, spc->stride_in_bytes, - spc->v_oversample); - - bc->x0 = (stbtt_int16) r->x; - bc->y0 = (stbtt_int16) r->y; - bc->x1 = (stbtt_int16) (r->x + r->w); - bc->y1 = (stbtt_int16) (r->y + r->h); - bc->xadvance = scale * advance; - bc->xoff = (float) x0 * recip_h + sub_x; - bc->yoff = (float) y0 * recip_v + sub_y; - bc->xoff2 = (x0 + r->w) * recip_h + sub_x; - bc->yoff2 = (y0 + r->h) * recip_v + sub_y; - } else { - return_value = 0; // if any fail, report failure - } - - ++k; - } - } - - // restore original values - spc->h_oversample = old_h_over; - spc->v_oversample = old_v_over; - - return return_value; -} - -STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects) -{ - stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects); -} - -STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) -{ - stbtt_fontinfo info; - int i,j,n, return_value = 1; - //stbrp_context *context = (stbrp_context *) spc->pack_info; - stbrp_rect *rects; - - // flag all characters as NOT packed - for (i=0; i < num_ranges; ++i) - for (j=0; j < ranges[i].num_chars; ++j) - ranges[i].chardata_for_range[j].x0 = - ranges[i].chardata_for_range[j].y0 = - ranges[i].chardata_for_range[j].x1 = - ranges[i].chardata_for_range[j].y1 = 0; - - n = 0; - for (i=0; i < num_ranges; ++i) - n += ranges[i].num_chars; - - rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context); - if (rects == NULL) - return 0; - - info.userdata = spc->user_allocator_context; - stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index)); - - n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects); - - stbtt_PackFontRangesPackRects(spc, rects, n); - - return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects); - - STBTT_free(rects, spc->user_allocator_context); - return return_value; -} - -STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, - int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range) -{ - stbtt_pack_range range; - range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range; - range.array_of_unicode_codepoints = NULL; - range.num_chars = num_chars_in_range; - range.chardata_for_range = chardata_for_range; - range.font_size = font_size; - return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); -} - -STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) -{ - float ipw = 1.0f / pw, iph = 1.0f / ph; - const stbtt_packedchar *b = chardata + char_index; - - if (align_to_integer) { - float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f); - float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f); - q->x0 = x; - q->y0 = y; - q->x1 = x + b->xoff2 - b->xoff; - q->y1 = y + b->yoff2 - b->yoff; - } else { - q->x0 = *xpos + b->xoff; - q->y0 = *ypos + b->yoff; - q->x1 = *xpos + b->xoff2; - q->y1 = *ypos + b->yoff2; - } - - q->s0 = b->x0 * ipw; - q->t0 = b->y0 * iph; - q->s1 = b->x1 * ipw; - q->t1 = b->y1 * iph; - - *xpos += b->xadvance; -} - -////////////////////////////////////////////////////////////////////////////// -// -// sdf computation -// - -#define STBTT_min(a,b) ((a) < (b) ? (a) : (b)) -#define STBTT_max(a,b) ((a) < (b) ? (b) : (a)) - -static int stbtt__ray_intersect_bezier(float orig[2], float ray[2], float q0[2], float q1[2], float q2[2], float hits[2][2]) -{ - float q0perp = q0[1]*ray[0] - q0[0]*ray[1]; - float q1perp = q1[1]*ray[0] - q1[0]*ray[1]; - float q2perp = q2[1]*ray[0] - q2[0]*ray[1]; - float roperp = orig[1]*ray[0] - orig[0]*ray[1]; - - float a = q0perp - 2*q1perp + q2perp; - float b = q1perp - q0perp; - float c = q0perp - roperp; - - float s0 = 0., s1 = 0.; - int num_s = 0; - - if (a != 0.0) { - float discr = b*b - a*c; - if (discr > 0.0) { - float rcpna = -1 / a; - float d = (float) STBTT_sqrt(discr); - s0 = (b+d) * rcpna; - s1 = (b-d) * rcpna; - if (s0 >= 0.0 && s0 <= 1.0) - num_s = 1; - if (d > 0.0 && s1 >= 0.0 && s1 <= 1.0) { - if (num_s == 0) s0 = s1; - ++num_s; - } - } - } else { - // 2*b*s + c = 0 - // s = -c / (2*b) - s0 = c / (-2 * b); - if (s0 >= 0.0 && s0 <= 1.0) - num_s = 1; - } - - if (num_s == 0) - return 0; - else { - float rcp_len2 = 1 / (ray[0]*ray[0] + ray[1]*ray[1]); - float rayn_x = ray[0] * rcp_len2, rayn_y = ray[1] * rcp_len2; - - float q0d = q0[0]*rayn_x + q0[1]*rayn_y; - float q1d = q1[0]*rayn_x + q1[1]*rayn_y; - float q2d = q2[0]*rayn_x + q2[1]*rayn_y; - float rod = orig[0]*rayn_x + orig[1]*rayn_y; - - float q10d = q1d - q0d; - float q20d = q2d - q0d; - float q0rd = q0d - rod; - - hits[0][0] = q0rd + s0*(2.0f - 2.0f*s0)*q10d + s0*s0*q20d; - hits[0][1] = a*s0+b; - - if (num_s > 1) { - hits[1][0] = q0rd + s1*(2.0f - 2.0f*s1)*q10d + s1*s1*q20d; - hits[1][1] = a*s1+b; - return 2; - } else { - return 1; - } - } -} - -static int equal(float *a, float *b) -{ - return (a[0] == b[0] && a[1] == b[1]); -} - -static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex *verts) -{ - int i; - float orig[2], ray[2] = { 1, 0 }; - float y_frac; - int winding = 0; - - orig[0] = x; - orig[1] = y; - - // make sure y never passes through a vertex of the shape - y_frac = (float) STBTT_fmod(y, 1.0f); - if (y_frac < 0.01f) - y += 0.01f; - else if (y_frac > 0.99f) - y -= 0.01f; - orig[1] = y; - - // test a ray from (-infinity,y) to (x,y) - for (i=0; i < nverts; ++i) { - if (verts[i].type == STBTT_vline) { - int x0 = (int) verts[i-1].x, y0 = (int) verts[i-1].y; - int x1 = (int) verts[i ].x, y1 = (int) verts[i ].y; - if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { - float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; - if (x_inter < x) - winding += (y0 < y1) ? 1 : -1; - } - } - if (verts[i].type == STBTT_vcurve) { - int x0 = (int) verts[i-1].x , y0 = (int) verts[i-1].y ; - int x1 = (int) verts[i ].cx, y1 = (int) verts[i ].cy; - int x2 = (int) verts[i ].x , y2 = (int) verts[i ].y ; - int ax = STBTT_min(x0,STBTT_min(x1,x2)), ay = STBTT_min(y0,STBTT_min(y1,y2)); - int by = STBTT_max(y0,STBTT_max(y1,y2)); - if (y > ay && y < by && x > ax) { - float q0[2],q1[2],q2[2]; - float hits[2][2]; - q0[0] = (float)x0; - q0[1] = (float)y0; - q1[0] = (float)x1; - q1[1] = (float)y1; - q2[0] = (float)x2; - q2[1] = (float)y2; - if (equal(q0,q1) || equal(q1,q2)) { - x0 = (int)verts[i-1].x; - y0 = (int)verts[i-1].y; - x1 = (int)verts[i ].x; - y1 = (int)verts[i ].y; - if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { - float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; - if (x_inter < x) - winding += (y0 < y1) ? 1 : -1; - } - } else { - int num_hits = stbtt__ray_intersect_bezier(orig, ray, q0, q1, q2, hits); - if (num_hits >= 1) - if (hits[0][0] < 0) - winding += (hits[0][1] < 0 ? -1 : 1); - if (num_hits >= 2) - if (hits[1][0] < 0) - winding += (hits[1][1] < 0 ? -1 : 1); - } - } - } - } - return winding; -} - -static float stbtt__cuberoot( float x ) -{ - if (x<0) - return -(float) STBTT_pow(-x,1.0f/3.0f); - else - return (float) STBTT_pow( x,1.0f/3.0f); -} - -// x^3 + c*x^2 + b*x + a = 0 -static int stbtt__solve_cubic(float a, float b, float c, float* r) -{ - float s = -a / 3; - float p = b - a*a / 3; - float q = a * (2*a*a - 9*b) / 27 + c; - float p3 = p*p*p; - float d = q*q + 4*p3 / 27; - if (d >= 0) { - float z = (float) STBTT_sqrt(d); - float u = (-q + z) / 2; - float v = (-q - z) / 2; - u = stbtt__cuberoot(u); - v = stbtt__cuberoot(v); - r[0] = s + u + v; - return 1; - } else { - float u = (float) STBTT_sqrt(-p/3); - float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative - float m = (float) STBTT_cos(v); - float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f; - r[0] = s + u * 2 * m; - r[1] = s - u * (m + n); - r[2] = s - u * (m - n); - - //STBTT_assert( STBTT_fabs(((r[0]+a)*r[0]+b)*r[0]+c) < 0.05f); // these asserts may not be safe at all scales, though they're in bezier t parameter units so maybe? - //STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f); - //STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f); - return 3; - } -} - -STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) -{ - float scale_x = scale, scale_y = scale; - int ix0,iy0,ix1,iy1; - int w,h; - unsigned char *data; - - // if one scale is 0, use same scale for both - if (scale_x == 0) scale_x = scale_y; - if (scale_y == 0) { - if (scale_x == 0) return NULL; // if both scales are 0, return NULL - scale_y = scale_x; - } - - stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1); - - // if empty, return NULL - if (ix0 == ix1 || iy0 == iy1) - return NULL; - - ix0 -= padding; - iy0 -= padding; - ix1 += padding; - iy1 += padding; - - w = (ix1 - ix0); - h = (iy1 - iy0); - - if (width ) *width = w; - if (height) *height = h; - if (xoff ) *xoff = ix0; - if (yoff ) *yoff = iy0; - - // invert for y-downwards bitmaps - scale_y = -scale_y; - - { - int x,y,i,j; - float *precompute; - stbtt_vertex *verts; - int num_verts = stbtt_GetGlyphShape(info, glyph, &verts); - data = (unsigned char *) STBTT_malloc(w * h, info->userdata); - precompute = (float *) STBTT_malloc(num_verts * sizeof(float), info->userdata); - - for (i=0,j=num_verts-1; i < num_verts; j=i++) { - if (verts[i].type == STBTT_vline) { - float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; - float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y; - float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0)); - precompute[i] = (dist == 0) ? 0.0f : 1.0f / dist; - } else if (verts[i].type == STBTT_vcurve) { - float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y; - float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y; - float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y; - float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; - float len2 = bx*bx + by*by; - if (len2 != 0.0f) - precompute[i] = 1.0f / (bx*bx + by*by); - else - precompute[i] = 0.0f; - } else - precompute[i] = 0.0f; - } - - for (y=iy0; y < iy1; ++y) { - for (x=ix0; x < ix1; ++x) { - float val; - float min_dist = 999999.0f; - float sx = (float) x + 0.5f; - float sy = (float) y + 0.5f; - float x_gspace = (sx / scale_x); - float y_gspace = (sy / scale_y); - - int winding = stbtt__compute_crossings_x(x_gspace, y_gspace, num_verts, verts); // @OPTIMIZE: this could just be a rasterization, but needs to be line vs. non-tesselated curves so a new path - - for (i=0; i < num_verts; ++i) { - float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; - - // check against every point here rather than inside line/curve primitives -- @TODO: wrong if multiple 'moves' in a row produce a garbage point, and given culling, probably more efficient to do within line/curve - float dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy); - if (dist2 < min_dist*min_dist) - min_dist = (float) STBTT_sqrt(dist2); - - if (verts[i].type == STBTT_vline) { - float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y; - - // coarse culling against bbox - //if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist && - // sy > STBTT_min(y0,y1)-min_dist && sy < STBTT_max(y0,y1)+min_dist) - float dist = (float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i]; - STBTT_assert(i != 0); - if (dist < min_dist) { - // check position along line - // x' = x0 + t*(x1-x0), y' = y0 + t*(y1-y0) - // minimize (x'-sx)*(x'-sx)+(y'-sy)*(y'-sy) - float dx = x1-x0, dy = y1-y0; - float px = x0-sx, py = y0-sy; - // minimize (px+t*dx)^2 + (py+t*dy)^2 = px*px + 2*px*dx*t + t^2*dx*dx + py*py + 2*py*dy*t + t^2*dy*dy - // derivative: 2*px*dx + 2*py*dy + (2*dx*dx+2*dy*dy)*t, set to 0 and solve - float t = -(px*dx + py*dy) / (dx*dx + dy*dy); - if (t >= 0.0f && t <= 1.0f) - min_dist = dist; - } - } else if (verts[i].type == STBTT_vcurve) { - float x2 = verts[i-1].x *scale_x, y2 = verts[i-1].y *scale_y; - float x1 = verts[i ].cx*scale_x, y1 = verts[i ].cy*scale_y; - float box_x0 = STBTT_min(STBTT_min(x0,x1),x2); - float box_y0 = STBTT_min(STBTT_min(y0,y1),y2); - float box_x1 = STBTT_max(STBTT_max(x0,x1),x2); - float box_y1 = STBTT_max(STBTT_max(y0,y1),y2); - // coarse culling against bbox to avoid computing cubic unnecessarily - if (sx > box_x0-min_dist && sx < box_x1+min_dist && sy > box_y0-min_dist && sy < box_y1+min_dist) { - int num=0; - float ax = x1-x0, ay = y1-y0; - float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; - float mx = x0 - sx, my = y0 - sy; - float res[3],px,py,t,it; - float a_inv = precompute[i]; - if (a_inv == 0.0) { // if a_inv is 0, it's 2nd degree so use quadratic formula - float a = 3*(ax*bx + ay*by); - float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by); - float c = mx*ax+my*ay; - if (a == 0.0) { // if a is 0, it's linear - if (b != 0.0) { - res[num++] = -c/b; - } - } else { - float discriminant = b*b - 4*a*c; - if (discriminant < 0) - num = 0; - else { - float root = (float) STBTT_sqrt(discriminant); - res[0] = (-b - root)/(2*a); - res[1] = (-b + root)/(2*a); - num = 2; // don't bother distinguishing 1-solution case, as code below will still work - } - } - } else { - float b = 3*(ax*bx + ay*by) * a_inv; // could precompute this as it doesn't depend on sample point - float c = (2*(ax*ax + ay*ay) + (mx*bx+my*by)) * a_inv; - float d = (mx*ax+my*ay) * a_inv; - num = stbtt__solve_cubic(b, c, d, res); - } - if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) { - t = res[0], it = 1.0f - t; - px = it*it*x0 + 2*t*it*x1 + t*t*x2; - py = it*it*y0 + 2*t*it*y1 + t*t*y2; - dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); - if (dist2 < min_dist * min_dist) - min_dist = (float) STBTT_sqrt(dist2); - } - if (num >= 2 && res[1] >= 0.0f && res[1] <= 1.0f) { - t = res[1], it = 1.0f - t; - px = it*it*x0 + 2*t*it*x1 + t*t*x2; - py = it*it*y0 + 2*t*it*y1 + t*t*y2; - dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); - if (dist2 < min_dist * min_dist) - min_dist = (float) STBTT_sqrt(dist2); - } - if (num >= 3 && res[2] >= 0.0f && res[2] <= 1.0f) { - t = res[2], it = 1.0f - t; - px = it*it*x0 + 2*t*it*x1 + t*t*x2; - py = it*it*y0 + 2*t*it*y1 + t*t*y2; - dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); - if (dist2 < min_dist * min_dist) - min_dist = (float) STBTT_sqrt(dist2); - } - } - } - } - if (winding == 0) - min_dist = -min_dist; // if outside the shape, value is negative - val = onedge_value + pixel_dist_scale * min_dist; - if (val < 0) - val = 0; - else if (val > 255) - val = 255; - data[(y-iy0)*w+(x-ix0)] = (unsigned char) val; - } - } - STBTT_free(precompute, info->userdata); - STBTT_free(verts, info->userdata); - } - return data; -} - -STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) -{ - return stbtt_GetGlyphSDF(info, scale, stbtt_FindGlyphIndex(info, codepoint), padding, onedge_value, pixel_dist_scale, width, height, xoff, yoff); -} - -STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata) -{ - STBTT_free(bitmap, userdata); -} - -////////////////////////////////////////////////////////////////////////////// -// -// font name matching -- recommended not to use this -// - -// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string -static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) -{ - stbtt_int32 i=0; - - // convert utf16 to utf8 and compare the results while converting - while (len2) { - stbtt_uint16 ch = s2[0]*256 + s2[1]; - if (ch < 0x80) { - if (i >= len1) return -1; - if (s1[i++] != ch) return -1; - } else if (ch < 0x800) { - if (i+1 >= len1) return -1; - if (s1[i++] != 0xc0 + (ch >> 6)) return -1; - if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; - } else if (ch >= 0xd800 && ch < 0xdc00) { - stbtt_uint32 c; - stbtt_uint16 ch2 = s2[2]*256 + s2[3]; - if (i+3 >= len1) return -1; - c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; - if (s1[i++] != 0xf0 + (c >> 18)) return -1; - if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; - if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; - if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; - s2 += 2; // plus another 2 below - len2 -= 2; - } else if (ch >= 0xdc00 && ch < 0xe000) { - return -1; - } else { - if (i+2 >= len1) return -1; - if (s1[i++] != 0xe0 + (ch >> 12)) return -1; - if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; - if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; - } - s2 += 2; - len2 -= 2; - } - return i; -} - -static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2) -{ - return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2); -} - -// returns results in whatever encoding you request... but note that 2-byte encodings -// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare -STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) -{ - stbtt_int32 i,count,stringOffset; - stbtt_uint8 *fc = font->data; - stbtt_uint32 offset = font->fontstart; - stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); - if (!nm) return NULL; - - count = ttUSHORT(fc+nm+2); - stringOffset = nm + ttUSHORT(fc+nm+4); - for (i=0; i < count; ++i) { - stbtt_uint32 loc = nm + 6 + 12 * i; - if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) - && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { - *length = ttUSHORT(fc+loc+8); - return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); - } - } - return NULL; -} - -static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) -{ - stbtt_int32 i; - stbtt_int32 count = ttUSHORT(fc+nm+2); - stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); - - for (i=0; i < count; ++i) { - stbtt_uint32 loc = nm + 6 + 12 * i; - stbtt_int32 id = ttUSHORT(fc+loc+6); - if (id == target_id) { - // find the encoding - stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); - - // is this a Unicode encoding? - if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { - stbtt_int32 slen = ttUSHORT(fc+loc+8); - stbtt_int32 off = ttUSHORT(fc+loc+10); - - // check if there's a prefix match - stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); - if (matchlen >= 0) { - // check for target_id+1 immediately following, with same encoding & language - if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { - slen = ttUSHORT(fc+loc+12+8); - off = ttUSHORT(fc+loc+12+10); - if (slen == 0) { - if (matchlen == nlen) - return 1; - } else if (matchlen < nlen && name[matchlen] == ' ') { - ++matchlen; - if (stbtt_CompareUTF8toUTF16_bigendian_internal((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) - return 1; - } - } else { - // if nothing immediately following - if (matchlen == nlen) - return 1; - } - } - } - - // @TODO handle other encodings - } - } - return 0; -} - -static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) -{ - stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name); - stbtt_uint32 nm,hd; - if (!stbtt__isfont(fc+offset)) return 0; - - // check italics/bold/underline flags in macStyle... - if (flags) { - hd = stbtt__find_table(fc, offset, "head"); - if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; - } - - nm = stbtt__find_table(fc, offset, "name"); - if (!nm) return 0; - - if (flags) { - // if we checked the macStyle flags, then just check the family and ignore the subfamily - if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; - if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; - if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; - } else { - if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; - if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; - if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; - } - - return 0; -} - -static int stbtt_FindMatchingFont_internal(unsigned char *font_collection, char *name_utf8, stbtt_int32 flags) -{ - stbtt_int32 i; - for (i=0;;++i) { - stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); - if (off < 0) return off; - if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) - return off; - } -} - -#if defined(__GNUC__) || defined(__clang__) -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wcast-qual" -#endif - -STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, - float pixel_height, unsigned char *pixels, int pw, int ph, - int first_char, int num_chars, stbtt_bakedchar *chardata) -{ - return stbtt_BakeFontBitmap_internal((unsigned char *) data, offset, pixel_height, pixels, pw, ph, first_char, num_chars, chardata); -} - -STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index) -{ - return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index); -} - -STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data) -{ - return stbtt_GetNumberOfFonts_internal((unsigned char *) data); -} - -STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset) -{ - return stbtt_InitFont_internal(info, (unsigned char *) data, offset); -} - -STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags) -{ - return stbtt_FindMatchingFont_internal((unsigned char *) fontdata, (char *) name, flags); -} - -STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) -{ - return stbtt_CompareUTF8toUTF16_bigendian_internal((char *) s1, len1, (char *) s2, len2); -} - -#if defined(__GNUC__) || defined(__clang__) -#pragma GCC diagnostic pop -#endif - -#endif // STB_TRUETYPE_IMPLEMENTATION - - -// FULL VERSION HISTORY -// -// 1.19 (2018-02-11) OpenType GPOS kerning (horizontal only), STBTT_fmod -// 1.18 (2018-01-29) add missing function -// 1.17 (2017-07-23) make more arguments const; doc fix -// 1.16 (2017-07-12) SDF support -// 1.15 (2017-03-03) make more arguments const -// 1.14 (2017-01-16) num-fonts-in-TTC function -// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts -// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual -// 1.11 (2016-04-02) fix unused-variable warning -// 1.10 (2016-04-02) allow user-defined fabs() replacement -// fix memory leak if fontsize=0.0 -// fix warning from duplicate typedef -// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges -// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges -// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; -// allow PackFontRanges to pack and render in separate phases; -// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); -// fixed an assert() bug in the new rasterizer -// replace assert() with STBTT_assert() in new rasterizer -// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) -// also more precise AA rasterizer, except if shapes overlap -// remove need for STBTT_sort -// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC -// 1.04 (2015-04-15) typo in example -// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes -// 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++ -// 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match -// non-oversampled; STBTT_POINT_SIZE for packed case only -// 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling -// 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg) -// 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID -// 0.8b (2014-07-07) fix a warning -// 0.8 (2014-05-25) fix a few more warnings -// 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back -// 0.6c (2012-07-24) improve documentation -// 0.6b (2012-07-20) fix a few more warnings -// 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels, -// stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty -// 0.5 (2011-12-09) bugfixes: -// subpixel glyph renderer computed wrong bounding box -// first vertex of shape can be off-curve (FreeSans) -// 0.4b (2011-12-03) fixed an error in the font baking example -// 0.4 (2011-12-01) kerning, subpixel rendering (tor) -// bugfixes for: -// codepoint-to-glyph conversion using table fmt=12 -// codepoint-to-glyph conversion using table fmt=4 -// stbtt_GetBakedQuad with non-square texture (Zer) -// updated Hello World! sample to use kerning and subpixel -// fixed some warnings -// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) -// userdata, malloc-from-userdata, non-zero fill (stb) -// 0.2 (2009-03-11) Fix unsigned/signed char warnings -// 0.1 (2009-03-09) First public release -// - -/* ------------------------------------------------------------------------------- -This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------- -ALTERNATIVE A - MIT License -Copyright (c) 2017 Sean Barrett -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. ------------------------------------------------------------------------------- -ALTERNATIVE B - Public Domain (www.unlicense.org) -This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------- -*/ diff --git a/src/lib/geogram_gfx/third_party/ImGui/imgui.h b/src/lib/geogram_gfx/third_party/ImGui/imgui.h index 644a40dc..a6cc137a 100644 --- a/src/lib/geogram_gfx/third_party/ImGui/imgui.h +++ b/src/lib/geogram_gfx/third_party/ImGui/imgui.h @@ -59,6 +59,7 @@ #endif #ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wpragmas" // [Bruno Levy] #pragma GCC diagnostic ignored "-Wunknown-pragmas" // [Bruno Levy] #ifndef __clang__ #pragma GCC diagnostic ignored "-Wclass-memaccess" // [Bruno Levy] diff --git a/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_android.cpp b/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_android.cpp index 5da406cd..57212ae4 100644 --- a/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_android.cpp +++ b/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_android.cpp @@ -47,6 +47,7 @@ #include #include #include +#include namespace { double g_Time = 0.0; @@ -54,6 +55,7 @@ namespace { float g_mouseY = 0.0f; bool g_mousePressed[5] = {false, false, false, false, false}; bool g_resetKeys = false; + ImGui_ImplAndroid_MouseUserCallback g_mouse_CB = nullptr; } // Some utilities functions that interact with Android. @@ -65,10 +67,18 @@ namespace AndroidUtils { // Converts a keycode to a unicode. // deviceId, keyCode, metaState can be obtained from the InputEvent. jint keycode_to_unicode( - struct android_app* app, int32_t deviceId, int32_t keyCode, int32_t metaState + struct android_app* app, + int32_t deviceId, int32_t keyCode, int32_t metaState ); } +void ImGui_ImplAndroid_SetMouseUserCallback( + ImGui_ImplAndroid_MouseUserCallback CB +) { + g_mouse_CB = CB; +} + + bool ImGui_ImplAndroid_Init(struct android_app* app) { g_Time = 0.0; @@ -205,6 +215,20 @@ int32_t ImGui_ImplAndroid_FingerEvent( } g_mouseX = AMotionEvent_getX(event, nb_fingers-1); g_mouseY = AMotionEvent_getY(event, nb_fingers-1); + + // Emulate zoom, button 3 + if(!ImGui::GetIO().WantCaptureMouse && g_mouse_CB != nullptr && nb_fingers == 2) { + float x1 = AMotionEvent_getX(event, 0); + float y1 = AMotionEvent_getY(event, 0); + float x2 = AMotionEvent_getX(event, 1); + float y2 = AMotionEvent_getY(event, 1); + float length = sqrtf((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)); + g_mouse_CB(0.0f, length, 3); + g_mousePressed[0] = false; + g_mousePressed[1] = false; + g_mousePressed[2] = false; + } + return 1; } @@ -260,6 +284,7 @@ int32_t ImGui_ImplAndroid_MouseEvent( io.MouseWheelH += hscroll; io.MouseWheel += vscroll; } + return 1; } @@ -281,6 +306,21 @@ int32_t ImGui_ImplAndroid_MotionEvent( default: break; } + + if(!ImGui::GetIO().WantCaptureMouse && g_mouse_CB != nullptr) { + int btn = -1; + if(g_mousePressed[0]) { + btn = 0; + } else if(g_mousePressed[1]) { + btn = 1; + } else if(g_mousePressed[2]) { + btn = 2; + } + if(btn != -1) { + g_mouse_CB(g_mouseX, g_mouseY, btn); + } + } + return result; } diff --git a/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_android.h b/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_android.h index cb2391ab..d74564b5 100644 --- a/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_android.h +++ b/src/lib/geogram_gfx/third_party/ImGui/imgui_impl_android.h @@ -8,7 +8,9 @@ #include // param app: if non-null, registers input handler to specified app. -IMGUI_IMPL_API bool ImGui_ImplAndroid_Init(struct android_app* app = nullptr); +IMGUI_IMPL_API bool ImGui_ImplAndroid_Init( + struct android_app* app = nullptr +); IMGUI_IMPL_API void ImGui_ImplAndroid_Shutdown(); @@ -24,4 +26,16 @@ IMGUI_IMPL_API void ImGui_ImplAndroid_EndFrame(); IMGUI_IMPL_API int32_t ImGui_ImplAndroid_InputEvent( struct android_app* app, AInputEvent* event ); -#endif \ No newline at end of file + +typedef void (*ImGui_ImplAndroid_MouseUserCallback)( + float x, float y, int button +); + +// Registers a user mouse event handler called when no UI element +// is under the mouse pointer. +IMGUI_IMPL_API void ImGui_ImplAndroid_SetMouseUserCallback( + ImGui_ImplAndroid_MouseUserCallback CB +); + + +#endif diff --git a/src/lib/geogram_gfx/third_party/ImGui/titi b/src/lib/geogram_gfx/third_party/ImGui/titi deleted file mode 100644 index ac1ae104..00000000 --- a/src/lib/geogram_gfx/third_party/ImGui/titi +++ /dev/null @@ -1,45 +0,0 @@ -glup_compat.h: * [Bruno Levy 11/26/2017] -imconfig.h:// [Bruno] I'm now using c++11... -imconfig.h:// [Bruno] export ImGUI symbols. -imgui.cpp: g.FrameCountEnded == -1 || // [Ben Li & Bruno Levy], avoids an FPE because PlatformImeLastPos is not initialized -imgui_draw.cpp:#pragma clang diagnostic ignored "-Wunknown-pragmas" // [Bruno Levy] 05/23/2016: so that it does not complain on older compilers -imgui_draw.cpp: // [Bruno Levy]: replaced "={}" with memset() (={} generates a warning on MSVC). -imgui.h:#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" // [Bruno Levy] -imgui.h:#pragma clang diagnostic ignored "-Wc++11-long-long" // [Bruno Levy] -imgui.h:#pragma GCC diagnostic ignored "-Wunknown-pragmas" // [Bruno Levy] -imgui.h:#pragma GCC diagnostic ignored "-Wclass-memaccess" // [Bruno Levy] -imgui.h: void SetBit(int n) { UsedChars[n >> 3] |= (unsigned char) (1 << (n & 7)); } // Set bit 'c' in the array [Bruno Levy] added cast to silent warning -imgui_impl_android.cpp:// [Bruno Levy] Sun Aug 19 08:01:39 CEST 2018 -imgui_impl_android.h: * Author: Bruno Levy Sun Aug 19 08:01:39 CEST 2018 -imgui_impl_glfw.cpp:#ifndef __ANDROID__ /* [Bruno] */ -imgui_impl_glfw.cpp:// [Bruno] adapted to geogram glfw compile -imgui_impl_glfw.cpp:// [Bruno] adapted to geogram glfw compile -imgui_impl_glfw.cpp:// [Bruno Levy] 01/06/2017 Do not use GLFW3 clipboard under emscripten, use built-in -imgui_impl_glfw.cpp: // [Bruno Levy] 01/06/2017 -imgui_impl_glfw.cpp:// [Bruno Levy] 01/06/2017 Do not use GLFW3 clipboard under emscripten, use built-in -imgui_impl_glfw.cpp: //[Bruno Levy] 05/16/2016 Under emscripten, it seems that the window is never focused (so I bypass the test). -imgui_impl_glfw.cpp:#endif /* __ANDROID__ [Bruno] */ -imgui_impl_glfw.h:/* [Bruno] C-style comment */ -imgui_impl_glfw.h:#ifndef __ANDROID__ /* [Bruno] */ -imgui_impl_glfw.h:/* [Bruno] */ -imgui_impl_glfw.h:/* [Bruno] */ -imgui_impl_glfw.h:#endif /* __ANDROID__ [Bruno] */ -imgui_impl_opengl2.cpp:// [Bruno 05/16/2016] conditional compilation -imgui_impl_opengl2.cpp: // [Bruno Levy] 05/18/2016 Added these missing state variables. -imgui_impl_opengl2.cpp: glMatrixMode(GL_TEXTURE); // [Bruno Levy] 05/18/2016 Restore texture matrix. -imgui_impl_opengl2.cpp:#endif // [Bruno Levy] conditional compile. -imgui_impl_opengl2.h:/* [Bruno] C-style comment */ -imgui_impl_opengl2.h:// [Bruno 05/16/2016] conditional compilation -imgui_impl_opengl2.h:/* [Bruno] */ -imgui_impl_opengl2.h:/* [Bruno] */ -imgui_impl_opengl3.cpp:#if defined(USE_GL_ES3) && !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM) // [Bruno] else glBindSampler() undefined with Emscripten -imgui_impl_opengl3.cpp:#ifdef GL_UNPACK_ROW_LENGTH // [Bruno] -imgui_impl_opengl3.h:/* [Bruno] C-style comment */ -imgui_impl_opengl3.h:/* [Bruno] */ -imgui_impl_opengl3.h:/* [Bruno] */ -imgui_impl_win32.cpp:#ifdef _WIN32 /* [Bruno] */ -imgui_impl_win32.cpp:#endif /* [Bruno] */ -imgui_impl_win32.h:/* [Bruno] C-style comment */ -imgui_impl_win32.h:/* [Bruno] */ -imgui_impl_win32.h:/* [Bruno] */ -README.txt:My patches/changes indicated by [Bruno Levy] tags diff --git a/src/lib/geogram_gfx/third_party/ImGuiColorTextEdit/TextEditor.cpp b/src/lib/geogram_gfx/third_party/ImGuiColorTextEdit/TextEditor.cpp index 73b3281c..ec3e1d8b 100755 --- a/src/lib/geogram_gfx/third_party/ImGuiColorTextEdit/TextEditor.cpp +++ b/src/lib/geogram_gfx/third_party/ImGuiColorTextEdit/TextEditor.cpp @@ -81,10 +81,13 @@ namespace { #endif +// [Bruno Levy] functions for management of HighDPI displays. namespace { - // [Bruno Levy] functions for management of HighDPI displays. - -#ifndef __EMSCRIPTEN__ +#if defined(__EMSCRIPTEN__) || defined(__ANDROID__) + double pixel_ratio() { + return 1.0; + } +#else /** * \brief Computes the pixel ratio for hidpi devices. * \details Uses the current GLFW window. @@ -97,17 +100,6 @@ namespace { glfwGetWindowSize(window, &win_size[0], &win_size[1]); return double(buf_size[0]) / double(win_size[0]); } - - /** - * \brief Computes the scaling factor for hidpi devices. - * \details Uses the current GLFW window. - */ - double hidpi_scaling() { - float xscale, yscale; - GLFWwindow* window = glfwGetCurrentContext(); - glfwGetWindowContentScale(window, &xscale, &yscale); - return 0.5 * double(xscale + yscale); - } #endif } @@ -507,11 +499,7 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) auto xadv = (g.Font->IndexAdvanceX['X']); // [Bruno Levy] apply highdpi scaling -#ifdef __EMSCRIPTEN__ - float s = 1.0f; -#else - float s = 1.0f / pixel_ratio(); -#endif + float s = 1.0f / float(pixel_ratio()); mCharAdvance = ImVec2(s*xadv, s*(g.Font->FontSize + mLineSpacing)); // TODO: apply pixel scaling for HiDPI displays. //[Bruno Levy] commented-out (I prefer to use default style) @@ -663,7 +651,7 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); } - lastClick = ImGui::GetTime(); + lastClick = float(ImGui::GetTime()); } else if (click) { @@ -674,7 +662,7 @@ void TextEditor::Render(const char* aTitle, const ImVec2& aSize, bool aBorder) mSelectionMode = SelectionMode::Normal; SetSelection(mInteractiveStart, mInteractiveEnd, mSelectionMode); - lastClick = ImGui::GetTime(); + lastClick = float(ImGui::GetTime()); } else if (ImGui::IsMouseDragging(0) && ImGui::IsMouseDown(0)) { diff --git a/src/tests/test_locks/main.cpp b/src/tests/test_locks/main.cpp old mode 100755 new mode 100644 index a72c60c2..bead6eef --- a/src/tests/test_locks/main.cpp +++ b/src/tests/test_locks/main.cpp @@ -114,25 +114,6 @@ namespace { Process::leave_critical_section(); } - /** - * \brief Accesses the critical section with locking - * \details This function is executed by each thread: - * it does nb_times random access in the critical section - * using locking. In single_lock mode, - * the critical section is locked/unlocked entirely, otherwise, the - * accessed elements are locked/unlocked individually. - * \param[in] pid The id of the thread - */ - void test_nolocks(index_t pid) { - index_t j = 0; - for(index_t i = 0; i < nb_times_; ++i) { - j = (j + 7) % index_t(data_.size()); - data_[j] = signed_index_t(pid); - fast_pause(); - data_[j] = -1; - } - } - protected: /** * \brief Locks an critical section element @@ -210,17 +191,13 @@ int main(int argc, char** argv) { if(CmdLine::get_arg_bool("locks")) { parallel_for( - parallel_for_member_callback( - &lock_test, &LockTest::test_locks - ), - 0, Process::max_threads() + 0, Process::max_threads(), + std::bind(&LockTest::test_locks, &lock_test, std::placeholders::_1) ); } else { parallel_for( - parallel_for_member_callback( - &lock_test, &LockTest::test_nolocks - ), - 0, Process::max_threads() + 0, Process::max_threads(), + std::bind(&LockTest::test_locks, &lock_test, std::placeholders::_1) ); } } diff --git a/src/tests/test_logger/main.cpp b/src/tests/test_logger/main.cpp index 91cd524a..2a0521c8 100755 --- a/src/tests/test_logger/main.cpp +++ b/src/tests/test_logger/main.cpp @@ -80,7 +80,7 @@ int main(int argc, char** argv) { } try { DoIt doit; - parallel_for(doit, 0, 10000); + parallel_for(0, 10000, doit); } catch(const std::exception& e) { std::cerr << "Received an exception: " << e.what() << std::endl; diff --git a/svn-commit.tmp b/svn-commit.tmp new file mode 100644 index 00000000..b19f171a --- /dev/null +++ b/svn-commit.tmp @@ -0,0 +1,28 @@ + +--This line, and those below, will be ignored-- + +M src/AndroidApps/VorpalineDemo/app/src/main/cpp/CMakeLists.txt +M src/AndroidApps/VorpalineDemo/app/src/main/cpp/android_main.cpp +M src/lib/geogram/CMakeLists.txt +M src/lib/geogram/basic/common.cpp +A src/lib/geogram/image +AM src/lib/geogram/image/color.h +AM src/lib/geogram/image/colormap.cpp +AM src/lib/geogram/image/colormap.h +AM src/lib/geogram/image/image.cpp +AM src/lib/geogram/image/image.h +AM src/lib/geogram/image/image_library.cpp +AM src/lib/geogram/image/image_library.h +A src/lib/geogram/image/image_rasterizer.cpp +A src/lib/geogram/image/image_rasterizer.h +AM src/lib/geogram/image/image_serializer.cpp +AM src/lib/geogram/image/image_serializer.h +AM src/lib/geogram/image/image_serializer_pgm.cpp +AM src/lib/geogram/image/image_serializer_pgm.h +A src/lib/geogram/image/image_serializer_stb.cpp +AM src/lib/geogram/image/image_serializer_stb.h +AM src/lib/geogram/image/image_serializer_xpm.cpp +AM src/lib/geogram/image/image_serializer_xpm.h +AM src/lib/geogram/image/morpho_math.cpp +AM src/lib/geogram/image/morpho_math.h +M src/lib/geogram/points/kd_tree.h diff --git a/tests/Remesh.txt b/tests/Remesh.txt old mode 100755 new mode 100644 index dbbe5abe..6aecdba2 --- a/tests/Remesh.txt +++ b/tests/Remesh.txt @@ -39,7 +39,7 @@ joint.off (profile=cad) mask.off (gradation=1) [Tags] weekly_valgrind - Run Test mask.off gradation=1 + Run Test mask.off gradation=1 sys:multithread=false test_isect.obj (profile=convert, isect=true) [Tags] daily_valgrind diff --git a/tests/out.mesh b/tests/out.mesh deleted file mode 100644 index 24ad840c..00000000 --- a/tests/out.mesh +++ /dev/null @@ -1,18872 +0,0 @@ -MeshVersionFormatted 1 - -Dimension 3 - -Vertices -6475 -2.2074 16.6592 0 0 -2.1466 16.5882 0 0 -2.2084 16.657 0 0 -2.0858 16.5194 0 0 -2.0858 16.5165 0 0 -2.1466 16.5847 0 0 -2.2801 16.5959 0 0 -2.2721 16.7274 0 0 -2.2711 16.73 0 0 -2.2084 16.6631 0 0 -2.1466 16.5914 0 0 -2.0858 16.5222 0 0 -2.025 16.4505 0 0 -2.025 16.4476 0 0 -2.025 16.4444 0 0 -2.0858 16.5126 0 0 -2.2189 16.5251 0 0 -2.3498 16.536 0 0 -2.4103 16.6029 0 0 -2.3418 16.6644 0 0 -2.4059 16.7316 0 0 -2.3377 16.7962 0 0 -2.3374 16.7991 0 0 -2.2724 16.7338 0 0 -2.2087 16.6663 0 0 -2.1466 16.5939 0 0 -2.0858 16.5245 0 0 -2.025 16.4528 0 0 -1.9641 16.3811 0 0 -1.9641 16.3788 0 0 -1.9641 16.3759 0 0 -1.9641 16.3724 0 0 -2.0246 16.4406 0 0 -2.1498 16.4486 0 0 -2.2797 16.4614 0 0 -2.4289 16.4653 0 0 -2.4782 16.5456 0 0 -2.4724 16.6682 0 0 -2.5361 16.7319 0 0 -2.4715 16.7966 0 0 -2.4052 16.8635 0 0 -2.4055 16.8667 0 0 -2.339 16.8033 0 0 -2.2737 16.7373 0 0 -2.2093 16.6688 0 0 -2.1466 16.5959 0 0 -2.0858 16.5261 0 0 -2.025 16.4544 0 0 -1.9641 16.383 0 0 -1.903 16.3113 0 0 -1.903 16.3097 0 0 -1.903 16.3071 0 0 -1.903 16.3042 0 0 -1.903 16.3007 0 0 -1.9641 16.3686 0 0 -2.0336 16.3122 0 0 -2.0954 16.3827 0 0 -2.2048 16.3827 0 0 -2.2583 16.3145 0 0 -2.3652 16.3593 0 0 -2.4712 16.3324 0 0 -2.5029 16.4016 0 0 -2.5464 16.4806 0 0 -2.5915 16.5632 0 0 -2.539 16.6093 0 0 -2.5867 16.6583 0 0 -2.6379 16.7034 0 0 -2.5902 16.7806 0 0 -2.539 16.8603 0 0 -2.4744 16.9284 0 0 -2.4756 16.9326 0 0 -2.4078 16.8715 0 0 -2.3406 16.8071 0 0 -2.2746 16.7405 0 0 -2.21 16.6708 0 0 -2.1466 16.5975 0 0 -2.0858 16.5274 0 0 -2.025 16.4557 0 0 -1.9641 16.3843 0 0 -1.903 16.3129 0 0 -1.839 16.2393 0 0 -1.8399 16.238 0 0 -1.8403 16.2364 0 0 -1.8406 16.2335 0 0 -1.8428 16.2322 0 0 -1.903 16.2966 0 0 -1.9785 16.2476 0 0 -2.0483 16.2092 0 0 -2.0982 16.2687 0 0 -2.1552 16.3299 0 0 -2.2004 16.2786 0 0 -2.2308 16.2418 0 0 -2.2788 16.2485 0 0 -2.3742 16.2482 0 0 -2.4734 16.2546 0 0 -2.5208 16.3209 0 0 -2.5538 16.3603 0 0 -2.6008 16.4332 0 0 -2.6456 16.52 0 0 -2.6181 16.6151 0 0 -2.684 16.6186 0 0 -2.7769 16.7162 0 0 -2.7224 16.7841 0 0 -2.669 16.8535 0 0 -2.6082 16.9217 0 0 -2.5454 16.9918 0 0 -2.5477 16.9963 0 0 -2.4785 16.9377 0 0 -2.41 16.8756 0 0 -2.3422 16.8106 0 0 -2.2753 16.7428 0 0 -2.2103 16.6724 0 0 -2.1466 16.5987 0 0 -2.0858 16.528 0 0 -2.025 16.4563 0 0 -1.9641 16.3849 0 0 -1.903 16.3135 0 0 -1.8377 16.2389 0 0 -1.773 16.1698 0 0 -1.7746 16.1698 0 0 -1.7756 16.1682 0 0 -1.7756 16.165 0 0 -1.7775 16.1631 0 0 -1.8441 16.229 0 0 -1.935 16.1864 0 0 -2.015 16.1532 0 0 -2.0646 16.1375 0 0 -2.1063 16.1746 0 0 -2.1584 16.2325 0 0 -2.2128 16.1999 0 0 -2.2852 16.1874 0 0 -2.3812 16.1784 0 0 -2.4849 16.1784 0 0 -2.5403 16.269 0 0 -2.6101 16.3049 0 0 -2.6722 16.3686 0 0 -2.7234 16.4547 0 0 -2.7807 16.5568 0 0 -2.8454 16.658 0 0 -2.8969 16.7463 0 0 -2.8393 16.7914 0 0 -2.79 16.8449 0 0 -2.7381 16.9118 0 0 -2.6789 16.9816 0 0 -2.6184 17.0533 0 0 -2.6216 17.0584 0 0 -2.6264 17.0648 0 0 -2.5554 17.0075 0 0 -2.4846 16.9467 0 0 -2.4139 16.8827 0 0 -2.3444 16.8154 0 0 -2.2769 16.746 0 0 -2.2109 16.6743 0 0 -2.1466 16.5997 0 0 -2.0858 16.528 0 0 -2.025 16.4566 0 0 -1.9641 16.3852 0 0 -1.903 16.3138 0 0 -1.8355 16.2377 0 0 -1.8358 16.2377 0 0 -1.7708 16.1692 0 0 -1.7039 16.1029 0 0 -1.7064 16.1035 0 0 -1.7045 16.0991 0 0 -1.7036 16.0949 0 0 -1.7074 16.0939 0 0 -1.7823 16.1634 0 0 -1.8675 16.0927 0 0 -1.9949 16.0773 0 0 -2.0778 16.0799 0 0 -2.1671 16.1151 0 0 -2.2903 16.0959 0 0 -2.3883 16.0818 0 0 -2.4923 16.0818 0 0 -2.5925 16.0799 0 0 -2.7103 16.0869 0 0 -2.8229 16.173 0 0 -2.8691 16.2498 0 0 -2.9164 16.3283 0 0 -2.9648 16.438 0 0 -3.0105 16.5338 0 0 -3.0585 16.6298 0 0 -3.1094 16.7249 0 0 -3.0246 16.7738 0 0 -3.0765 16.8587 0 0 -2.9475 16.9076 0 0 -2.8847 17.02 0 0 -2.8249 17.0949 0 0 -2.7685 17.1695 0 0 -2.7746 17.1762 0 0 -2.7032 17.1253 0 0 -2.6312 17.0706 0 0 -2.5589 17.012 0 0 -2.4865 16.9499 0 0 -2.4151 16.8846 0 0 -2.345 16.8167 0 0 -2.2775 16.7469 0 0 -2.2839 16.6096 0 0 -2.2205 16.5363 0 0 -2.1588 16.4659 0 0 -2.0973 16.3952 0 0 -2.0358 16.3241 0 0 -1.9744 16.2521 0 0 -1.9103 16.1797 0 0 -1.8434 16.109 0 0 -1.7705 16.1698 0 0 -1.7705 16.1698 0 0 -1.7029 16.1045 0 0 -1.7033 16.1039 0 0 -1.6335 16.0414 0 0 -1.5614 15.9816 0 0 -1.5621 15.9806 0 0 -1.5576 15.9749 0 0 -1.5544 15.9691 0 0 -1.5602 15.9697 0 0 -1.573 15.9758 0 0 -1.6523 16.0389 0 0 -1.6924 16.019 0 0 -1.6856 15.9806 0 0 -1.7231 15.8801 0 0 -1.8031 15.8945 0 0 -1.8953 15.8916 0 0 -1.9907 15.8888 0 0 -2.0922 15.8859 0 0 -2.194 15.8843 0 0 -2.2954 15.8833 0 0 -2.3972 15.8827 0 0 -2.4987 15.8824 0 0 -2.6005 15.9806 0 0 -2.702 15.9806 0 0 -2.8034 15.9806 0 0 -2.8121 16.0898 0 0 -2.8822 16.1538 0 0 -2.9318 16.1996 0 0 -2.9968 16.2924 0 0 -3.0569 16.383 0 0 -3.1018 16.4765 0 0 -3.1488 16.5696 0 0 -3.1978 16.6628 0 0 -3.2493 16.7549 0 0 -3.1626 16.819 0 0 -3.1421 16.9563 0 0 -3.0457 17.0152 0 0 -2.9699 17.0853 0 0 -2.9078 17.1544 0 0 -2.8463 17.2248 0 0 -2.853 17.2319 0 0 -2.8607 17.2393 0 0 -2.7877 17.19 0 0 -2.7135 17.1365 0 0 -2.6383 17.0789 0 0 -2.5627 17.0174 0 0 -2.4885 16.9531 0 0 -2.4164 16.8868 0 0 -2.4836 16.8164 0 0 -2.548 16.7476 0 0 -2.4814 16.6823 0 0 -2.5419 16.6141 0 0 -2.4792 16.5488 0 0 -2.4183 16.4819 0 0 -2.3588 16.4118 0 0 -2.2983 16.3417 0 0 -2.2375 16.2716 0 0 -2.177 16.2015 0 0 -2.1162 16.1295 0 0 -2.0522 16.0517 0 0 -1.9833 15.9733 0 0 -1.8409 15.9701 0 0 -1.702 15.9758 0 0 -1.6274 15.9134 0 0 -1.5608 15.9822 0 0 -1.5611 15.9819 0 0 -1.4869 15.9249 0 0 -1.4872 15.9243 0 0 -1.4881 15.9236 0 0 -1.4821 15.9169 0 0 -1.4811 15.9128 0 0 -1.4814 15.9086 0 0 -1.4942 15.914 0 0 -1.5083 15.9201 0 0 -1.5842 15.9806 0 0 -1.6456 15.8631 0 0 -1.6965 15.7908 0 0 -1.7516 15.8103 0 0 -1.8121 15.8161 0 0 -1.8966 15.8078 0 0 -1.9875 15.8049 0 0 -2.0947 15.7994 0 0 -2.1927 15.7953 0 0 -2.2948 15.7918 0 0 -2.3966 15.7898 0 0 -2.4917 15.7121 0 0 -2.6002 15.7882 0 0 -2.702 15.802 0 0 -2.7817 15.7994 0 0 -2.8598 15.7914 0 0 -2.9712 15.7745 0 0 -3.0342 15.8817 0 0 -3.0067 15.9806 0 0 -3.1085 15.9806 0 0 -3.1504 16.0946 0 0 -3.1997 16.2015 0 0 -3.2416 16.2908 0 0 -3.2852 16.3795 0 0 -3.3303 16.4675 0 0 -3.3774 16.5552 0 0 -3.426 16.6416 0 0 -3.4766 16.7271 0 0 -3.5297 16.8119 0 0 -3.4439 16.8711 0 0 -3.3594 16.9368 0 0 -3.2788 16.9902 0 0 -3.2186 17.1371 0 0 -3.1319 17.1368 0 0 -3.1011 17.1858 0 0 -3.056 17.2498 0 0 -3.0061 17.3289 0 0 -3.0141 17.3362 0 0 -2.9414 17.2924 0 0 -2.8681 17.246 0 0 -2.7935 17.1954 0 0 -2.7167 17.14 0 0 -2.6395 17.0805 0 0 -2.563 17.0181 0 0 -2.4888 16.9537 0 0 -2.5538 16.8814 0 0 -2.6162 16.8106 0 0 -2.6725 16.7389 0 0 -2.6063 16.6778 0 0 -2.6603 16.6055 0 0 -2.5979 16.5434 0 0 -2.5374 16.4797 0 0 -2.4785 16.4147 0 0 -2.4212 16.3465 0 0 -2.363 16.2777 0 0 -2.3038 16.2082 0 0 -2.2442 16.1388 0 0 -2.1853 16.0683 0 0 -2.1242 15.9905 0 0 -2.0563 15.906 0 0 -1.9103 15.8974 0 0 -1.7666 15.9019 0 0 -1.6892 15.8366 0 0 -1.5506 15.8535 0 0 -1.4869 15.9252 0 0 -1.4104 15.8708 0 0 -1.4107 15.8705 0 0 -1.411 15.8702 0 0 -1.4116 15.8692 0 0 -1.3953 15.8558 0 0 -1.3694 15.8359 0 0 -1.3944 15.8471 0 0 -1.4126 15.8548 0 0 -1.4305 15.8625 0 0 -1.4885 15.7882 0 0 -1.5704 15.8292 0 0 -1.6325 15.7594 0 0 -1.6812 15.7098 0 0 -1.7353 15.7518 0 0 -1.7647 15.7754 0 0 -1.8066 15.7668 0 0 -1.8806 15.7482 0 0 -1.9728 15.7322 0 0 -2.0758 15.7233 0 0 -2.1792 15.7124 0 0 -2.2919 15.7057 0 0 -2.3892 15.6452 0 0 -2.4792 15.6455 0 0 -2.6095 15.7101 0 0 -2.6988 15.7268 0 0 -2.7714 15.737 0 0 -2.8342 15.7271 0 0 -2.9228 15.7053 0 0 -3.0163 15.6791 0 0 -3.0704 15.7457 0 0 -3.1219 15.8183 0 0 -3.1082 15.9236 0 0 -3.1693 15.8964 0 0 -3.2099 15.9806 0 0 -3.25 16.0728 0 0 -3.29 16.1612 0 0 -3.3313 16.2489 0 0 -3.3742 16.3363 0 0 -3.4187 16.4227 0 0 -3.4648 16.5081 0 0 -3.5124 16.593 0 0 -3.5621 16.6765 0 0 -3.6139 16.7594 0 0 -3.668 16.8404 0 0 -3.5848 16.8955 0 0 -3.5006 16.9579 0 0 -3.4193 17.0254 0 0 -3.3418 17.0696 0 0 -3.3313 17.2117 0 0 -3.1824 17.2191 0 0 -3.1357 17.2972 0 0 -3.088 17.3779 0 0 -3.0966 17.3849 0 0 -3.023 17.3433 0 0 -2.9494 17.2991 0 0 -2.8742 17.2514 0 0 -2.797 17.1989 0 0 -2.718 17.1419 0 0 -2.6395 17.0811 0 0 -2.5627 17.0181 0 0 -2.6258 16.9441 0 0 -2.6863 16.8718 0 0 -2.7407 16.7985 0 0 -2.7906 16.7236 0 0 -2.7247 16.6653 0 0 -2.7724 16.5895 0 0 -2.71 16.5309 0 0 -2.6498 16.4704 0 0 -2.5912 16.4083 0 0 -2.5342 16.3446 0 0 -2.4792 16.2783 0 0 -2.4235 16.2111 0 0 -2.3662 16.1429 0 0 -2.3085 16.0744 0 0 -2.2513 16.0056 0 0 -2.1952 15.9316 0 0 -2.1309 15.842 0 0 -1.9824 15.8241 0 0 -1.8335 15.8257 0 0 -1.7535 15.7572 0 0 -1.6095 15.7745 0 0 -1.5275 15.7156 0 0 -1.4715 15.7972 0 0 -1.3905 15.7434 0 0 -1.3322 15.8196 0 0 -1.3322 15.8193 0 0 -1.3326 15.8187 0 0 -1.3332 15.8177 0 0 -1.3294 15.8129 0 0 -1.3262 15.8094 0 0 -1.2874 15.7825 0 0 -1.3082 15.7905 0 0 -1.33 15.7994 0 0 -1.3515 15.8084 0 0 -1.4078 15.7361 0 0 -1.4664 15.6679 0 0 -1.5486 15.7175 0 0 -1.6095 15.6503 0 0 -1.7378 15.6624 0 0 -1.7852 15.721 0 0 -1.8534 15.6897 0 0 -1.9452 15.6628 0 0 -2.0461 15.6471 0 0 -2.162 15.6301 0 0 -2.2926 15.6167 0 0 -2.3537 15.5728 0 0 -2.3988 15.6019 0 0 -2.4532 15.5882 0 0 -2.5477 15.56 0 0 -2.6152 15.6365 0 0 -2.7135 15.6736 0 0 -2.7689 15.7009 0 0 -2.8101 15.68 0 0 -2.8806 15.6538 0 0 -2.9603 15.618 0 0 -3.0291 15.6035 0 0 -3.1117 15.6647 0 0 -3.1619 15.7649 0 0 -3.2115 15.8411 0 0 -3.2567 15.9272 0 0 -3.3076 16.0203 0 0 -3.3585 16.1154 0 0 -3.3991 16.2025 0 0 -3.4417 16.2886 0 0 -3.4859 16.3737 0 0 -3.5317 16.4576 0 0 -3.5793 16.5408 0 0 -3.629 16.6224 0 0 -3.6799 16.7031 0 0 -3.7333 16.7822 0 0 -3.7884 16.8596 0 0 -3.724 16.9201 0 0 -3.6421 16.9777 0 0 -3.5605 17.043 0 0 -3.4833 17.1013 0 0 -3.4084 17.1493 0 0 -3.3889 17.2665 0 0 -3.3434 17.317 0 0 -3.2733 17.2748 0 0 -3.2164 17.3423 0 0 -3.1709 17.4239 0 0 -3.1802 17.431 0 0 -3.1056 17.3916 0 0 -3.0313 17.35 0 0 -2.9558 17.3049 0 0 -2.878 17.2549 0 0 -2.798 17.2005 0 0 -2.7176 17.1419 0 0 -2.7164 17.1413 0 0 -2.6389 17.0808 0 0 -2.6997 17.0049 0 0 -2.758 16.931 0 0 -2.8105 16.8561 0 0 -2.8585 16.7796 0 0 -2.9017 16.7015 0 0 -2.8364 16.6464 0 0 -2.8777 16.567 0 0 -2.8159 16.5113 0 0 -2.7557 16.4541 0 0 -2.6978 16.3948 0 0 -2.6411 16.334 0 0 -2.5864 16.2716 0 0 -2.5336 16.2073 0 0 -2.4798 16.1413 0 0 -2.4248 16.067 0 0 -2.371 15.9995 0 0 -2.3178 15.9326 0 0 -2.2621 15.8609 0 0 -2.2058 15.7841 0 0 -2.0576 15.7518 0 0 -1.9036 15.7476 0 0 -1.8207 15.6752 0 0 -1.6709 15.6922 0 0 -1.5858 15.6314 0 0 -1.4984 15.5735 0 0 -1.4433 15.6599 0 0 -1.3569 15.6074 0 0 -1.2689 15.5581 0 0 -1.2221 15.6455 0 0 -1.17 15.7255 0 0 -1.17 15.7252 0 0 -1.1703 15.7245 0 0 -1.1706 15.7236 0 0 -1.1712 15.7217 0 0 -1.0925 15.6781 0 0 -1.1066 15.681 0 0 -1.1338 15.6903 0 0 -1.1626 15.7005 0 0 -1.1908 15.7101 0 0 -1.1088 15.666 0 0 -1.1613 15.6013 0 0 -1.2205 15.5488 0 0 -1.2781 15.5027 0 0 -1.3521 15.527 0 0 -1.3854 15.4877 0 0 -1.4904 15.4502 0 0 -1.5899 15.4502 0 0 -1.6895 15.4502 0 0 -1.7948 15.495 0 0 -1.8892 15.4505 0 0 -1.9904 15.4563 0 0 -2.096 15.4582 0 0 -2.1837 15.4067 0 0 -2.2916 15.4038 0 0 -2.3838 15.4009 0 0 -2.4011 15.4307 0 0 -2.4475 15.4688 0 0 -2.5294 15.448 0 0 -2.6069 15.4281 0 0 -2.6587 15.4624 0 0 -2.718 15.5024 0 0 -2.7698 15.5459 0 0 -2.8476 15.513 0 0 -2.8684 15.5056 0 0 -2.9183 15.5338 0 0 -2.974 15.5059 0 0 -3.0685 15.5034 0 0 -3.1357 15.5146 0 0 -3.2055 15.5254 0 0 -3.1795 15.5398 0 0 -3.2528 15.5501 0 0 -3.2628 15.6509 0 0 -3.3412 15.753 0 0 -3.3569 15.8187 0 0 -3.4097 15.8391 0 0 -3.506 15.9454 0 0 -3.5243 16.0475 0 0 -3.5515 16.1311 0 0 -3.5858 16.2028 0 0 -3.628 16.2857 0 0 -3.6722 16.367 0 0 -3.7189 16.447 0 0 -3.7676 16.5261 0 0 -3.8178 16.6032 0 0 -3.87 16.6791 0 0 -3.9241 16.7533 0 0 -3.9126 16.8782 0 0 -3.9058 17.0091 0 0 -3.8431 17.0738 0 0 -3.7656 17.1378 0 0 -3.6904 17.2082 0 0 -3.5944 17.293 0 0 -3.4792 17.3026 0 0 -3.4686 17.3772 0 0 -3.3822 17.4262 0 0 -3.3415 17.5104 0 0 -3.3508 17.5168 0 0 -3.274 17.4812 0 0 -3.1978 17.4438 0 0 -3.1213 17.4035 0 0 -3.0425 17.359 0 0 -2.9612 17.31 0 0 -2.8783 17.2565 0 0 -2.8767 17.2556 0 0 -2.9311 17.1749 0 0 -2.8521 17.1199 0 0 -2.9068 17.0427 0 0 -2.9551 16.9656 0 0 -2.9987 16.8865 0 0 -3.0377 16.8058 0 0 -3.072 16.7233 0 0 -3.1114 16.6349 0 0 -3.0483 16.5875 0 0 -3.0842 16.5158 0 0 -3.0233 16.4659 0 0 -2.9638 16.414 0 0 -2.9062 16.3609 0 0 -2.8502 16.3058 0 0 -2.7961 16.2492 0 0 -2.7436 16.1912 0 0 -2.6927 16.132 0 0 -2.6437 16.0699 0 0 -2.5934 16.0062 0 0 -2.5323 15.93 0 0 -2.4798 15.8663 0 0 -2.4344 15.7998 0 0 -2.3889 15.7354 0 0 -2.3438 15.6701 0 0 -2.208 15.6314 0 0 -2.1482 15.5277 0 0 -2.0582 15.4435 0 0 -1.9741 15.521 0 0 -1.8828 15.4425 0 0 -1.8018 15.5184 0 0 -1.7097 15.4518 0 0 -1.6155 15.39 0 0 -1.508 15.335 0 0 -1.4628 15.4265 0 0 -1.3678 15.3734 0 0 -1.3172 15.4672 0 0 -1.1786 15.512 0 0 -1.1354 15.601 0 0 -1.0861 15.6833 0 0 -1.0861 15.6829 0 0 -1.0864 15.6823 0 0 -1.0867 15.681 0 0 -1.0871 15.6791 0 0 -0.9936 15.6362 0 0 -0.9744 15.6244 0 0 -1.0144 15.6365 0 0 -1.0464 15.6464 0 0 -1.0781 15.6564 0 0 -0.9939 15.616 0 0 -1.0269 15.625 0 0 -1.0768 15.5639 0 0 -1.13 15.5181 0 0 -1.2068 15.4627 0 0 -1.305 15.4467 0 0 -1.3214 15.4777 0 0 -1.394 15.4473 0 0 -1.4901 15.4089 0 0 -1.5896 15.4089 0 0 -1.6888 15.4086 0 0 -1.7881 15.4083 0 0 -1.7894 15.4502 0 0 -1.8876 15.408 0 0 -1.9852 15.4073 0 0 -2.08 15.4067 0 0 -2.0864 15.3804 0 0 -2.1856 15.3808 0 0 -2.2852 15.3808 0 0 -2.3844 15.3814 0 0 -2.4449 15.3987 0 0 -2.484 15.4169 0 0 -2.5608 15.3926 0 0 -2.6847 15.3856 0 0 -2.7039 15.4121 0 0 -2.7349 15.4374 0 0 -2.7906 15.4688 0 0 -2.8495 15.4816 0 0 -2.9103 15.4941 0 0 -3.0045 15.4918 0 0 -3.0979 15.4902 0 0 -3.1623 15.5011 0 0 -3.2304 15.512 0 0 -3.3018 15.5226 0 0 -3.2778 15.536 0 0 -3.3527 15.5459 0 0 -3.3281 15.5597 0 0 -3.3281 15.6528 0 0 -3.4055 15.6621 0 0 -3.4055 15.755 0 0 -3.4846 15.7639 0 0 -3.4939 15.8497 0 0 -3.5729 15.8606 0 0 -3.579 15.948 0 0 -3.5947 16.0363 0 0 -3.6162 16.1087 0 0 -3.6459 16.1749 0 0 -3.6946 16.2517 0 0 -3.7452 16.3343 0 0 -3.7912 16.4102 0 0 -3.8396 16.4857 0 0 -3.8892 16.5606 0 0 -3.9411 16.6339 0 0 -3.9945 16.7063 0 0 -3.9801 16.8257 0 0 -3.9711 16.9502 0 0 -3.9676 17.0802 0 0 -3.9068 17.1474 0 0 -3.8322 17.2149 0 0 -3.7759 17.2837 0 0 -3.7128 17.3625 0 0 -3.5739 17.4134 0 0 -3.4516 17.4566 0 0 -3.4283 17.5501 0 0 -3.4379 17.5558 0 0 -3.3601 17.5225 0 0 -3.2826 17.487 0 0 -3.2051 17.4489 0 0 -3.1261 17.407 0 0 -3.0445 17.3609 0 0 -2.9609 17.31 0 0 -2.9593 17.3094 0 0 -3.0112 17.2271 0 0 -3.0614 17.1471 0 0 -2.9833 17.0962 0 0 -3.0294 17.0174 0 0 -3.0707 16.9371 0 0 -3.1075 16.8555 0 0 -3.1395 16.7716 0 0 -3.1757 16.6807 0 0 -3.2074 16.6016 0 0 -3.1453 16.5584 0 0 -3.1808 16.4781 0 0 -3.1251 16.4396 0 0 -3.0659 16.3904 0 0 -3.0089 16.3401 0 0 -2.9532 16.2882 0 0 -2.8991 16.2348 0 0 -2.8473 16.1797 0 0 -2.7967 16.1227 0 0 -2.7477 16.0658 0 0 -2.701 16.0046 0 0 -2.6523 15.9416 0 0 -2.5896 15.8612 0 0 -2.5365 15.7962 0 0 -2.4917 15.7325 0 0 -2.4494 15.6733 0 0 -2.41 15.6141 0 0 -2.2855 15.5751 0 0 -2.2241 15.471 0 0 -2.1379 15.3798 0 0 -2.0374 15.2937 0 0 -1.9667 15.3584 0 0 -1.8694 15.2812 0 0 -1.7894 15.3692 0 0 -1.6879 15.3001 0 0 -1.5464 15.2233 0 0 -1.4414 15.2303 0 0 -1.4065 15.2982 0 0 -1.2705 15.3235 0 0 -1.2237 15.4195 0 0 -1.1284 15.375 0 0 -1.0871 15.4694 0 0 -1.047 15.56 0 0 -1.0009 15.6442 0 0 -1.0009 15.6439 0 0 -1.0013 15.6429 0 0 -1.0016 15.6416 0 0 -0.9155 15.6061 0 0 -0.9155 15.6039 0 0 -0.9168 15.6019 0 0 -0.8844 15.5863 0 0 -0.9238 15.5965 0 0 -0.9597 15.6067 0 0 -0.8723 15.5703 0 0 -0.9084 15.5789 0 0 -0.9437 15.5872 0 0 -0.9926 15.5296 0 0 -1.0355 15.4854 0 0 -1.0742 15.4445 0 0 -1.192 15.4089 0 0 -1.2916 15.4089 0 0 -1.3908 15.4089 0 0 -1.3908 15.3801 0 0 -1.4904 15.3801 0 0 -1.5896 15.3801 0 0 -1.6888 15.3801 0 0 -1.7884 15.3804 0 0 -1.8876 15.3804 0 0 -1.9872 15.3804 0 0 -2.0864 15.3641 0 0 -2.1856 15.3641 0 0 -2.2852 15.3641 0 0 -2.3844 15.3641 0 0 -2.4696 15.383 0 0 -2.5832 15.3641 0 0 -2.6824 15.3641 0 0 -2.7772 15.383 0 0 -2.7785 15.4051 0 0 -2.7823 15.4233 0 0 -2.8297 15.4323 0 0 -2.886 15.4688 0 0 -2.9439 15.4803 0 0 -3.0377 15.4793 0 0 -3.1309 15.4784 0 0 -3.1901 15.4883 0 0 -3.2551 15.4989 0 0 -3.3249 15.5094 0 0 -3.3988 15.5197 0 0 -3.3764 15.5325 0 0 -3.4532 15.5424 0 0 -3.4295 15.5555 0 0 -3.4052 15.5693 0 0 -3.4846 15.5779 0 0 -3.4846 15.6711 0 0 -3.5659 15.7719 0 0 -3.6488 15.7799 0 0 -3.6488 15.8724 0 0 -3.6565 15.955 0 0 -3.6632 16.029 0 0 -3.6795 16.0901 0 0 -3.7071 16.1468 0 0 -3.7532 16.2169 0 0 -3.8153 16.2978 0 0 -3.8633 16.3705 0 0 -3.9145 16.4508 0 0 -3.9638 16.5226 0 0 -4.0147 16.5949 0 0 -4.055 16.6538 0 0 -4.104 16.6992 0 0 -4.0425 16.7677 0 0 -4.0384 16.8958 0 0 -4.032 17.02 0 0 -4.0323 17.1487 0 0 -3.9737 17.2188 0 0 -3.903 17.2895 0 0 -3.8473 17.3612 0 0 -3.7999 17.4223 0 0 -3.6718 17.4499 0 0 -3.5525 17.5011 0 0 -3.5163 17.5872 0 0 -3.5262 17.593 0 0 -3.4468 17.5616 0 0 -3.3687 17.528 0 0 -3.2903 17.4918 0 0 -3.2106 17.4528 0 0 -3.1286 17.4092 0 0 -3.0445 17.3612 0 0 -3.0432 17.3609 0 0 -3.0928 17.2767 0 0 -3.1408 17.1954 0 0 -3.1827 17.1141 0 0 -3.1053 17.0667 0 0 -3.1443 16.9854 0 0 -3.1786 16.9025 0 0 -3.2083 16.818 0 0 -3.2448 16.7265 0 0 -3.2762 16.6519 0 0 -3.3156 16.5677 0 0 -3.242 16.5171 0 0 -3.2756 16.4406 0 0 -3.2167 16.399 0 0 -3.1642 16.3587 0 0 -3.105 16.3119 0 0 -3.0528 16.2671 0 0 -2.998 16.2127 0 0 -2.9529 16.1628 0 0 -2.9033 16.1058 0 0 -2.854 16.0488 0 0 -2.8047 15.9918 0 0 -2.7567 15.9364 0 0 -2.7093 15.875 0 0 -2.6479 15.7927 0 0 -2.5941 15.7271 0 0 -2.5467 15.6663 0 0 -2.5051 15.6135 0 0 -2.468 15.5671 0 0 -2.3579 15.5222 0 0 -2.2948 15.4144 0 0 -2.2084 15.318 0 0 -2.103 15.2274 0 0 -2.0016 15.1634 0 0 -1.9395 15.2191 0 0 -1.8646 15.1266 0 0 -1.781 15.1938 0 0 -1.6968 15.11 0 0 -1.4965 15.092 0 0 -1.3921 15.158 0 0 -1.3166 15.2345 0 0 -1.1524 15.2754 0 0 -1.0118 15.3289 0 0 -0.9939 15.4304 0 0 -0.9574 15.5226 0 0 -0.9145 15.608 0 0 -0.9145 15.6077 0 0 -0.9148 15.6071 0 0 -0.8278 15.5747 0 0 -0.8284 15.5738 0 0 -0.8367 15.5741 0 0 -0.8556 15.5783 0 0 -0.7769 15.5501 0 0 -0.8038 15.5552 0 0 -0.8348 15.5613 0 0 -0.7542 15.5325 0 0 -0.7842 15.5373 0 0 -0.8227 15.545 0 0 -0.8598 15.5523 0 0 -0.9078 15.4989 0 0 -0.9536 15.464 0 0 -0.9891 15.4333 0 0 -1.0826 15.4083 0 0 -1.0928 15.3801 0 0 -1.192 15.3801 0 0 -1.2916 15.3801 0 0 -1.2916 15.3641 0 0 -1.3908 15.3641 0 0 -1.4904 15.3641 0 0 -1.5896 15.3641 0 0 -1.6888 15.3641 0 0 -1.7884 15.3641 0 0 -1.8876 15.3641 0 0 -1.9872 15.3641 0 0 -2.0547 15.3555 0 0 -2.1527 15.3555 0 0 -2.2503 15.3555 0 0 -2.3482 15.3555 0 0 -2.4459 15.3558 0 0 -2.484 15.3641 0 0 -2.5438 15.3558 0 0 -2.6411 15.3558 0 0 -2.7385 15.3558 0 0 -2.782 15.3641 0 0 -2.8684 15.3817 0 0 -2.8434 15.4057 0 0 -2.9199 15.4413 0 0 -2.9814 15.4688 0 0 -3.0768 15.4688 0 0 -3.1722 15.4688 0 0 -3.2212 15.4768 0 0 -3.2797 15.4864 0 0 -3.3463 15.4966 0 0 -3.4183 15.5069 0 0 -3.4948 15.5168 0 0 -3.4756 15.5293 0 0 -3.555 15.5389 0 0 -3.5326 15.5514 0 0 -3.5089 15.5645 0 0 -3.5896 15.5728 0 0 -3.5659 15.5866 0 0 -3.5659 15.6791 0 0 -3.6488 15.6871 0 0 -3.733 15.787 0 0 -3.733 15.8795 0 0 -3.7311 15.9589 0 0 -3.7308 16.0226 0 0 -3.7211 16.0757 0 0 -3.7624 16.1128 0 0 -3.8149 16.1724 0 0 -3.877 16.2469 0 0 -3.9446 16.3193 0 0 -3.9984 16.4051 0 0 -4.0352 16.4889 0 0 -4.0717 16.5658 0 0 -4.088 16.617 0 0 -4.1347 16.6288 0 0 -4.2096 16.6445 0 0 -4.1911 16.761 0 0 -4.1078 16.8452 0 0 -4.0982 16.964 0 0 -4.095 17.0869 0 0 -4.1002 17.214 0 0 -4.0435 17.2869 0 0 -3.9769 17.3606 0 0 -3.9126 17.4428 0 0 -3.8486 17.4611 0 0 -3.7663 17.4844 0 0 -3.7266 17.5661 0 0 -3.6392 17.5347 0 0 -3.6056 17.6218 0 0 -3.6152 17.6272 0 0 -3.5348 17.5981 0 0 -3.4555 17.5664 0 0 -3.3764 17.5328 0 0 -3.2961 17.4957 0 0 -3.2138 17.455 0 0 -3.129 17.4099 0 0 -3.1286 17.4099 0 0 -3.176 17.3244 0 0 -3.2218 17.2415 0 0 -3.2615 17.1592 0 0 -3.2961 17.0757 0 0 -3.2195 17.0315 0 0 -3.2516 16.948 0 0 -3.2871 16.8631 0 0 -3.3207 16.7754 0 0 -3.3495 16.7053 0 0 -3.3892 16.6227 0 0 -3.4302 16.545 0 0 -3.3617 16.5005 0 0 -3.3277 16.3699 0 0 -3.2509 16.3218 0 0 -3.1994 16.2847 0 0 -3.1379 16.2412 0 0 -3.0893 16.1999 0 0 -3.0409 16.149 0 0 -3.0009 16.1 0 0 -2.952 16.0427 0 0 -2.9036 15.9864 0 0 -2.8543 15.9281 0 0 -2.8063 15.8695 0 0 -2.7592 15.811 0 0 -2.6997 15.7373 0 0 -2.6472 15.6692 0 0 -2.5976 15.6058 0 0 -2.5557 15.5677 0 0 -2.5189 15.5408 0 0 -2.4347 15.4777 0 0 -2.3707 15.3564 0 0 -2.2884 15.2514 0 0 -2.1632 15.1375 0 0 -2.0493 15.1048 0 0 -1.9372 15.0815 0 0 -1.8134 15.0325 0 0 -1.6399 15.0011 0 0 -1.4427 14.9781 0 0 -1.3162 15.0623 0 0 -1.1812 15.1516 0 0 -1.0413 15.1567 0 0 -1.0294 15.2377 0 0 -0.9334 15.2982 0 0 -0.8992 15.3952 0 0 -0.8662 15.4883 0 0 -0.8275 15.5757 0 0 -0.8275 15.5757 0 0 -0.7385 15.5453 0 0 -0.7388 15.544 0 0 -0.7513 15.5453 0 0 -0.6581 15.5174 0 0 -0.6556 15.512 0 0 -0.7135 15.5242 0 0 -0.6319 15.4989 0 0 -0.6668 15.5043 0 0 -0.6969 15.5078 0 0 -0.7372 15.5142 0 0 -0.7762 15.5206 0 0 -0.8335 15.4774 0 0 -0.8912 15.4441 0 0 -0.9353 15.4265 0 0 -1.0006 15.4083 0 0 -0.9936 15.3801 0 0 -0.9936 15.3641 0 0 -1.0928 15.3641 0 0 -1.192 15.3641 0 0 -1.2721 15.3555 0 0 -1.3697 15.3555 0 0 -1.4677 15.3555 0 0 -1.5656 15.3555 0 0 -1.6632 15.3555 0 0 -1.7612 15.3555 0 0 -1.8591 15.3555 0 0 -1.9571 15.3555 0 0 -2.024 15.3462 0 0 -2.1203 15.3462 0 0 -2.2167 15.3462 0 0 -2.313 15.3462 0 0 -2.4094 15.3462 0 0 -2.5054 15.3465 0 0 -2.6011 15.3465 0 0 -2.6965 15.3465 0 0 -2.7906 15.3465 0 0 -2.8351 15.3558 0 0 -2.8812 15.3641 0 0 -2.9808 15.3641 0 0 -3.0201 15.3872 0 0 -2.9583 15.4166 0 0 -2.9907 15.4499 0 0 -3.0429 15.4419 0 0 -3.1584 15.432 0 0 -3.2452 15.4435 0 0 -3.2676 15.4688 0 0 -3.3034 15.4745 0 0 -3.3636 15.4838 0 0 -3.4337 15.4941 0 0 -3.5092 15.5043 0 0 -3.5889 15.5139 0 0 -3.5742 15.5264 0 0 -3.6565 15.5354 0 0 -3.637 15.5475 0 0 -3.6139 15.56 0 0 -3.6971 15.568 0 0 -3.6725 15.5808 0 0 -3.6485 15.5943 0 0 -3.733 15.6016 0 0 -3.733 15.6945 0 0 -3.8191 15.7937 0 0 -3.8188 15.8862 0 0 -3.8191 15.9547 0 0 -3.821 16.0363 0 0 -3.8927 16.0984 0 0 -3.9641 16.158 0 0 -4.0678 16.2367 0 0 -4.0998 16.3622 0 0 -4.1117 16.4646 0 0 -4.1306 16.5517 0 0 -4.2087 16.5485 0 0 -4.312 16.6483 0 0 -4.3114 16.7556 0 0 -4.2391 16.8849 0 0 -4.1645 16.9259 0 0 -4.2074 17.0081 0 0 -4.1542 17.044 0 0 -4.161 17.1503 0 0 -4.1702 17.2757 0 0 -4.1168 17.3513 0 0 -4.0515 17.4191 0 0 -4.0137 17.4822 0 0 -3.909 17.5318 0 0 -3.8354 17.5139 0 0 -3.8149 17.5952 0 0 -3.6955 17.6541 0 0 -3.7051 17.6592 0 0 -3.6238 17.632 0 0 -3.5435 17.6026 0 0 -3.4635 17.5709 0 0 -3.3825 17.5363 0 0 -3.2999 17.4982 0 0 -3.2151 17.456 0 0 -3.2157 17.4566 0 0 -3.2605 17.3699 0 0 -3.3041 17.286 0 0 -3.3415 17.2024 0 0 -3.3732 17.1183 0 0 -3.3998 17.0328 0 0 -3.3252 16.9912 0 0 -3.3582 16.9031 0 0 -3.3892 16.8174 0 0 -3.4161 16.7457 0 0 -3.4523 16.6612 0 0 -3.4904 16.5837 0 0 -3.4856 16.4829 0 0 -3.4209 16.4358 0 0 -3.3796 16.3026 0 0 -3.2874 16.2514 0 0 -3.2372 16.2137 0 0 -3.1792 16.165 0 0 -3.1328 16.1237 0 0 -3.0896 16.0834 0 0 -3.0505 16.037 0 0 -3.0019 15.9777 0 0 -2.9551 15.9211 0 0 -2.9059 15.8651 0 0 -2.8534 15.8049 0 0 -2.8085 15.7498 0 0 -2.7551 15.6797 0 0 -2.7032 15.6077 0 0 -2.6575 15.5488 0 0 -2.6011 15.4986 0 0 -2.5304 15.4534 0 0 -2.4552 15.2895 0 0 -2.3924 15.1733 0 0 -2.3172 15.0763 0 0 -2.257 14.9928 0 0 -2.1431 15.0447 0 0 -2.0794 15.0495 0 0 -2.0019 15.0427 0 0 -1.8985 14.9918 0 0 -1.878 14.9198 0 0 -1.7734 14.9416 0 0 -1.5832 14.8827 0 0 -1.3655 14.8564 0 0 -1.2465 14.9438 0 0 -1.122 15.0162 0 0 -1.0134 15.0722 0 0 -0.9805 15.1628 0 0 -0.9577 15.2149 0 0 -0.8867 15.1778 0 0 -0.8342 15.2649 0 0 -0.8034 15.3632 0 0 -0.774 15.4576 0 0 -0.7385 15.5466 0 0 -0.7385 15.5462 0 0 -0.6482 15.52 0 0 -0.6485 15.5194 0 0 -0.6511 15.5184 0 0 -0.5666 15.4944 0 0 -0.5502 15.487 0 0 -0.5858 15.4899 0 0 -0.5499 15.479 0 0 -0.5647 15.4803 0 0 -0.5845 15.4809 0 0 -0.6127 15.4825 0 0 -0.654 15.4874 0 0 -0.694 15.4925 0 0 -0.7445 15.4528 0 0 -0.8243 15.415 0 0 -0.9216 15.4057 0 0 -0.9094 15.3801 0 0 -0.894 15.3641 0 0 -0.9785 15.3555 0 0 -1.0762 15.3555 0 0 -1.1741 15.3555 0 0 -1.2529 15.3462 0 0 -1.3495 15.3462 0 0 -1.4459 15.3462 0 0 -1.5422 15.3462 0 0 -1.6386 15.3462 0 0 -1.7349 15.3462 0 0 -1.8313 15.3462 0 0 -1.9276 15.3462 0 0 -1.9949 15.3366 0 0 -2.0899 15.3366 0 0 -2.1847 15.3366 0 0 -2.2797 15.3366 0 0 -2.3745 15.3366 0 0 -2.4689 15.3366 0 0 -2.563 15.3366 0 0 -2.6565 15.3366 0 0 -2.7487 15.3369 0 0 -2.8393 15.3372 0 0 -2.8838 15.3468 0 0 -2.9311 15.3558 0 0 -3.0256 15.3561 0 0 -3.08 15.3641 0 0 -3.1463 15.3782 0 0 -3.2151 15.4009 0 0 -3.2874 15.4313 0 0 -3.3626 15.4688 0 0 -3.4375 15.4803 0 0 -3.5157 15.4915 0 0 -3.5966 15.5021 0 0 -3.6805 15.5117 0 0 -3.6718 15.5232 0 0 -3.757 15.5322 0 0 -3.7413 15.5437 0 0 -3.7208 15.5559 0 0 -3.8066 15.5632 0 0 -3.782 15.5757 0 0 -3.757 15.5882 0 0 -3.8428 15.5949 0 0 -3.8191 15.6083 0 0 -3.8191 15.7012 0 0 -3.9062 15.7998 0 0 -3.9058 15.8824 0 0 -3.9004 15.9441 0 0 -3.9007 15.9937 0 0 -3.9523 16.0248 0 0 -4.0233 16.0571 0 0 -4.1238 16.0872 0 0 -4.2195 16.2341 0 0 -4.2186 16.3481 0 0 -4.2138 16.4512 0 0 -4.3146 16.543 0 0 -4.419 16.6407 0 0 -4.418 16.7434 0 0 -4.4219 16.8571 0 0 -4.3255 16.867 0 0 -4.2724 16.9873 0 0 -4.2304 17.0562 0 0 -4.2023 17.0949 0 0 -4.2256 17.1989 0 0 -4.2986 17.2546 0 0 -4.2435 17.3334 0 0 -4.1933 17.4115 0 0 -4.1264 17.4707 0 0 -4.0739 17.5085 0 0 -3.9977 17.552 0 0 -3.895 17.6205 0 0 -3.7864 17.6842 0 0 -3.7961 17.6887 0 0 -3.7138 17.6637 0 0 -3.6325 17.6365 0 0 -3.5515 17.607 0 0 -3.4702 17.5747 0 0 -3.387 17.5392 0 0 -3.3021 17.4998 0 0 -3.3041 17.5008 0 0 -3.3466 17.4131 0 0 -3.3876 17.3279 0 0 -3.4222 17.2434 0 0 -3.4513 17.1583 0 0 -3.4753 17.0718 0 0 -3.5032 16.9803 0 0 -3.4302 16.9422 0 0 -3.4587 16.8571 0 0 -3.4833 16.7847 0 0 -3.5166 16.7021 0 0 -3.5528 16.6205 0 0 -3.5358 16.5177 0 0 -3.5409 16.4284 0 0 -3.5 16.3599 0 0 -3.507 16.2473 0 0 -3.4042 16.2239 0 0 -3.3329 16.1896 0 0 -3.2762 16.1471 0 0 -3.2106 16.101 0 0 -3.1639 16.0667 0 0 -3.1251 16.0373 0 0 -3.0912 15.995 0 0 -3.0467 15.9326 0 0 -3.0038 15.8702 0 0 -2.9564 15.803 0 0 -2.903 15.73 0 0 -2.8639 15.681 0 0 -2.8188 15.6125 0 0 -2.7676 15.544 0 0 -2.7221 15.4816 0 0 -2.6786 15.4201 0 0 -2.6235 15.3414 0 0 -2.5413 15.2108 0 0 -2.4782 15.1138 0 0 -2.412 15.021 0 0 -2.3559 14.9419 0 0 -2.2897 14.8523 0 0 -2.2061 14.9195 0 0 -2.1152 14.9742 0 0 -2.0547 15.0098 0 0 -1.9686 14.9698 0 0 -1.9542 14.9121 0 0 -1.9641 14.8398 0 0 -1.8771 14.8155 0 0 -1.742 14.7931 0 0 -1.5861 14.6343 0 0 -1.4795 14.7527 0 0 -1.2772 14.7201 0 0 -1.1735 14.8071 0 0 -1.0695 14.8926 0 0 -0.9689 14.9672 0 0 -0.8819 15.0264 0 0 -0.944 15.1138 0 0 -0.7734 15.1058 0 0 -0.7333 15.2358 0 0 -0.7065 15.335 0 0 -0.6805 15.4304 0 0 -0.6482 15.5203 0 0 -0.5573 15.4976 0 0 -0.5573 15.4976 0 0 -0.5576 15.4966 0 0 -0.5611 15.496 0 0 -0.483 15.4768 0 0 -0.3876 15.4573 0 0 -0.3768 15.4515 0 0 -0.3755 15.4477 0 0 -0.4408 15.4579 0 0 -0.5022 15.4694 0 0 -0.501 15.4659 0 0 -0.4962 15.4605 0 0 -0.4808 15.4502 0 0 -0.5547 15.4595 0 0 -0.6088 15.4665 0 0 -0.6562 15.4301 0 0 -0.6911 15.4019 0 0 -0.807 15.3792 0 0 -0.7004 15.3785 0 0 -0.6953 15.3641 0 0 -0.7826 15.3555 0 0 -0.8675 15.3462 0 0 -0.9501 15.3363 0 0 -1.0448 15.3363 0 0 -1.1399 15.3363 0 0 -1.218 15.3257 0 0 -1.3118 15.3257 0 0 -1.4052 15.3257 0 0 -1.499 15.3257 0 0 -1.5928 15.326 0 0 -1.6866 15.326 0 0 -1.7801 15.326 0 0 -1.8739 15.326 0 0 -1.9424 15.3148 0 0 -2.0349 15.3148 0 0 -2.1274 15.3148 0 0 -2.2196 15.3148 0 0 -2.3117 15.3148 0 0 -2.4036 15.3151 0 0 -2.4952 15.3151 0 0 -2.5854 15.3151 0 0 -2.6748 15.3155 0 0 -2.7618 15.3158 0 0 -2.8463 15.3161 0 0 -2.8482 15.2207 0 0 -2.8828 15.2284 0 0 -2.9251 15.239 0 0 -2.9311 15.1583 0 0 -2.9792 15.1721 0 0 -3.0307 15.1845 0 0 -3.087 15.1957 0 0 -3.1533 15.2041 0 0 -3.2186 15.2197 0 0 -3.2913 15.2434 0 0 -3.3652 15.2575 0 0 -3.434 15.2742 0 0 -3.5118 15.2854 0 0 -3.5925 15.2959 0 0 -3.6763 15.3059 0 0 -3.7628 15.3151 0 0 -3.7666 15.4217 0 0 -3.8553 15.4301 0 0 -3.9455 15.4377 0 0 -3.9455 15.537 0 0 -3.9343 15.5472 0 0 -4.0259 15.5539 0 0 -4.007 15.5651 0 0 -3.9833 15.5767 0 0 -4.0729 15.5824 0 0 -4.0438 15.5946 0 0 -4.0153 15.6071 0 0 -3.9948 15.6202 0 0 -3.9948 15.7127 0 0 -4.0752 15.8074 0 0 -4.0614 15.8932 0 0 -4.1638 15.9006 0 0 -4.2525 16.0043 0 0 -4.3399 16.0168 0 0 -4.4267 16.035 0 0 -4.4234 16.1372 0 0 -4.4231 16.238 0 0 -4.4225 16.3375 0 0 -4.4215 16.4374 0 0 -4.5233 16.5398 0 0 -4.6251 16.6416 0 0 -4.6251 16.7415 0 0 -4.6251 16.8414 0 0 -4.6251 16.9419 0 0 -4.6261 17.0434 0 0 -4.5316 17.0571 0 0 -4.4443 17.0747 0 0 -4.4475 17.2095 0 0 -4.5022 17.3087 0 0 -4.4555 17.3667 0 0 -4.4084 17.4352 0 0 -4.3585 17.5168 0 0 -4.3076 17.625 0 0 -4.1757 17.6868 0 0 -4.0758 17.6653 0 0 -3.9705 17.7364 0 0 -3.9801 17.7402 0 0 -3.8959 17.7194 0 0 -3.8127 17.6967 0 0 -3.7304 17.6714 0 0 -3.6479 17.6439 0 0 -3.5646 17.6134 0 0 -3.4798 17.5795 0 0 -3.4843 17.5818 0 0 -3.5214 17.4918 0 0 -3.5573 17.4044 0 0 -3.587 17.318 0 0 -3.611 17.2312 0 0 -3.6347 17.1365 0 0 -3.6527 17.0488 0 0 -3.6709 16.9646 0 0 -3.6975 16.8967 0 0 -3.6206 16.8564 0 0 -3.6514 16.7847 0 0 -3.6879 16.7146 0 0 -3.7263 16.6448 0 0 -3.7023 16.5408 0 0 -3.6408 16.4953 0 0 -3.6152 16.4195 0 0 -3.6408 16.3689 0 0 -3.6744 16.294 0 0 -3.7019 16.2127 0 0 -3.6152 16.1855 0 0 -3.5348 16.0904 0 0 -3.434 16.084 0 0 -3.3662 15.964 0 0 -3.2663 15.9582 0 0 -3.1943 15.955 0 0 -3.1747 15.851 0 0 -3.1165 15.802 0 0 -3.1267 15.7261 0 0 -3.0448 15.6231 0 0 -2.9766 15.6013 0 0 -2.9369 15.5888 0 0 -2.9257 15.5469 0 0 -2.9043 15.4729 0 0 -2.8742 15.3846 0 0 -2.8332 15.2956 0 0 -2.7753 15.189 0 0 -2.71 15.0703 0 0 -2.6507 14.9864 0 0 -2.587 14.8936 0 0 -2.5246 14.818 0 0 -2.4504 14.7278 0 0 -2.3268 14.7127 0 0 -2.1786 14.7114 0 0 -2.0851 14.6288 0 0 -1.9952 14.5571 0 0 -1.8831 14.4755 0 0 -1.7452 14.375 0 0 -1.5557 14.4045 0 0 -1.3819 14.432 0 0 -1.2974 14.5283 0 0 -1.1488 14.5341 0 0 -1.0634 14.6112 0 0 -0.9718 14.6807 0 0 -0.8764 14.7454 0 0 -0.7833 14.7947 0 0 -0.6725 14.8398 0 0 -0.5653 14.8772 0 0 -0.597 15.0072 0 0 -0.547 15.1279 0 0 -0.5294 15.1887 0 0 -0.5099 15.2895 0 0 -0.3953 15.3699 0 0 -0.3736 15.463 0 0 -0.3736 15.4627 0 0 -0.3742 15.4617 0 0 -0.3883 15.4624 0 0 -0.2923 15.4493 0 0 -0.2939 15.447 0 0 -0.2929 15.4441 0 0 -0.291 15.4406 0 0 -0.2878 15.4358 0 0 -0.3614 15.4413 0 0 -0.4309 15.4525 0 0 -0.4161 15.4451 0 0 -0.402 15.4374 0 0 -0.4229 15.4352 0 0 -0.4757 15.4397 0 0 -0.5234 15.4435 0 0 -0.5759 15.4124 0 0 -0.605 15.3788 0 0 -0.596 15.3641 0 0 -0.685 15.3555 0 0 -0.7711 15.3462 0 0 -0.855 15.3363 0 0 -0.9366 15.3257 0 0 -1.0304 15.3257 0 0 -1.1242 15.3257 0 0 -1.2017 15.3145 0 0 -1.2942 15.3145 0 0 -1.3867 15.3145 0 0 -1.4795 15.3148 0 0 -1.572 15.3148 0 0 -1.6645 15.3148 0 0 -1.757 15.3148 0 0 -1.8495 15.3148 0 0 -1.9187 15.303 0 0 -2.0102 15.303 0 0 -2.1018 15.303 0 0 -2.193 15.303 0 0 -2.2842 15.3033 0 0 -2.3751 15.3033 0 0 -2.4657 15.3033 0 0 -2.555 15.3033 0 0 -2.6431 15.3036 0 0 -2.7288 15.3039 0 0 -2.8117 15.3043 0 0 -2.8133 15.2101 0 0 -2.8156 15.1157 0 0 -2.8511 15.1324 0 0 -2.8873 15.1445 0 0 -2.8911 15.0485 0 0 -2.9353 15.0645 0 0 -2.984 15.0799 0 0 -3.0368 15.0949 0 0 -3.0934 15.1096 0 0 -3.1542 15.1237 0 0 -3.2189 15.1372 0 0 -3.2871 15.1503 0 0 -3.3591 15.1628 0 0 -3.4337 15.1746 0 0 -3.5118 15.1855 0 0 -3.5925 15.1961 0 0 -3.676 15.206 0 0 -3.7628 15.2153 0 0 -3.8517 15.2236 0 0 -3.8517 15.3235 0 0 -3.943 15.3311 0 0 -4.0361 15.3382 0 0 -4.0384 15.4445 0 0 -4.0384 15.5437 0 0 -4.1331 15.5498 0 0 -4.119 15.5597 0 0 -4.0985 15.5709 0 0 -4.1904 15.576 0 0 -4.1613 15.5879 0 0 -4.128 15.6 0 0 -4.0995 15.6128 0 0 -4.0845 15.625 0 0 -4.0845 15.7178 0 0 -4.1741 15.8142 0 0 -4.2615 15.9096 0 0 -4.3521 15.9147 0 0 -4.443 15.9214 0 0 -4.5259 16.0398 0 0 -4.524 16.1407 0 0 -4.524 16.2402 0 0 -4.524 16.3401 0 0 -4.5236 16.4403 0 0 -4.6251 16.5418 0 0 -4.7263 16.6426 0 0 -4.7263 16.7425 0 0 -4.7263 16.8423 0 0 -4.7266 16.9422 0 0 -4.7266 17.0421 0 0 -4.7266 17.1419 0 0 -4.6347 17.1535 0 0 -4.5547 17.1643 0 0 -4.5729 17.2719 0 0 -4.5425 17.3519 0 0 -4.5195 17.4015 0 0 -4.482 17.4736 0 0 -4.4465 17.5594 0 0 -4.4126 17.7008 0 0 -4.3421 17.7447 0 0 -4.272 17.7124 0 0 -4.1574 17.7783 0 0 -4.0637 17.7585 0 0 -4.0729 17.7623 0 0 -3.9878 17.7437 0 0 -3.9039 17.7229 0 0 -3.821 17.7002 0 0 -3.7381 17.6749 0 0 -3.6549 17.6471 0 0 -3.5701 17.616 0 0 -3.5755 17.6182 0 0 -3.6104 17.527 0 0 -3.6437 17.439 0 0 -3.6709 17.3519 0 0 -3.6923 17.2645 0 0 -3.7125 17.1682 0 0 -3.7311 17.0818 0 0 -3.7477 16.9985 0 0 -3.7736 16.9345 0 0 -3.8089 16.8791 0 0 -3.7247 16.8289 0 0 -3.7634 16.7636 0 0 -3.7935 16.6925 0 0 -3.8255 16.641 0 0 -3.7692 16.5917 0 0 -3.7452 16.4947 0 0 -3.6757 16.4515 0 0 -3.7122 16.4012 0 0 -3.7576 16.3321 0 0 -3.7845 16.2498 0 0 -3.8021 16.1695 0 0 -3.7304 16.1359 0 0 -3.6357 16.1099 0 0 -3.6456 16.0238 0 0 -3.5518 15.9998 0 0 -3.4596 15.9755 0 0 -3.4622 15.8769 0 0 -3.3777 15.8654 0 0 -3.2893 15.8539 0 0 -3.2106 15.7425 0 0 -3.137 15.642 0 0 -3.0608 15.5511 0 0 -2.984 15.5443 0 0 -2.9897 15.4624 0 0 -2.9968 15.334 0 0 -2.9295 15.2117 0 0 -2.8726 15.1122 0 0 -2.8063 15.0024 0 0 -2.7426 14.9224 0 0 -2.6648 14.8241 0 0 -2.5938 14.7466 0 0 -2.5272 14.6717 0 0 -2.4763 14.6292 0 0 -2.403 14.6586 0 0 -2.2564 14.6228 0 0 -2.1546 14.52 0 0 -2.0611 14.4384 0 0 -1.9484 14.3526 0 0 -1.8006 14.2511 0 0 -1.6248 14.2915 0 0 -1.4555 14.3276 0 0 -1.3002 14.3574 0 0 -1.2282 14.4496 0 0 -1.0976 14.4646 0 0 -1.0176 14.5363 0 0 -0.9308 14.5997 0 0 -0.8387 14.6538 0 0 -0.751 14.7044 0 0 -0.6488 14.7412 0 0 -0.5438 14.7681 0 0 -0.4366 14.7854 0 0 -0.4545 14.9032 0 0 -0.4824 15.0645 0 0 -0.4405 15.1717 0 0 -0.4107 15.2722 0 0 -0.299 15.3568 0 0 -0.2807 15.4505 0 0 -0.2807 15.4502 0 0 -0.2836 15.4499 0 0 -0.1965 15.4397 0 0 -0.1962 15.4374 0 0 -0.1959 15.4345 0 0 -0.1953 15.431 0 0 -0.194 15.4269 0 0 -0.282 15.4307 0 0 -0.3505 15.4349 0 0 -0.3444 15.4291 0 0 -0.3851 15.4317 0 0 -0.3643 15.4237 0 0 -0.3944 15.4227 0 0 -0.4382 15.4233 0 0 -0.3752 15.3792 0 0 -0.4968 15.3641 0 0 -0.5871 15.3555 0 0 -0.6748 15.3462 0 0 -0.7599 15.3363 0 0 -0.8431 15.3257 0 0 -0.9238 15.3145 0 0 -1.0163 15.3145 0 0 -1.1091 15.3145 0 0 -1.1853 15.3027 0 0 -1.2769 15.303 0 0 -1.3687 15.303 0 0 -1.4603 15.303 0 0 -1.5518 15.303 0 0 -1.6437 15.303 0 0 -1.7353 15.303 0 0 -1.8268 15.303 0 0 -1.8959 15.2905 0 0 -1.9869 15.2905 0 0 -2.0778 15.2905 0 0 -2.1687 15.2905 0 0 -2.2593 15.2905 0 0 -2.3492 15.2905 0 0 -2.4392 15.2905 0 0 -2.5278 15.2908 0 0 -2.6149 15.2908 0 0 -2.7 15.2911 0 0 -2.782 15.2918 0 0 -2.781 15.1932 0 0 -2.7849 15.0991 0 0 -2.8156 15.0158 0 0 -2.8511 15.0325 0 0 -2.942 15.0261 0 0 -2.9801 15.0418 0 0 -3.0227 15.0568 0 0 -3.0691 15.0715 0 0 -3.1194 15.0859 0 0 -3.1738 15.0997 0 0 -3.2317 15.1135 0 0 -3.2935 15.1263 0 0 -3.3585 15.1388 0 0 -3.427 15.1506 0 0 -3.4984 15.1618 0 0 -3.5729 15.1727 0 0 -3.6504 15.1826 0 0 -3.7304 15.1919 0 0 -3.813 15.2005 0 0 -3.8978 15.2085 0 0 -3.9427 15.2313 0 0 -4.0361 15.2383 0 0 -4.1309 15.2444 0 0 -4.1309 15.3443 0 0 -4.1331 15.4505 0 0 -4.2291 15.4557 0 0 -4.2291 15.5552 0 0 -4.2135 15.5648 0 0 -4.3082 15.5693 0 0 -4.2807 15.5808 0 0 -4.2452 15.5933 0 0 -4.2083 15.6058 0 0 -4.1811 15.6183 0 0 -4.175 15.6295 0 0 -4.175 15.7223 0 0 -4.2647 15.818 0 0 -4.3559 15.8215 0 0 -4.4478 15.8254 0 0 -4.54 15.8302 0 0 -4.5342 15.931 0 0 -4.6267 15.9425 0 0 -4.6251 16.0424 0 0 -4.6251 16.1423 0 0 -4.6251 16.2421 0 0 -4.6251 16.342 0 0 -4.6251 16.4419 0 0 -4.7263 16.5427 0 0 -4.8277 16.543 0 0 -4.8277 16.6429 0 0 -4.8277 16.7428 0 0 -4.8277 16.8426 0 0 -4.8277 16.9425 0 0 -4.8277 17.0424 0 0 -4.8277 17.1423 0 0 -4.7269 17.2418 0 0 -4.6453 17.2565 0 0 -4.5918 17.35 0 0 -4.5848 17.4182 0 0 -4.5672 17.5069 0 0 -4.538 17.5942 0 0 -4.5371 17.7204 0 0 -4.4417 17.8218 0 0 -4.3466 17.81 0 0 -4.2516 17.7956 0 0 -4.1664 17.7815 0 0 -4.0803 17.7652 0 0 -3.9955 17.7466 0 0 -3.9119 17.7261 0 0 -3.829 17.7034 0 0 -3.7458 17.6778 0 0 -3.6613 17.6496 0 0 -3.668 17.6522 0 0 -3.7003 17.56 0 0 -3.7314 17.471 0 0 -3.7557 17.3833 0 0 -3.774 17.2956 0 0 -3.7919 17.1983 0 0 -3.8076 17.1109 0 0 -3.8217 17.0321 0 0 -3.8396 16.9672 0 0 -3.862 16.9166 0 0 -3.8818 16.8807 0 0 -3.8575 16.8273 0 0 -3.8582 16.721 0 0 -3.8636 16.674 0 0 -3.8617 16.5869 0 0 -3.8149 16.5398 0 0 -3.79 16.4457 0 0 -3.8486 16.3836 0 0 -3.861 16.27 0 0 -3.869 16.1756 0 0 -3.8201 16.116 0 0 -3.7708 16.06 0 0 -3.7525 15.9105 0 0 -3.6533 15.9 0 0 -3.564 15.8881 0 0 -3.5601 15.7898 0 0 -3.4577 15.777 0 0 -3.3777 15.7678 0 0 -3.2993 15.7559 0 0 -3.2352 15.6567 0 0 -3.2372 15.5731 0 0 -3.145 15.5594 0 0 -3.0787 15.4717 0 0 -3.1059 15.3712 0 0 -3.0883 15.2143 0 0 -3.0288 15.1288 0 0 -2.9737 15.0328 0 0 -2.9135 14.9297 0 0 -2.8271 14.8376 0 0 -2.7455 14.7354 0 0 -2.6735 14.6513 0 0 -2.5966 14.5687 0 0 -2.4968 14.52 0 0 -2.3601 14.5142 0 0 -2.2404 14.3795 0 0 -2.1459 14.2921 0 0 -2.0185 14.1999 0 0 -1.8559 14.1058 0 0 -1.6773 14.1705 0 0 -1.517 14.2188 0 0 -1.3662 14.2563 0 0 -1.2349 14.2995 0 0 -1.1703 14.3856 0 0 -1.0358 14.3917 0 0 -0.9632 14.4576 0 0 -0.8841 14.5162 0 0 -0.8002 14.5664 0 0 -0.7113 14.6087 0 0 -0.6188 14.6416 0 0 -0.5234 14.6657 0 0 -0.4264 14.6801 0 0 -0.3284 14.6849 0 0 -0.331 14.7937 0 0 -0.3348 14.9032 0 0 -0.3303 15.028 0 0 -0.3252 15.1522 0 0 -0.3105 15.2585 0 0 -0.2023 15.3475 0 0 -0.1873 15.4419 0 0 -0.1876 15.4416 0 0 -0.1946 15.4413 0 0 -0.0983 15.4339 0 0 -0.0983 15.4317 0 0 -0.098 15.4291 0 0 -0.098 15.4256 0 0 -0.0976 15.4214 0 0 -0.1921 15.4221 0 0 -0.2753 15.4249 0 0 -0.2692 15.4192 0 0 -0.3447 15.4249 0 0 -0.2769 15.4153 0 0 -0.2964 15.4118 0 0 -0.3191 15.4083 0 0 -0.2202 15.3929 0 0 -0.2663 15.3913 0 0 -0.2881 15.3744 0 0 -0.298 15.3641 0 0 -0.3972 15.3641 0 0 -0.4891 15.3555 0 0 -0.5784 15.3462 0 0 -0.6648 15.3363 0 0 -0.7494 15.3257 0 0 -0.8313 15.3145 0 0 -0.9104 15.3027 0 0 -1.0019 15.3027 0 0 -1.0938 15.3027 0 0 -1.1674 15.2902 0 0 -1.2586 15.2902 0 0 -1.3495 15.2902 0 0 -1.4408 15.2902 0 0 -1.5317 15.2905 0 0 -1.6229 15.2905 0 0 -1.7138 15.2905 0 0 -1.8047 15.2905 0 0 -1.8729 15.277 0 0 -1.9638 15.277 0 0 -2.0544 15.277 0 0 -2.145 15.2774 0 0 -2.2356 15.2774 0 0 -2.3255 15.2774 0 0 -2.4148 15.2774 0 0 -2.5032 15.2777 0 0 -2.5902 15.2777 0 0 -2.6751 15.278 0 0 -2.7567 15.2783 0 0 -2.7561 15.1797 0 0 -2.7586 15.0821 0 0 -2.7849 14.9992 0 0 -2.8786 14.9944 0 0 -2.9081 15.0104 0 0 -3.0006 15.0053 0 0 -3.0329 15.02 0 0 -3.0694 15.0347 0 0 -3.1098 15.0491 0 0 -3.1542 15.0632 0 0 -3.202 15.077 0 0 -3.2538 15.0901 0 0 -3.3092 15.1029 0 0 -3.3678 15.1154 0 0 -3.4299 15.1272 0 0 -3.4948 15.1384 0 0 -3.563 15.149 0 0 -3.6341 15.1592 0 0 -3.708 15.1689 0 0 -3.7839 15.1778 0 0 -3.8623 15.1861 0 0 -3.9433 15.1938 0 0 -3.9846 15.2159 0 0 -4.0736 15.2226 0 0 -4.1642 15.2284 0 0 -4.2276 15.2498 0 0 -4.2276 15.3497 0 0 -4.3255 15.3542 0 0 -4.3268 15.4601 0 0 -4.3268 15.5597 0 0 -4.4254 15.5632 0 0 -4.402 15.5735 0 0 -4.3665 15.5856 0 0 -4.3229 15.5991 0 0 -4.2829 15.6119 0 0 -4.2583 15.6234 0 0 -4.2666 15.6333 0 0 -4.2663 15.7261 0 0 -4.3582 15.7293 0 0 -4.4507 15.7325 0 0 -4.5432 15.7357 0 0 -4.6357 15.7396 0 0 -4.6325 15.8366 0 0 -4.7263 15.8436 0 0 -4.7263 15.9435 0 0 -4.7263 16.0434 0 0 -4.7263 16.1432 0 0 -4.7263 16.2431 0 0 -4.7263 16.343 0 0 -4.7263 16.4428 0 0 -4.8277 16.4432 0 0 -4.8277 16.4605 0 0 -4.8277 16.5645 0 0 -4.8277 16.6637 0 0 -4.8277 16.8164 0 0 -4.8277 16.8942 0 0 -4.8277 16.9691 0 0 -4.8277 17.0622 0 0 -4.8277 17.1583 0 0 -4.8277 17.2421 0 0 -4.7365 17.3433 0 0 -4.6584 17.3503 0 0 -4.6571 17.4323 0 0 -4.6472 17.5232 0 0 -4.6328 17.6205 0 0 -4.6337 17.7357 0 0 -4.5371 17.8311 0 0 -4.4484 17.8244 0 0 -4.3543 17.8125 0 0 -4.2602 17.7985 0 0 -4.1728 17.7844 0 0 -4.0877 17.7681 0 0 -4.0035 17.7495 0 0 -3.9206 17.7293 0 0 -3.8377 17.7066 0 0 -3.7535 17.6807 0 0 -3.7618 17.6835 0 0 -3.7916 17.591 0 0 -3.8194 17.5011 0 0 -3.8412 17.4127 0 0 -3.8562 17.3244 0 0 -3.8687 17.2248 0 0 -3.8793 17.1359 0 0 -3.8882 17.0568 0 0 -3.8985 16.987 0 0 -3.9074 16.9326 0 0 -3.9161 16.8836 0 0 -3.9186 16.833 0 0 -3.9202 16.7447 0 0 -3.9218 16.6503 0 0 -3.9859 16.5363 0 0 -3.9081 16.5091 0 0 -3.8508 16.4835 0 0 -3.8786 16.4432 0 0 -3.9321 16.3766 0 0 -3.9407 16.2729 0 0 -3.9407 16.1743 0 0 -3.8713 16.1051 0 0 -3.8617 16.0267 0 0 -3.8482 15.8958 0 0 -3.7439 15.7802 0 0 -3.6495 15.7937 0 0 -3.6533 15.6961 0 0 -3.5601 15.6945 0 0 -3.451 15.6823 0 0 -3.3738 15.6746 0 0 -3.3073 15.6663 0 0 -3.3037 15.5811 0 0 -3.2989 15.5085 0 0 -3.2445 15.5011 0 0 -3.1626 15.4835 0 0 -3.1818 15.4032 0 0 -3.1607 15.2732 0 0 -3.1815 15.1432 0 0 -3.1472 15.0712 0 0 -3.112 14.98 0 0 -3.0717 14.8359 0 0 -2.9263 14.7146 0 0 -2.8415 14.6218 0 0 -2.7727 14.5367 0 0 -2.7016 14.4272 0 0 -2.5531 14.3545 0 0 -2.4404 14.2988 0 0 -2.3409 14.2172 0 0 -2.2509 14.1353 0 0 -2.1312 13.9983 0 0 -1.9164 13.9323 0 0 -1.7314 14.0376 0 0 -1.5688 14.1074 0 0 -1.418 14.1535 0 0 -1.291 14.2076 0 0 -1.16 14.2399 0 0 -1.1015 14.319 0 0 -1.1015 14.319 0 0 -1.0355 14.3917 0 0 -0.9629 14.4576 0 0 -0.8838 14.5159 0 0 -0.7996 14.5664 0 0 -0.7109 14.6084 0 0 -0.6188 14.6413 0 0 -0.5234 14.6653 0 0 -0.4264 14.6797 0 0 -0.3284 14.6845 0 0 -0.2372 14.6842 0 0 -0.2189 14.6849 0 0 -0.2189 14.7934 0 0 -0.2183 14.9009 0 0 -0.2209 15.0069 0 0 -0.2135 15.1333 0 0 -0.2119 15.2444 0 0 -0.1053 15.3385 0 0 -0.0938 15.4368 0 0 -0.0973 15.4365 0 0 -0.0983 15.4355 0 0 -0 15.4336 0 0 -0 15.432 0 0 -0 15.4297 0 0 -0 15.4269 0 0 -0 15.4237 0 0 -0 15.4195 0 0 -0.0973 15.4166 0 0 -0.1882 15.4163 0 0 -0.1793 15.4105 0 0 -0.1796 15.4051 0 0 -0.1751 15.3952 0 0 -0.1287 15.3862 0 0 -0.1527 15.3836 0 0 -0.1802 15.3798 0 0 -0.1991 15.3708 0 0 -0.1985 15.3641 0 0 -0.2935 15.3551 0 0 -0.3912 15.3551 0 0 -0.4817 15.3459 0 0 -0.5701 15.3359 0 0 -0.6556 15.3257 0 0 -0.7388 15.3145 0 0 -0.8188 15.3027 0 0 -0.8944 15.2902 0 0 -0.9853 15.2902 0 0 -1.0765 15.2902 0 0 -1.1463 15.277 0 0 -1.2372 15.277 0 0 -1.3281 15.277 0 0 -1.4187 15.277 0 0 -1.5096 15.277 0 0 -1.6005 15.277 0 0 -1.6914 15.277 0 0 -1.7823 15.277 0 0 -1.8489 15.2633 0 0 -1.9401 15.2633 0 0 -2.031 15.2633 0 0 -2.1219 15.2633 0 0 -2.2125 15.2633 0 0 -2.3028 15.2633 0 0 -2.3927 15.2633 0 0 -2.4814 15.2633 0 0 -2.5688 15.2636 0 0 -2.6539 15.2639 0 0 -2.7359 15.2642 0 0 -2.7356 15.1653 0 0 -2.7372 15.0648 0 0 -2.7586 14.9822 0 0 -2.8537 14.9784 0 0 -2.9484 14.9742 0 0 -2.9724 14.9899 0 0 -3.0659 14.9851 0 0 -3.0928 14.9995 0 0 -3.1235 15.0139 0 0 -3.1581 15.0277 0 0 -3.1965 15.0415 0 0 -3.2384 15.0549 0 0 -3.2842 15.0677 0 0 -3.3332 15.0802 0 0 -3.3857 15.0927 0 0 -3.4417 15.1042 0 0 -3.5006 15.1154 0 0 -3.5624 15.1263 0 0 -3.627 15.1362 0 0 -3.6946 15.1458 0 0 -3.7644 15.1551 0 0 -3.8367 15.1634 0 0 -3.9113 15.1714 0 0 -3.9881 15.1788 0 0 -4.0259 15.2005 0 0 -4.1104 15.2069 0 0 -4.1965 15.2124 0 0 -4.2557 15.2335 0 0 -4.3255 15.2543 0 0 -4.4244 15.2582 0 0 -4.4247 15.358 0 0 -4.4254 15.464 0 0 -4.5249 15.4669 0 0 -4.5249 15.5664 0 0 -4.4935 15.5773 0 0 -4.4455 15.5907 0 0 -4.3924 15.6055 0 0 -4.3489 15.6183 0 0 -4.3265 15.6285 0 0 -4.3588 15.6368 0 0 -4.4519 15.6394 0 0 -4.5454 15.6416 0 0 -4.6389 15.6429 0 0 -4.7279 15.7437 0 0 -4.8277 15.8439 0 0 -4.8277 15.9438 0 0 -4.8277 16.0437 0 0 -4.8277 16.1436 0 0 -4.8277 16.2434 0 0 -4.8277 16.3433 0 0 -4.8277 16.3606 0 0 -4.8277 16.3715 0 0 -4.8277 16.4701 0 0 -4.8277 16.5686 0 0 -4.8277 16.6672 0 0 -4.8277 16.7825 0 0 -4.8277 16.9041 0 0 -4.8277 16.9259 0 0 -4.8277 16.9803 0 0 -4.8277 17.0725 0 0 -4.8277 17.1666 0 0 -4.8277 17.2597 0 0 -4.8277 17.342 0 0 -4.8277 17.4419 0 0 -4.7371 17.4409 0 0 -4.7282 17.5414 0 0 -4.7295 17.6362 0 0 -4.7311 17.7476 0 0 -4.6325 17.8378 0 0 -4.5432 17.8334 0 0 -4.4529 17.826 0 0 -4.3594 17.8148 0 0 -4.266 17.8007 0 0 -4.1799 17.7866 0 0 -4.0953 17.7706 0 0 -4.0128 17.7527 0 0 -3.9299 17.7322 0 0 -3.8463 17.7095 0 0 -3.8562 17.7124 0 0 -3.8831 17.6189 0 0 -3.9084 17.5283 0 0 -3.927 17.4396 0 0 -3.9391 17.3506 0 0 -3.9478 17.2505 0 0 -3.9555 17.1608 0 0 -3.9609 17.0805 0 0 -3.968 17.0059 0 0 -3.9727 16.9492 0 0 -3.9769 16.8977 0 0 -3.9868 16.8475 0 0 -4.0163 16.7902 0 0 -4.088 16.7079 0 0 -4.1168 16.5626 0 0 -4.0083 16.4537 0 0 -4.0227 16.3734 0 0 -4.0393 16.2729 0 0 -4.0393 16.1743 0 0 -3.9423 16.093 0 0 -3.9439 16.0078 0 0 -3.9407 15.8785 0 0 -3.8421 15.7802 0 0 -3.7439 15.6817 0 0 -3.6507 15.5965 0 0 -3.5598 15.6016 0 0 -3.4436 15.5917 0 0 -3.3662 15.5863 0 0 -3.3524 15.5078 0 0 -3.3005 15.4697 0 0 -3.2624 15.4451 0 0 -3.2304 15.3254 0 0 -3.2336 15.1954 0 0 -3.2276 15.1106 0 0 -3.2234 15.0562 0 0 -3.2218 14.9649 0 0 -3.226 14.8513 0 0 -3.129 14.6871 0 0 -2.9932 14.6173 0 0 -2.9036 14.5459 0 0 -2.8422 14.4845 0 0 -2.8162 14.4035 0 0 -2.8156 14.2819 0 0 -2.7112 14.2505 0 0 -2.5966 14.2082 0 0 -2.4968 14.1554 0 0 -2.4075 14.0981 0 0 -2.3361 14.037 0 0 -2.2727 13.9429 0 0 -2.111 13.7953 0 0 -1.9417 13.7719 0 0 -1.7785 13.8872 0 0 -1.6146 13.9861 0 0 -1.4622 14.0479 0 0 -1.3374 14.1109 0 0 -1.2103 14.1557 0 0 -1.2103 14.1557 0 0 -1.1597 14.2399 0 0 -1.1597 14.2386 0 0 -1.1015 14.3174 0 0 -1.0355 14.3904 0 0 -0.9629 14.4563 0 0 -0.8838 14.5146 0 0 -0.7996 14.5651 0 0 -0.7109 14.6071 0 0 -0.6188 14.64 0 0 -0.5234 14.6641 0 0 -0.4264 14.6785 0 0 -0.3284 14.6833 0 0 -0.2551 14.6836 0 0 -0.1921 14.6836 0 0 -0.1725 14.6845 0 0 -0.1095 14.6849 0 0 -0.1095 14.7927 0 0 -0.1079 14.899 0 0 -0.1079 15.0062 0 0 -0.1085 15.1135 0 0 -0.104 15.2316 0 0 -0 15.3276 0 0 -0 15.4349 0 0 -0 15.4345 0 0 -0 15.3321 0 0 -0 15.3321 0 0 -0 15.3305 0 0 -0 15.3283 0 0 -0 15.3251 0 0 -0 15.3212 0 0 -0 15.3164 0 0 -0 15.4147 0 0 -0.0963 15.4112 0 0 -0.0947 15.4051 0 0 -0 15.4035 0 0 -0 15.3968 0 0 -0 15.3897 0 0 -0.0739 15.3836 0 0 -0.0813 15.3782 0 0 -0.0941 15.3708 0 0 -0.1309 15.3676 0 0 -0.0992 15.3641 0 0 -0.1956 15.3548 0 0 -0.289 15.3452 0 0 -0.3854 15.3455 0 0 -0.475 15.3356 0 0 -0.5618 15.3254 0 0 -0.646 15.3145 0 0 -0.7269 15.3027 0 0 -0.8034 15.2902 0 0 -0.8739 15.2767 0 0 -0.9645 15.277 0 0 -1.0554 15.277 0 0 -1.12 15.263 0 0 -1.2109 15.263 0 0 -1.3022 15.263 0 0 -1.3934 15.263 0 0 -1.4843 15.263 0 0 -1.5755 15.263 0 0 -1.6667 15.263 0 0 -1.7577 15.2633 0 0 -1.823 15.2486 0 0 -1.9148 15.2486 0 0 -2.0067 15.2486 0 0 -2.0982 15.2486 0 0 -2.1898 15.2486 0 0 -2.281 15.2486 0 0 -2.3716 15.2486 0 0 -2.4616 15.2489 0 0 -2.5502 15.2489 0 0 -2.6363 15.2489 0 0 -2.7196 15.2492 0 0 -2.7192 15.1503 0 0 -2.7205 15.0475 0 0 -2.7372 14.9649 0 0 -2.8332 14.9621 0 0 -2.9289 14.9589 0 0 -3.0432 14.9704 0 0 -3.1594 14.9803 0 0 -3.1847 14.9941 0 0 -3.2138 15.0075 0 0 -3.2464 15.0206 0 0 -3.2826 15.0338 0 0 -3.3226 15.0463 0 0 -3.3655 15.0584 0 0 -3.4123 15.0706 0 0 -3.4619 15.0818 0 0 -3.5147 15.093 0 0 -3.5704 15.1036 0 0 -3.629 15.1138 0 0 -3.6901 15.1234 0 0 -3.7541 15.1327 0 0 -3.8204 15.141 0 0 -3.8889 15.1493 0 0 -3.9593 15.1567 0 0 -4.032 15.1634 0 0 -4.1062 15.1698 0 0 -4.0662 15.1852 0 0 -4.1466 15.1913 0 0 -4.2282 15.1964 0 0 -4.2836 15.2172 0 0 -4.3492 15.238 0 0 -4.4436 15.2415 0 0 -4.5246 15.261 0 0 -4.5246 15.3612 0 0 -4.6251 15.3638 0 0 -4.6248 15.4691 0 0 -4.6248 15.5683 0 0 -4.5796 15.5811 0 0 -4.5134 15.5975 0 0 -4.45 15.6131 0 0 -4.4026 15.625 0 0 -4.3751 15.6324 0 0 -4.4388 15.632 0 0 -4.5249 15.6314 0 0 -4.621 15.6301 0 0 -4.7227 15.6282 0 0 -4.7333 15.6439 0 0 -4.8277 15.7441 0 0 -4.8277 15.7802 0 0 -4.8277 15.8785 0 0 -4.8277 15.9829 0 0 -4.8277 16.0053 0 0 -4.8277 16.0619 0 0 -4.8277 16.1592 0 0 -4.8277 16.2601 0 0 -4.8277 16.2729 0 0 -4.8277 16.2729 0 0 -4.8277 16.3715 0 0 -4.8277 16.4701 0 0 -4.8277 16.5686 0 0 -4.8277 16.6672 0 0 -4.8277 16.7658 0 0 -4.8277 16.8641 0 0 -4.8277 16.8836 0 0 -4.8277 16.9627 0 0 -4.8277 16.9764 0 0 -4.8277 17.0725 0 0 -4.8277 17.1618 0 0 -4.8277 17.2623 0 0 -4.8277 17.3618 0 0 -4.8277 17.3596 0 0 -4.8277 17.4595 0 0 -4.8277 17.5417 0 0 -4.8277 17.6416 0 0 -4.8277 17.7424 0 0 -4.8277 17.8436 0 0 -4.7282 17.842 0 0 -4.6379 17.8401 0 0 -4.5464 17.8353 0 0 -4.4577 17.8276 0 0 -4.3649 17.8164 0 0 -4.2724 17.8029 0 0 -4.1875 17.7892 0 0 -4.1049 17.7735 0 0 -4.023 17.7556 0 0 -3.9401 17.7351 0 0 -3.9513 17.7383 0 0 -3.9756 17.6442 0 0 -3.998 17.5533 0 0 -4.0137 17.464 0 0 -4.022 17.375 0 0 -4.0265 17.2738 0 0 -4.0297 17.1823 0 0 -4.0307 17.0978 0 0 -4.0326 17.0197 0 0 -4.0339 16.9579 0 0 -4.031 16.9054 0 0 -4.0291 16.8663 0 0 -4.0681 16.8423 0 0 -4.1389 16.811 0 0 -4.2359 16.681 0 0 -4.2365 16.5686 0 0 -4.1299 16.4627 0 0 -4.1379 16.3715 0 0 -4.1379 16.2729 0 0 -4.1379 16.1743 0 0 -4.0393 16.0757 0 0 -4.039 15.9921 0 0 -4.0393 15.8785 0 0 -3.9407 15.7802 0 0 -3.8421 15.6817 0 0 -3.7442 15.5827 0 0 -3.652 15.497 0 0 -3.5585 15.5085 0 0 -3.4321 15.5082 0 0 -3.4113 15.4269 0 0 -3.3373 15.4509 0 0 -3.3057 15.3961 0 0 -3.3719 15.343 0 0 -3.3047 15.2518 0 0 -3.2749 15.1352 0 0 -3.2964 15.0667 0 0 -3.3204 14.9784 0 0 -3.3489 14.8606 0 0 -3.2519 14.7271 0 0 -3.1674 14.5831 0 0 -3.0425 14.5296 0 0 -2.9468 14.4816 0 0 -2.8927 14.4544 0 0 -2.893 14.3955 0 0 -2.8998 14.3017 0 0 -2.9164 14.1865 0 0 -2.8217 14.1634 0 0 -2.7237 14.141 0 0 -2.6235 14.1135 0 0 -2.5281 14.0735 0 0 -2.4459 14.0239 0 0 -2.3895 13.9813 0 0 -2.3537 13.9147 0 0 -2.3354 13.8065 0 0 -2.2426 13.803 0 0 -2.2298 13.6676 0 0 -2.1149 13.6359 0 0 -1.9913 13.5914 0 0 -1.8015 13.6071 0 0 -1.7983 13.7492 0 0 -1.6533 13.8481 0 0 -1.5003 13.9378 0 0 -1.3745 14.0098 0 0 -1.2522 14.0671 0 0 -1.2522 14.0671 0 0 -1.2522 14.0658 0 0 -1.2103 14.1548 0 0 -1.2103 14.1525 0 0 -1.1597 14.2367 0 0 -1.1015 14.3155 0 0 -1.0355 14.3881 0 0 -0.9629 14.4541 0 0 -0.8838 14.5126 0 0 -0.7996 14.5629 0 0 -0.7109 14.6048 0 0 -0.6188 14.6381 0 0 -0.5234 14.6618 0 0 -0.4264 14.6762 0 0 -0.3284 14.681 0 0 -0.2606 14.6823 0 0 -0.1949 14.6829 0 0 -0.144 14.6836 0 0 -0.0893 14.6845 0 0 -0 14.6849 0 0 -0 14.7921 0 0 -0 14.899 0 0 -0 15.0062 0 0 -0 15.1135 0 0 -0 15.2204 0 0 -0 15.3308 0 0 -0 15.2188 0 0 -0 15.2261 0 0 -0 15.2303 0 0 -0 15.2313 0 0 -0 15.2297 0 0 -0 15.2271 0 0 -0 15.2236 0 0 -0 15.2188 0 0 -0 15.214 0 0 -0 15.3113 0 0 -0 15.4096 0 0 -0 15.3055 0 0 -0 15.2988 0 0 -0 15.2918 0 0 -0 15.3817 0 0 -0 15.3734 0 0 -0 15.3641 0 0 -0.0976 15.3545 0 0 -0.1927 15.3446 0 0 -0.2849 15.3347 0 0 -0.38 15.3353 0 0 -0.468 15.3251 0 0 -0.4609 15.3139 0 0 -0.5438 15.302 0 0 -0.6213 15.2898 0 0 -0.6921 15.2767 0 0 -0.7554 15.263 0 0 -0.8115 15.2482 0 0 -0.9036 15.2482 0 0 -0.9955 15.2482 0 0 -1.0493 15.2329 0 0 -1.1424 15.2329 0 0 -1.2356 15.2329 0 0 -1.3287 15.2329 0 0 -1.4219 15.2332 0 0 -1.5153 15.2332 0 0 -1.6085 15.2332 0 0 -1.7017 15.2332 0 0 -1.765 15.2172 0 0 -1.8598 15.2172 0 0 -1.9545 15.2172 0 0 -2.0493 15.2172 0 0 -2.1437 15.2172 0 0 -2.2381 15.2172 0 0 -2.3322 15.2172 0 0 -2.426 15.2172 0 0 -2.5192 15.2172 0 0 -2.6114 15.2172 0 0 -2.701 15.2172 0 0 -2.701 15.117 0 0 -2.7013 15.013 0 0 -2.7013 14.9128 0 0 -2.7084 14.9304 0 0 -2.8054 14.9288 0 0 -2.9023 14.9269 0 0 -3.0099 14.9403 0 0 -3.1203 14.9525 0 0 -3.2324 14.9621 0 0 -3.2506 14.8724 0 0 -3.274 14.8852 0 0 -3.3005 14.898 0 0 -3.3306 14.9102 0 0 -3.3639 14.9224 0 0 -3.4004 14.9342 0 0 -3.4401 14.9457 0 0 -3.483 14.9569 0 0 -3.5288 14.9678 0 0 -3.5768 14.9781 0 0 -3.6264 14.9877 0 0 -3.6747 14.9963 0 0 -3.7122 15.0027 0 0 -3.7644 15.0107 0 0 -3.8229 15.019 0 0 -3.885 15.0267 0 0 -3.9497 15.0344 0 0 -4.016 15.0411 0 0 -4.0841 15.0475 0 0 -4.1539 15.0533 0 0 -4.2253 15.0587 0 0 -4.2906 15.1644 0 0 -4.3652 15.1685 0 0 -4.4407 15.1721 0 0 -4.4183 15.1887 0 0 -4.499 15.1916 0 0 -4.4807 15.2082 0 0 -4.5528 15.2274 0 0 -4.6347 15.2463 0 0 -4.7311 15.2476 0 0 -4.8277 15.2649 0 0 -4.8277 15.3667 0 0 -4.8277 15.4681 0 0 -4.8277 15.5699 0 0 -4.8277 15.5885 0 0 -4.6878 15.5987 0 0 -4.8277 15.6071 0 0 -4.8277 15.5018 0 0 -4.8277 15.5098 0 0 -4.8277 15.6096 0 0 -4.8277 15.5831 0 0 -4.7291 15.5831 0 0 -4.6305 15.5831 0 0 -4.6305 15.6817 0 0 -4.6305 15.7802 0 0 -4.6305 15.8785 0 0 -4.7291 15.9771 0 0 -4.7291 16.0757 0 0 -4.6305 16.0757 0 0 -4.6305 16.1743 0 0 -4.6305 16.2729 0 0 -4.6305 16.3715 0 0 -4.6305 16.4701 0 0 -4.6305 16.5686 0 0 -4.6305 16.6672 0 0 -4.6305 16.7658 0 0 -4.6305 16.8641 0 0 -4.7291 16.9627 0 0 -4.8277 17.0613 0 0 -4.8277 17.1599 0 0 -4.8277 17.2585 0 0 -4.8277 17.357 0 0 -4.8277 17.4556 0 0 -4.8277 17.5545 0 0 -4.8277 17.6531 0 0 -4.8277 17.6474 0 0 -4.8277 17.7463 0 0 -4.8277 17.8474 0 0 -4.7333 17.8458 0 0 -4.6408 17.843 0 0 -4.5528 17.8375 0 0 -4.4718 17.8308 0 0 -4.3822 17.8206 0 0 -4.29 17.8074 0 0 -4.2096 17.7943 0 0 -4.129 17.7793 0 0 -4.143 17.7821 0 0 -4.1619 17.6868 0 0 -4.1786 17.5952 0 0 -4.1875 17.5056 0 0 -4.1885 17.4163 0 0 -4.1853 17.3132 0 0 -4.1811 17.2172 0 0 -4.1776 17.132 0 0 -4.1731 17.0478 0 0 -4.1693 16.9704 0 0 -4.2522 16.9774 0 0 -4.3351 16.9627 0 0 -4.3351 16.8641 0 0 -4.4334 16.8641 0 0 -4.4334 16.7658 0 0 -4.4334 16.6672 0 0 -4.4334 16.5686 0 0 -4.3351 16.4701 0 0 -4.3351 16.3715 0 0 -4.3351 16.2729 0 0 -4.3351 16.1743 0 0 -4.2365 16.0757 0 0 -4.2365 15.9771 0 0 -4.2365 15.8785 0 0 -4.1379 15.7802 0 0 -4.0393 15.6817 0 0 -3.9407 15.5831 0 0 -3.8421 15.4845 0 0 -3.7458 15.3856 0 0 -3.7477 15.287 0 0 -3.6418 15.2735 0 0 -3.532 15.1208 0 0 -3.5665 15.0258 0 0 -3.6123 14.9201 0 0 -3.5006 14.7518 0 0 -3.4074 14.6116 0 0 -3.3133 14.4778 0 0 -3.224 14.3625 0 0 -3.1002 14.3392 0 0 -3.1299 14.2342 0 0 -3.1469 14.1263 0 0 -3.1613 14.0229 0 0 -3.0569 14.0069 0 0 -2.952 13.9877 0 0 -2.8476 13.9627 0 0 -2.7567 13.9384 0 0 -2.6645 13.9192 0 0 -2.5784 13.9128 0 0 -2.4827 13.9003 0 0 -2.4891 13.8142 0 0 -2.4939 13.7086 0 0 -2.4987 13.6119 0 0 -2.4075 13.6042 0 0 -2.4158 13.5015 0 0 -2.3166 13.4989 0 0 -2.2167 13.4867 0 0 -2.1203 13.4774 0 0 -2.0467 13.4682 0 0 -2.0442 13.3555 0 0 -1.8963 13.3132 0 0 -1.767 13.3484 0 0 -1.6671 13.4787 0 0 -1.5461 13.5805 0 0 -1.54 13.7038 0 0 -1.426 13.7921 0 0 -1.3092 13.8792 0 0 -1.3092 13.8795 0 0 -1.3092 13.8782 0 0 -1.3092 13.8763 0 0 -1.3092 13.8734 0 0 -1.2852 13.9688 0 0 -1.2852 13.965 0 0 -1.2522 14.0575 0 0 -1.2103 14.1461 0 0 -1.1597 14.23 0 0 -1.1015 14.3091 0 0 -1.0355 14.3817 0 0 -0.9629 14.4473 0 0 -0.8838 14.5059 0 0 -0.7996 14.5562 0 0 -0.7109 14.5981 0 0 -0.6188 14.6311 0 0 -0.5234 14.6551 0 0 -0.4264 14.6695 0 0 -0.3284 14.6743 0 0 -0.2625 14.6769 0 0 -0.1969 14.6788 0 0 -0.1328 14.6807 0 0 -0.0698 14.6823 0 0 -0 14.6836 0 0 -0 14.7787 0 0 -0 14.8779 0 0 -0 14.9864 0 0 -0 14.9794 0 0 -0 14.9851 0 0 -0 14.9848 0 0 -0 14.9842 0 0 -0 14.981 0 0 -0 14.9768 0 0 -0 14.972 0 0 -0 15.0988 0 0 -0 15.2015 0 0 -0 15.1945 0 0 -0 15.2841 0 0 -0 15.2754 0 0 -0 15.2665 0 0 -0 15.3542 0 0 -0.0963 15.3443 0 0 -0.1898 15.334 0 0 -0.2807 15.3238 0 0 -0.3745 15.3244 0 0 -0.3684 15.3129 0 0 -0.4523 15.3017 0 0 -0.5304 15.2895 0 0 -0.6015 15.2767 0 0 -0.6642 15.263 0 0 -0.7196 15.2482 0 0 -0.7698 15.2329 0 0 -0.863 15.2329 0 0 -0.9561 15.2329 0 0 -1.007 15.2169 0 0 -1.1018 15.2169 0 0 -1.1965 15.2169 0 0 -1.2913 15.2169 0 0 -1.386 15.2169 0 0 -1.4808 15.2169 0 0 -1.5755 15.2172 0 0 -1.6703 15.2172 0 0 -1.7349 15.2002 0 0 -1.8313 15.2002 0 0 -1.9276 15.2002 0 0 -2.024 15.2002 0 0 -2.1203 15.2002 0 0 -2.2167 15.2002 0 0 -2.313 15.2002 0 0 -2.4097 15.2002 0 0 -2.5061 15.2002 0 0 -2.6024 15.2002 0 0 -2.6988 15.2002 0 0 -2.6988 15.0984 0 0 -2.6988 14.997 0 0 -2.6988 14.8952 0 0 -2.7986 14.9118 0 0 -2.8959 14.9112 0 0 -2.9997 14.9253 0 0 -3.1062 14.9381 0 0 -3.2157 14.9489 0 0 -3.2311 14.8596 0 0 -3.2484 14.7697 0 0 -3.2711 14.7825 0 0 -3.297 14.795 0 0 -3.3265 14.8071 0 0 -3.3588 14.8193 0 0 -3.3946 14.8308 0 0 -3.4337 14.8424 0 0 -3.4756 14.8536 0 0 -3.5205 14.8644 0 0 -3.5681 14.8747 0 0 -3.6174 14.8846 0 0 -3.6674 14.8936 0 0 -3.7164 14.9019 0 0 -3.7711 14.9099 0 0 -3.8306 14.9182 0 0 -3.8927 14.9262 0 0 -3.9571 14.9336 0 0 -4.0233 14.9403 0 0 -4.0912 14.9467 0 0 -4.1603 14.9525 0 0 -4.2314 14.9576 0 0 -4.298 15.0632 0 0 -4.3719 15.0674 0 0 -4.4468 15.0706 0 0 -4.5169 15.1749 0 0 -4.5941 15.1772 0 0 -4.5803 15.1938 0 0 -4.5665 15.2108 0 0 -4.644 15.2294 0 0 -4.7359 15.2306 0 0 -4.8277 15.2479 0 0 -4.8277 15.1788 0 0 -4.8277 15.2217 0 0 -4.8277 15.3001 0 0 -4.8277 15.3843 0 0 -4.8277 15.4864 0 0 -4.8277 15.4938 0 0 -4.8277 15.3897 0 0 -4.8277 15.3888 0 0 -4.8277 15.4931 0 0 -4.8277 15.4845 0 0 -4.7291 15.4845 0 0 -4.6305 15.4845 0 0 -4.532 15.4845 0 0 -4.532 15.5831 0 0 -4.532 15.6817 0 0 -4.532 15.7802 0 0 -4.532 15.8785 0 0 -4.6305 15.9771 0 0 -4.532 15.9771 0 0 -4.532 16.0757 0 0 -4.532 16.1743 0 0 -4.532 16.2729 0 0 -4.532 16.3715 0 0 -4.532 16.4701 0 0 -4.532 16.5686 0 0 -4.4334 16.4701 0 0 -4.4334 16.3715 0 0 -4.4334 16.2729 0 0 -4.4334 16.1743 0 0 -4.3351 16.0757 0 0 -4.3351 15.9771 0 0 -4.3351 15.8785 0 0 -4.2365 15.7802 0 0 -4.1379 15.6817 0 0 -4.0393 15.5831 0 0 -3.9407 15.4845 0 0 -3.8421 15.3859 0 0 -3.8425 15.287 0 0 -3.8428 15.1884 0 0 -3.7532 15.1781 0 0 -3.6613 15.1608 0 0 -3.6792 15.0539 0 0 -3.7144 14.9563 0 0 -3.7637 14.8843 0 0 -3.6818 14.7943 0 0 -3.6546 14.618 0 0 -3.5272 14.6205 0 0 -3.435 14.4918 0 0 -3.3351 14.3728 0 0 -3.2391 14.2489 0 0 -3.2528 14.1407 0 0 -3.2653 14.0363 0 0 -3.2775 13.9333 0 0 -3.1744 13.9208 0 0 -3.0714 13.907 0 0 -2.9683 13.891 0 0 -2.8652 13.8725 0 0 -2.7628 13.85 0 0 -2.6696 13.828 0 0 -2.5822 13.812 0 0 -2.5835 13.7204 0 0 -2.5938 13.6276 0 0 -2.6095 13.5312 0 0 -2.5096 13.5146 0 0 -2.5265 13.416 0 0 -2.4276 13.3984 0 0 -2.323 13.3926 0 0 -2.2144 13.3859 0 0 -2.1255 13.3709 0 0 -2.1418 13.2646 0 0 -2.0554 13.2563 0 0 -1.943 13.2227 0 0 -1.8175 13.214 0 0 -1.7378 13.2431 0 0 -1.6539 13.2607 0 0 -1.6597 13.3657 0 0 -1.5442 13.4736 0 0 -1.4331 13.5789 0 0 -1.4308 13.6874 0 0 -1.3236 13.7822 0 0 -1.3236 13.7825 0 0 -1.3236 13.7812 0 0 -1.3236 13.7793 0 0 -1.3236 13.7764 0 0 -1.3236 13.7729 0 0 -1.3092 13.8699 0 0 -1.3092 13.8654 0 0 -1.2852 13.9608 0 0 -1.2522 14.053 0 0 -1.2103 14.1417 0 0 -1.1597 14.2255 0 0 -1.1015 14.3046 0 0 -1.0355 14.3769 0 0 -0.9629 14.4429 0 0 -0.8838 14.5011 0 0 -0.7996 14.5517 0 0 -0.7109 14.5936 0 0 -0.6188 14.6266 0 0 -0.5234 14.6503 0 0 -0.4264 14.6647 0 0 -0.3284 14.6695 0 0 -0.2625 14.673 0 0 -0.1969 14.6759 0 0 -0.1319 14.6781 0 0 -0.0672 14.6804 0 0 -0 14.682 0 0 -0 14.7774 0 0 -0 14.8731 0 0 -0 14.8708 0 0 -0 14.8683 0 0 -0 14.8648 0 0 -0 14.8606 0 0 -0 14.8561 0 0 -0 14.851 0 0 -0 14.8449 0 0 -0 14.9662 0 0 -0 15.0923 0 0 -0 15.0853 0 0 -0 15.1868 0 0 -0 15.1785 0 0 -0 15.1695 0 0 -0 15.2556 0 0 -0 15.3436 0 0 -0 15.3321 0 0 -0.0935 15.3215 0 0 -0.1841 15.3107 0 0 -0.2705 15.2998 0 0 -0.3611 15.3007 0 0 -0.4401 15.2889 0 0 -0.5115 15.2764 0 0 -0.5739 15.2626 0 0 -0.628 15.2482 0 0 -0.6767 15.2329 0 0 -0.7231 15.2169 0 0 -0.8179 15.2169 0 0 -0.9126 15.2169 0 0 -0.9638 15.2002 0 0 -1.0602 15.2002 0 0 -1.1565 15.2002 0 0 -1.2529 15.2002 0 0 -1.3492 15.2002 0 0 -1.4456 15.2002 0 0 -1.5419 15.2002 0 0 -1.6386 15.2002 0 0 -1.6386 15.1055 0 0 -1.7349 15.1055 0 0 -1.8313 15.1055 0 0 -1.9276 15.1055 0 0 -2.024 15.1055 0 0 -2.1203 15.1055 0 0 -2.2167 15.1055 0 0 -2.313 15.1055 0 0 -2.4097 15.1055 0 0 -2.5061 15.1055 0 0 -2.6024 15.1039 0 0 -2.5989 14.997 0 0 -2.5973 14.8952 0 0 -2.6988 14.7966 0 0 -2.7964 14.7975 0 0 -2.7964 14.8952 0 0 -2.8937 14.8952 0 0 -2.9932 14.9102 0 0 -3.0966 14.9237 0 0 -3.2029 14.9355 0 0 -3.2151 14.8465 0 0 -3.2292 14.7569 0 0 -3.2471 14.6673 0 0 -3.2695 14.6801 0 0 -3.2951 14.6925 0 0 -3.3239 14.7047 0 0 -3.3562 14.7166 0 0 -3.3914 14.7284 0 0 -3.4299 14.7399 0 0 -3.4715 14.7508 0 0 -3.516 14.7617 0 0 -3.5637 14.7722 0 0 -3.6133 14.7822 0 0 -3.6648 14.7915 0 0 -3.7183 14.8004 0 0 -3.7752 14.8091 0 0 -3.8351 14.8174 0 0 -3.8975 14.8254 0 0 -3.9615 14.8324 0 0 -4.0275 14.8392 0 0 -4.095 14.8456 0 0 -4.1642 14.851 0 0 -4.2346 14.8561 0 0 -4.3034 14.9621 0 0 -4.377 14.9659 0 0 -4.4516 14.9694 0 0 -4.5224 15.0735 0 0 -4.5985 15.0757 0 0 -4.6715 15.1788 0 0 -4.6626 15.1957 0 0 -4.6533 15.2124 0 0 -4.7403 15.2137 0 0 -4.8277 15.231 0 0 -4.8277 15.1512 0 0 -4.8277 15.0879 0 0 -4.8277 15.1231 0 0 -4.8277 15.1455 0 0 -4.8277 15.2754 0 0 -4.8277 15.3209 0 0 -4.8277 15.3913 0 0 -4.8277 15.2745 0 0 -4.8277 15.2873 0 0 -4.8277 15.3859 0 0 -4.8277 15.3859 0 0 -4.7291 15.3859 0 0 -4.6305 15.3859 0 0 -4.532 15.3859 0 0 -4.4334 15.3859 0 0 -4.4334 15.4845 0 0 -4.4334 15.5831 0 0 -4.4334 15.6817 0 0 -4.4334 15.7802 0 0 -4.4334 15.8785 0 0 -4.3351 15.7802 0 0 -4.2365 15.6817 0 0 -4.1379 15.5831 0 0 -4.0393 15.4845 0 0 -3.9407 15.3859 0 0 -3.9411 15.2873 0 0 -3.9414 15.1887 0 0 -3.8431 15.0895 0 0 -3.7628 15.0725 0 0 -3.7833 14.9778 0 0 -3.8002 14.9237 0 0 -3.8396 14.8504 0 0 -3.8137 14.7393 0 0 -3.7848 14.6122 0 0 -3.7733 14.5066 0 0 -3.6488 14.5037 0 0 -3.5422 14.4976 0 0 -3.443 14.3808 0 0 -3.3463 14.2607 0 0 -3.3582 14.1529 0 0 -3.3694 14.0482 0 0 -3.3806 13.9448 0 0 -3.3911 13.8424 0 0 -3.289 13.8312 0 0 -3.1869 13.819 0 0 -3.0848 13.8062 0 0 -2.9827 13.7918 0 0 -2.8809 13.7764 0 0 -2.7791 13.7588 0 0 -2.6779 13.739 0 0 -2.6939 13.6452 0 0 -2.7093 13.5476 0 0 -2.725 13.4483 0 0 -2.6258 13.4326 0 0 -2.6258 13.432 0 0 -2.5269 13.4157 0 0 -2.4276 13.3981 0 0 -2.4276 13.2822 0 0 -2.3297 13.2828 0 0 -2.2225 13.2755 0 0 -2.1511 13.174 0 0 -2.0662 13.1695 0 0 -1.967 13.1513 0 0 -1.8799 13.1823 0 0 -1.8034 13.1365 0 0 -1.7177 13.1433 0 0 -1.6376 13.1519 0 0 -1.5413 13.2617 0 0 -1.5426 13.3677 0 0 -1.4337 13.4733 0 0 -1.3284 13.4733 0 0 -1.3284 13.5789 0 0 -1.3284 13.6849 0 0 -1.3284 13.6846 0 0 -1.3284 13.6833 0 0 -1.3284 13.6814 0 0 -1.3284 13.6785 0 0 -1.3284 13.675 0 0 -1.3284 13.6708 0 0 -1.3236 13.7684 0 0 -1.3236 13.7633 0 0 -1.3092 13.8603 0 0 -1.2852 13.9554 0 0 -1.2522 14.0475 0 0 -1.2103 14.1362 0 0 -1.1597 14.2204 0 0 -1.1015 14.2991 0 0 -1.0355 14.3715 0 0 -0.9629 14.4374 0 0 -0.8838 14.4957 0 0 -0.7996 14.5463 0 0 -0.7109 14.5882 0 0 -0.6188 14.6212 0 0 -0.5234 14.6448 0 0 -0.4264 14.6593 0 0 -0.3284 14.6641 0 0 -0.2625 14.6682 0 0 -0.1969 14.6717 0 0 -0.1316 14.6749 0 0 -0.0659 14.6775 0 0 -0 14.6801 0 0 -0 14.7751 0 0 -0 14.7722 0 0 -0 14.769 0 0 -0 14.7649 0 0 -0 14.7604 0 0 -0 14.7553 0 0 -0 14.7492 0 0 -0 14.8385 0 0 -0 14.9595 0 0 -0 14.9521 0 0 -0 15.0776 0 0 -0 15.0693 0 0 -0 15.0607 0 0 -0 15.1576 0 0 -0 15.1455 0 0 -0 15.2322 0 0 -0 15.3203 0 0 -0.0919 15.3091 0 0 -0.1799 15.2982 0 0 -0.2622 15.287 0 0 -0.3508 15.2882 0 0 -0.4225 15.2758 0 0 -0.4843 15.2623 0 0 -0.5371 15.2482 0 0 -0.5839 15.2329 0 0 -0.6284 15.2169 0 0 -0.6744 15.2002 0 0 -0.7708 15.2002 0 0 -0.8675 15.2002 0 0 -0.9638 15.1055 0 0 -1.0602 15.1055 0 0 -1.1565 15.1055 0 0 -1.2529 15.1055 0 0 -1.3492 15.1055 0 0 -1.4456 15.1055 0 0 -1.5419 15.1055 0 0 -1.5419 15.0104 0 0 -1.6386 15.0104 0 0 -1.7349 15.0104 0 0 -1.8313 15.0104 0 0 -1.9276 15.0104 0 0 -2.024 15.0104 0 0 -2.1203 15.0104 0 0 -2.2167 15.0104 0 0 -2.313 15.0104 0 0 -2.4097 15.0104 0 0 -2.5051 15.0078 0 0 -2.5 14.9121 0 0 -2.4939 14.7966 0 0 -2.5966 14.7947 0 0 -2.7004 14.6954 0 0 -2.7973 14.6986 0 0 -2.8937 14.6989 0 0 -2.8937 14.7982 0 0 -2.9913 14.8952 0 0 -3.0905 14.9093 0 0 -3.1936 14.9221 0 0 -3.2026 14.8334 0 0 -3.2138 14.7441 0 0 -3.2282 14.6545 0 0 -3.2468 14.5655 0 0 -3.2689 14.5783 0 0 -3.2945 14.5908 0 0 -3.3233 14.6029 0 0 -3.3553 14.6148 0 0 -3.3905 14.6266 0 0 -3.4289 14.6378 0 0 -3.4702 14.649 0 0 -3.515 14.6599 0 0 -3.5624 14.6705 0 0 -3.6123 14.6804 0 0 -3.6645 14.69 0 0 -3.7195 14.6993 0 0 -3.7772 14.7079 0 0 -3.837 14.7162 0 0 -3.8991 14.7239 0 0 -3.9631 14.7313 0 0 -4.0291 14.738 0 0 -4.0963 14.7441 0 0 -4.1651 14.7495 0 0 -4.2355 14.7546 0 0 -4.3066 14.8606 0 0 -4.3799 14.8648 0 0 -4.4542 14.868 0 0 -4.5268 14.972 0 0 -4.6021 14.9742 0 0 -4.675 15.0773 0 0 -4.7496 15.1797 0 0 -4.7452 15.1967 0 0 -4.8277 15.214 0 0 -4.8277 15.1336 0 0 -4.8277 15.0539 0 0 -4.8277 15.0027 0 0 -4.8277 15.0475 0 0 -4.8277 15.0559 0 0 -4.8277 15.069 0 0 -4.8277 15.1596 0 0 -4.8277 15.1791 0 0 -4.8277 15.1887 0 0 -4.8277 15.2873 0 0 -4.7291 15.2873 0 0 -4.6305 15.2873 0 0 -4.532 15.2873 0 0 -4.4334 15.2873 0 0 -4.3351 15.2873 0 0 -4.3351 15.3859 0 0 -4.3351 15.4845 0 0 -4.3351 15.5831 0 0 -4.3351 15.6817 0 0 -4.2365 15.5831 0 0 -4.1379 15.4845 0 0 -4.0393 15.3859 0 0 -4.0393 15.2873 0 0 -4.0393 15.1887 0 0 -3.9417 15.0901 0 0 -3.8543 14.988 0 0 -3.8514 14.915 0 0 -3.9407 14.9045 0 0 -3.9356 14.826 0 0 -3.927 14.7095 0 0 -3.9081 14.6103 0 0 -3.8943 14.5088 0 0 -3.8806 14.4083 0 0 -3.7628 14.4029 0 0 -3.6562 14.3955 0 0 -3.5499 14.3885 0 0 -3.4529 14.2707 0 0 -3.4632 14.1634 0 0 -3.4734 14.0587 0 0 -3.4836 13.9554 0 0 -3.4936 13.8526 0 0 -3.5035 13.7508 0 0 -3.402 13.7406 0 0 -3.3002 13.7297 0 0 -3.1991 13.7178 0 0 -3.0976 13.7054 0 0 -2.9964 13.6916 0 0 -2.8953 13.6775 0 0 -2.7945 13.6618 0 0 -2.8095 13.5629 0 0 -2.8242 13.4637 0 0 -2.725 13.448 0 0 -2.725 13.447 0 0 -2.6261 13.431 0 0 -2.5272 13.4144 0 0 -2.4276 13.3971 0 0 -2.4276 13.2819 0 0 -2.4276 13.1657 0 0 -2.4276 13.166 0 0 -2.3354 13.1695 0 0 -2.2292 13.1727 0 0 -2.1588 13.0796 0 0 -2.0733 13.0866 0 0 -1.9763 13.0792 0 0 -1.8828 13.1282 0 0 -1.8822 13.0629 0 0 -1.7954 13.0578 0 0 -1.7068 13.0533 0 0 -1.6261 13.06 0 0 -1.541 13.1558 0 0 -1.434 13.2614 0 0 -1.4337 13.3673 0 0 -1.3284 13.3673 0 0 -1.3284 13.367 0 0 -1.3284 13.4726 0 0 -1.3284 13.5786 0 0 -1.3284 13.5773 0 0 -1.3284 13.5754 0 0 -1.3284 13.5728 0 0 -1.3284 13.5693 0 0 -1.3284 13.5648 0 0 -1.3284 13.6657 0 0 -1.3284 13.6596 0 0 -1.3236 13.7575 0 0 -1.3092 13.8542 0 0 -1.2852 13.9493 0 0 -1.2522 14.0418 0 0 -1.2103 14.1301 0 0 -1.1597 14.214 0 0 -1.1015 14.2927 0 0 -1.0355 14.3654 0 0 -0.9629 14.4313 0 0 -0.8838 14.4896 0 0 -0.7996 14.5399 0 0 -0.7109 14.5818 0 0 -0.6188 14.6148 0 0 -0.5234 14.6388 0 0 -0.4264 14.6529 0 0 -0.3284 14.658 0 0 -0.2628 14.6631 0 0 -0.1969 14.6673 0 0 -0.1312 14.6708 0 0 -0.0656 14.6743 0 0 -0 14.6772 0 0 -0 14.674 0 0 -0 14.6698 0 0 -0 14.6653 0 0 -0 14.6599 0 0 -0 14.6541 0 0 -0 14.7428 0 0 -0 14.8311 0 0 -0 14.7358 0 0 -0 14.7281 0 0 -0 14.8151 0 0 -0 14.8062 0 0 -0 14.9176 0 0 -0 15.0354 0 0 -0 15.133 0 0 -0 15.2201 0 0 -0 15.3075 0 0 -0.0899 15.2963 0 0 -0.1745 15.285 0 0 -0.25 15.2735 0 0 -0.3355 15.2748 0 0 -0.3969 15.262 0 0 -0.4478 15.2479 0 0 -0.4917 15.2329 0 0 -0.5339 15.2169 0 0 -0.5781 15.2002 0 0 -0.6658 15.109 0 0 -0.7615 15.1077 0 0 -0.8675 15.1055 0 0 -0.9638 15.0104 0 0 -1.0602 15.0104 0 0 -1.1565 15.0104 0 0 -1.2529 15.0104 0 0 -1.3492 15.0104 0 0 -1.4456 15.0104 0 0 -1.4453 14.9259 0 0 -1.5419 14.9157 0 0 -1.6386 14.9157 0 0 -1.7349 14.9157 0 0 -1.8313 14.9157 0 0 -1.9276 14.9157 0 0 -2.024 14.9157 0 0 -2.1203 14.9157 0 0 -2.2167 14.9157 0 0 -2.313 14.9157 0 0 -2.4081 14.9157 0 0 -2.4065 14.8164 0 0 -2.3947 14.713 0 0 -2.4904 14.6906 0 0 -2.5966 14.6935 0 0 -2.5986 14.5962 0 0 -2.7029 14.5952 0 0 -2.7993 14.5975 0 0 -2.8867 14.593 0 0 -2.9836 14.5911 0 0 -2.9865 14.6932 0 0 -2.9913 14.7975 0 0 -3.0886 14.8952 0 0 -3.1882 14.9086 0 0 -3.1936 14.8203 0 0 -3.2016 14.731 0 0 -3.2132 14.6416 0 0 -3.2282 14.5527 0 0 -3.2282 14.4512 0 0 -3.2471 14.464 0 0 -3.2692 14.4768 0 0 -3.2948 14.4893 0 0 -3.3236 14.5014 0 0 -3.3556 14.5133 0 0 -3.3908 14.5251 0 0 -3.4289 14.5363 0 0 -3.4705 14.5475 0 0 -3.515 14.5584 0 0 -3.5624 14.569 0 0 -3.6126 14.5789 0 0 -3.6648 14.5885 0 0 -3.7199 14.5978 0 0 -3.7775 14.6064 0 0 -3.8373 14.6144 0 0 -3.8994 14.6224 0 0 -3.9631 14.6295 0 0 -4.0291 14.6362 0 0 -4.0963 14.6423 0 0 -4.1648 14.6481 0 0 -4.2352 14.6532 0 0 -4.3069 14.6577 0 0 -4.3076 14.7591 0 0 -4.3805 14.7633 0 0 -4.4548 14.7665 0 0 -4.5291 14.8705 0 0 -4.6037 14.8728 0 0 -4.6773 14.9758 0 0 -4.7512 15.0783 0 0 -4.8277 15.1801 0 0 -4.8277 15.197 0 0 -4.8277 15.116 0 0 -4.8277 15.0354 0 0 -4.8277 14.9557 0 0 -4.8277 14.8542 0 0 -4.8277 14.915 0 0 -4.8277 14.9902 0 0 -4.8277 14.9864 0 0 -4.8277 14.9915 0 0 -4.8277 14.9915 0 0 -4.8277 15.0805 0 0 -4.8277 15.0901 0 0 -4.7291 15.0901 0 0 -4.7291 15.1887 0 0 -4.6305 15.1887 0 0 -4.532 15.1887 0 0 -4.4334 15.1887 0 0 -4.3351 15.1887 0 0 -4.2365 15.1887 0 0 -4.2365 15.2873 0 0 -4.2365 15.3859 0 0 -4.2365 15.4845 0 0 -4.1379 15.3859 0 0 -4.1379 15.2873 0 0 -4.1379 15.1887 0 0 -4.0396 15.0901 0 0 -3.9423 14.9915 0 0 -4.0396 14.9915 0 0 -4.04 14.8932 0 0 -4.0384 14.8091 0 0 -4.039 14.6973 0 0 -4.0352 14.6042 0 0 -4.0182 14.5062 0 0 -4.0006 14.4089 0 0 -3.9926 14.3062 0 0 -3.8761 14.303 0 0 -3.7701 14.2959 0 0 -3.6645 14.2879 0 0 -3.5589 14.2796 0 0 -3.5681 14.1733 0 0 -3.5774 14.0687 0 0 -3.5867 13.965 0 0 -3.596 13.8622 0 0 -3.6049 13.7601 0 0 -3.6142 13.6589 0 0 -3.5131 13.6497 0 0 -3.4123 13.6394 0 0 -3.3117 13.6285 0 0 -3.2109 13.617 0 0 -3.1104 13.6045 0 0 -3.0099 13.5914 0 0 -2.9097 13.5776 0 0 -2.9238 13.4781 0 0 -2.8242 13.463 0 0 -2.8245 13.4621 0 0 -2.7256 13.4451 0 0 -2.6274 13.4294 0 0 -2.5275 13.4125 0 0 -2.4276 13.3952 0 0 -2.4276 13.2806 0 0 -2.4276 13.1644 0 0 -2.4276 13.0482 0 0 -2.4276 13.0495 0 0 -2.4276 13.0498 0 0 -2.3358 13.0613 0 0 -2.2385 13.0716 0 0 -2.1664 12.9928 0 0 -2.0755 12.9928 0 0 -1.975 12.988 0 0 -1.8793 12.9848 0 0 -1.7897 12.9762 0 0 -1.694 12.9727 0 0 -1.6187 12.9762 0 0 -1.5365 13.0636 0 0 -1.434 13.1558 0 0 -1.3284 13.1558 0 0 -1.3284 13.2614 0 0 -1.3284 13.2611 0 0 -1.3284 13.3657 0 0 -1.3284 13.4717 0 0 -1.3284 13.4698 0 0 -1.3284 13.4669 0 0 -1.3284 13.4634 0 0 -1.3284 13.4592 0 0 -1.3284 13.5597 0 0 -1.3284 13.554 0 0 -1.3284 13.6529 0 0 -1.3236 13.7508 0 0 -1.3092 13.8475 0 0 -1.2852 13.9426 0 0 -1.2522 14.0347 0 0 -1.2103 14.1231 0 0 -1.1597 14.2073 0 0 -1.1015 14.2857 0 0 -1.0355 14.3584 0 0 -0.9629 14.424 0 0 -0.8838 14.4826 0 0 -0.7996 14.5328 0 0 -0.7109 14.5747 0 0 -0.6188 14.6077 0 0 -0.5234 14.6314 0 0 -0.4264 14.6458 0 0 -0.3284 14.6506 0 0 -0.2628 14.6567 0 0 -0.1969 14.6618 0 0 -0.1312 14.6663 0 0 -0.0656 14.6701 0 0 -0.0656 14.6657 0 0 -0.0656 14.6602 0 0 -0.0656 14.6545 0 0 -0.0656 14.6481 0 0 -0.0656 14.6407 0 0 -0 14.6407 0 0 -0 14.633 0 0 -0 14.7198 0 0 -0 14.7108 0 0 -0 14.7966 0 0 -0 14.9064 0 0 -0 15.0219 0 0 -0 15.1208 0 0 -0 15.2076 0 0 -0 15.294 0 0 -0.0871 15.2828 0 0 -0.1665 15.2716 0 0 -0.2327 15.2604 0 0 -0.3127 15.2614 0 0 -0.3614 15.2479 0 0 -0.4017 15.2329 0 0 -0.4401 15.2169 0 0 -0.4817 15.2002 0 0 -0.5781 15.1052 0 0 -0.6511 15.0114 0 0 -0.7423 15.013 0 0 -0.8518 15.0126 0 0 -0.9638 14.9157 0 0 -1.0602 14.9157 0 0 -1.1565 14.9157 0 0 -1.2529 14.9157 0 0 -1.3563 14.9227 0 0 -1.4475 14.8571 0 0 -1.5339 14.8376 0 0 -1.6386 14.8206 0 0 -1.7349 14.8206 0 0 -1.8313 14.8206 0 0 -1.9276 14.8206 0 0 -2.024 14.8206 0 0 -2.1203 14.8206 0 0 -2.2167 14.8206 0 0 -2.313 14.8206 0 0 -2.3108 14.7258 0 0 -2.3002 14.6285 0 0 -2.3879 14.624 0 0 -2.483 14.6055 0 0 -2.5992 14.4829 0 0 -2.6994 14.4704 0 0 -2.7919 14.4781 0 0 -2.8838 14.4893 0 0 -2.982 14.4893 0 0 -3.0838 14.489 0 0 -3.0842 14.5904 0 0 -3.0851 14.6919 0 0 -3.0867 14.7934 0 0 -3.1863 14.7937 0 0 -3.1882 14.8068 0 0 -3.193 14.7182 0 0 -3.2013 14.6288 0 0 -3.2128 14.5399 0 0 -3.2128 14.4384 0 0 -3.2132 14.3372 0 0 -3.2285 14.35 0 0 -3.2474 14.3628 0 0 -3.2695 14.3753 0 0 -3.2951 14.3878 0 0 -3.3239 14.4 0 0 -3.3559 14.4118 0 0 -3.3911 14.4237 0 0 -3.4295 14.4352 0 0 -3.4711 14.4461 0 0 -3.5157 14.457 0 0 -3.5627 14.4672 0 0 -3.6126 14.4774 0 0 -3.6651 14.487 0 0 -3.7202 14.496 0 0 -3.7775 14.5046 0 0 -3.8373 14.513 0 0 -3.8991 14.5207 0 0 -3.9631 14.528 0 0 -4.0288 14.5347 0 0 -4.096 14.5408 0 0 -4.1645 14.5466 0 0 -4.2349 14.5514 0 0 -4.3066 14.5562 0 0 -4.3796 14.56 0 0 -4.3802 14.6615 0 0 -4.4542 14.6647 0 0 -4.5284 14.6676 0 0 -4.5291 14.769 0 0 -4.6037 14.7713 0 0 -4.6782 14.7726 0 0 -4.6786 14.8744 0 0 -4.7525 14.9768 0 0 -4.8277 15.0786 0 0 -4.8277 15.0975 0 0 -4.8277 15.0158 0 0 -4.8277 14.9345 0 0 -4.8277 14.8254 0 0 -4.8277 14.7284 0 0 -4.8277 14.7511 0 0 -4.8277 14.7706 0 0 -4.8277 14.8907 0 0 -4.8277 14.8929 0 0 -4.8277 14.8929 0 0 -4.8277 14.8929 0 0 -4.8277 14.9915 0 0 -4.7291 14.9915 0 0 -4.6305 14.9915 0 0 -4.6305 15.0901 0 0 -4.532 15.0901 0 0 -4.4334 15.0901 0 0 -4.3351 15.0901 0 0 -4.2365 15.0901 0 0 -4.1379 15.0901 0 0 -4.1379 14.9915 0 0 -4.1379 14.8932 0 0 -4.1379 14.7947 0 0 -4.1373 14.6964 0 0 -4.136 14.5988 0 0 -4.1328 14.5021 0 0 -4.1254 14.4086 0 0 -4.1117 14.3062 0 0 -4.1034 14.2057 0 0 -3.9878 14.2041 0 0 -3.8831 14.1974 0 0 -3.7781 14.19 0 0 -3.6731 14.182 0 0 -3.6818 14.0773 0 0 -3.6901 13.9736 0 0 -3.6984 13.8709 0 0 -3.7067 13.7687 0 0 -3.7151 13.6676 0 0 -3.7234 13.5671 0 0 -3.6229 13.5584 0 0 -3.523 13.5492 0 0 -3.4228 13.5392 0 0 -3.3226 13.5283 0 0 -3.2228 13.5168 0 0 -3.1229 13.5047 0 0 -3.0233 13.4915 0 0 -2.9238 13.4778 0 0 -2.9238 13.4765 0 0 -2.9241 13.4746 0 0 -2.8249 13.4602 0 0 -2.8252 13.4576 0 0 -2.7263 13.4426 0 0 -2.628 13.4269 0 0 -2.5275 13.4099 0 0 -2.4276 13.3923 0 0 -2.4276 13.2787 0 0 -2.4276 13.1628 0 0 -2.4276 13.0466 0 0 -2.4081 12.949 0 0 -2.4081 12.9509 0 0 -2.4081 12.9522 0 0 -2.4081 12.9525 0 0 -2.3182 12.9864 0 0 -2.2423 12.9928 0 0 -2.2426 12.9093 0 0 -2.1703 12.8974 0 0 -2.0778 12.8958 0 0 -1.9779 12.8929 0 0 -1.8828 12.8894 0 0 -1.7826 12.8856 0 0 -1.685 12.8856 0 0 -1.5976 12.891 0 0 -1.5352 12.98 0 0 -1.4453 12.9871 0 0 -1.434 13.0604 0 0 -1.3284 13.0498 0 0 -1.3284 13.0495 0 0 -1.3284 13.1551 0 0 -1.3284 13.2598 0 0 -1.3284 13.3638 0 0 -1.3284 13.3613 0 0 -1.3284 13.3577 0 0 -1.3284 13.3536 0 0 -1.3284 13.4541 0 0 -1.3284 13.4483 0 0 -1.3284 13.5472 0 0 -1.3284 13.6455 0 0 -1.3236 13.7431 0 0 -1.3092 13.8398 0 0 -1.2852 13.9349 0 0 -1.2522 14.0271 0 0 -1.2103 14.1154 0 0 -1.1597 14.1996 0 0 -1.1015 14.278 0 0 -1.0355 14.3507 0 0 -0.9629 14.4163 0 0 -0.8838 14.4746 0 0 -0.7996 14.5248 0 0 -0.7109 14.5667 0 0 -0.6188 14.5997 0 0 -0.5234 14.6234 0 0 -0.4264 14.6378 0 0 -0.3284 14.6426 0 0 -0.2628 14.6497 0 0 -0.1969 14.6557 0 0 -0.1312 14.6609 0 0 -0.1312 14.6551 0 0 -0.1312 14.6484 0 0 -0.1312 14.641 0 0 -0.1312 14.633 0 0 -0.0656 14.633 0 0 -0 14.6247 0 0 -0 14.6157 0 0 -0 14.7012 0 0 -0 14.7863 0 0 -0 14.8945 0 0 -0 15.0088 0 0 -0 15.108 0 0 -0 15.1941 0 0 -0 15.2802 0 0 -0.0832 15.2687 0 0 -0.1559 15.2585 0 0 -0.2077 15.2473 0 0 -0.2807 15.2476 0 0 -0.315 15.2332 0 0 -0.3473 15.2172 0 0 -0.3854 15.2002 0 0 -0.4817 15.1048 0 0 -0.4955 15.0101 0 0 -0.5784 15.0094 0 0 -0.638 14.9371 0 0 -0.7231 14.9304 0 0 -0.8364 14.9221 0 0 -0.8108 14.8177 0 0 -0.9699 14.7883 0 0 -1.0765 14.8302 0 0 -1.1629 14.8315 0 0 -1.2622 14.8305 0 0 -1.3671 14.8414 0 0 -1.4465 14.8027 0 0 -1.516 14.7652 0 0 -1.6367 14.745 0 0 -1.7353 14.7431 0 0 -1.8313 14.7262 0 0 -1.9276 14.7262 0 0 -2.024 14.7258 0 0 -2.1203 14.7258 0 0 -2.2167 14.7258 0 0 -2.2167 14.6314 0 0 -2.2833 14.5453 0 0 -2.3604 14.5162 0 0 -2.4427 14.4733 0 0 -2.4705 14.3737 0 0 -2.6053 14.3699 0 0 -2.702 14.3708 0 0 -2.7973 14.3737 0 0 -2.8911 14.3788 0 0 -2.983 14.3869 0 0 -3.0835 14.3872 0 0 -3.1863 14.3872 0 0 -3.1863 14.489 0 0 -3.1863 14.5904 0 0 -3.1863 14.6919 0 0 -3.1879 14.705 0 0 -3.193 14.616 0 0 -3.201 14.5271 0 0 -3.2013 14.4256 0 0 -3.2013 14.3241 0 0 -3.2013 14.223 0 0 -3.2135 14.2358 0 0 -3.2288 14.2486 0 0 -3.2477 14.2614 0 0 -3.2698 14.2742 0 0 -3.2957 14.2863 0 0 -3.3245 14.2985 0 0 -3.3566 14.3107 0 0 -3.3918 14.3222 0 0 -3.4302 14.3337 0 0 -3.4715 14.3446 0 0 -3.516 14.3555 0 0 -3.5633 14.366 0 0 -3.613 14.376 0 0 -3.6655 14.3856 0 0 -3.7205 14.3945 0 0 -3.7778 14.4032 0 0 -3.8373 14.4115 0 0 -3.8991 14.4192 0 0 -3.9628 14.4265 0 0 -4.0281 14.4333 0 0 -4.0957 14.4393 0 0 -4.1642 14.4448 0 0 -4.2346 14.4499 0 0 -4.306 14.4544 0 0 -4.379 14.4582 0 0 -4.4526 14.4618 0 0 -4.4532 14.5632 0 0 -4.5275 14.5658 0 0 -4.6024 14.568 0 0 -4.603 14.6695 0 0 -4.6779 14.6711 0 0 -4.7525 14.6721 0 0 -4.7528 14.7735 0 0 -4.7531 14.875 0 0 -4.8277 14.9768 0 0 -4.8277 14.9947 0 0 -4.8277 14.8932 0 0 -4.8277 14.9112 0 0 -4.8277 14.8087 0 0 -4.8277 14.7082 0 0 -4.8277 14.6231 0 0 -4.8277 14.6352 0 0 -4.8277 14.657 0 0 -4.8277 14.6775 0 0 -4.8277 14.7822 0 0 -4.8277 14.7947 0 0 -4.8277 14.7947 0 0 -4.7291 14.7947 0 0 -4.7291 14.8929 0 0 -4.6305 14.8929 0 0 -4.532 14.8929 0 0 -4.532 14.9915 0 0 -4.4334 14.9915 0 0 -4.3351 14.9915 0 0 -4.2365 14.9915 0 0 -4.2365 14.8932 0 0 -4.2362 14.7947 0 0 -4.2362 14.6961 0 0 -4.2355 14.5978 0 0 -4.2346 14.4998 0 0 -4.2317 14.4025 0 0 -4.2263 14.3065 0 0 -4.216 14.2134 0 0 -4.2029 14.1096 0 0 -4.0982 14.1052 0 0 -3.9942 14.0994 0 0 -3.8898 14.093 0 0 -3.7858 14.0856 0 0 -3.7935 13.9819 0 0 -3.8012 13.8789 0 0 -3.8085 13.7767 0 0 -3.8162 13.6753 0 0 -3.8236 13.5748 0 0 -3.813 13.5735 0 0 -3.7125 13.5658 0 0 -3.6232 13.5581 0 0 -3.523 13.5488 0 0 -3.4228 13.5386 0 0 -3.3229 13.528 0 0 -3.2228 13.5165 0 0 -3.1232 13.5043 0 0 -3.0233 13.4912 0 0 -3.0236 13.4903 0 0 -3.0236 13.4883 0 0 -2.9398 13.4746 0 0 -2.9657 13.4755 0 0 -2.838 13.4547 0 0 -2.7039 13.4352 0 0 -2.628 13.4234 0 0 -2.5275 13.4064 0 0 -2.4276 13.3888 0 0 -2.4276 13.2761 0 0 -2.4276 13.1599 0 0 -2.4276 13.0437 0 0 -2.4081 12.9464 0 0 -2.3537 12.8661 0 0 -2.3534 12.8686 0 0 -2.3534 12.8705 0 0 -2.3534 12.8715 0 0 -2.3534 12.8718 0 0 -2.3009 12.9253 0 0 -2.2727 12.8187 0 0 -2.1776 12.7998 0 0 -2.0778 12.7998 0 0 -1.9779 12.7998 0 0 -1.878 12.7998 0 0 -1.7781 12.7998 0 0 -1.6783 12.7998 0 0 -1.5784 12.7998 0 0 -1.5227 12.9096 0 0 -1.4606 12.9291 0 0 -1.3473 12.9544 0 0 -1.3473 12.9538 0 0 -1.3284 13.0482 0 0 -1.3284 13.1542 0 0 -1.3284 13.2582 0 0 -1.3284 13.2553 0 0 -1.3284 13.2521 0 0 -1.3284 13.2476 0 0 -1.3284 13.3484 0 0 -1.3284 13.3427 0 0 -1.3284 13.4416 0 0 -1.3284 13.5399 0 0 -1.3284 13.6372 0 0 -1.3236 13.7348 0 0 -1.3092 13.8318 0 0 -1.2852 13.9265 0 0 -1.2522 14.0184 0 0 -1.2103 14.1071 0 0 -1.1597 14.191 0 0 -1.1015 14.2694 0 0 -1.0355 14.3417 0 0 -0.9629 14.4077 0 0 -0.8838 14.4656 0 0 -0.7996 14.5162 0 0 -0.7109 14.5578 0 0 -0.6188 14.5908 0 0 -0.5234 14.6144 0 0 -0.4264 14.6288 0 0 -0.3284 14.6336 0 0 -0.2628 14.642 0 0 -0.1969 14.649 0 0 -0.1969 14.6413 0 0 -0.1969 14.6333 0 0 -0.1312 14.6244 0 0 -0.0656 14.6247 0 0 -0.0656 14.6154 0 0 -0 14.6064 0 0 -0 14.6909 0 0 -0 14.7755 0 0 -0 14.8827 0 0 -0 14.9963 0 0 -0 14.9838 0 0 -0 15.0946 0 0 -0 15.1801 0 0 -0 15.2655 0 0 -0.0787 15.255 0 0 -0.1428 15.246 0 0 -0.1706 15.2348 0 0 -0.2359 15.2335 0 0 -0.2577 15.2172 0 0 -0.289 15.2002 0 0 -0.3854 15.1042 0 0 -0.386 15.0069 0 0 -0.4008 14.9182 0 0 -0.5086 14.9345 0 0 -0.5768 14.9416 0 0 -0.5771 14.8926 0 0 -0.6242 14.8718 0 0 -0.7013 14.8446 0 0 -0.7679 14.7114 0 0 -0.8752 14.6625 0 0 -1.0493 14.7073 0 0 -1.0983 14.7662 0 0 -1.1632 14.7534 0 0 -1.2717 14.7466 0 0 -1.3844 14.77 0 0 -1.4837 14.6705 0 0 -1.6456 14.6637 0 0 -1.7365 14.6759 0 0 -1.8278 14.657 0 0 -1.9283 14.6503 0 0 -2.0237 14.6426 0 0 -2.1203 14.632 0 0 -2.208 14.5591 0 0 -2.2733 14.4752 0 0 -2.3422 14.4512 0 0 -2.3915 14.432 0 0 -2.3988 14.3737 0 0 -2.411 14.2732 0 0 -2.491 14.2742 0 0 -2.6056 14.2739 0 0 -2.7026 14.2742 0 0 -2.7989 14.2751 0 0 -2.8953 14.2771 0 0 -2.9913 14.2806 0 0 -3.0848 14.2857 0 0 -3.1863 14.2857 0 0 -3.1879 14.4 0 0 -3.1879 14.5014 0 0 -3.1879 14.6032 0 0 -3.193 14.5142 0 0 -3.193 14.4128 0 0 -3.193 14.3113 0 0 -3.193 14.2098 0 0 -3.193 14.1087 0 0 -3.202 14.1218 0 0 -3.2141 14.1346 0 0 -3.2295 14.1477 0 0 -3.2487 14.1605 0 0 -3.2711 14.173 0 0 -3.2967 14.1855 0 0 -3.3258 14.1977 0 0 -3.3578 14.2095 0 0 -3.3934 14.221 0 0 -3.4318 14.2326 0 0 -3.4731 14.2434 0 0 -3.5173 14.2543 0 0 -3.5646 14.2646 0 0 -3.6142 14.2745 0 0 -3.6667 14.2841 0 0 -3.7215 14.2931 0 0 -3.7788 14.3017 0 0 -3.8383 14.31 0 0 -3.8998 14.3177 0 0 -3.9631 14.3251 0 0 -4.0288 14.3315 0 0 -4.0957 14.3379 0 0 -4.1645 14.3433 0 0 -4.2346 14.3484 0 0 -4.306 14.3529 0 0 -4.3783 14.3568 0 0 -4.4516 14.36 0 0 -4.5259 14.3628 0 0 -4.5268 14.4643 0 0 -4.6014 14.4666 0 0 -4.6766 14.4678 0 0 -4.6773 14.5696 0 0 -4.7525 14.5706 0 0 -4.8277 14.5706 0 0 -4.8277 14.6724 0 0 -4.8277 14.7739 0 0 -4.8277 14.8753 0 0 -4.8277 14.7918 0 0 -4.8277 14.69 0 0 -4.8277 14.6932 0 0 -4.8277 14.6074 0 0 -4.8277 14.513 0 0 -4.8277 14.5293 0 0 -4.8277 14.5459 0 0 -4.8277 14.5607 0 0 -4.8277 14.5805 0 0 -4.8277 14.5975 0 0 -4.8277 14.6961 0 0 -4.8277 14.6961 0 0 -4.7291 14.6961 0 0 -4.6305 14.6961 0 0 -4.6305 14.7947 0 0 -4.532 14.7947 0 0 -4.4334 14.7947 0 0 -4.4334 14.8929 0 0 -4.3351 14.8932 0 0 -4.3348 14.7947 0 0 -4.3348 14.6961 0 0 -4.3348 14.5975 0 0 -4.3341 14.4992 0 0 -4.3332 14.4009 0 0 -4.3313 14.3033 0 0 -4.3271 14.2063 0 0 -4.32 14.1109 0 0 -4.3111 14.0098 0 0 -4.2071 14.0063 0 0 -4.1037 14.0011 0 0 -4.0003 13.9957 0 0 -3.8969 13.989 0 0 -3.9036 13.8859 0 0 -3.9103 13.7838 0 0 -3.917 13.6823 0 0 -3.9238 13.5818 0 0 -3.9238 13.5815 0 0 -3.9295 13.5802 0 0 -3.7845 13.5693 0 0 -3.693 13.5626 0 0 -3.6232 13.5568 0 0 -3.5051 13.546 0 0 -3.4049 13.5357 0 0 -3.3229 13.5267 0 0 -3.2231 13.5152 0 0 -3.1232 13.5031 0 0 -3.1114 13.4999 0 0 -3.1011 13.4967 0 0 -3.0243 13.4867 0 0 -3.0285 13.4855 0 0 -3.0246 13.481 0 0 -2.9414 13.4656 0 0 -2.8662 13.4493 0 0 -2.7288 13.4317 0 0 -2.6728 13.4269 0 0 -2.6139 13.417 0 0 -2.5275 13.4022 0 0 -2.4276 13.3846 0 0 -2.4276 13.2726 0 0 -2.4276 13.1564 0 0 -2.4276 13.0405 0 0 -2.4081 12.9429 0 0 -2.3537 12.8629 0 0 -2.2727 12.8094 0 0 -2.2727 12.8126 0 0 -2.2727 12.8155 0 0 -2.2727 12.8171 0 0 -2.2727 12.8184 0 0 -2.1776 12.7995 0 0 -2.0778 12.7995 0 0 -1.9779 12.7995 0 0 -1.878 12.7995 0 0 -1.7781 12.7995 0 0 -1.6783 12.7995 0 0 -1.5784 12.7995 0 0 -1.4817 12.8193 0 0 -1.4014 12.8734 0 0 -1.4011 12.8731 0 0 -1.3473 12.9528 0 0 -1.3284 13.0466 0 0 -1.3284 13.1522 0 0 -1.3284 13.1497 0 0 -1.3284 13.1461 0 0 -1.3284 13.142 0 0 -1.3284 13.2428 0 0 -1.3284 13.2371 0 0 -1.3284 13.3363 0 0 -1.3284 13.4346 0 0 -1.3284 13.5319 0 0 -1.3284 13.6282 0 0 -1.3236 13.7258 0 0 -1.3092 13.8225 0 0 -1.2852 13.9173 0 0 -1.2522 14.0091 0 0 -1.2103 14.0975 0 0 -1.1597 14.1813 0 0 -1.1015 14.2601 0 0 -1.0355 14.3324 0 0 -0.9629 14.3981 0 0 -0.8838 14.456 0 0 -0.7996 14.5066 0 0 -0.7109 14.5482 0 0 -0.6188 14.5812 0 0 -0.5234 14.6048 0 0 -0.4264 14.6192 0 0 -0.3284 14.624 0 0 -0.2628 14.6333 0 0 -0.2628 14.624 0 0 -0.1969 14.6244 0 0 -0.1312 14.6151 0 0 -0.0656 14.6058 0 0 -0 14.5962 0 0 -0 14.5856 0 0 -0 14.6801 0 0 -0 14.6689 0 0 -0 14.7639 0 0 -0 14.8708 0 0 -0 14.8587 0 0 -0 14.9714 0 0 -0 15.0808 0 0 -0 15.1657 0 0 -0 15.2502 0 0 -0.0749 15.2412 0 0 -0.1309 15.2367 0 0 -0.0781 15.2294 0 0 -0.106 15.2207 0 0 -0.1741 15.2181 0 0 -0.1927 15.2002 0 0 -0.289 15.1032 0 0 -0.2903 15.004 0 0 -0.3009 14.8996 0 0 -0.4251 14.8247 0 0 -0.5307 14.8657 0 0 -0.483 14.7124 0 0 -0.6002 14.6612 0 0 -0.6562 14.5066 0 0 -0.7554 14.4691 0 0 -0.8515 14.423 0 0 -0.9427 14.3683 0 0 -1.0099 14.4579 0 0 -1.1031 14.3878 0 0 -1.1873 14.4794 0 0 -1.2916 14.3734 0 0 -1.3719 14.2761 0 0 -1.4929 14.3465 0 0 -1.6255 14.4272 0 0 -1.7894 14.4934 0 0 -1.9497 14.497 0 0 -2.031 14.5075 0 0 -2.111 14.4954 0 0 -2.0957 14.4272 0 0 -2.169 14.4016 0 0 -2.2199 14.2876 0 0 -2.2058 14.1765 0 0 -2.314 14.1769 0 0 -2.412 14.0812 0 0 -2.5093 14.0821 0 0 -2.6063 14.0828 0 0 -2.7026 14.0828 0 0 -2.7996 14.0828 0 0 -2.8963 14.0828 0 0 -2.9929 14.0828 0 0 -3.0896 14.0828 0 0 -3.1863 14.0828 0 0 -3.0912 14.0959 0 0 -3.0003 14.1112 0 0 -3.0099 14.126 0 0 -3.0233 14.1404 0 0 -3.0406 14.1548 0 0 -3.0617 14.1692 0 0 -3.0864 14.1829 0 0 -3.1149 14.1967 0 0 -3.1472 14.2105 0 0 -3.1831 14.2239 0 0 -3.2221 14.2367 0 0 -3.265 14.2495 0 0 -3.3111 14.2617 0 0 -3.3604 14.2739 0 0 -3.4129 14.2854 0 0 -3.4683 14.2966 0 0 -3.5265 14.3071 0 0 -3.5877 14.3174 0 0 -3.6517 14.327 0 0 -3.7183 14.3363 0 0 -3.7868 14.3449 0 0 -3.8578 14.3529 0 0 -3.9311 14.3606 0 0 -4.006 14.3673 0 0 -4.0829 14.3737 0 0 -4.1619 14.3795 0 0 -4.2413 14.3846 0 0 -4.3226 14.3891 0 0 -4.4049 14.3926 0 0 -4.4881 14.3958 0 0 -4.5736 14.3981 0 0 -4.6581 14.4 0 0 -4.667 14.383 0 0 -4.7474 14.3843 0 0 -4.7429 14.4009 0 0 -4.7381 14.4176 0 0 -4.7336 14.4345 0 0 -4.7295 14.4512 0 0 -4.8277 14.358 0 0 -4.8277 14.3747 0 0 -4.8277 14.3965 0 0 -4.8277 14.4003 0 0 -4.8277 14.4003 0 0 -4.7291 14.4003 0 0 -4.7291 14.4989 0 0 -4.6305 14.4989 0 0 -4.532 14.4989 0 0 -4.532 14.4003 0 0 -4.5316 14.3017 0 0 -4.531 14.2034 0 0 -4.53 14.1052 0 0 -4.5284 14.0069 0 0 -4.5256 13.9089 0 0 -4.522 13.8104 0 0 -4.4199 13.8081 0 0 -4.3181 13.8049 0 0 -4.216 13.8007 0 0 -4.1142 13.7959 0 0 -4.1193 13.6945 0 0 -4.1248 13.5936 0 0 -4.1248 13.5933 0 0 -4.1296 13.5927 0 0 -4.1405 13.5917 0 0 -4.1533 13.5904 0 0 -4.0605 13.5847 0 0 -3.9699 13.5783 0 0 -3.8809 13.5709 0 0 -3.7941 13.5629 0 0 -3.7461 13.5623 0 0 -3.6395 13.5546 0 0 -3.5323 13.5427 0 0 -3.4011 13.5306 0 0 -3.3623 13.528 0 0 -3.3108 13.5223 0 0 -3.3335 13.5219 0 0 -3.2055 13.5056 0 0 -3.2801 13.5088 0 0 -3.1824 13.4935 0 0 -3.1085 13.48 0 0 -3.0377 13.4659 0 0 -2.9712 13.4512 0 0 -2.9087 13.4362 0 0 -2.8265 13.4285 0 0 -2.7573 13.4214 0 0 -2.7042 13.4163 0 0 -2.628 13.4083 0 0 -2.5278 13.3913 0 0 -2.4276 13.3741 0 0 -2.4276 13.2633 0 0 -2.4276 13.1474 0 0 -2.4276 13.0316 0 0 -2.4078 12.9339 0 0 -2.3537 12.8539 0 0 -2.273 12.8004 0 0 -2.1776 12.7819 0 0 -2.0778 12.7819 0 0 -2.0778 12.7867 0 0 -2.0778 12.7905 0 0 -2.0778 12.794 0 0 -2.0778 12.7966 0 0 -1.9779 12.7966 0 0 -1.878 12.7966 0 0 -1.7781 12.7966 0 0 -1.6783 12.7966 0 0 -1.5784 12.7966 0 0 -1.4817 12.8158 0 0 -1.4011 12.8702 0 0 -1.3473 12.9483 0 0 -1.3473 12.9448 0 0 -1.3473 12.941 0 0 -1.3473 12.9362 0 0 -1.3284 13.0258 0 0 -1.3284 13.0194 0 0 -1.3284 13.125 0 0 -1.3284 13.2233 0 0 -1.3284 13.3206 0 0 -1.3284 13.4173 0 0 -1.3284 13.513 0 0 -1.3284 13.6077 0 0 -1.3236 13.705 0 0 -1.3092 13.8017 0 0 -1.2852 13.8965 0 0 -1.2522 13.9883 0 0 -1.2103 14.0764 0 0 -1.1597 14.1602 0 0 -1.1015 14.2386 0 0 -1.0355 14.311 0 0 -0.9629 14.3763 0 0 -0.8838 14.4345 0 0 -0.7996 14.4845 0 0 -0.7109 14.5264 0 0 -0.6188 14.5594 0 0 -0.5234 14.5831 0 0 -0.4264 14.5972 0 0 -0.3284 14.602 0 0 -0.2628 14.6032 0 0 -0.1969 14.6042 0 0 -0.1312 14.6052 0 0 -0.0656 14.5956 0 0 -0.0656 14.5847 0 0 -0 14.5741 0 0 -0 14.5623 0 0 -0 14.6567 0 0 -0 14.7521 0 0 -0 14.7399 0 0 -0 14.8456 0 0 -0 14.9582 0 0 -0 15.0661 0 0 -0 15.1503 0 0 -0 15.2342 0 0 -0 15.2175 0 0 -0 15.2002 0 0 -0.0963 15.2002 0 0 -0.1927 15.1016 0 0 -0.1965 14.9982 0 0 -0.2119 14.8852 0 0 -0.3172 14.7915 0 0 -0.2161 14.7787 0 0 -0.2177 14.6733 0 0 -0.331 14.5639 0 0 -0.4469 14.5591 0 0 -0.5525 14.5328 0 0 -0.6207 14.4189 0 0 -0.7129 14.3859 0 0 -0.8015 14.3446 0 0 -0.8854 14.295 0 0 -0.9638 14.2374 0 0 -1.0278 14.3049 0 0 -1.1075 14.2332 0 0 -1.1933 14.3071 0 0 -1.2714 14.2201 0 0 -1.3403 14.1256 0 0 -1.4443 14.1701 0 0 -1.5701 14.2287 0 0 -1.7068 14.295 0 0 -1.8729 14.3667 0 0 -1.9862 14.4217 0 0 -2.0355 14.4554 0 0 -2.0554 14.3552 0 0 -2.1443 14.3126 0 0 -2.1203 14.191 0 0 -2.1239 14.0632 0 0 -2.2103 14.078 0 0 -2.3143 14.0799 0 0 -2.3172 13.9854 0 0 -2.4142 13.9874 0 0 -2.5105 13.9886 0 0 -2.6069 13.989 0 0 -2.7026 13.989 0 0 -2.7052 14.0059 0 0 -2.7052 14.0997 0 0 -2.8012 14.0975 0 0 -2.8979 14.0972 0 0 -2.9945 14.0965 0 0 -2.9039 14.1125 0 0 -2.9139 14.1279 0 0 -2.9279 14.1433 0 0 -2.9462 14.1583 0 0 -2.9683 14.1733 0 0 -2.9942 14.1881 0 0 -3.0243 14.2028 0 0 -3.0582 14.2172 0 0 -3.0957 14.231 0 0 -3.1373 14.2447 0 0 -3.1821 14.2582 0 0 -3.2304 14.2713 0 0 -3.2823 14.2838 0 0 -3.3377 14.2959 0 0 -3.3963 14.3078 0 0 -3.4577 14.319 0 0 -3.522 14.3299 0 0 -3.5893 14.3398 0 0 -3.6594 14.3497 0 0 -3.7317 14.3587 0 0 -3.8063 14.3673 0 0 -3.8834 14.375 0 0 -3.9625 14.3824 0 0 -4.0432 14.3891 0 0 -4.1264 14.3952 0 0 -4.2103 14.4003 0 0 -4.2957 14.4051 0 0 -4.3825 14.4089 0 0 -4.4699 14.4121 0 0 -4.5598 14.4147 0 0 -4.6488 14.4166 0 0 -4.6402 14.4333 0 0 -4.6315 14.4499 0 0 -4.7295 14.3577 0 0 -4.8277 14.2646 0 0 -4.8277 14.2812 0 0 -4.8277 14.2953 0 0 -4.8277 14.3017 0 0 -4.8277 14.3017 0 0 -4.7291 14.3017 0 0 -4.6305 14.4003 0 0 -4.6305 14.3017 0 0 -4.6302 14.2031 0 0 -4.6299 14.1045 0 0 -4.6296 14.0063 0 0 -4.6286 13.9077 0 0 -4.627 13.8094 0 0 -4.6254 13.7108 0 0 -4.5243 13.7092 0 0 -4.4231 13.7066 0 0 -4.322 13.7034 0 0 -4.2205 13.6993 0 0 -4.225 13.5984 0 0 -4.2205 13.5978 0 0 -4.2234 13.5975 0 0 -4.2336 13.5965 0 0 -4.2471 13.5956 0 0 -4.2461 13.689 0 0 -4.1523 13.6839 0 0 -4.0601 13.6782 0 0 -3.9695 13.6718 0 0 -3.8806 13.6644 0 0 -3.7938 13.6564 0 0 -3.7096 13.5546 0 0 -3.6738 13.5549 0 0 -3.5813 13.5444 0 0 -3.4465 13.5322 0 0 -3.3796 13.5232 0 0 -3.4241 13.5239 0 0 -3.33 13.5104 0 0 -3.2557 13.4979 0 0 -3.1847 13.4851 0 0 -3.1171 13.4717 0 0 -3.0534 13.4576 0 0 -2.9932 13.4432 0 0 -2.8508 13.4205 0 0 -2.7727 13.4128 0 0 -2.6709 13.4022 0 0 -2.6331 13.4035 0 0 -2.5275 13.3849 0 0 -2.4276 13.3677 0 0 -2.4276 13.2575 0 0 -2.4276 13.1417 0 0 -2.4276 13.0258 0 0 -2.4078 12.9285 0 0 -2.3537 12.8485 0 0 -2.273 12.795 0 0 -2.1776 12.7761 0 0 -2.0778 12.7761 0 0 -1.9779 12.7761 0 0 -1.9779 12.7819 0 0 -1.9779 12.7867 0 0 -1.9779 12.7905 0 0 -1.9779 12.794 0 0 -1.878 12.794 0 0 -1.7781 12.794 0 0 -1.6783 12.794 0 0 -1.5784 12.794 0 0 -1.4817 12.8132 0 0 -1.4011 12.8677 0 0 -1.4011 12.8641 0 0 -1.4011 12.8603 0 0 -1.4011 12.8552 0 0 -1.3473 12.9304 0 0 -1.3473 12.924 0 0 -1.3284 13.0123 0 0 -1.3284 13.1177 0 0 -1.3284 13.2153 0 0 -1.3284 13.312 0 0 -1.3284 13.4077 0 0 -1.3284 13.5024 0 0 -1.3284 13.5962 0 0 -1.3236 13.6935 0 0 -1.3092 13.7902 0 0 -1.2852 13.8846 0 0 -1.2522 13.9765 0 0 -1.2103 14.0645 0 0 -1.1597 14.1484 0 0 -1.1015 14.2265 0 0 -1.0355 14.2988 0 0 -0.9629 14.3644 0 0 -0.8838 14.4224 0 0 -0.7996 14.4726 0 0 -0.7109 14.5142 0 0 -0.6188 14.5469 0 0 -0.5234 14.5706 0 0 -0.4264 14.585 0 0 -0.3284 14.5898 0 0 -0.2628 14.5914 0 0 -0.1969 14.5933 0 0 -0.1312 14.5946 0 0 -0.1312 14.5834 0 0 -0.0656 14.5731 0 0 -0.0656 14.561 0 0 -0 14.5498 0 0 -0 14.6445 0 0 -0 14.6314 0 0 -0 14.7274 0 0 -0 14.8299 0 0 -0 14.8174 0 0 -0 14.9425 0 0 -0 15.0511 0 0 -0 15.1343 0 0 -0 15.1167 0 0 -0 15.0949 0 0 -0.0963 15.0988 0 0 -0 14.9893 0 0 -0 14.884 0 0 -0 14.7783 0 0 -0 14.6727 0 0 -0.1079 14.6727 0 0 -0.1085 14.5674 0 0 -0.2189 14.5674 0 0 -0.2189 14.4618 0 0 -0.3284 14.4618 0 0 -0.4289 14.4566 0 0 -0.5256 14.4422 0 0 -0.6188 14.4384 0 0 -0.7109 14.4057 0 0 -0.7996 14.3641 0 0 -0.8838 14.3145 0 0 -0.9629 14.2569 0 0 -1.0355 14.1919 0 0 -1.0365 14.1724 0 0 -1.1018 14.101 0 0 -1.1789 14.1548 0 0 -1.242 14.0696 0 0 -1.2967 13.9784 0 0 -1.3992 14.0248 0 0 -1.5016 14.0562 0 0 -1.6341 14.0968 0 0 -1.7814 14.1375 0 0 -1.9792 14.2271 0 0 -1.9958 14.036 0 0 -2.1651 13.9538 0 0 -2.2343 13.9765 0 0 -2.2577 13.8929 0 0 -2.3294 13.8933 0 0 -2.4158 13.8945 0 0 -2.5115 13.8952 0 0 -2.6072 13.8955 0 0 -2.7026 13.8955 0 0 -2.7052 13.9125 0 0 -2.7119 13.9294 0 0 -2.7119 14.0229 0 0 -2.7119 14.1167 0 0 -2.8073 14.1138 0 0 -2.8178 14.1298 0 0 -2.8326 14.1461 0 0 -2.8518 14.1618 0 0 -2.8748 14.1778 0 0 -2.9023 14.1932 0 0 -2.9337 14.2086 0 0 -2.9692 14.2236 0 0 -3.0089 14.2383 0 0 -3.0525 14.2527 0 0 -3.0995 14.2668 0 0 -3.1504 14.2806 0 0 -3.2051 14.2937 0 0 -3.2631 14.3068 0 0 -3.3245 14.319 0 0 -3.3892 14.3308 0 0 -3.4567 14.342 0 0 -3.5275 14.3529 0 0 -3.6008 14.3632 0 0 -3.677 14.3728 0 0 -3.7554 14.3814 0 0 -3.8364 14.3897 0 0 -3.9196 14.3974 0 0 -4.0044 14.4045 0 0 -4.0915 14.4109 0 0 -4.1795 14.4163 0 0 -4.2695 14.4211 0 0 -4.3604 14.4253 0 0 -4.4529 14.4288 0 0 -4.5467 14.4313 0 0 -4.5342 14.448 0 0 -4.6315 14.3564 0 0 -4.7295 14.2643 0 0 -4.8277 14.1708 0 0 -4.8277 14.1877 0 0 -4.8277 14.199 0 0 -4.8277 14.2041 0 0 -4.8277 14.2031 0 0 -4.7291 14.2031 0 0 -4.7291 14.1045 0 0 -4.7288 14.0059 0 0 -4.7288 13.9073 0 0 -4.7285 13.8091 0 0 -4.7279 13.7105 0 0 -4.627 13.6103 0 0 -4.5265 13.6084 0 0 -4.426 13.6058 0 0 -4.3255 13.6026 0 0 -4.3172 13.602 0 0 -4.3133 13.6013 0 0 -4.321 13.6007 0 0 -4.3421 13.5997 0 0 -4.3412 13.6935 0 0 -4.2461 13.7825 0 0 -4.1523 13.7777 0 0 -4.0601 13.7719 0 0 -3.9695 13.7652 0 0 -3.8806 13.7582 0 0 -3.7938 13.7502 0 0 -3.709 13.7415 0 0 -3.709 13.6481 0 0 -3.6274 13.545 0 0 -3.498 13.5341 0 0 -3.5477 13.5351 0 0 -3.4705 13.5245 0 0 -3.3966 13.5133 0 0 -3.3255 13.5018 0 0 -3.2576 13.4893 0 0 -3.1933 13.4765 0 0 -3.1322 13.463 0 0 -3.0749 13.4493 0 0 -2.9372 13.4282 0 0 -2.7973 13.4048 0 0 -2.7295 13.3987 0 0 -2.6347 13.3856 0 0 -2.5275 13.3776 0 0 -2.4276 13.3603 0 0 -2.4276 13.2511 0 0 -2.4276 13.1353 0 0 -2.4276 13.0194 0 0 -2.4078 12.9221 0 0 -2.3537 12.8421 0 0 -2.273 12.7886 0 0 -2.1776 12.77 0 0 -2.0778 12.77 0 0 -1.9779 12.77 0 0 -1.878 12.77 0 0 -1.878 12.7761 0 0 -1.878 12.7819 0 0 -1.878 12.7867 0 0 -1.878 12.7905 0 0 -1.7781 12.7905 0 0 -1.6783 12.7905 0 0 -1.5784 12.7905 0 0 -1.4817 12.81 0 0 -1.4817 12.8059 0 0 -1.4817 12.8011 0 0 -1.4011 12.8497 0 0 -1.4011 12.8433 0 0 -1.3473 12.917 0 0 -1.3284 13.0043 0 0 -1.3284 13.11 0 0 -1.3284 13.2066 0 0 -1.3284 13.3024 0 0 -1.3284 13.3971 0 0 -1.3284 13.4912 0 0 -1.3284 13.584 0 0 -1.3236 13.6814 0 0 -1.3092 13.7777 0 0 -1.2852 13.8721 0 0 -1.2522 13.964 0 0 -1.2103 14.052 0 0 -1.1597 14.1356 0 0 -1.1015 14.214 0 0 -1.0355 14.286 0 0 -0.9629 14.3513 0 0 -0.8838 14.4096 0 0 -0.7996 14.4595 0 0 -0.7109 14.5011 0 0 -0.6188 14.5341 0 0 -0.5234 14.5578 0 0 -0.4264 14.5719 0 0 -0.3284 14.5767 0 0 -0.2625 14.5792 0 0 -0.1969 14.5815 0 0 -0.1312 14.5715 0 0 -0.1312 14.5591 0 0 -0.0656 14.5482 0 0 -0 14.5367 0 0 -0 14.5229 0 0 -0 14.6183 0 0 -0 14.7156 0 0 -0 14.705 0 0 -0 14.697 0 0 -0 14.8107 0 0 -0 14.9275 0 0 -0 15.0338 0 0 -0 15.0101 0 0 -0 14.9041 0 0 -0 14.7982 0 0 -0 14.6919 0 0 -0 14.5789 0 0 -0 14.5674 0 0 -0 14.4618 0 0 -0.1095 14.4618 0 0 -0.0893 14.4784 0 0 -0.1437 14.4928 0 0 -0.1917 14.4925 0 0 -0.2551 14.4931 0 0 -0.3284 14.4989 0 0 -0.4264 14.4941 0 0 -0.4264 14.5114 0 0 -0.5234 14.497 0 0 -0.6188 14.4736 0 0 -0.7109 14.4409 0 0 -0.7996 14.3993 0 0 -0.8838 14.3494 0 0 -0.9629 14.2918 0 0 -1.0355 14.2265 0 0 -1.1015 14.1378 0 0 -1.1597 14.06 0 0 -1.2103 13.9595 0 0 -1.2522 13.8718 0 0 -1.2852 13.7809 0 0 -1.2852 13.7623 0 0 -1.3771 13.7835 0 0 -1.4846 13.8107 0 0 -1.5858 13.8238 0 0 -1.7177 13.8379 0 0 -1.7417 13.7089 0 0 -1.8857 13.715 0 0 -2.0717 13.7252 0 0 -2.1764 13.8296 0 0 -2.1674 13.7249 0 0 -2.2352 13.714 0 0 -2.3226 13.7076 0 0 -2.4174 13.7079 0 0 -2.5125 13.7082 0 0 -2.6075 13.7086 0 0 -2.7026 13.7086 0 0 -2.7052 13.7255 0 0 -2.7119 13.7425 0 0 -2.7231 13.7595 0 0 -2.7388 13.7761 0 0 -2.7388 13.8699 0 0 -2.7388 13.9634 0 0 -2.7388 14.0568 0 0 -2.7592 14.0735 0 0 -2.7839 14.0901 0 0 -2.813 14.1064 0 0 -2.8463 14.1224 0 0 -2.8838 14.1381 0 0 -2.9254 14.1535 0 0 -2.9712 14.1685 0 0 -3.0211 14.1833 0 0 -3.0745 14.1977 0 0 -3.1319 14.2114 0 0 -3.193 14.2249 0 0 -3.2573 14.2377 0 0 -3.3252 14.2498 0 0 -3.3963 14.2617 0 0 -3.4702 14.2729 0 0 -3.547 14.2835 0 0 -3.6267 14.2934 0 0 -3.709 14.3027 0 0 -3.7938 14.3113 0 0 -3.8806 14.3193 0 0 -3.9695 14.3267 0 0 -4.0601 14.3331 0 0 -4.1523 14.3388 0 0 -4.2461 14.344 0 0 -4.3412 14.3481 0 0 -4.4372 14.3516 0 0 -4.5342 14.2611 0 0 -4.6315 14.1692 0 0 -4.7295 14.077 0 0 -4.8277 13.9838 0 0 -4.8277 13.9979 0 0 -4.8277 14.0079 0 0 -4.8277 14.0085 0 0 -4.8277 13.9134 0 0 -4.8277 13.8398 0 0 -4.8277 13.7527 0 0 -4.8277 13.6113 0 0 -4.7343 13.6109 0 0 -4.7304 13.6093 0 0 -4.6325 13.6081 0 0 -4.5352 13.6061 0 0 -4.5342 13.6996 0 0 -4.4372 13.7905 0 0 -4.3412 13.8805 0 0 -4.2461 13.9698 0 0 -4.1523 13.9646 0 0 -4.0601 13.9589 0 0 -3.9695 13.9525 0 0 -3.8806 13.9451 0 0 -3.7938 13.9371 0 0 -3.709 13.9285 0 0 -3.6267 13.9192 0 0 -3.547 13.9093 0 0 -3.547 13.8158 0 0 -3.547 13.7223 0 0 -3.4702 13.7118 0 0 -3.3963 13.7006 0 0 -3.3252 13.6887 0 0 -3.2573 13.6762 0 0 -3.193 13.6634 0 0 -3.1319 13.65 0 0 -3.0745 13.6362 0 0 -3.0211 13.6221 0 0 -3.0211 13.5283 0 0 -2.9712 13.5136 0 0 -2.9257 13.4051 0 0 -2.838 13.3981 0 0 -2.7849 13.3817 0 0 -2.6709 13.3731 0 0 -2.5333 13.3507 0 0 -2.4276 13.3337 0 0 -2.4276 13.3433 0 0 -2.4276 13.2358 0 0 -2.4276 13.1202 0 0 -2.4276 13.0043 0 0 -2.4078 12.9074 0 0 -2.3537 12.8273 0 0 -2.273 12.7742 0 0 -2.1776 12.7553 0 0 -2.0778 12.7553 0 0 -1.9779 12.7553 0 0 -1.878 12.7553 0 0 -1.7781 12.7553 0 0 -1.6783 12.7553 0 0 -1.6783 12.763 0 0 -1.6783 12.77 0 0 -1.6783 12.7761 0 0 -1.6783 12.7819 0 0 -1.5784 12.7761 0 0 -1.5784 12.77 0 0 -1.4817 12.7822 0 0 -1.4011 12.8286 0 0 -1.3473 12.9006 0 0 -1.3284 12.9864 0 0 -1.3284 13.0917 0 0 -1.3284 13.1868 0 0 -1.3284 13.2806 0 0 -1.3284 13.3741 0 0 -1.3284 13.4662 0 0 -1.3284 13.5575 0 0 -1.3236 13.6545 0 0 -1.3092 13.7505 0 0 -1.2852 13.8449 0 0 -1.2522 13.9365 0 0 -1.2103 14.0245 0 0 -1.1597 14.1077 0 0 -1.1015 14.1858 0 0 -1.0355 14.2579 0 0 -0.9629 14.3232 0 0 -0.8838 14.3811 0 0 -0.7996 14.431 0 0 -0.7109 14.4726 0 0 -0.6188 14.5053 0 0 -0.5234 14.529 0 0 -0.4264 14.5434 0 0 -0.3284 14.5482 0 0 -0.2625 14.5523 0 0 -0.1969 14.5424 0 0 -0.1316 14.5325 0 0 -0.1325 14.5191 0 0 -0.0695 14.5075 0 0 -0 14.4934 0 0 -0 14.4781 0 0 -0.0755 14.4934 0 0 -0.1351 14.5056 0 0 -0.1946 14.5018 0 0 -0.2602 14.5075 0 0 -0.3284 14.5162 0 0 -0.3284 14.5325 0 0 -0.4264 14.5277 0 0 -0.5234 14.5133 0 0 -0.6188 14.4899 0 0 -0.7109 14.4573 0 0 -0.7996 14.4157 0 0 -0.8838 14.3657 0 0 -0.9629 14.3078 0 0 -1.0355 14.2425 0 0 -1.1015 14.1548 0 0 -1.1597 14.0767 0 0 -1.2103 13.9768 0 0 -1.2522 13.8891 0 0 -1.2852 13.7982 0 0 -1.3092 13.6868 0 0 -1.3092 13.6689 0 0 -1.41 13.6887 0 0 -1.5109 13.699 0 0 -1.6143 13.705 0 0 -1.6328 13.586 0 0 -1.7609 13.5898 0 0 -1.9097 13.586 0 0 -2.1146 13.6061 0 0 -2.1779 13.6602 0 0 -2.2324 13.6433 0 0 -2.3217 13.6253 0 0 -2.4177 13.6148 0 0 -2.5125 13.6148 0 0 -2.6075 13.6148 0 0 -2.7026 13.6148 0 0 -2.7052 13.6317 0 0 -2.7119 13.6487 0 0 -2.7231 13.666 0 0 -2.7388 13.6826 0 0 -2.7592 13.6993 0 0 -2.7592 13.7927 0 0 -2.7592 13.8865 0 0 -2.7592 13.98 0 0 -2.7839 13.9963 0 0 -2.813 14.0127 0 0 -2.8463 14.0287 0 0 -2.8838 14.0447 0 0 -2.9254 14.06 0 0 -2.9712 14.0751 0 0 -3.0211 14.0898 0 0 -3.0745 14.1039 0 0 -3.1319 14.118 0 0 -3.193 14.1311 0 0 -3.2573 14.1439 0 0 -3.3252 14.1564 0 0 -3.3963 14.1682 0 0 -3.4702 14.1794 0 0 -3.547 14.19 0 0 -3.6267 14.1999 0 0 -3.709 14.2092 0 0 -3.7938 14.2178 0 0 -3.8806 14.2258 0 0 -3.9695 14.2329 0 0 -4.0601 14.2396 0 0 -4.1523 14.2454 0 0 -4.2461 14.2505 0 0 -4.3412 14.2547 0 0 -4.4372 14.1647 0 0 -4.5342 14.0738 0 0 -4.6315 13.9822 0 0 -4.7295 13.8901 0 0 -4.8277 13.7966 0 0 -4.7295 13.7963 0 0 -4.6315 13.795 0 0 -4.5342 13.8869 0 0 -4.4372 13.9774 0 0 -4.3412 14.0674 0 0 -4.2461 14.1567 0 0 -4.3412 14.1612 0 0 -4.4372 14.0712 0 0 -4.5342 13.9803 0 0 -4.6315 13.8888 0 0 -4.1523 14.1519 0 0 -4.0601 14.1461 0 0 -3.9695 14.1394 0 0 -3.8806 14.1324 0 0 -3.7938 14.1244 0 0 -3.709 14.1157 0 0 -3.6267 14.1064 0 0 -3.547 14.0965 0 0 -3.4702 14.086 0 0 -3.3963 14.0748 0 0 -3.3963 13.981 0 0 -3.3963 13.8875 0 0 -3.3252 13.8757 0 0 -3.2573 13.8635 0 0 -3.193 13.8507 0 0 -3.1319 13.8372 0 0 -3.0745 13.8235 0 0 -3.0211 13.8091 0 0 -2.9712 13.7943 0 0 -2.9254 13.7793 0 0 -2.9254 13.6858 0 0 -2.8838 13.6705 0 0 -2.8838 13.5767 0 0 -2.8838 13.4832 0 0 -2.8463 13.3741 0 0 -2.813 13.3581 0 0 -2.6683 13.3427 0 0 -2.6126 13.3433 0 0 -2.54 13.3305 0 0 -2.4276 13.3123 0 0 -2.4276 13.2073 0 0 -2.4276 13.0917 0 0 -2.4276 13.102 0 0 -2.4276 12.9864 0 0 -2.4078 12.8894 0 0 -2.3537 12.8097 0 0 -2.273 12.7563 0 0 -2.1776 12.7377 0 0 -2.0778 12.7377 0 0 -1.9779 12.7377 0 0 -1.878 12.7377 0 0 -1.7781 12.7377 0 0 -1.6783 12.7377 0 0 -1.5784 12.7377 0 0 -1.4817 12.7569 0 0 -1.4817 12.7662 0 0 -1.4011 12.8107 0 0 -1.3473 12.8811 0 0 -1.3284 12.9653 0 0 -1.3284 13.0703 0 0 -1.3284 13.1638 0 0 -1.3284 13.2563 0 0 -1.3284 13.3475 0 0 -1.3284 13.4381 0 0 -1.3284 13.5274 0 0 -1.3236 13.6244 0 0 -1.3092 13.7204 0 0 -1.2852 13.8145 0 0 -1.3092 13.7041 0 0 -1.3236 13.5911 0 0 -1.3236 13.5735 0 0 -1.426 13.5831 0 0 -1.5256 13.586 0 0 -1.531 13.4739 0 0 -1.6392 13.4698 0 0 -1.7641 13.4566 0 0 -1.8985 13.4365 0 0 -2.0598 13.4218 0 0 -2.2151 13.5629 0 0 -2.3156 13.5386 0 0 -2.4126 13.5226 0 0 -2.5128 13.5213 0 0 -2.6079 13.5213 0 0 -2.7026 13.5213 0 0 -2.7052 13.5383 0 0 -2.7119 13.5552 0 0 -2.7231 13.5722 0 0 -2.7388 13.5892 0 0 -2.7592 13.6058 0 0 -2.7839 13.6221 0 0 -2.7839 13.7159 0 0 -2.7839 13.8094 0 0 -2.7839 13.9029 0 0 -2.813 13.9192 0 0 -2.8463 13.9352 0 0 -2.8838 13.9509 0 0 -2.9254 13.9666 0 0 -2.9712 13.9816 0 0 -3.0211 13.9963 0 0 -3.0745 14.0104 0 0 -3.1319 14.0245 0 0 -3.193 14.0376 0 0 -3.2573 14.0504 0 0 -3.3252 14.0629 0 0 -3.3252 13.9694 0 0 -3.2573 13.957 0 0 -3.193 13.9442 0 0 -3.1319 13.9307 0 0 -3.0745 13.9169 0 0 -3.0211 13.9025 0 0 -2.9712 13.8878 0 0 -2.9254 13.8728 0 0 -2.8463 13.8417 0 0 -2.8838 13.8574 0 0 -2.8838 13.7639 0 0 -2.8463 13.7483 0 0 -2.8463 13.6545 0 0 -2.8463 13.561 0 0 -2.8463 13.4675 0 0 -2.813 13.4515 0 0 -2.7839 13.3417 0 0 -2.6706 13.3267 0 0 -2.5429 13.3203 0 0 -2.4276 13.3004 0 0 -2.4276 13.1961 0 0 -2.4276 13.0808 0 0 -2.4276 12.9653 0 0 -2.4276 12.9762 0 0 -2.4078 12.8792 0 0 -2.3537 12.7995 0 0 -2.273 12.7463 0 0 -2.1776 12.7278 0 0 -2.0778 12.7278 0 0 -1.9779 12.7278 0 0 -1.878 12.7278 0 0 -1.7781 12.7278 0 0 -1.6783 12.7278 0 0 -1.5784 12.7278 0 0 -1.4817 12.747 0 0 -1.4011 12.8008 0 0 -1.3473 12.8705 0 0 -1.3284 12.9538 0 0 -1.3284 13.0588 0 0 -1.3284 13.1513 0 0 -1.3284 13.2428 0 0 -1.3284 13.3334 0 0 -1.3284 13.423 0 0 -1.3284 13.5114 0 0 -1.3236 13.6084 0 0 -1.3284 13.4947 0 0 -1.3284 13.4768 0 0 -1.4324 13.4794 0 0 -1.4308 13.3728 0 0 -1.5294 13.3619 0 0 -1.6357 13.3443 0 0 -1.7593 13.3187 0 0 -1.8892 13.2902 0 0 -2.056 13.2249 0 0 -2.1994 13.2611 0 0 -2.1952 13.4105 0 0 -2.3089 13.4032 0 0 -2.4091 13.4099 0 0 -2.5093 13.4173 0 0 -2.6079 13.4278 0 0 -2.7026 13.4278 0 0 -2.7052 13.4448 0 0 -2.7119 13.4618 0 0 -2.7231 13.4787 0 0 -2.7388 13.4957 0 0 -2.7592 13.5123 0 0 -2.7839 13.5287 0 0 -2.813 13.545 0 0 -2.813 13.6385 0 0 -2.813 13.7322 0 0 -2.813 13.8257 0 0 -1.4283 13.2684 0 0 -1.525 13.2572 0 0 -1.6306 13.2342 0 0 -1.7554 13.2041 0 0 -1.8809 13.1727 0 0 -1.9904 13.1375 0 0 -2.1101 13.1317 0 0 -2.2112 13.1612 0 0 -2.3175 13.1775 0 0 -2.3153 13.2771 0 0 -2.4123 13.2944 0 0 -2.5109 13.311 0 0 -2.6104 13.3244 0 0 -2.7026 13.334 0 0 -2.7052 13.3513 0 0 -2.7119 13.3683 0 0 -2.7231 13.3853 0 0 -2.7388 13.4019 0 0 -2.7592 13.4186 0 0 -2.7839 13.4352 0 0 -2.7592 13.3251 0 0 -2.6536 13.3107 0 0 -2.5384 13.3065 0 0 -2.4276 13.288 0 0 -2.4276 13.1846 0 0 -2.4276 13.069 0 0 -2.4276 12.9538 0 0 -2.4078 12.8571 0 0 -2.4078 12.8683 0 0 -2.3537 12.7889 0 0 -2.273 12.7358 0 0 -2.1776 12.7169 0 0 -2.0778 12.7169 0 0 -1.9779 12.7169 0 0 -1.878 12.7169 0 0 -1.7781 12.7169 0 0 -1.6783 12.7169 0 0 -1.5784 12.7169 0 0 -1.4817 12.7364 0 0 -1.4011 12.7902 0 0 -1.3473 12.859 0 0 -1.3284 12.9413 0 0 -1.3284 13.0463 0 0 -1.3284 13.1378 0 0 -1.3284 13.2284 0 0 -1.3284 13.3184 0 0 -1.3284 13.407 0 0 -1.3284 13.3904 0 0 -1.3284 13.3728 0 0 -1.3284 13.2684 0 0 -1.4244 13.1644 0 0 -1.516 13.1442 0 0 -1.6251 13.1234 0 0 -1.7497 13.0965 0 0 -1.871 13.068 0 0 -1.9683 13.0616 0 0 -2.0499 13.1029 0 0 -2.1248 13.0597 0 0 -2.2208 13.0722 0 0 -2.3194 13.0805 0 0 -2.4276 13.0805 0 0 -2.4276 13.1951 0 0 -2.5192 13.2108 0 0 -2.611 13.2262 0 0 -2.7026 13.2406 0 0 -2.7052 13.2575 0 0 -2.7119 13.2748 0 0 -2.7231 13.2918 0 0 -2.7391 13.3084 0 0 -2.6424 13.296 0 0 -2.5278 13.2777 0 0 -2.5278 13.2918 0 0 -2.4276 13.2745 0 0 -2.4276 13.1718 0 0 -2.4276 13.0565 0 0 -2.4276 12.9413 0 0 -2.4078 12.8446 0 0 -2.3537 12.7652 0 0 -2.3537 12.7774 0 0 -2.273 12.7243 0 0 -2.1776 12.7057 0 0 -2.0778 12.7057 0 0 -1.9779 12.7057 0 0 -1.878 12.7057 0 0 -1.7781 12.7057 0 0 -1.6783 12.7057 0 0 -1.5784 12.7057 0 0 -1.4817 12.7249 0 0 -1.4011 12.7787 0 0 -1.3473 12.8469 0 0 -1.3284 12.9282 0 0 -1.3284 13.0328 0 0 -1.3284 13.1237 0 0 -1.3284 13.2137 0 0 -1.3284 13.3024 0 0 -1.3284 13.1977 0 0 -1.3284 13.1814 0 0 -1.3284 13.1644 0 0 -1.3284 13.06 0 0 -1.4145 13.0466 0 0 -1.5006 13.028 0 0 -1.595 12.9983 0 0 -1.7279 12.9384 0 0 -1.8668 12.9631 0 0 -1.9564 12.9557 0 0 -2.0483 13.0492 0 0 -2.128 12.9663 0 0 -2.2119 12.9682 0 0 -2.3166 12.9659 0 0 -2.4276 12.9659 0 0 -2.4276 13.0975 0 0 -2.4276 13.2134 0 0 -2.5195 13.2281 0 0 -2.611 13.2428 0 0 -2.6162 13.2611 0 0 -2.6267 13.2783 0 0 -2.5278 13.2627 0 0 -2.4276 13.2454 0 0 -2.4276 13.2601 0 0 -2.4276 13.1586 0 0 -2.4276 13.0434 0 0 -2.4276 12.9282 0 0 -2.4078 12.8315 0 0 -2.3537 12.7524 0 0 -2.273 12.6993 0 0 -2.273 12.7121 0 0 -2.1776 12.6935 0 0 -2.0778 12.6935 0 0 -1.9779 12.6935 0 0 -1.878 12.6935 0 0 -1.7781 12.6935 0 0 -1.6783 12.6935 0 0 -1.5784 12.6935 0 0 -1.4817 12.7127 0 0 -1.4011 12.7665 0 0 -1.3473 12.8337 0 0 -1.3284 12.9144 0 0 -1.3284 13.0191 0 0 -1.3284 13.1087 0 0 -1.3284 13.0933 0 0 -1.3284 13.077 0 0 -1.3284 12.9723 0 0 -1.3284 12.9557 0 0 -1.4078 12.9445 0 0 -1.4856 12.9275 0 0 -1.5666 12.9019 0 0 -1.6562 12.8619 0 0 -1.709 12.8283 0 0 -1.773 12.852 0 0 -1.8719 12.875 0 0 -1.9641 12.8638 0 0 -2.0493 12.9576 0 0 -2.1312 12.8661 0 0 -2.209 12.8677 0 0 -2.3137 12.8699 0 0 -2.4276 12.8517 0 0 -2.4276 12.9829 0 0 -2.4276 13.1138 0 0 -2.4276 13.2297 0 0 -2.5259 13.2467 0 0 -2.4276 13.1295 0 0 -2.4276 13.1445 0 0 -2.4276 13.0293 0 0 -2.4276 12.9144 0 0 -2.4078 12.8177 0 0 -2.3537 12.7387 0 0 -2.273 12.6858 0 0 -2.1776 12.667 0 0 -2.1776 12.6807 0 0 -2.0778 12.6807 0 0 -1.9779 12.6807 0 0 -1.878 12.6807 0 0 -1.7781 12.6807 0 0 -1.6783 12.6807 0 0 -1.5784 12.6807 0 0 -1.4817 12.6999 0 0 -1.4011 12.7534 0 0 -1.4011 12.7399 0 0 -1.3473 12.8056 0 0 -1.3284 12.8846 0 0 -1.3284 12.9887 0 0 -1.3284 12.8683 0 0 -1.3284 12.8517 0 0 -1.403 12.8536 0 0 -1.4705 12.8424 0 0 -1.5442 12.82 0 0 -1.6331 12.7979 0 0 -1.7023 12.7774 0 0 -1.7833 12.7816 0 0 -1.8777 12.787 0 0 -1.9705 12.7838 0 0 -2.0502 12.8552 0 0 -2.0589 12.78 0 0 -2.145 12.78 0 0 -2.2208 12.78 0 0 -2.307 12.7896 0 0 -2.4087 12.7575 0 0 -2.4276 12.8683 0 0 -2.4276 12.9992 0 0 -2.4276 12.8843 0 0 -2.4078 12.7883 0 0 -2.3537 12.7092 0 0 -2.273 12.6564 0 0 -2.1776 12.6375 0 0 -2.0778 12.6375 0 0 -1.9779 12.6375 0 0 -1.9779 12.6529 0 0 -1.878 12.6529 0 0 -1.7781 12.6529 0 0 -1.6783 12.6529 0 0 -1.5784 12.6529 0 0 -1.4817 12.6721 0 0 -1.4011 12.7255 0 0 -1.3473 12.7902 0 0 -1.4011 12.7105 0 0 -1.4011 12.6945 0 0 -1.4017 12.6772 0 0 -1.4827 12.6241 0 0 -1.5784 12.6055 0 0 -1.6783 12.6055 0 0 -1.7781 12.6055 0 0 -1.878 12.6055 0 0 -1.9779 12.6055 0 0 -2.0778 12.6055 0 0 -2.1776 12.6055 0 0 -2.2737 12.6241 0 0 -1.7781 12.6218 0 0 -1.6783 12.6218 0 0 -1.5784 12.6218 0 0 -1.4817 12.641 0 0 -1.4817 12.6567 0 0 -1.5784 12.6375 0 0 -1.6783 12.6375 0 0 -1.7781 12.6375 0 0 -1.878 12.6375 0 0 -1.878 12.6218 0 0 -1.9779 12.6218 0 0 -2.0778 12.6218 0 0 -2.1776 12.6218 0 0 -2.273 12.6404 0 0 -2.3537 12.6932 0 0 -2.4078 12.7723 0 0 -2.3547 12.6775 0 0 -2.2929 12.7326 0 0 -2.2324 12.7127 0 0 -2.1604 12.6945 0 0 -2.0704 12.6894 0 0 -1.975 12.6897 0 0 -1.8739 12.6932 0 0 -1.7781 12.6967 0 0 -1.6892 12.7031 0 0 -1.6072 12.7121 0 0 -1.5227 12.73 0 0 -1.4539 12.7607 0 0 -1.4036 12.7886 0 0 -1.3473 12.7572 0 0 -1.3473 12.7742 0 0 -1.4817 12.6862 0 0 -1.5784 12.667 0 0 -1.6783 12.667 0 0 -1.7781 12.667 0 0 -1.878 12.667 0 0 -1.9779 12.667 0 0 -2.0778 12.667 0 0 -2.0778 12.6529 0 0 -2.1776 12.6529 0 0 -2.273 12.6714 0 0 -2.3537 12.7243 0 0 -2.4078 12.8033 0 0 -2.4276 12.9 0 0 -2.4276 13.0146 0 0 -1.3284 13.0043 0 0 -1.3284 12.9 0 0 -1.3473 12.82 0 0 -1.3284 13.2857 0 0 -1.2522 13.9057 0 0 -1.2103 13.9934 0 0 -1.1015 14.1708 0 0 -1.1597 14.0927 0 0 -1.2103 14.0095 0 0 -1.2522 13.9214 0 0 -1.2852 13.8302 0 0 -1.3092 13.7361 0 0 -1.3236 13.6397 0 0 -1.3284 13.5427 0 0 -1.3284 13.4525 0 0 -1.3284 13.3613 0 0 -1.3284 13.2687 0 0 -1.3284 13.1756 0 0 -1.3284 13.0815 0 0 -1.3284 12.9762 0 0 -1.3473 12.8913 0 0 -1.4011 12.82 0 0 -1.4817 12.7745 0 0 -1.5784 12.763 0 0 -1.5784 12.7553 0 0 -1.5784 12.747 0 0 -1.6783 12.747 0 0 -1.7781 12.747 0 0 -1.878 12.747 0 0 -1.9779 12.747 0 0 -2.0778 12.747 0 0 -2.1776 12.747 0 0 -2.273 12.7655 0 0 -2.3537 12.8187 0 0 -2.4078 12.8987 0 0 -2.4276 12.9957 0 0 -2.4276 13.1116 0 0 -2.4276 13.2271 0 0 -2.4276 13.2175 0 0 -2.4276 13.3235 0 0 -2.5368 13.3404 0 0 -2.6383 13.3552 0 0 -2.7413 13.3651 0 0 -2.8841 13.3897 0 0 -2.9254 13.4986 0 0 -2.9254 13.5924 0 0 -2.9712 13.6074 0 0 -2.9712 13.7009 0 0 -3.0211 13.7156 0 0 -3.0745 13.7297 0 0 -3.1319 13.7438 0 0 -3.193 13.7569 0 0 -3.2573 13.77 0 0 -3.3252 13.7822 0 0 -3.3963 13.794 0 0 -3.4702 13.8052 0 0 -3.4702 13.8987 0 0 -3.4702 13.9922 0 0 -3.547 14.0027 0 0 -3.6267 14.013 0 0 -3.709 14.0223 0 0 -3.7938 14.0306 0 0 -3.8806 14.0386 0 0 -3.9695 14.0459 0 0 -4.0601 14.0523 0 0 -4.1523 14.0581 0 0 -4.2461 14.0632 0 0 -4.3412 13.9739 0 0 -4.4372 13.884 0 0 -4.5342 13.7934 0 0 -4.6315 13.7015 0 0 -4.7295 13.7028 0 0 -4.8277 13.7031 0 0 -4.8277 13.8337 0 0 -4.8277 13.866 0 0 -4.8277 13.9173 0 0 -4.8277 13.9064 0 0 -4.8277 13.8904 0 0 -4.7295 13.9835 0 0 -4.6315 14.0757 0 0 -4.5342 14.1676 0 0 -4.4372 14.2582 0 0 -0.2625 14.5376 0 0 -0.1965 14.5283 0 0 -0.1959 14.5146 0 0 -0.2618 14.5229 0 0 -4.8277 13.6097 0 0 -0 14.5914 0 0 -0 14.6048 0 0 -0 14.5085 0 0 -0.0669 14.5213 0 0 -0.0659 14.5351 0 0 -0.1312 14.5459 0 0 -0.1969 14.5559 0 0 -0.1969 14.569 0 0 -0.2625 14.5661 0 0 -0.3284 14.5629 0 0 -0.4264 14.5581 0 0 -0.5234 14.5437 0 0 -0.6188 14.52 0 0 -0.7109 14.4874 0 0 -0.7996 14.4457 0 0 -0.8838 14.3958 0 0 -0.9629 14.3379 0 0 -1.0355 14.2723 0 0 -1.1015 14.2002 0 0 -1.1597 14.1221 0 0 -1.2103 14.0386 0 0 -1.2522 13.9506 0 0 -1.2852 13.859 0 0 -1.3092 13.7646 0 0 -1.3236 13.6682 0 0 -1.3284 13.5712 0 0 -1.3284 13.479 0 0 -1.3284 13.3859 0 0 -1.3284 13.2918 0 0 -1.3284 13.197 0 0 -1.3284 13.101 0 0 -1.3284 12.9957 0 0 -1.3473 12.9093 0 0 -1.4011 12.8363 0 0 -1.4817 12.7896 0 0 -1.4817 12.7956 0 0 -1.5784 12.7819 0 0 -1.5784 12.7867 0 0 -1.6783 12.7867 0 0 -1.7781 12.7867 0 0 -1.7781 12.7819 0 0 -1.7781 12.7761 0 0 -1.7781 12.77 0 0 -1.7781 12.763 0 0 -1.878 12.763 0 0 -1.9779 12.763 0 0 -2.0778 12.763 0 0 -2.1776 12.763 0 0 -2.273 12.7819 0 0 -2.3537 12.835 0 0 -2.4078 12.915 0 0 -2.4276 13.0123 0 0 -2.4276 13.1279 0 0 -2.4276 13.2441 0 0 -2.4276 13.3523 0 0 -2.5281 13.3689 0 0 -2.5336 13.3613 0 0 -2.6335 13.3779 0 0 -2.7074 13.3904 0 0 -2.7535 13.3923 0 0 -2.8851 13.4128 0 0 -2.9715 13.4202 0 0 -3.0214 13.4349 0 0 -3.0745 13.5427 0 0 -3.1319 13.5565 0 0 -3.193 13.57 0 0 -3.2573 13.5828 0 0 -3.3252 13.5952 0 0 -3.3963 13.6068 0 0 -3.4702 13.618 0 0 -3.547 13.6285 0 0 -3.6267 13.6388 0 0 -3.6267 13.7322 0 0 -3.6267 13.8257 0 0 -3.709 13.835 0 0 -3.7938 13.8436 0 0 -3.8806 13.8516 0 0 -3.9695 13.8587 0 0 -4.0601 13.8654 0 0 -4.1523 13.8712 0 0 -4.2461 13.8763 0 0 -4.3412 13.787 0 0 -4.4372 13.697 0 0 -4.4382 13.6032 0 0 -4.3988 13.6036 0 0 -4.3658 13.6032 0 0 -4.3927 13.6045 0 0 -4.4814 13.6068 0 0 -4.6273 13.6097 0 0 -4.7272 13.6116 0 0 -4.8277 13.6119 0 0 -4.8277 13.7105 0 0 -4.8277 13.8091 0 0 -4.8277 13.9073 0 0 -4.8277 14.0059 0 0 -4.8277 14.1045 0 0 -4.8277 14.1036 0 0 -4.8277 14.1023 0 0 -4.8277 14.0917 0 0 -4.8277 14.0773 0 0 -4.7295 14.1705 0 0 -4.6315 14.263 0 0 -4.5342 14.3545 0 0 -4.4372 14.4454 0 0 -4.3412 14.4416 0 0 -4.2461 14.4374 0 0 -4.1523 14.4323 0 0 -4.0601 14.4265 0 0 -3.9695 14.4201 0 0 -3.8806 14.4131 0 0 -3.7938 14.4051 0 0 -3.709 14.3965 0 0 -3.6267 14.3872 0 0 -3.547 14.3769 0 0 -3.4702 14.3664 0 0 -3.3963 14.3552 0 0 -3.3252 14.3436 0 0 -3.2573 14.3312 0 0 -3.193 14.3183 0 0 -3.1319 14.3049 0 0 -3.0745 14.2911 0 0 -3.0211 14.2767 0 0 -2.9712 14.262 0 0 -2.9254 14.247 0 0 -2.8838 14.2316 0 0 -2.8463 14.2159 0 0 -2.813 14.1999 0 0 -2.7839 14.1836 0 0 -2.7592 14.1669 0 0 -2.7388 14.1503 0 0 -2.7231 14.1337 0 0 -2.7231 14.0402 0 0 -2.7231 13.9464 0 0 -2.7231 13.8529 0 0 -2.7119 13.836 0 0 -2.7052 13.819 0 0 -2.7026 13.802 0 0 -2.6075 13.802 0 0 -2.5121 13.8017 0 0 -2.4167 13.8014 0 0 -2.3217 13.8004 0 0 -2.2461 13.8123 0 0 -2.2055 13.8907 0 0 -2.0803 13.8741 0 0 -1.8707 13.8568 0 0 -1.831 13.9986 0 0 -1.6821 13.9685 0 0 -1.5483 13.9429 0 0 -1.4475 13.9195 0 0 -1.3415 13.883 0 0 -1.2526 13.8532 0 0 -1.2106 13.9406 0 0 -1.1604 14.0235 0 0 -1.1597 14.0424 0 0 -1.1015 14.1202 0 0 -1.0355 14.2095 0 0 -0.9629 14.2745 0 0 -0.8838 14.3324 0 0 -0.7996 14.382 0 0 -0.7109 14.4237 0 0 -0.6188 14.4563 0 0 -0.5234 14.48 0 0 -0.5234 14.4618 0 0 -0.4264 14.4758 0 0 -0.3284 14.4806 0 0 -0.2369 14.4819 0 0 -0.1722 14.479 0 0 -0.1076 14.7783 0 0 -0.1072 14.884 0 0 -0.1047 14.9893 0 0 -0.3287 14.6852 0 0 -0.1969 14.6148 0 0 -0.2628 14.6141 0 0 -0.3284 14.6135 0 0 -0.4264 14.6087 0 0 -0.5234 14.5943 0 0 -0.6188 14.5706 0 0 -0.7109 14.5376 0 0 -0.7996 14.496 0 0 -0.8838 14.4457 0 0 -0.9629 14.3875 0 0 -1.0355 14.3219 0 0 -1.1015 14.2495 0 0 -1.1597 14.1711 0 0 -1.2103 14.0876 0 0 -1.2522 13.9992 0 0 -1.2852 13.907 0 0 -1.3092 13.8126 0 0 -1.3236 13.7159 0 0 -1.3284 13.6183 0 0 -1.3284 13.5226 0 0 -1.3284 13.4262 0 0 -1.3284 13.3289 0 0 -1.3284 13.2307 0 0 -1.3284 13.1314 0 0 -1.3284 13.1372 0 0 -1.3284 13.0316 0 0 -1.3284 13.0364 0 0 -1.3284 13.0405 0 0 -1.3284 13.0437 0 0 -1.3473 12.9509 0 0 -1.4011 12.8718 0 0 -1.4817 12.819 0 0 -1.4817 12.8177 0 0 -1.5784 12.7982 0 0 -1.6783 12.7982 0 0 -1.7781 12.7982 0 0 -1.878 12.7982 0 0 -1.9779 12.7982 0 0 -2.0778 12.7982 0 0 -2.1776 12.7982 0 0 -2.1776 12.7966 0 0 -2.1776 12.794 0 0 -2.1776 12.7905 0 0 -2.1776 12.7867 0 0 -2.273 12.8052 0 0 -2.3537 12.8587 0 0 -2.4078 12.939 0 0 -2.4276 13.0364 0 0 -2.4276 13.1522 0 0 -2.4276 13.2684 0 0 -2.4276 13.3798 0 0 -2.5275 13.3971 0 0 -2.6319 13.4153 0 0 -2.7157 13.4224 0 0 -2.8031 13.4336 0 0 -2.8876 13.4432 0 0 -2.9567 13.4592 0 0 -3.0294 13.4736 0 0 -3.1069 13.4883 0 0 -3.0832 13.4915 0 0 -3.2154 13.5107 0 0 -3.2176 13.513 0 0 -3.3188 13.5248 0 0 -3.386 13.5322 0 0 -3.4776 13.5405 0 0 -3.6133 13.5546 0 0 -3.668 13.5594 0 0 -3.7051 13.5613 0 0 -3.837 13.5706 0 0 -3.9487 13.5792 0 0 -4.0438 13.586 0 0 -4.0272 13.5866 0 0 -4.0243 13.5876 0 0 -4.0243 13.5879 0 0 -4.0182 13.6887 0 0 -4.0124 13.7902 0 0 -4.0064 13.8926 0 0 -4.1088 13.8984 0 0 -4.2115 13.9032 0 0 -4.3143 13.9073 0 0 -4.4174 13.9099 0 0 -4.4231 14.0095 0 0 -4.4279 14.1064 0 0 -4.4308 14.2041 0 0 -4.4321 14.3023 0 0 -4.4327 14.4006 0 0 -4.4331 14.4989 0 0 -4.4334 14.5975 0 0 -4.4334 14.6961 0 0 -4.532 14.6961 0 0 -4.532 14.5975 0 0 -4.6305 14.5975 0 0 -4.7291 14.5975 0 0 -4.8277 14.5975 0 0 -4.8277 14.4989 0 0 -4.8277 14.4989 0 0 -4.8277 14.4829 0 0 -4.8277 14.4682 0 0 -4.8277 14.4515 0 0 -4.8277 14.4349 0 0 -4.8277 14.4179 0 0 -4.8277 14.4013 0 0 -4.8277 14.4963 0 0 -4.8277 14.5911 0 0 -4.8277 14.5776 0 0 -4.8277 14.4691 0 0 -4.7522 14.4688 0 0 -4.7519 14.3673 0 0 -4.8277 14.3843 0 0 -4.8277 14.481 0 0 -4.8277 14.3676 0 0 -4.6763 14.3664 0 0 -4.6008 14.3651 0 0 -4.587 14.3814 0 0 -4.5067 14.3792 0 0 -4.4279 14.3763 0 0 -4.3501 14.3728 0 0 -4.2733 14.3686 0 0 -4.1978 14.3638 0 0 -4.1232 14.3587 0 0 -4.0505 14.3526 0 0 -3.9795 14.3462 0 0 -3.9103 14.3388 0 0 -3.8431 14.3315 0 0 -3.7778 14.3232 0 0 -3.7148 14.3145 0 0 -3.6542 14.3052 0 0 -3.5963 14.2956 0 0 -3.5409 14.2857 0 0 -3.4884 14.2748 0 0 -3.4385 14.2639 0 0 -3.3918 14.2527 0 0 -3.3482 14.2409 0 0 -3.3076 14.229 0 0 -3.2705 14.2166 0 0 -3.2365 14.2041 0 0 -3.2058 14.191 0 0 -3.1786 14.1781 0 0 -3.1552 14.1647 0 0 -3.1351 14.1513 0 0 -3.1187 14.1375 0 0 -3.1059 14.1237 0 0 -3.0966 14.11 0 0 -3.1879 14.0956 0 0 -3.1879 14.1967 0 0 -3.1863 14.1842 0 0 -3.0896 14.1817 0 0 -2.9929 14.1801 0 0 -2.8963 14.1791 0 0 -2.7996 14.1785 0 0 -2.7026 14.1781 0 0 -2.6056 14.1781 0 0 -2.5086 14.1778 0 0 -2.4113 14.1772 0 0 -2.3137 14.2729 0 0 -2.3297 14.3795 0 0 -2.2484 14.3894 0 0 -2.1895 14.4819 0 0 -2.1216 14.561 0 0 -2.0282 14.5706 0 0 -1.9344 14.576 0 0 -1.8146 14.5863 0 0 -1.7394 14.6199 0 0 -1.6796 14.5818 0 0 -1.5579 14.5351 0 0 -1.4139 14.4557 0 0 -1.282 14.6116 0 0 -1.1351 14.6586 0 0 -1.0826 14.5539 0 0 -0.9801 14.6109 0 0 -0.9113 14.5191 0 0 -0.8076 14.5709 0 0 -0.7052 14.6167 0 0 -0.66 14.755 0 0 -0.5823 14.8049 0 0 -3.1879 14.2985 0 0 -3.1863 14.8952 0 0 -0 14.6477 0 0 -0 15.0488 0 0 -0 14.9275 0 0 -0 14.9361 0 0 -0 14.9441 0 0 -0 14.8235 0 0 -0 15.2441 0 0 -4.4334 16.0757 0 0 -4.4334 15.9771 0 0 -0.2762 15.3119 0 0 -0.1869 15.3225 0 0 -0.0947 15.3331 0 0 -4.8277 17.8487 0 0 -4.7327 17.8471 0 0 -4.6392 17.8436 0 0 -4.556 17.8385 0 0 -4.4846 17.8327 0 0 -4.3959 17.8228 0 0 -4.3031 17.81 0 0 -4.224 17.7972 0 0 -4.2397 17.8001 0 0 -4.256 17.7044 0 0 -4.2695 17.6128 0 0 -4.2698 17.5193 0 0 -4.2714 17.4342 0 0 -4.2685 17.3292 0 0 -4.265 17.2316 0 0 -4.2631 17.1483 0 0 -4.2589 17.0651 0 0 -4.3351 17.0613 0 0 -4.4334 17.0613 0 0 -4.4334 16.9627 0 0 -4.532 16.9627 0 0 -4.532 16.8641 0 0 -4.6305 16.9627 0 0 -4.7291 17.0613 0 0 -4.7291 17.1599 0 0 -4.8277 17.2585 0 0 -4.8277 17.357 0 0 -4.8277 17.4556 0 0 -4.8277 17.5542 0 0 -4.8277 17.6528 0 0 -4.8277 17.7498 0 0 -4.8277 17.7511 0 0 -4.8277 17.8497 0 0 -4.7285 17.8478 0 0 -4.6325 17.8439 0 0 -4.5633 17.8394 0 0 -4.5057 17.835 0 0 -4.4138 17.825 0 0 -4.3194 17.8125 0 0 -4.337 17.8151 0 0 -4.3508 17.7191 0 0 -4.3514 17.6266 0 0 -4.353 17.535 0 0 -4.3537 17.4486 0 0 -4.3476 17.3426 0 0 -4.3415 17.2434 0 0 -4.3351 17.1599 0 0 -4.4334 17.1599 0 0 -4.532 17.1599 0 0 -4.532 17.0613 0 0 -4.6305 17.0613 0 0 -4.6305 17.1599 0 0 -4.7291 17.2585 0 0 -4.7291 17.357 0 0 -4.6305 17.357 0 0 -4.6305 17.4556 0 0 -4.7291 17.5542 0 0 -4.7291 17.6528 0 0 -4.8277 17.7511 0 0 -4.8277 17.85 0 0 -4.7131 17.8478 0 0 -4.6635 17.8458 0 0 -4.62 17.8433 0 0 -4.5909 17.8417 0 0 -4.5326 17.8372 0 0 -4.435 17.8276 0 0 -4.4458 17.7316 0 0 -4.4423 17.6384 0 0 -4.4414 17.543 0 0 -4.4363 17.4566 0 0 -4.4334 17.357 0 0 -4.4334 17.2585 0 0 -4.532 17.2585 0 0 -4.6305 17.2585 0 0 -4.532 17.357 0 0 -4.532 17.4556 0 0 -4.5339 17.5549 0 0 -4.6305 17.5542 0 0 -4.6318 17.6528 0 0 -4.636 17.7479 0 0 -4.7291 17.7511 0 0 -4.7291 17.8484 0 0 -4.6309 17.8442 0 0 -4.5419 17.648 0 0 -4.5406 17.7408 0 0 -4.8277 17.6528 0 0 -4.8277 17.5542 0 0 -4.7291 17.4556 0 0 -4.532 16.7658 0 0 -4.532 16.6672 0 0 -0 15.2079 0 0 -0 15.1045 0 0 -0 15.1093 0 0 -0 15.1141 0 0 -0 15.1173 0 0 -0 15.118 0 0 -0 15.1141 0 0 -0 15.1023 0 0 -0 15.102 0 0 -0 15.1106 0 0 -0 15.0021 0 0 -0 14.8926 0 0 -0 14.7825 0 0 -0 14.6845 0 0 -0.0759 14.6836 0 0 -0.1357 14.6826 0 0 -0.1965 14.6813 0 0 -0.2622 14.6801 0 0 -0.3284 14.6781 0 0 -0.4264 14.6733 0 0 -0.5234 14.6589 0 0 -0.6188 14.6349 0 0 -0.7109 14.602 0 0 -0.7996 14.56 0 0 -0.8838 14.5098 0 0 -0.9629 14.4512 0 0 -1.0355 14.3853 0 0 -1.1015 14.3126 0 0 -1.1597 14.2338 0 0 -1.2103 14.1497 0 0 -1.2522 14.061 0 0 -1.2522 14.0639 0 0 -1.2852 13.9717 0 0 -1.2852 13.9736 0 0 -1.2852 13.9749 0 0 -1.2855 13.9742 0 0 -1.4023 13.9064 0 0 -1.5307 13.8171 0 0 -1.6735 13.7249 0 0 -1.6728 13.5988 0 0 -1.7842 13.4781 0 0 -1.9446 13.4707 0 0 -2.0592 13.5328 0 0 -2.121 13.5482 0 0 -2.2221 13.5693 0 0 -2.3204 13.5885 0 0 -2.3265 13.6916 0 0 -2.4043 13.7079 0 0 -2.4135 13.8097 0 0 -2.419 13.8993 0 0 -2.4241 13.9493 0 0 -2.4712 13.9666 0 0 -2.5563 13.9989 0 0 -2.6514 14.0133 0 0 -2.7397 14.0299 0 0 -2.8284 14.0523 0 0 -2.9321 14.0828 0 0 -3.04 14.1077 0 0 -3.0182 14.213 0 0 -2.9926 14.3232 0 0 -2.9708 14.4083 0 0 -3.0729 14.4371 0 0 -3.1994 14.4592 0 0 -3.2791 14.602 0 0 -3.3783 14.7364 0 0 -3.4648 14.8766 0 0 -3.4299 14.997 0 0 -3.3998 15.093 0 0 -3.3646 15.1842 0 0 -3.4859 15.269 0 0 -3.5348 15.4006 0 0 -3.6398 15.384 0 0 -3.7448 15.4841 0 0 -3.8421 15.5831 0 0 -3.9407 15.6817 0 0 -4.0393 15.7802 0 0 -4.1379 15.8785 0 0 -4.1379 15.9771 0 0 -4.1379 16.0757 0 0 -4.2365 16.1743 0 0 -4.2365 16.2729 0 0 -4.2365 16.3715 0 0 -4.2365 16.4701 0 0 -4.3351 16.5686 0 0 -4.3351 16.6672 0 0 -4.337 16.7754 0 0 -4.2407 16.7866 0 0 -4.2464 16.8727 0 0 -4.1603 16.8862 0 0 -4.0864 16.899 0 0 -4.0934 16.9633 0 0 -4.097 17.0325 0 0 -4.1005 17.1138 0 0 -4.1024 17.1996 0 0 -4.1037 17.294 0 0 -4.1053 17.3967 0 0 -4.1005 17.486 0 0 -4.088 17.5757 0 0 -4.0685 17.6669 0 0 -4.047 17.7617 0 0 -4.0342 17.7588 0 0 -4.1161 17.7764 0 0 -4.1971 17.7917 0 0 -4.2797 17.8052 0 0 -4.3719 17.8183 0 0 -4.4638 17.8292 0 0 -4.5499 17.8366 0 0 -4.6398 17.8417 0 0 -4.7327 17.8442 0 0 -4.8277 17.8458 0 0 -4.8277 17.7415 0 0 -4.8277 17.6416 0 0 -4.8277 17.5529 0 0 -4.8277 17.5594 0 0 -4.8277 17.4576 0 0 -4.8277 17.456 0 0 -4.8277 17.357 0 0 -4.8277 17.2594 0 0 -4.8277 17.1599 0 0 -4.8277 17.0613 0 0 -4.8277 16.9627 0 0 -4.7291 16.8641 0 0 -4.7291 16.7658 0 0 -4.7291 16.6672 0 0 -4.7291 16.5686 0 0 -4.7291 16.4701 0 0 -4.7291 16.3715 0 0 -4.7291 16.2729 0 0 -4.7291 16.1743 0 0 -4.8277 16.1743 0 0 -4.8277 16.1685 0 0 -4.8277 16.0699 0 0 -4.8277 16.0757 0 0 -4.8277 15.9771 0 0 -4.7291 15.8785 0 0 -4.7291 15.7802 0 0 -4.7291 15.6817 0 0 -4.8277 15.6817 0 0 -4.8277 15.6628 0 0 -4.8277 15.6442 0 0 -4.8277 15.6256 0 0 -4.709 15.6131 0 0 -4.5969 15.618 0 0 -4.4939 15.6218 0 0 -4.5624 15.6067 0 0 -4.6523 15.5869 0 0 -4.7253 15.5696 0 0 -4.7253 15.4697 0 0 -4.7266 15.3654 0 0 -4.7263 15.2642 0 0 -4.6251 15.263 0 0 -4.5387 15.2444 0 0 -4.4625 15.2249 0 0 -4.3726 15.2213 0 0 -4.3956 15.205 0 0 -4.3111 15.2012 0 0 -4.3383 15.1849 0 0 -4.2596 15.1804 0 0 -4.1824 15.1756 0 0 -4.2173 15.1596 0 0 -4.1456 15.1544 0 0 -4.0752 15.1484 0 0 -4.0067 15.142 0 0 -3.9401 15.1349 0 0 -3.8751 15.1272 0 0 -3.8127 15.1189 0 0 -3.7525 15.1106 0 0 -3.6946 15.1013 0 0 -3.6395 15.0917 0 0 -3.5867 15.0818 0 0 -3.5371 15.0712 0 0 -3.4904 15.0603 0 0 -3.4465 15.0491 0 0 -3.4058 15.0376 0 0 -3.3684 15.0254 0 0 -3.3345 15.0133 0 0 -3.3037 15.0008 0 0 -3.2762 14.988 0 0 -3.2525 14.9752 0 0 -3.1379 14.9662 0 0 -3.0246 14.9557 0 0 -2.9135 14.9429 0 0 -2.8169 14.9451 0 0 -2.7205 14.9477 0 0 -2.7084 15.0299 0 0 -2.708 15.1343 0 0 -2.708 15.2335 0 0 -2.6223 15.2335 0 0 -2.5336 15.2332 0 0 -2.4433 15.2332 0 0 -2.3518 15.2332 0 0 -2.2596 15.2332 0 0 -2.1667 15.2332 0 0 -2.0739 15.2332 0 0 -1.9811 15.2332 0 0 -1.8879 15.2332 0 0 -1.7948 15.2332 0 0 -1.7311 15.2486 0 0 -1.6389 15.2486 0 0 -1.547 15.2482 0 0 -1.4552 15.2482 0 0 -1.3633 15.2482 0 0 -1.2714 15.2482 0 0 -1.1792 15.2482 0 0 -1.0874 15.2482 0 0 -1.0288 15.263 0 0 -0.9376 15.263 0 0 -0.8463 15.263 0 0 -0.783 15.2767 0 0 -0.7122 15.2902 0 0 -0.6354 15.3023 0 0 -0.5535 15.3142 0 0 -0.0893 15.3916 0 0 -0.0925 15.3993 0 0 -3.926 16.4464 0 0 -0.3524 15.406 0 0 -0.5032 15.3872 0 0 -0.6229 15.3968 0 0 -0.3899 15.4605 0 0 -0.4741 15.4777 0 0 -0.4664 15.4777 0 0 -0.4661 15.4784 0 0 -0.4661 15.4787 0 0 -0.4914 15.3865 0 0 -0.5861 15.4067 0 0 -0.6085 15.31 0 0 -0.6175 15.2082 0 0 -0.6226 15.1128 0 0 -0.7068 14.9531 0 0 -0.8262 14.9137 0 0 -0.9187 14.8507 0 0 -1.0198 14.778 0 0 -1.1168 14.7009 0 0 -1.2077 14.6157 0 0 -1.3777 14.6253 0 0 -1.4718 14.5181 0 0 -1.6828 14.505 0 0 -1.8194 14.6045 0 0 -1.9324 14.6756 0 0 -2.0217 14.7316 0 0 -2.0951 14.7934 0 0 -2.0266 14.8708 0 0 -2.0013 14.9121 0 0 -2.0227 14.9499 0 0 -2.073 14.9137 0 0 -2.1536 14.8552 0 0 -2.2362 14.7879 0 0 -2.3723 14.7844 0 0 -2.4401 14.8808 0 0 -2.5003 14.9579 0 0 -2.5646 15.0488 0 0 -2.6264 15.1324 0 0 -2.6997 15.2604 0 0 -2.7551 15.3596 0 0 -2.7951 15.4246 0 0 -2.8367 15.4963 0 0 -2.8761 15.568 0 0 -2.91 15.617 0 0 -2.9491 15.6557 0 0 -3.0224 15.7239 0 0 -3.0611 15.8247 0 0 -3.105 15.8964 0 0 -3.1373 15.9694 0 0 -3.1498 16.0104 0 0 -3.1875 16.0197 0 0 -3.2439 16.0392 0 0 -3.3377 16.0872 0 0 -3.3633 16.1458 0 0 -3.4225 16.1544 0 0 -3.5173 16.1672 0 0 -3.5902 16.2639 0 0 -3.5803 16.3516 0 0 -3.5803 16.4019 0 0 -3.5832 16.4595 0 0 -3.5925 16.551 0 0 -3.6549 16.5936 0 0 -3.613 16.6634 0 0 -3.5781 16.7409 0 0 -3.5515 16.8218 0 0 -3.5288 16.8945 0 0 -3.6002 16.93 0 0 -3.5774 17.0149 0 0 -3.5521 17.109 0 0 -3.5307 17.196 0 0 -3.5041 17.2818 0 0 -3.4718 17.3673 0 0 -3.4334 17.4537 0 0 -3.3937 17.5424 0 0 -3.3905 17.5408 0 0 -3.4753 17.5776 0 0 -3.5585 17.6106 0 0 -3.6405 17.6403 0 0 -3.7221 17.6679 0 0 -3.8044 17.6928 0 0 -3.8879 17.7159 0 0 -3.878 17.7114 0 0 -3.9849 17.6442 0 0 -4.0806 17.5702 0 0 -4.1869 17.5549 0 0 -4.2743 17.4672 0 0 -4.3197 17.3862 0 0 -4.3815 17.311 0 0 -4.3457 17.1669 0 0 -4.3597 17.0706 0 0 -4.264 17.1285 0 0 -4.2813 17.0632 0 0 -4.3521 16.971 0 0 -4.4308 16.9598 0 0 -4.523 16.9451 0 0 -4.5217 16.8426 0 0 -4.522 16.7409 0 0 -4.5227 16.6397 0 0 -4.4206 16.5386 0 0 -4.3172 16.439 0 0 -4.3194 16.3366 0 0 -4.321 16.2351 0 0 -4.3249 16.1336 0 0 -4.2384 16.1083 0 0 -4.1514 15.9909 0 0 -4.0473 15.9742 0 0 -3.9702 15.9624 0 0 -3.982 15.8878 0 0 -3.9945 15.8052 0 0 -3.9062 15.7073 0 0 -3.9062 15.6144 0 0 -3.9295 15.6013 0 0 -3.9564 15.5885 0 0 -3.8687 15.5824 0 0 -3.894 15.5703 0 0 -3.9164 15.5584 0 0 -3.8281 15.5514 0 0 -3.8447 15.5402 0 0 -3.8556 15.5293 0 0 -3.7669 15.521 0 0 -3.6805 15.4128 0 0 -3.5973 15.4028 0 0 -3.5182 15.3916 0 0 -3.4363 15.3718 0 0 -3.3646 15.3519 0 0 -3.2884 15.3276 0 0 -3.2151 15.3046 0 0 -3.1472 15.2876 0 0 -3.0822 15.2758 0 0 -3.0265 15.2652 0 0 -2.9756 15.255 0 0 -2.9747 15.3471 0 0 -2.9276 15.3375 0 0 -2.8847 15.3273 0 0 -2.7986 15.327 0 0 -2.71 15.3267 0 0 -2.6194 15.3263 0 0 -2.5275 15.3263 0 0 -2.435 15.326 0 0 -2.3418 15.326 0 0 -2.2484 15.326 0 0 -2.1549 15.326 0 0 -2.0614 15.326 0 0 -1.9677 15.326 0 0 -1.9001 15.3366 0 0 -1.805 15.3366 0 0 -1.71 15.3366 0 0 -1.6149 15.3366 0 0 -1.5198 15.3363 0 0 -1.4251 15.3363 0 0 -1.33 15.3363 0 0 -1.2349 15.3363 0 0 -1.1565 15.3462 0 0 -1.0602 15.3462 0 0 -0.9638 15.3462 0 0 -0.8806 15.3555 0 0 -0.7948 15.3641 0 0 -0.5227 15.4768 0 0 -0.4529 15.464 0 0 -0.4728 15.4717 0 0 -1.4091 15.5184 0 0 -1.556 15.4835 0 0 -1.6463 15.5434 0 0 -1.7349 15.6071 0 0 -1.8915 15.5904 0 0 -1.9772 15.6679 0 0 -2.0608 15.5943 0 0 -2.1386 15.6865 0 0 -2.2756 15.7284 0 0 -2.3274 15.7982 0 0 -2.377 15.866 0 0 -2.4283 15.9307 0 0 -2.4811 15.996 0 0 -2.539 16.0715 0 0 -2.5909 16.1359 0 0 -2.6431 16.1964 0 0 -2.6968 16.2562 0 0 -2.7516 16.3151 0 0 -2.8076 16.3727 0 0 -2.8655 16.4288 0 0 -2.9251 16.4832 0 0 -2.9865 16.5357 0 0 -2.9411 16.6208 0 0 -3.0061 16.673 0 0 -2.9689 16.7546 0 0 -2.9279 16.834 0 0 -2.8822 16.9118 0 0 -2.8316 16.988 0 0 -2.7749 17.0635 0 0 -2.7954 17.1992 0 0 -2.7973 17.2005 0 0 -2.8793 17.2565 0 0 -2.9599 17.3084 0 0 -3.0381 17.3554 0 0 -3.1139 17.398 0 0 -3.1892 17.4377 0 0 -3.265 17.4752 0 0 -3.2554 17.4684 0 0 -3.2986 17.3855 0 0 -3.4065 17.3548 0 0 -3.4244 17.3097 0 0 -3.4516 17.2261 0 0 -3.5419 17.1864 0 0 -3.6232 17.1269 0 0 -3.7023 17.0584 0 0 -3.7823 16.9979 0 0 -3.846 16.9355 0 0 -3.8556 16.8042 0 0 -3.8012 16.7287 0 0 -3.7487 16.6512 0 0 -3.6981 16.5725 0 0 -3.6491 16.4921 0 0 -3.6017 16.4105 0 0 -3.5569 16.3279 0 0 -3.5141 16.2437 0 0 -3.4734 16.1583 0 0 -3.4401 16.0706 0 0 -3.393 15.9364 0 0 -3.3143 15.8644 0 0 -3.265 15.7626 0 0 -3.2036 15.7258 0 0 -3.1927 15.6458 0 0 -3.1085 15.529 0 0 -3.04 15.5178 0 0 -2.9692 15.5639 0 0 -2.9039 15.5626 0 0 -2.8255 15.5978 0 0 -2.7586 15.633 0 0 -2.7 15.5799 0 0 -2.637 15.5296 0 0 -2.5816 15.4877 0 0 -2.499 15.5107 0 0 -2.4161 15.5459 0 0 -2.3655 15.4966 0 0 -2.3201 15.4441 0 0 -2.2029 15.4656 0 0 -2.1674 15.4973 0 0 -2.1037 15.4976 0 0 -1.9942 15.4976 0 0 -1.8876 15.4963 0 0 -1.7996 15.5341 0 0 -1.7468 15.5607 0 0 -1.7071 15.5104 0 0 -1.5954 15.5306 0 0 -1.4696 15.5024 0 0 -1.4289 15.5619 0 0 -1.2993 15.5808 0 0 -1.2442 15.6426 0 0 -2.2785 15.5152 0 0 -2.2042 15.5318 0 0 -2.1197 15.5453 0 0 -1.9977 15.5533 0 0 -1.8751 15.5603 0 0 -1.7986 15.6167 0 0 -1.6725 15.5994 0 0 -1.5246 15.6045 0 0 -1.3835 15.6221 0 0 -1.3268 15.6874 0 0 -1.2714 15.7575 0 0 -1.2465 15.7482 0 0 -1.2212 15.7383 0 0 -1.2039 15.7332 0 0 -1.1876 15.7271 0 0 -1.2721 15.7761 0 0 -1.2606 15.7713 0 0 -1.2529 15.769 0 0 -1.2522 15.77 0 0 -1.2519 15.7706 0 0 -1.2519 15.771 0 0 -1.307 15.6929 0 0 -2.3867 15.7095 0 0 -2.9331 17.2853 0 0 -2.9254 17.278 0 0 -2.9852 17.2056 0 0 -3.0438 17.1384 0 0 -3.1002 17.084 0 0 -3.1837 17.0379 0 0 -3.2189 16.9111 0 0 -3.3031 16.8465 0 0 -3.3892 16.7834 0 0 -3.337 16.6948 0 0 -3.2874 16.6055 0 0 -3.2394 16.5155 0 0 -3.1936 16.4249 0 0 -3.1495 16.3337 0 0 -3.0922 16.2476 0 0 -2.9997 16.1256 0 0 -2.8969 16.0911 0 0 -2.9052 15.9806 0 0 -2.8867 15.8843 0 0 -2.7945 15.8862 0 0 -2.702 15.882 0 0 -2.6005 15.882 0 0 -2.4984 15.7889 0 0 -1.6331 16.0418 0 0 -1.6331 16.0421 0 0 -1.7029 16.1045 0 0 -1.774 16.0411 0 0 -1.9126 16.0411 0 0 -1.9808 16.1154 0 0 -2.0454 16.1903 0 0 -2.1072 16.2626 0 0 -2.1683 16.3337 0 0 -2.2298 16.4038 0 0 -2.2919 16.4739 0 0 -2.354 16.5466 0 0 -2.4167 16.6154 0 0 -2.3482 16.6804 0 0 -2.4148 16.7492 0 0 -2.346 16.8177 0 0 -2.4158 16.8859 0 0 -2.4878 16.9518 0 0 -2.5614 17.0152 0 0 -2.6354 17.0754 0 0 -2.7087 17.1314 0 0 -2.7813 17.1832 0 0 -2.4987 15.9806 0 0 -2.3972 15.9806 0 0 -2.2954 15.9806 0 0 -2.194 15.9806 0 0 -2.0922 15.9806 0 0 -1.9907 15.9806 0 0 -1.8889 15.9806 0 0 -1.7874 15.9806 0 0 -1.7602 16.0459 0 0 -1.7186 16.1 0 0 -1.6415 16.0341 0 0 -1.6351 16.0328 0 0 -1.6293 16.0315 0 0 -1.6348 16.0392 0 0 -1.6341 16.0405 0 0 -2.6971 17.1186 0 0 -2.6923 17.1122 0 0 -2.7509 17.0392 0 0 -2.8079 16.9678 0 0 -2.855 16.8836 0 0 -2.8745 16.8305 0 0 -2.9318 16.8142 0 0 -2.9699 16.6922 0 0 -2.9196 16.5949 0 0 -2.8719 16.496 0 0 -2.8185 16.3894 0 0 -2.765 16.3001 0 0 -2.6901 16.2117 0 0 -2.5675 16.1884 0 0 -2.1466 16.5994 0 0 -2.2106 16.6733 0 0 -2.2762 16.7447 0 0 -2.3434 16.8135 0 0 -2.4123 16.8795 0 0 -2.4817 16.9425 0 0 -2.5515 17.0021 0 0 - -Triangles -12355 -4653 4708 4709 0 -4709 4997 4875 0 -4874 4709 4875 0 -4709 4708 5610 0 -4874 4995 4709 0 -4995 4874 4471 0 -4993 4995 5648 0 -4995 4993 4709 0 -4875 4652 4874 0 -4873 4708 4472 0 -5610 4873 4994 0 -4873 5610 4708 0 -4285 4994 4873 0 -5610 4994 5383 0 -5383 4834 5610 0 -3677 4834 5383 0 -3274 5482 4834 0 -4834 5482 5610 0 -5383 4994 4092 0 -5610 5482 4707 0 -4993 5384 4709 0 -5611 4709 5610 0 -5384 4833 4709 0 -4833 5384 3676 0 -5483 4833 3273 0 -4833 5483 4709 0 -4653 4472 4708 0 -4472 4653 2882 0 -4285 4472 2882 0 -4472 4285 4873 0 -5483 2886 4653 0 -5483 4653 4709 0 -5647 4092 4994 0 -4710 4092 5647 0 -4710 3883 4092 0 -4092 3883 5383 0 -3677 3475 4834 0 -5609 3475 3677 0 -3274 3080 5482 0 -5609 3080 3274 0 -3274 3475 5609 0 -3475 3274 4834 0 -3677 3883 5609 0 -3883 3677 5383 0 -4285 2882 5647 0 -4285 5647 4994 0 -4993 4091 5384 0 -4654 5482 2888 0 -4707 4872 5611 0 -4872 4707 4473 0 -5377 4872 4286 0 -4872 5377 5611 0 -4654 4473 4707 0 -4473 4654 2690 0 -4286 4473 2689 0 -4473 4286 4872 0 -4707 5482 4654 0 -5382 5377 4093 0 -5481 4706 5611 0 -5611 4706 5612 0 -5382 5611 5377 0 -4835 5611 5382 0 -4835 5481 5611 0 -3476 5481 4835 0 -3081 4655 5481 0 -5481 4655 4706 0 -4835 5382 3884 0 -4871 4706 4655 0 -5377 4286 5646 0 -4707 5611 5610 0 -5646 4093 5377 0 -4093 5646 2689 0 -5609 3884 4093 0 -4093 3884 5382 0 -3678 3476 4835 0 -5609 4526 3476 0 -4526 3275 3476 0 -3476 3275 5481 0 -3678 3884 5609 0 -3884 3678 4835 0 -3081 2889 4655 0 -4526 2889 3081 0 -2694 4526 2693 0 -2889 2694 4655 0 -2693 5942 4655 0 -5945 5942 4526 0 -4474 5942 5943 0 -5945 5943 5942 0 -4526 5942 2693 0 -4655 5942 4474 0 -4526 3081 3275 0 -2694 2693 4655 0 -5646 4286 2689 0 -3275 3081 5481 0 -4526 2694 2889 0 -5945 5944 5943 0 -3080 2888 5482 0 -4709 5077 4997 0 -5281 4688 5283 0 -5283 5284 5142 0 -5281 5283 4984 0 -5374 4688 5281 0 -5192 4688 5238 0 -5238 5374 5492 0 -5192 5238 4463 0 -5143 4688 5192 0 -5281 5393 5374 0 -5238 4688 5374 0 -5075 4875 4996 0 -4997 4651 4996 0 -4996 4470 5075 0 -4992 4875 5075 0 -4832 4875 5385 0 -4992 4090 5385 0 -5385 3675 4832 0 -5484 4875 4832 0 -5649 4992 5075 0 -5385 4875 4992 0 -4688 5143 5068 0 -4996 4875 4997 0 -5244 4688 5068 0 -5068 5143 4272 0 -5244 5068 3869 0 -4985 4688 5244 0 -5392 4688 5243 0 -5243 4985 3869 0 -5392 5243 3869 0 -4825 4688 5392 0 -5244 3869 4985 0 -5243 4688 4985 0 -5491 4688 5282 0 -5282 4825 3869 0 -5491 5282 3869 0 -5373 4688 5491 0 -5239 4688 4464 0 -5201 4464 5373 0 -4329 3466 4331 0 -5201 4277 5239 0 -5239 4464 5201 0 -4277 4688 5239 0 -5373 5491 5201 0 -4464 4688 5373 0 -5392 3869 4825 0 -5282 4688 4825 0 -5192 4275 5143 0 -5484 4832 3272 0 -4652 4471 4874 0 -4875 5484 4652 0 -3076 4652 5484 0 -4471 4284 4995 0 -5648 4995 4284 0 -3071 4284 4471 0 -4284 4876 5648 0 -4091 4993 5648 0 -4471 4652 3071 0 -5609 2887 2888 0 -2887 2692 4654 0 -2887 5609 2692 0 -2692 5609 2691 0 -2692 2691 4654 0 -2690 2688 4473 0 -2690 5609 2688 0 -2688 5609 2689 0 -2688 2689 4473 0 -5609 2690 2691 0 -2691 2690 4654 0 -3882 5384 4091 0 -2888 2887 4654 0 -3676 5384 3882 0 -4710 3882 4091 0 -4710 3676 3882 0 -3474 4833 3676 0 -3079 5483 3273 0 -4710 3273 3474 0 -4710 3079 3273 0 -3078 5483 3079 0 -3474 3676 4710 0 -3273 4833 3474 0 -2885 4653 2886 0 -4710 2886 3078 0 -4710 2885 2886 0 -2884 4653 2885 0 -2881 4653 2883 0 -4710 2883 2884 0 -3080 5609 2888 0 -4710 2882 2881 0 -2881 2883 4710 0 -2882 4653 2881 0 -2884 2885 4710 0 -2883 4653 2884 0 -3078 3079 4710 0 -2886 5483 3078 0 -4710 4091 4876 0 -5609 4093 2689 0 -4688 4277 4276 0 -4688 5284 5283 0 -4523 4709 5611 0 -5609 3476 3678 0 -5614 4704 4522 0 -4657 4704 5614 0 -4476 4869 4704 0 -4704 4869 4522 0 -5379 4869 4289 0 -4523 4837 5614 0 -5612 5613 4523 0 -4838 5379 4096 0 -5614 4522 4523 0 -4869 5379 4522 0 -5615 4521 5478 0 -4522 4521 4331 0 -4838 4521 4522 0 -4521 5615 4331 0 -4838 5478 4521 0 -3887 5478 4838 0 -3479 4658 5478 0 -5478 4658 5615 0 -4522 5379 4838 0 -5615 4658 4703 0 -4523 5613 4705 0 -4523 4522 4331 0 -4705 4870 4523 0 -4870 4705 4475 0 -5380 4870 5644 0 -4870 5380 4523 0 -4837 5479 5614 0 -5479 4837 3680 0 -4657 5479 3277 0 -5479 4657 5614 0 -5380 4095 4837 0 -5380 4837 4523 0 -4476 4289 4869 0 -4289 4476 4524 0 -5643 4289 4524 0 -4289 5643 5379 0 -4096 3887 4838 0 -3887 4096 2240 0 -3681 3479 5478 0 -3479 3681 2240 0 -3887 2240 3681 0 -3887 3681 5478 0 -5643 4524 4096 0 -5643 4096 5379 0 -4657 4524 4476 0 -4657 4476 4704 0 -5613 4656 4705 0 -4658 3479 3278 0 -4868 4520 4331 0 -4331 4520 4330 0 -4703 4331 5615 0 -4868 4331 4703 0 -4839 4868 5642 0 -4703 4290 4868 0 -5477 4839 3888 0 -4868 4839 4520 0 -4658 4477 4703 0 -4520 4839 5477 0 -4330 5616 4702 0 -5616 4330 4520 0 -4332 4702 5616 0 -4867 4330 4702 0 -5477 4659 5616 0 -3682 4659 5477 0 -3279 4478 4659 0 -4332 5616 4659 0 -5616 4520 5477 0 -4659 4478 4332 0 -4290 4703 4477 0 -4523 4331 3264 0 -4290 4333 5642 0 -4333 4290 4477 0 -2242 5642 4333 0 -4290 5642 4868 0 -3278 3084 4333 0 -3084 3278 4335 0 -2892 3084 4335 0 -3084 2892 4333 0 -4477 4658 4333 0 -4097 5642 2242 0 -4097 3888 4839 0 -2242 3888 4097 0 -2242 3682 3888 0 -3888 3682 5477 0 -3480 3279 4659 0 -3480 4334 3279 0 -3085 2893 4478 0 -3085 4334 2893 0 -4334 3085 3279 0 -3279 3085 4478 0 -3480 3682 2242 0 -3682 3480 4659 0 -2697 4333 2892 0 -5642 4097 4839 0 -3278 4333 4658 0 -4291 4478 2893 0 -3479 2240 3278 0 -4709 4523 3069 0 -5378 4871 4287 0 -4871 4655 4474 0 -4287 4871 4474 0 -4871 5378 5612 0 -4287 5645 5378 0 -5645 4287 5944 0 -4094 5645 5944 0 -5378 5645 5381 0 -4474 5944 4287 0 -5381 5645 4094 0 -5612 5381 4836 0 -5381 5612 5378 0 -3885 4836 5381 0 -5612 4836 5480 0 -4656 5613 5480 0 -3082 4656 5480 0 -4656 4525 4475 0 -4475 4705 4656 0 -5480 4836 3477 0 -4475 4288 4870 0 -5381 4094 3885 0 -4871 5612 4706 0 -3885 3679 4836 0 -3679 3885 4526 0 -3477 3679 4526 0 -3679 3477 4836 0 -3477 4526 3276 0 -3885 4094 4526 0 -4526 5944 5945 0 -3082 3276 2461 0 -4094 5944 4526 0 -3477 3276 5480 0 -3082 2890 4656 0 -2239 2890 3082 0 -2239 2695 2890 0 -4656 2890 4525 0 -4525 2695 5940 0 -2239 5941 5940 0 -4525 5941 2464 0 -2239 2462 2464 0 -2464 5941 2239 0 -4525 5940 5941 0 -5940 2695 2239 0 -2695 4525 2890 0 -2239 3277 2462 0 -3276 3082 5480 0 -5943 5944 4474 0 -4525 2464 2462 0 -4095 5380 5644 0 -4870 4288 5644 0 -5644 4288 4525 0 -3886 4837 4095 0 -3680 4837 3886 0 -3886 4095 4525 0 -3680 3886 4525 0 -3478 5479 3680 0 -5644 4525 4095 0 -2240 4335 3278 0 -2697 5938 4333 0 -2039 5938 4335 0 -2467 5938 2039 0 -5938 2467 4333 0 -2243 2039 2242 0 -2039 2243 2467 0 -2039 4334 2242 0 -2243 2242 4333 0 -4335 5938 2697 0 -2467 2243 4333 0 -5479 3478 3277 0 -4096 4524 2240 0 -3277 4524 4657 0 -3680 2462 3478 0 -4336 3083 3277 0 -3277 3083 4524 0 -2696 2891 2038 0 -2891 4336 2038 0 -2462 3277 3478 0 -2891 2696 4524 0 -3680 4525 2462 0 -3083 2891 4524 0 -5939 2466 4524 0 -2466 5939 2038 0 -2465 2466 2038 0 -2466 2465 4524 0 -2240 2038 4335 0 -2038 2240 2465 0 -1843 1844 4335 0 -1844 2039 4335 0 -4335 2038 1843 0 -2697 2892 4335 0 -5939 2696 2038 0 -2465 2240 4524 0 -3083 4336 2891 0 -2696 5939 4524 0 -4288 4475 4525 0 -4334 3480 2242 0 -4526 2461 3276 0 -5480 5613 5612 0 -5611 5612 4523 0 -4334 2039 1844 0 -4710 5647 2882 0 -5357 5284 4688 0 -4688 4276 5656 0 -5201 5656 4276 0 -5201 5193 5656 0 -4688 5656 5193 0 -4688 5657 5658 0 -5201 5658 5657 0 -5201 5659 5658 0 -4688 5658 5659 0 -5657 5193 5201 0 -4688 5193 5657 0 -4688 5144 4080 0 -5201 4080 5144 0 -5201 3871 4080 0 -4688 4080 3871 0 -4688 3665 3664 0 -5201 3664 3665 0 -5201 5069 3664 0 -4688 3664 5069 0 -3665 3871 5201 0 -4688 3871 3665 0 -5144 5659 5201 0 -4688 5659 5144 0 -4688 5200 4986 0 -5201 4986 5200 0 -5201 5242 4986 0 -4688 4986 5242 0 -4688 5391 4826 0 -5201 4826 5391 0 -5201 5241 4826 0 -4688 4826 5241 0 -5391 5242 5201 0 -4688 5242 5391 0 -4688 5490 5240 0 -3663 5240 5490 0 -3461 4646 5240 0 -4688 5240 4646 0 -4688 4465 5194 0 -5152 5194 4465 0 -4688 4278 5654 0 -5152 5654 4278 0 -4278 5194 5152 0 -4688 5194 4278 0 -4465 4646 3461 0 -4688 4646 4465 0 -5490 5241 5201 0 -4688 5241 5490 0 -5200 5069 5201 0 -4688 5069 5200 0 -4688 5655 5145 0 -5152 5145 5655 0 -5152 4084 5145 0 -4688 5145 4084 0 -4688 4083 4082 0 -5152 4082 4083 0 -5152 5070 4082 0 -4688 4082 5070 0 -4083 4084 5152 0 -4688 4084 4083 0 -4688 4081 3872 0 -5152 3872 4081 0 -5152 3666 3872 0 -4688 3872 3666 0 -4688 3462 4987 0 -5152 4987 3462 0 -5152 5199 4987 0 -4688 4987 5199 0 -3462 3666 5152 0 -4688 3666 3462 0 -4081 5070 5152 0 -4688 5070 4081 0 -4688 5390 5198 0 -5152 5198 5390 0 -5152 4827 5198 0 -4688 5198 4827 0 -4689 5489 5197 0 -5152 5197 5489 0 -5152 4647 5197 0 -4689 5197 4647 0 -5489 4827 5152 0 -4689 4827 5489 0 -4689 5195 4466 0 -5152 4466 5195 0 -3260 4279 4466 0 -4689 4466 4279 0 -4689 5146 5653 0 -5102 5653 5146 0 -4689 4086 4085 0 -5102 4085 4086 0 -4086 5653 5102 0 -4689 5653 4086 0 -5146 4279 3260 0 -4689 4279 5146 0 -5195 4647 5152 0 -4689 4647 5195 0 -5390 5199 5152 0 -4688 5199 5390 0 -5655 5654 5152 0 -4688 5654 5655 0 -5236 5358 5357 0 -5317 5357 5318 0 -5316 5357 5317 0 -5284 5315 5280 0 -5316 5314 5357 0 -5235 5314 5316 0 -5355 5279 5314 0 -5314 5279 5357 0 -5316 5317 5277 0 -5315 5358 5190 0 -5284 5375 5237 0 -5066 5280 5315 0 -4983 5375 5280 0 -5284 5237 5191 0 -5283 5142 5067 0 -4643 5191 5237 0 -4461 5142 5191 0 -5283 5067 4984 0 -5237 5375 4823 0 -5284 5191 5142 0 -5357 5279 5278 0 -5284 5280 5375 0 -5278 5236 5357 0 -5355 5236 5278 0 -5355 5190 5236 0 -5236 5190 5358 0 -4640 4983 5066 0 -5190 5356 5141 0 -5286 5394 4983 0 -5141 5066 5315 0 -5355 5278 5279 0 -5190 5141 5315 0 -4983 5394 5375 0 -5394 5286 4823 0 -4823 5286 5493 0 -5394 4823 5375 0 -5493 4643 5237 0 -4643 5285 4642 0 -4642 4461 5191 0 -4461 5285 4460 0 -5285 4461 4642 0 -4643 4642 5191 0 -5286 4643 5493 0 -4823 5493 5237 0 -5141 5356 5066 0 -5066 4983 5280 0 -5357 4688 5318 0 -5142 4461 4460 0 -5374 5393 4824 0 -4984 5067 4078 0 -5393 4984 4078 0 -5374 4824 5492 0 -5238 4644 4463 0 -5492 4824 4077 0 -5245 4644 5492 0 -5192 4463 4462 0 -5393 4077 4824 0 -5238 5492 4644 0 -3876 5102 3875 0 -4462 4275 5192 0 -4275 4274 5143 0 -4689 3876 3875 0 -5143 4274 4273 0 -5245 4275 4462 0 -5245 4274 4275 0 -5143 4273 4272 0 -4462 4463 5245 0 -4272 5660 5068 0 -4463 4644 5245 0 -4984 5393 5281 0 -4458 5067 4459 0 -5286 4458 4459 0 -4077 4458 5286 0 -4271 5067 4458 0 -4078 5067 5661 0 -5661 4271 4077 0 -4078 5661 4077 0 -4077 5393 4078 0 -4458 4077 4271 0 -5661 5067 4271 0 -5068 5660 4079 0 -5245 4272 4273 0 -5245 5660 4272 0 -5068 4079 3870 0 -3870 4079 5245 0 -5660 5245 4079 0 -5245 5491 3869 0 -5245 3869 3870 0 -5102 3876 4085 0 -5201 4276 4277 0 -4273 4274 5245 0 -5068 3870 3869 0 -5285 4459 4460 0 -5067 5142 4459 0 -5142 4460 4459 0 -3874 3875 5102 0 -5359 5317 5318 0 -5284 5358 5315 0 -5284 5357 5358 0 -4689 4085 3876 0 -4689 3874 4988 0 -4988 3874 5102 0 -3873 4988 5102 0 -4689 4988 3873 0 -4689 3667 3464 0 -3464 3667 5102 0 -3463 3464 5102 0 -4689 3464 3463 0 -3873 5102 3667 0 -4689 3873 3667 0 -4689 5151 5389 0 -5389 5151 5102 0 -4828 5389 5102 0 -4689 5389 4828 0 -4689 5376 5488 0 -5488 5376 5102 0 -4648 5488 5102 0 -4689 5488 4648 0 -4828 5102 5376 0 -4689 4828 5376 0 -3463 5102 5151 0 -4689 3463 5151 0 -4512 4467 5147 0 -5147 4467 5102 0 -4280 5147 5102 0 -4512 5147 4280 0 -4512 5652 5072 0 -5072 5652 3261 0 -4087 5072 3261 0 -4512 5072 4087 0 -4280 3261 5652 0 -4512 4280 5652 0 -4512 3878 4989 0 -4989 3878 5079 0 -3877 4989 5079 0 -4512 4989 3877 0 -4512 3671 3670 0 -3670 3671 5079 0 -4512 3669 3668 0 -3668 3669 5079 0 -3670 5079 3669 0 -4512 3670 3669 0 -3877 5079 3671 0 -4512 3877 3671 0 -4087 3261 3878 0 -4512 4087 3878 0 -4648 5102 4467 0 -4512 4648 4467 0 -4512 3465 3263 0 -3263 3465 5079 0 -3262 3263 5079 0 -4512 3263 3262 0 -4512 5150 4829 0 -4829 5150 5079 0 -5079 5149 5487 0 -4512 4829 5487 0 -3262 5079 5150 0 -4512 3262 5150 0 -4512 5149 4649 0 -5079 5148 4649 0 -5079 4468 5148 0 -4512 4649 5148 0 -4513 4468 4281 0 -5079 5073 4281 0 -3068 5651 5073 0 -4513 4281 5073 0 -4281 4468 5079 0 -4512 5148 4468 0 -4649 5149 5079 0 -4512 5487 5149 0 -4513 5651 4088 0 -3068 4990 4088 0 -3068 3879 4990 0 -4513 4088 4990 0 -4513 3879 3673 0 -3068 5387 3673 0 -3068 3672 5387 0 -4513 3673 5387 0 -3673 3879 3068 0 -4513 4990 3879 0 -4513 3672 3470 0 -3068 3469 3470 0 -3068 4830 3469 0 -4514 3470 3469 0 -4515 4830 3468 0 -5078 3467 3468 0 -4329 3467 3466 0 -5078 3264 3466 0 -3466 3467 5078 0 -4328 3468 3467 0 -3468 4830 5078 0 -4515 3469 4830 0 -3470 3672 3068 0 -4513 5387 3672 0 -4088 5651 3068 0 -4513 5073 5651 0 -3668 5079 3465 0 -4512 3668 3465 0 -3264 3069 4523 0 -5078 5077 3069 0 -5078 5486 5077 0 -3069 5077 4709 0 -5486 4650 4997 0 -5078 5076 4650 0 -5078 4469 5076 0 -4650 5076 4997 0 -4650 5486 5078 0 -5077 5486 4997 0 -4469 5074 4997 0 -5078 4282 5074 0 -5078 5650 4282 0 -5074 4282 4997 0 -5650 4991 4997 0 -4998 4089 4991 0 -4998 3880 4089 0 -4991 4089 4997 0 -4991 5650 5078 0 -4282 5650 4997 0 -5074 4469 5078 0 -5076 4469 4997 0 -3880 5386 4997 0 -4998 3674 5386 0 -4998 3472 3674 0 -5386 3674 4997 0 -3472 4831 4997 0 -4998 3471 4831 0 -4998 3270 3471 0 -4831 3471 4997 0 -4831 3472 4998 0 -3674 3472 4997 0 -3270 3269 4997 0 -4998 5485 3269 0 -4998 3268 5485 0 -3269 5485 4997 0 -3268 3267 4997 0 -4998 3266 3267 0 -3266 3265 4997 0 -4998 3070 3265 0 -3265 3266 4998 0 -3267 3266 4997 0 -3267 3268 4998 0 -5485 3268 4997 0 -3269 3270 4998 0 -3471 3270 4997 0 -5386 3880 4998 0 -4089 3880 4997 0 -3070 4651 4997 0 -4998 4470 4651 0 -4998 4283 4470 0 -4651 4470 4996 0 -4283 5649 5075 0 -4998 4090 5649 0 -4876 3881 4090 0 -5649 4090 4992 0 -5649 4283 4998 0 -4470 4283 5075 0 -3881 3675 5385 0 -4876 3473 3675 0 -4876 3272 3473 0 -3675 3473 4832 0 -3272 3271 5484 0 -4876 3077 3271 0 -3077 3076 5484 0 -4876 3075 3076 0 -3076 3077 4876 0 -3271 3077 5484 0 -3271 3272 4876 0 -3473 3272 4832 0 -3675 3881 4876 0 -4090 3881 5385 0 -3075 3074 4652 0 -4876 3073 3074 0 -4876 3072 3073 0 -3074 3073 4652 0 -4876 4284 3071 0 -3072 4876 3071 0 -4710 5609 3883 0 -3072 3071 4652 0 -4876 3074 3075 0 -3073 3072 4652 0 -2037 4336 2239 0 -2461 4526 5946 0 -2239 3082 2461 0 -2239 4336 3277 0 -4336 2037 1843 0 -5608 4527 2461 0 -1843 4144 1844 0 -4337 4144 1843 0 -4144 1663 1487 0 -4527 2239 2461 0 -5609 5946 4526 0 -4336 1843 2038 0 -5946 5609 4710 0 -4876 4091 5648 0 -4998 4651 3070 0 -3076 3075 4652 0 -5078 3069 3264 0 -3265 3070 4997 0 -4829 5079 5487 0 -3466 3264 4331 0 -5245 5492 4077 0 -4689 3875 3874 0 -4329 4331 4330 0 -1844 4144 1664 0 -4852 4853 4854 0 -4852 5462 4853 0 -4674 5463 5464 0 -5462 5463 4674 0 -4674 5464 4673 0 -4854 4688 4855 0 -4493 4673 4672 0 -4306 4493 4492 0 -4306 4492 4687 0 -4492 4493 4672 0 -4672 4510 4492 0 -4491 4687 4492 0 -4673 5464 4672 0 -4510 4672 5464 0 -4690 4856 4855 0 -4690 4855 4688 0 -4689 4690 4688 0 -4689 4856 4690 0 -4857 4691 5626 0 -5626 4691 4689 0 -4692 5626 4512 0 -4691 4856 4689 0 -4689 4512 5626 0 -5457 4856 4691 0 -4510 5464 4850 0 -4852 5463 5462 0 -4851 5463 4852 0 -5465 4510 4850 0 -4851 4856 5457 0 -4851 4852 4855 0 -4850 4849 5465 0 -5456 4850 4851 0 -5465 5466 4671 0 -4851 4850 5463 0 -4855 4856 4851 0 -5464 5463 4850 0 -4671 5467 4670 0 -4855 4852 4854 0 -4827 4689 4688 0 -5465 4671 4510 0 -4686 4305 4491 0 -4113 4686 4304 0 -4112 5460 4303 0 -5461 4112 4303 0 -4113 4304 5460 0 -3701 3905 3904 0 -4675 3701 3904 0 -4495 3700 3903 0 -3699 3500 4495 0 -4675 3903 3700 0 -5461 4111 3905 0 -4491 4510 4304 0 -4491 4305 4687 0 -4303 4304 4510 0 -4491 4304 4686 0 -4670 4490 4510 0 -4669 4490 4670 0 -4488 4489 4490 0 -4490 4489 4510 0 -4510 4671 4670 0 -4303 4510 4489 0 -4303 4111 5461 0 -4111 4303 4302 0 -3904 4111 4110 0 -4111 3904 3905 0 -3902 3904 4110 0 -5631 4110 4302 0 -3699 4495 3903 0 -3903 3904 3902 0 -3902 4110 4109 0 -4110 4111 4302 0 -4303 4489 4302 0 -3904 3903 4675 0 -4302 4489 4301 0 -4304 4303 5460 0 -4492 4510 4491 0 -3902 3698 3903 0 -4692 4857 5626 0 -5457 4857 5625 0 -5625 4692 4512 0 -4857 4692 5625 0 -5457 5456 4851 0 -5625 4693 4858 0 -4513 4693 5625 0 -4858 4693 5624 0 -5625 4858 5457 0 -5625 4512 4513 0 -5457 4858 5458 0 -5466 5465 4849 0 -4849 5456 5455 0 -4511 5466 4849 0 -4850 5456 4849 0 -4849 5455 4511 0 -5455 5458 4860 0 -4848 5455 4847 0 -5455 5456 5458 0 -5456 5457 5458 0 -5455 4848 4511 0 -4858 4859 5458 0 -5466 5467 4671 0 -4513 5624 4693 0 -5624 4513 5623 0 -4694 5624 4695 0 -5624 4694 4858 0 -5623 4695 5624 0 -4695 5623 5621 0 -4696 4695 4697 0 -4695 4696 4694 0 -4513 4514 5623 0 -4694 4696 4859 0 -4859 4860 5458 0 -4860 4859 4861 0 -4847 4860 4846 0 -4860 4847 5455 0 -4846 4324 4847 0 -4846 4861 4325 0 -5470 4324 4846 0 -4324 5469 4847 0 -4846 4325 5470 0 -4846 4860 4861 0 -4859 4696 4861 0 -4847 5469 4848 0 -4696 4325 4861 0 -4694 4859 4858 0 -4468 4513 4512 0 -5468 4848 5469 0 -5467 4511 4669 0 -4511 5467 5466 0 -4488 4669 4511 0 -4669 4670 5467 0 -4668 4486 4487 0 -4667 4668 5468 0 -4668 4487 4511 0 -5468 4668 4511 0 -4848 5468 4511 0 -4300 4511 4487 0 -4301 4489 4488 0 -4488 4511 4301 0 -5631 4301 5632 0 -5631 4302 4301 0 -4109 5631 4108 0 -5632 4301 4300 0 -5632 4300 5633 0 -4108 5631 5632 0 -4511 4300 4301 0 -4109 4110 5631 0 -4487 4299 4300 0 -4488 4490 4669 0 -4486 4667 4485 0 -4486 4668 4667 0 -4485 4667 4324 0 -4299 4487 4486 0 -4666 4485 4324 0 -4485 4666 4325 0 -4298 4485 4325 0 -4485 4298 4486 0 -4324 5470 4666 0 -4486 4298 4299 0 -5633 4106 5632 0 -4300 4299 5633 0 -5633 4299 5634 0 -5632 4106 4108 0 -4105 4106 5633 0 -4105 5634 3895 0 -4108 4106 4107 0 -3896 4106 4105 0 -4105 3689 3896 0 -4105 5633 5634 0 -4299 4298 5634 0 -4109 4108 4107 0 -4298 4104 5634 0 -5468 5469 4667 0 -4324 4667 5469 0 -3698 3902 4109 0 -5470 4325 4666 0 -4857 5457 4691 0 -4648 4512 4689 0 -4106 3896 3897 0 -4494 3499 3498 0 -3497 5808 4494 0 -3298 5808 3497 0 -3498 3499 3699 0 -3498 3699 3698 0 -4307 3298 3496 0 -3499 3500 3699 0 -3495 3297 4307 0 -3495 3296 3297 0 -3493 3295 4115 0 -3493 4508 3295 0 -4115 3296 3494 0 -3497 4494 3498 0 -4509 3496 3497 0 -3697 3498 3698 0 -3498 3697 4509 0 -3497 3496 3298 0 -3498 4509 3497 0 -3899 3695 3696 0 -2256 3694 3695 0 -3696 4509 3697 0 -3696 3697 3900 0 -4509 3696 3695 0 -3495 3494 3296 0 -2481 3494 3495 0 -5924 3493 3494 0 -3494 3493 4115 0 -3694 3492 4509 0 -2480 3291 3492 0 -4509 3291 3097 0 -3291 4509 3492 0 -3097 3291 2480 0 -4509 3495 3496 0 -3695 3694 4509 0 -4508 3493 3292 0 -3492 3694 2480 0 -3496 3495 4307 0 -3699 3903 3698 0 -3495 4509 2481 0 -3293 3294 3292 0 -3099 3293 3292 0 -2712 2907 3098 0 -5923 2712 3098 0 -3099 3098 2907 0 -3906 2482 3098 0 -4685 3906 3098 0 -3702 4678 2906 0 -4677 3702 2906 0 -4685 2906 4678 0 -5923 3098 2482 0 -3098 2711 2906 0 -3292 2711 3098 0 -2906 3502 4677 0 -2906 4685 3098 0 -2905 5925 2710 0 -5925 2905 3097 0 -2257 3097 2905 0 -3494 2481 5924 0 -3097 2257 4509 0 -2257 2905 2710 0 -2906 4676 3502 0 -4676 2906 2711 0 -3501 4676 2711 0 -3292 5924 2711 0 -2481 4509 2257 0 -5627 5924 2481 0 -3493 5924 3292 0 -5627 2481 2257 0 -5627 2711 5924 0 -2710 5925 2257 0 -5925 5627 2257 0 -5925 3097 2480 0 -3098 3099 3292 0 -3294 4508 3292 0 -3697 3698 3901 0 -3899 3696 3900 0 -3900 3901 4107 0 -4323 3899 3900 0 -3695 3899 3693 0 -4107 3898 3900 0 -3898 4107 3897 0 -3692 3898 3691 0 -4323 3900 3898 0 -3698 4109 3901 0 -3693 3899 4323 0 -3693 3491 2256 0 -4323 3491 3693 0 -4323 3290 3491 0 -3491 3290 2256 0 -3096 3290 4323 0 -2051 3490 3289 0 -4323 3692 3490 0 -2051 3289 3095 0 -3898 3692 4323 0 -3290 3096 2256 0 -3692 3489 3490 0 -3693 2256 3695 0 -3489 3692 3691 0 -3691 3897 3690 0 -3489 3691 3488 0 -3288 3490 3489 0 -3690 3488 3691 0 -3488 3690 3286 0 -3287 3488 2900 0 -3488 3287 3489 0 -3897 3896 3690 0 -3489 3287 3288 0 -3289 3288 3094 0 -3289 3490 3288 0 -3094 3288 3093 0 -3289 3094 2902 0 -2901 2902 3094 0 -2901 3093 2475 0 -2903 3095 2902 0 -2706 2902 2901 0 -2901 4134 2706 0 -2901 3094 3093 0 -3288 3287 3093 0 -3289 2902 3095 0 -3287 2705 3093 0 -3691 3898 3897 0 -4107 4106 3897 0 -5929 2902 2706 0 -2709 2255 2904 0 -2479 2709 5926 0 -2709 2904 4323 0 -2052 5926 2709 0 -2254 2051 3095 0 -2051 4323 3490 0 -2478 5927 1854 0 -2052 2709 4323 0 -2904 3096 4323 0 -5927 2478 2708 0 -2255 2256 3096 0 -3694 2256 2480 0 -4322 2256 2255 0 -2480 5627 5925 0 -2051 2052 4323 0 -2478 2254 2708 0 -2052 2051 4322 0 -2052 2479 5926 0 -2254 4322 2051 0 -2479 2052 2255 0 -2479 2255 2709 0 -5627 2480 2256 0 -2254 2478 4321 0 -3096 2904 2255 0 -2903 2707 2708 0 -2903 2902 2707 0 -2708 2707 5928 0 -2903 2708 2254 0 -2707 2476 5928 0 -2476 2707 5929 0 -2476 5929 4134 0 -5928 2476 2252 0 -2902 5929 2707 0 -2708 5928 2477 0 -2253 5927 2477 0 -2253 2477 5630 0 -2050 2253 5630 0 -2050 5927 2253 0 -2049 5630 2477 0 -2049 2252 4134 0 -5630 2478 1854 0 -1854 2050 5630 0 -4321 2478 5630 0 -5928 2049 2477 0 -5928 2252 2049 0 -1854 5927 2050 0 -2476 4134 2252 0 -2477 5927 2708 0 -2903 2254 3095 0 -2255 2052 4322 0 -2706 4134 5929 0 -3900 3697 3901 0 -4109 4107 3901 0 -1853 5630 2049 0 -3896 3487 3690 0 -4700 4517 4328 0 -4328 4517 4327 0 -4518 4328 4329 0 -5618 4328 4518 0 -5618 4700 4328 0 -4700 5618 4138 0 -4865 4700 4138 0 -4700 4865 4517 0 -4518 4866 5618 0 -4517 4865 5619 0 -4515 4327 4516 0 -4327 4515 4328 0 -4516 4327 4699 0 -5620 4515 4516 0 -4699 5619 4137 0 -4842 5619 4865 0 -4842 4137 5619 0 -5619 4699 4327 0 -4327 4517 5619 0 -5620 4516 4699 0 -4865 4138 4842 0 -4515 3468 4328 0 -4329 4519 5617 0 -3470 4514 4513 0 -4840 5617 4519 0 -4329 5617 4701 0 -4866 4701 4139 0 -5617 4139 4701 0 -5617 5476 4139 0 -4701 4866 4329 0 -4519 4330 4867 0 -4866 4841 5618 0 -4138 4841 5475 0 -4138 5618 4841 0 -5475 4841 4139 0 -4138 5475 4661 0 -4480 4661 3684 0 -4139 4661 5475 0 -4480 4293 4138 0 -3281 4293 4480 0 -4293 3087 5639 0 -4661 4480 4138 0 -4841 4866 4139 0 -4842 4138 5474 0 -4840 4519 4867 0 -4518 4329 4866 0 -4515 4514 3469 0 -4293 5639 4138 0 -4515 5622 4514 0 -4515 5620 5622 0 -4698 5622 5620 0 -5621 4514 5622 0 -4864 4137 4843 0 -4864 4699 4137 0 -4843 4137 5473 0 -5620 4864 4136 0 -4699 4864 5620 0 -5620 4136 4698 0 -4326 4697 5621 0 -4698 5621 5622 0 -4863 5621 4698 0 -4697 4695 5621 0 -4843 5473 4136 0 -4698 4136 4863 0 -4663 5473 4137 0 -4326 5621 4863 0 -4864 4843 4136 0 -4663 4136 5473 0 -4663 4137 4482 0 -4862 4696 4697 0 -5474 4662 4137 0 -5474 4138 4662 0 -4481 4662 3891 0 -4662 4481 4137 0 -4138 4100 3891 0 -5935 4100 5639 0 -2043 3891 4100 0 -3891 4662 4138 0 -4138 5639 4100 0 -4481 3891 3685 0 -5638 4294 3282 0 -3685 4294 4481 0 -3483 4294 3685 0 -4294 5638 4137 0 -3483 3282 4294 0 -3483 5801 3282 0 -3088 2896 5638 0 -3088 5801 2896 0 -5801 3088 3282 0 -3282 3088 5638 0 -3483 3685 2043 0 -4101 4137 5638 0 -3685 3891 2043 0 -4481 4294 4137 0 -4137 4842 5474 0 -4101 5638 2896 0 -4661 4139 3890 0 -4329 4328 3467 0 -5476 4840 4098 0 -4332 4840 4867 0 -4098 4840 4332 0 -4840 5476 5617 0 -4291 5641 4332 0 -5641 4291 2468 0 -4098 5641 2040 0 -5641 4098 4332 0 -4478 4291 4332 0 -5476 4098 3889 0 -4660 4479 4139 0 -4479 4660 3481 0 -3086 4292 4479 0 -4479 4292 4139 0 -3889 3683 4660 0 -3683 3889 2040 0 -4141 3481 3683 0 -3683 3481 4660 0 -4660 5476 3889 0 -3280 4479 3481 0 -4098 2040 3889 0 -5476 4660 4139 0 -2468 4291 5937 0 -5937 2698 4334 0 -2468 5937 4334 0 -2244 5641 2468 0 -2244 4334 2040 0 -4334 2244 2468 0 -3683 2040 4141 0 -3481 4141 3280 0 -2893 2698 4291 0 -2040 5641 2244 0 -3280 3086 4479 0 -3086 3280 4141 0 -2894 3086 4141 0 -3086 2894 4292 0 -4143 1664 1487 0 -4141 2699 2894 0 -4292 2699 5640 0 -5936 2699 4141 0 -1844 1845 4334 0 -1845 2040 4334 0 -4143 1845 1844 0 -2894 2699 4292 0 -1844 1664 4143 0 -5937 4291 2698 0 -2893 4334 2698 0 -5640 2699 5936 0 -5640 4099 4139 0 -4099 5640 2042 0 -3890 4099 2042 0 -4099 3890 4139 0 -3684 3482 4480 0 -3482 3684 4140 0 -3281 3482 4140 0 -3482 3281 4480 0 -3890 2042 3684 0 -3890 3684 4661 0 -3087 2895 5639 0 -2895 3087 4140 0 -2700 2895 4140 0 -2895 2700 5639 0 -5935 2470 4100 0 -2470 5935 4140 0 -2246 2470 1847 0 -2470 2246 4100 0 -2700 4140 5935 0 -2700 5935 5639 0 -3281 4140 3087 0 -3281 3087 4293 0 -2042 5640 2245 0 -2245 2469 1846 0 -2042 2245 1846 0 -4140 3684 2042 0 -1666 1846 4141 0 -4141 1846 2469 0 -4141 2469 5936 0 -1666 4141 1845 0 -5640 5936 2469 0 -2042 1846 4140 0 -2043 1847 5801 0 -2246 1847 2043 0 -2470 4140 1847 0 -4140 1667 1847 0 -4142 1665 1488 0 -1845 1665 4142 0 -4140 1666 1667 0 -1666 1845 4142 0 -1665 1487 1488 0 -1666 4140 1846 0 -1845 4143 1665 0 -2043 5801 3483 0 -4143 1487 1665 0 -5640 2469 2245 0 -1845 4141 2040 0 -2246 2043 4100 0 -1664 4144 1487 0 -4292 5640 4139 0 -4867 4702 4332 0 -1489 1666 4142 0 -4862 4845 4325 0 -4845 4862 4326 0 -5471 4845 4326 0 -4845 5471 4325 0 -4326 4844 5472 0 -4326 4862 4697 0 -5472 4844 4136 0 -5471 4326 4665 0 -4863 4844 4326 0 -4326 4103 4297 0 -4325 4665 4484 0 -4665 4325 5471 0 -4484 4665 4326 0 -4325 4484 4297 0 -4297 5635 4325 0 -3894 5635 4297 0 -3688 4104 5635 0 -4104 4298 5635 0 -4484 4326 4297 0 -4104 3895 5634 0 -4664 4326 5472 0 -4325 4696 4862 0 -4664 4136 4483 0 -4136 4664 5472 0 -4102 4483 4136 0 -4664 4483 4326 0 -4482 4295 4136 0 -4295 4482 3892 0 -5637 4295 3484 0 -4295 5637 4136 0 -4663 4482 4136 0 -4102 4136 5637 0 -4296 5636 4326 0 -3687 5636 4296 0 -3485 4103 5636 0 -5636 4103 4326 0 -3688 3894 5803 0 -3894 2898 5803 0 -3688 3486 4104 0 -5803 3486 3688 0 -3894 4103 3090 0 -3894 3688 5635 0 -3893 4296 4483 0 -4103 3894 4297 0 -5637 3283 4102 0 -4483 4296 4326 0 -4863 4136 4844 0 -4104 3486 3895 0 -3896 3689 3487 0 -3689 3895 3091 0 -3487 3689 4135 0 -3286 3690 3487 0 -2900 3488 3092 0 -3092 3286 4135 0 -2900 3092 4135 0 -2705 3287 2900 0 -3487 4135 3286 0 -3092 3488 3286 0 -1853 2049 4134 0 -3093 2705 5930 0 -3093 5930 2475 0 -5630 1853 1673 0 -2251 2901 2475 0 -2475 5930 4135 0 -2251 2475 4135 0 -4134 2901 2251 0 -2705 4135 5930 0 -4134 2251 2048 0 -2900 4135 2705 0 -4105 3895 3689 0 -3091 4135 3689 0 -3091 3285 5803 0 -2899 3091 5803 0 -3091 2899 4135 0 -2704 5803 5931 0 -5803 2704 2899 0 -5931 5803 2474 0 -2704 5931 4135 0 -3285 3486 5803 0 -2899 2704 4135 0 -2048 4135 1852 0 -2251 4135 2048 0 -4134 1852 1672 0 -2048 1852 4134 0 -2474 2250 4135 0 -2250 2474 5803 0 -2047 1851 4135 0 -1851 2047 5803 0 -2250 5803 2047 0 -2250 2047 4135 0 -5931 2474 4135 0 -1851 1671 4135 0 -1672 1852 4135 0 -3895 3285 3091 0 -3895 3486 3285 0 -1851 5803 1671 0 -4483 4102 3893 0 -4298 4325 5635 0 -3686 4295 3892 0 -3892 4101 2471 0 -3686 3892 2044 0 -3484 4295 3686 0 -3089 4102 3283 0 -3283 3484 2044 0 -3089 3283 2044 0 -2897 4102 3089 0 -3686 2044 3484 0 -3283 5637 3484 0 -3893 2897 2702 0 -3893 4102 2897 0 -5802 2702 2897 0 -3893 2702 5933 0 -5933 2472 3893 0 -1849 2472 5802 0 -2248 2472 1849 0 -3893 2472 3687 0 -5933 2702 5802 0 -2248 3687 2472 0 -2897 3089 5802 0 -4482 4137 3892 0 -2471 4101 5934 0 -5801 2471 5934 0 -1848 2471 5801 0 -2247 3892 2471 0 -2247 1848 2044 0 -1848 2247 2471 0 -2472 5933 5802 0 -3089 2044 5802 0 -5934 2701 5801 0 -2044 3892 2247 0 -1849 2045 2248 0 -1848 5802 2044 0 -5801 1668 1848 0 -2045 1849 5802 0 -5799 1489 5798 0 -5801 2701 2896 0 -1668 5801 1667 0 -1667 1666 5799 0 -4142 6143 1489 0 -1667 5801 1847 0 -5799 1666 1489 0 -1490 1667 5799 0 -1488 6143 4142 0 -4101 2701 5934 0 -3892 4137 4101 0 -3687 2248 2045 0 -3687 3485 5636 0 -3284 3485 2045 0 -3090 3284 2045 0 -3485 3284 4103 0 -3090 2898 3894 0 -2703 2898 2045 0 -2045 5932 2703 0 -2898 2703 5803 0 -3090 2045 2898 0 -3284 3090 4103 0 -5932 2473 5803 0 -2473 5932 1850 0 -2249 2473 1850 0 -2473 2249 5803 0 -1850 1670 5803 0 -1850 2046 2249 0 -5803 1670 1671 0 -1669 3928 1670 0 -5932 2045 1850 0 -1850 5803 2046 0 -1670 1850 1669 0 -2249 2046 5803 0 -3687 2045 3485 0 -2703 5932 5803 0 -1667 5800 1668 0 -1668 5802 1848 0 -1668 5800 1491 0 -5802 1668 1669 0 -6143 5798 1489 0 -1667 1490 5800 0 -3932 5798 6143 0 -5800 1490 1491 0 -5797 6143 1488 0 -1490 5799 5798 0 -1668 1491 3929 0 -3929 1669 1668 0 -1492 3929 1491 0 -1669 3929 1492 0 -5798 3932 6142 0 -6142 1490 5798 0 -3931 3930 6142 0 -3930 1490 6142 0 -1321 5797 6144 0 -3930 1491 1490 0 -6143 5797 1321 0 -1669 1492 3928 0 -5797 1488 6144 0 -5802 1669 1850 0 -2045 5802 1850 0 -3930 6141 1491 0 -6144 1488 5796 0 -3893 3687 4296 0 -2896 2701 4101 0 -4134 1673 1853 0 -5796 1488 1487 0 -4514 5621 5623 0 -4519 4329 4330 0 -1670 3928 1493 0 -2037 2239 4527 0 -5333 5331 5319 0 -5331 5332 5318 0 -5330 5319 5320 0 -5330 5333 5319 0 -5333 5330 5329 0 -5312 5333 5334 0 -5333 5312 5331 0 -5320 5321 5330 0 -5331 5312 5332 0 -5313 5359 5318 0 -5313 5332 5360 0 -5359 5313 5276 0 -5277 5317 5359 0 -5354 5277 5359 0 -5276 5313 5360 0 -5276 5360 5353 0 -5276 5354 5359 0 -5332 5312 5360 0 -5235 5316 5277 0 -5312 5311 5360 0 -5332 5313 5318 0 -5334 5333 5329 0 -5329 5321 5322 0 -5328 5334 5329 0 -5311 5312 5334 0 -5361 5311 5310 0 -5334 5335 5311 0 -5352 5361 5362 0 -5361 5353 5360 0 -5360 5311 5361 0 -5234 5353 5188 0 -5276 5353 5234 0 -5354 5234 5188 0 -5234 5354 5276 0 -5275 5233 5353 0 -5233 5275 5352 0 -5187 5352 5138 0 -5352 5187 5233 0 -5275 5361 5352 0 -5233 5187 5353 0 -5361 5275 5353 0 -5353 5187 5138 0 -5352 4978 4979 0 -5329 5330 5321 0 -5139 5188 5353 0 -5189 5355 5314 0 -5189 5235 5354 0 -5140 5189 5354 0 -5189 5140 5355 0 -5354 5139 5064 0 -5354 5235 5277 0 -5139 5353 5064 0 -5140 5354 5065 0 -5139 5354 5188 0 -4980 5354 5064 0 -5355 5065 4981 0 -5065 5355 5140 0 -5065 5354 4981 0 -5355 4981 4982 0 -5354 5395 4982 0 -4982 4981 5354 0 -5398 5354 4980 0 -4982 5395 5355 0 -5354 5287 5395 0 -5355 5395 4822 0 -5353 4980 5064 0 -5314 5235 5189 0 -5398 5353 5397 0 -5063 4979 5353 0 -5399 5353 4979 0 -5398 5397 5354 0 -5398 4980 5353 0 -4979 5063 5352 0 -4979 5289 5399 0 -5353 5399 4815 0 -5138 5352 5063 0 -5397 5353 5288 0 -5396 4821 5354 0 -5288 4821 5396 0 -5288 4820 4821 0 -4821 4820 5354 0 -4815 5288 5353 0 -4815 5289 4816 0 -4817 5288 4816 0 -4817 5289 4818 0 -5289 4817 4816 0 -4816 5288 4815 0 -5288 5396 5397 0 -5288 4817 4818 0 -5289 4815 5399 0 -5397 5396 5354 0 -5063 5353 5138 0 -5287 5354 4820 0 -5329 5322 5328 0 -5190 5355 5356 0 -5328 5335 5334 0 -5335 5328 5337 0 -5310 5335 5336 0 -5335 5310 5311 0 -5323 5337 5328 0 -5337 5323 5324 0 -5336 5337 5338 0 -5337 5336 5335 0 -5328 5322 5323 0 -5362 5310 5309 0 -5362 5274 5352 0 -5274 5362 5350 0 -5232 5274 5350 0 -5274 5232 5352 0 -5351 5350 5186 0 -5350 5351 5232 0 -5137 5186 5350 0 -5351 5186 5352 0 -5350 5362 5363 0 -5232 5351 5352 0 -5309 5310 5336 0 -5310 5362 5361 0 -5363 5309 5308 0 -5338 5337 5324 0 -5336 5338 5307 0 -5309 5363 5362 0 -5307 5309 5336 0 -5307 5338 5339 0 -5308 5307 5306 0 -5307 5308 5309 0 -5324 5325 5338 0 -5363 5308 5364 0 -5273 5231 5350 0 -5231 5273 5349 0 -5185 5231 5349 0 -5231 5185 5350 0 -5364 5272 5349 0 -5272 5364 5348 0 -5185 5349 5184 0 -5230 5349 5272 0 -5272 5348 5230 0 -5349 5273 5363 0 -5363 5364 5349 0 -5184 5349 5230 0 -5308 5366 5364 0 -5363 5273 5350 0 -5136 5350 5185 0 -5137 5062 5352 0 -5062 5137 5136 0 -4978 5062 5061 0 -5062 4978 5352 0 -5136 5061 5062 0 -5061 5136 5135 0 -4977 5061 5060 0 -5061 4977 4978 0 -5350 5136 5137 0 -4978 4977 5401 0 -5400 5289 4978 0 -4814 5400 5401 0 -4814 5290 5503 0 -4814 5289 5400 0 -5290 5501 5502 0 -5502 5503 5290 0 -4814 5401 4813 0 -5502 5289 5503 0 -5400 4978 5401 0 -5503 5289 4814 0 -4977 4976 5401 0 -5289 4979 4978 0 -5135 5060 5061 0 -5135 5136 5184 0 -5135 5184 5348 0 -5060 4976 4977 0 -5402 5401 4976 0 -4976 5060 5059 0 -5402 4976 5294 0 -5402 4813 5401 0 -5135 5134 5060 0 -4813 5402 4812 0 -5504 5290 4813 0 -5292 4631 5504 0 -5292 4632 4631 0 -4631 5290 5504 0 -5292 4813 4812 0 -5505 4812 5294 0 -4630 5292 5505 0 -4448 4630 5294 0 -5505 5294 4630 0 -5505 5292 4812 0 -5292 5504 4813 0 -5292 4630 4448 0 -5402 5294 4812 0 -5290 4814 4813 0 -5185 5184 5136 0 -5290 4631 4632 0 -5230 5348 5184 0 -5186 5137 5352 0 -5289 5502 5501 0 -5494 5495 5355 0 -5494 5355 4822 0 -5287 5494 4822 0 -5495 4641 5355 0 -5395 5287 4822 0 -5497 5496 5288 0 -4638 5497 5288 0 -5497 5287 5496 0 -5496 5287 4820 0 -4638 5287 5497 0 -4641 5356 5355 0 -5495 5287 4641 0 -4641 5287 4640 0 -4640 5066 4641 0 -5287 4457 4639 0 -4639 4640 5287 0 -5287 4638 4456 0 -4457 4983 4639 0 -5287 5495 5494 0 -4639 4983 4640 0 -4638 5288 4456 0 -4457 5286 4983 0 -4269 4456 5288 0 -5496 4820 5288 0 -5289 4637 5498 0 -5498 5288 4819 0 -5288 5498 4637 0 -5289 4268 4455 0 -5289 5665 4268 0 -5288 4637 4455 0 -4455 4637 5289 0 -5288 4455 4268 0 -5664 5287 4269 0 -4075 5664 5288 0 -3866 4075 5288 0 -5287 5664 4075 0 -5288 5665 4074 0 -5665 5288 4268 0 -4074 3865 5288 0 -5249 5248 3865 0 -3865 4074 5289 0 -3866 5288 3660 0 -5664 4269 5288 0 -5287 4075 3866 0 -4074 5665 5289 0 -5287 4456 4269 0 -4819 5288 4818 0 -3865 5248 5288 0 -5286 5285 4643 0 -5286 4457 4270 0 -5286 4270 5663 0 -5286 3868 4077 0 -5663 5662 5286 0 -5287 4076 5662 0 -5287 5286 4076 0 -5662 4076 5286 0 -5662 5663 5287 0 -5246 5286 3867 0 -5201 3662 3663 0 -3662 5201 5245 0 -3663 3461 5240 0 -3663 5490 5201 0 -3662 3868 5246 0 -3868 3662 5245 0 -5246 3868 5286 0 -3663 3662 5202 0 -4077 3868 5245 0 -3661 3662 5246 0 -5663 4270 5287 0 -5201 5491 5245 0 -3867 5287 5247 0 -5287 3867 5286 0 -5247 5287 3660 0 -3661 3867 5247 0 -3660 5248 3659 0 -3660 5287 3866 0 -5249 3659 5248 0 -5247 3660 3459 0 -5288 5248 3660 0 -3659 3459 3660 0 -5202 3661 3460 0 -5202 3662 3661 0 -3460 3661 5203 0 -5202 3460 3663 0 -5203 3459 3258 0 -3459 5203 3661 0 -3257 5154 3258 0 -5154 3460 3258 0 -3258 3459 3458 0 -3258 3460 5203 0 -3661 5247 3459 0 -3663 3460 5153 0 -3458 3459 3659 0 -3661 5246 3867 0 -4270 4457 5287 0 -3259 3460 5154 0 -5498 4819 5289 0 -5066 5356 4641 0 -5289 5500 5499 0 -5289 5501 5500 0 -5499 5500 5290 0 -5289 5499 4636 0 -5291 5290 4633 0 -5500 5501 5290 0 -4634 4633 5292 0 -5499 5290 4636 0 -4633 5290 4632 0 -4634 5291 4633 0 -4636 4454 5289 0 -4636 5290 4454 0 -4454 5290 4267 0 -4454 4267 5289 0 -5290 4073 5666 0 -5666 4267 5290 0 -4453 5290 5291 0 -5289 5666 5249 0 -3864 4073 5290 0 -4267 5666 5289 0 -4634 4635 5291 0 -5249 5666 4073 0 -4635 5292 4453 0 -4635 4634 5292 0 -4266 4453 5292 0 -4635 4453 5291 0 -4449 4450 5292 0 -4450 4449 5294 0 -4451 4450 5294 0 -4450 4451 5292 0 -4632 5292 4633 0 -4452 5292 4451 0 -4266 5667 5290 0 -4266 5292 5667 0 -5667 5292 4072 0 -5667 4072 5290 0 -5292 4265 5668 0 -4265 4452 5294 0 -5292 3863 4072 0 -4071 5668 5294 0 -4265 5294 5668 0 -5292 5668 4071 0 -5292 4452 4265 0 -3862 3863 5292 0 -4451 5294 4452 0 -4453 4266 5290 0 -4449 5292 4448 0 -3863 5290 4072 0 -3659 5204 3458 0 -5249 5250 3659 0 -5250 3658 3659 0 -3457 3458 5204 0 -3658 5250 5251 0 -3864 5250 5249 0 -3457 3658 5205 0 -3658 5204 3659 0 -5249 4073 3864 0 -3457 5204 3658 0 -3257 5155 3064 0 -5155 3257 3256 0 -3064 5155 3063 0 -3064 5154 3257 0 -3256 5156 3063 0 -3457 3456 3256 0 -3256 3063 5155 0 -3457 3256 3257 0 -3257 3458 3457 0 -5105 3064 3063 0 -3864 3863 5250 0 -3257 3258 3458 0 -5205 3658 3657 0 -3657 3658 5251 0 -3863 5251 5250 0 -5252 5251 3863 0 -5206 3657 3656 0 -5252 3657 5251 0 -3455 5206 5253 0 -3657 5206 5205 0 -5252 3863 3862 0 -5205 5206 3456 0 -5156 3456 3255 0 -5156 3256 3456 0 -3255 3456 5157 0 -5156 3255 3254 0 -3253 5157 5207 0 -3253 3255 5157 0 -3061 3254 3253 0 -3254 3255 3253 0 -5207 5157 3455 0 -5157 3456 3455 0 -3456 5206 3455 0 -5156 3254 3063 0 -5252 3656 3657 0 -3456 3457 5205 0 -3864 5290 3863 0 -5106 3063 3254 0 -5292 4071 3862 0 -5249 3865 5289 0 -5289 4819 4818 0 -5154 3064 3065 0 -4448 5294 4449 0 -5331 5318 5319 0 -5339 5306 5307 0 -5339 5325 5326 0 -5306 5339 5340 0 -5366 5308 5306 0 -5365 5348 5364 0 -5365 5366 5347 0 -5271 5365 5347 0 -5365 5271 5348 0 -5306 5305 5366 0 -5365 5364 5366 0 -5348 5229 5183 0 -5229 5348 5271 0 -5183 5229 5347 0 -5348 5183 5134 0 -5059 5060 5134 0 -5059 5134 5347 0 -4975 5059 5058 0 -4976 5059 4975 0 -5183 5347 5134 0 -5294 4976 4975 0 -5271 5347 5229 0 -5348 5134 5135 0 -5305 5306 5340 0 -5340 5326 5327 0 -5305 5340 5327 0 -5366 5305 5367 0 -5367 5269 5347 0 -5269 5367 5304 0 -5270 5269 5346 0 -5269 5270 5347 0 -5305 5341 5367 0 -5367 5347 5366 0 -5295 5403 4975 0 -5347 5058 5059 0 -5228 5347 5270 0 -5182 5347 5228 0 -5182 5133 5347 0 -5133 5182 5346 0 -4974 4975 5058 0 -4974 5058 5346 0 -5133 5346 5058 0 -5133 5058 5347 0 -5228 5346 5182 0 -5295 4975 4974 0 -5270 5346 5228 0 -5340 5339 5326 0 -5294 4975 5403 0 -5294 4811 5506 0 -3862 4071 5294 0 -5506 4811 5295 0 -5294 5506 4629 0 -4629 4447 5294 0 -4447 4629 5295 0 -4260 4447 5295 0 -4447 4260 5294 0 -5506 5295 4629 0 -5294 4260 4261 0 -4262 4263 5294 0 -4262 5294 4261 0 -4262 4261 5295 0 -4263 4264 5294 0 -5669 5294 4264 0 -4264 4263 5295 0 -5669 4264 5295 0 -3862 5294 5293 0 -4262 5295 4263 0 -4070 5294 5669 0 -4260 5295 4261 0 -5294 5403 4811 0 -5295 5404 4810 0 -5295 4070 5669 0 -4810 5404 5296 0 -5295 4810 5507 0 -5507 4628 5295 0 -4628 5507 5296 0 -4446 4628 5296 0 -4628 4446 5295 0 -4810 5296 5507 0 -5295 4446 4259 0 -4070 5295 3861 0 -5674 5295 4259 0 -5295 5674 5673 0 -4070 3861 5294 0 -5672 5295 5673 0 -5673 5674 5296 0 -5295 5254 3861 0 -5671 5672 5296 0 -5673 5296 5672 0 -5671 5295 5672 0 -4259 5296 5674 0 -5670 5295 5671 0 -4446 5296 4259 0 -4811 5403 5295 0 -4974 5404 5295 0 -3861 5293 5294 0 -4974 5346 5404 0 -5339 5338 5325 0 -5368 5269 5304 0 -5304 5341 5344 0 -5368 5304 5344 0 -5368 5346 5269 0 -5268 5226 5346 0 -5226 5268 5342 0 -5227 5226 5345 0 -5226 5227 5346 0 -5368 5344 5268 0 -5368 5268 5346 0 -5346 5181 5132 0 -5181 5346 5227 0 -5132 5181 5345 0 -5346 5132 5057 0 -5346 5057 5296 0 -4973 5057 5345 0 -5297 5405 4973 0 -5296 5057 4973 0 -5132 5345 5057 0 -5405 5296 4973 0 -5227 5345 5181 0 -5367 5341 5304 0 -5226 5342 5345 0 -5268 5344 5342 0 -5345 5342 5303 0 -4973 5345 5297 0 -5369 5303 5298 0 -5342 5298 5303 0 -5344 5298 5342 0 -5303 5369 5345 0 -5327 5459 5344 0 -5345 5369 5267 0 -5345 5225 5297 0 -5225 5345 5267 0 -5225 5267 5298 0 -5405 5297 4809 0 -5179 5298 5343 0 -5298 5179 5225 0 -5180 5343 5302 0 -5343 5180 5179 0 -5343 5257 5302 0 -5225 5179 5297 0 -5267 5369 5298 0 -5297 5179 5180 0 -5344 4232 5298 0 -5341 5327 5344 0 -5404 5346 5296 0 -5296 5405 4809 0 -5670 4069 5295 0 -4069 5670 5296 0 -3860 4069 5296 0 -4069 3860 5295 0 -5296 5508 4627 0 -5508 5296 4809 0 -4627 5508 5297 0 -5255 3860 5296 0 -5671 5296 5670 0 -5675 4064 5296 0 -4064 4065 5296 0 -5297 4065 4064 0 -5297 4066 4065 0 -4065 4066 5296 0 -4067 4068 5296 0 -5297 4068 4067 0 -5297 3859 4068 0 -4068 3859 5296 0 -4067 4066 5297 0 -4066 4067 5296 0 -4627 4445 5296 0 -5254 5295 3860 0 -4445 5297 4258 0 -5297 4445 4627 0 -5675 4258 5297 0 -4445 4258 5296 0 -5131 5302 5056 0 -5297 5180 5131 0 -5056 5302 5370 0 -5131 5056 5297 0 -5508 4809 5297 0 -4972 5297 5056 0 -5406 4808 5297 0 -5297 4972 5406 0 -5406 4972 5266 0 -5297 5256 3859 0 -5266 4808 5406 0 -5257 5266 5370 0 -4808 5256 5297 0 -5224 4808 5266 0 -5224 5266 5257 0 -5266 4972 5370 0 -5056 5370 4972 0 -5297 4064 5675 0 -5370 5302 5257 0 -4258 5675 5296 0 -5180 5302 5131 0 -5296 3859 5255 0 -5343 5298 5257 0 -5305 5327 5341 0 -5509 4808 5224 0 -3860 5255 5254 0 -5254 5293 3861 0 -3654 5254 5255 0 -5293 5254 3655 0 -3854 5256 3853 0 -3653 3859 5256 0 -3855 3854 3853 0 -5256 3854 3855 0 -5256 4808 5509 0 -3653 3654 5255 0 -5254 3654 3655 0 -3655 3656 5293 0 -5208 3655 3654 0 -5253 3656 3655 0 -3857 5256 3856 0 -5130 3856 3855 0 -5130 3857 3856 0 -5255 3859 3653 0 -3856 5256 3855 0 -3857 3858 5256 0 -5256 5509 4626 0 -3656 5252 5293 0 -4626 4444 5256 0 -4444 4626 5178 0 -4257 4444 5129 0 -4444 4257 5256 0 -4257 5129 5676 0 -5129 4444 5178 0 -5129 5178 5257 0 -5676 5129 4063 0 -4626 5509 5178 0 -4257 5676 5256 0 -4063 5130 3853 0 -5130 4063 5129 0 -5130 5055 3857 0 -4063 3853 5256 0 -4971 3857 5055 0 -4971 5055 5301 0 -4807 3857 5407 0 -4807 5407 5371 0 -4971 5371 5407 0 -5407 3857 4971 0 -5130 5299 5055 0 -5130 3855 3853 0 -5129 5299 5130 0 -5676 4063 5256 0 -5509 5224 5178 0 -5510 3857 4807 0 -5207 5253 3454 0 -3253 5207 5158 0 -3454 5253 5208 0 -5159 5207 3454 0 -5208 5209 3453 0 -5209 5208 3654 0 -3251 3453 5160 0 -3454 5208 5159 0 -5253 3655 5208 0 -3453 5159 5208 0 -5158 5107 3253 0 -5158 5207 3252 0 -5158 3252 5108 0 -3060 5107 5158 0 -5108 5159 3251 0 -5159 5108 3252 0 -3251 5159 3453 0 -3059 5108 3251 0 -3252 5207 5159 0 -5108 3060 5158 0 -3453 5209 3652 0 -3455 5253 5207 0 -5256 3858 3653 0 -3857 5510 4625 0 -3857 4625 3858 0 -3653 3858 3652 0 -4625 4443 3858 0 -4443 4625 5223 0 -4256 4443 5223 0 -3858 4443 5210 0 -5510 5265 4625 0 -5210 4443 4256 0 -5160 3652 3452 0 -5160 3453 3652 0 -3452 3652 5161 0 -5109 5160 3452 0 -3651 5161 3652 0 -3651 5210 3650 0 -5161 3451 5409 0 -3451 5161 3651 0 -3651 5372 3451 0 -3651 3652 5210 0 -3652 3858 5210 0 -3251 5160 5109 0 -5677 5210 4256 0 -3652 5209 3653 0 -3653 5209 3654 0 -5161 3250 3452 0 -4807 5265 5510 0 -5206 3656 5253 0 -5299 5301 5055 0 -5299 5129 5257 0 -5166 5301 5299 0 -5301 5371 4971 0 -5265 4807 5371 0 -5166 5265 5371 0 -5166 5223 5265 0 -5223 4625 5265 0 -5371 5301 5166 0 -4062 5677 5177 0 -3852 5210 4062 0 -5177 3852 4062 0 -5128 3647 3852 0 -3647 5210 3852 0 -3649 5210 3648 0 -5128 3649 3648 0 -5128 3650 3649 0 -3650 5210 3649 0 -3648 3647 5128 0 -3648 5210 3647 0 -5177 4256 5223 0 -5210 5677 4062 0 -5177 5677 4256 0 -5166 5128 5177 0 -5166 5053 5128 0 -5177 5128 3852 0 -5053 5211 3650 0 -5165 5211 5166 0 -5054 5211 5165 0 -5211 5054 3650 0 -5211 5053 5166 0 -5128 5053 3650 0 -5258 4970 3650 0 -4970 5258 5165 0 -5408 4970 5165 0 -4970 5408 3650 0 -4806 3651 5300 0 -4806 5300 5165 0 -5372 3651 5511 0 -5372 5511 5165 0 -4806 5165 5511 0 -5511 3651 4806 0 -5408 5165 5300 0 -5300 3651 5408 0 -5054 5165 5258 0 -5054 5258 3650 0 -5177 5223 5166 0 -5372 4624 3451 0 -4442 3451 4624 0 -4442 4624 5165 0 -5264 4442 5165 0 -5264 3451 4442 0 -5678 3451 4255 0 -5678 4255 5165 0 -4061 5678 5165 0 -4061 3451 5678 0 -5264 5165 4255 0 -4255 3451 5264 0 -3851 3451 5222 0 -3851 5222 5165 0 -3646 3851 5165 0 -3646 3451 3851 0 -3449 3451 3448 0 -3449 3448 5165 0 -5176 3451 3450 0 -5176 3450 5165 0 -3449 5165 3450 0 -3450 3451 3449 0 -3646 5165 3448 0 -3448 3451 3646 0 -4061 5165 5222 0 -5222 3451 4061 0 -5162 3451 5127 0 -5162 5127 5164 0 -5052 5162 5164 0 -5052 3451 5162 0 -5212 3451 4968 0 -5212 4968 5164 0 -4969 5212 5164 0 -4969 3451 5212 0 -5052 5164 4968 0 -4968 3451 5052 0 -5259 5161 5409 0 -5259 5409 5164 0 -4805 5259 5164 0 -4805 5161 5259 0 -4805 5512 3249 0 -5262 5512 5164 0 -5262 4623 3249 0 -4441 4623 5164 0 -5262 5164 4623 0 -5512 5262 3249 0 -4805 5164 5512 0 -4805 3249 5161 0 -4969 5164 5409 0 -5409 3451 4969 0 -5176 5165 5127 0 -5127 3451 5176 0 -5372 5165 4624 0 -3650 5408 3651 0 -5178 5224 5257 0 -3253 5107 3061 0 -5299 5257 5166 0 -5293 5252 3862 0 -3249 4623 4441 0 -5152 3461 3260 0 -5152 4465 3461 0 -3260 3461 5103 0 -3260 5102 5146 0 -5153 3259 5103 0 -5153 3460 3259 0 -5103 3259 3066 0 -5153 5103 3461 0 -3461 3663 5153 0 -3260 5103 5102 0 -3261 5079 3878 0 -3261 5102 5080 0 -5079 3261 5080 0 -5102 3261 4280 0 -3068 5079 5000 0 -5102 5103 5080 0 -5080 3066 3067 0 -5080 5000 5079 0 -5103 3066 5080 0 -3068 5073 5079 0 -3259 5104 3066 0 -4830 3068 5078 0 -2876 5104 2875 0 -3066 5104 2876 0 -5104 3065 3064 0 -2876 5081 3066 0 -2875 5105 2874 0 -5104 3064 2875 0 -2874 5105 3063 0 -2875 2874 5082 0 -3064 5105 2875 0 -2876 2875 5082 0 -5001 3067 2877 0 -3067 3066 5081 0 -2877 3067 5081 0 -5000 5080 3067 0 -5082 2683 2877 0 -5083 2683 5082 0 -5001 2877 2684 0 -5002 2877 2683 0 -5950 5002 5003 0 -2877 5081 2876 0 -2876 5082 2877 0 -2684 2877 5002 0 -5003 5002 2683 0 -5104 3259 3065 0 -3065 3259 5154 0 -2878 3067 5001 0 -2880 4998 2879 0 -4998 4876 4090 0 -2879 4998 5078 0 -4998 2880 4876 0 -5078 4999 2879 0 -4999 5078 5000 0 -2686 4999 2685 0 -2879 4999 2880 0 -5078 3068 5000 0 -2880 4999 4877 0 -2687 4877 4711 0 -2687 4876 2880 0 -2687 4711 5946 0 -2687 5946 4710 0 -4877 2686 5947 0 -2686 4877 4999 0 -5947 2686 4712 0 -4877 5947 4711 0 -2880 4877 2687 0 -4711 5947 2460 0 -4999 5000 2685 0 -4991 5078 4998 0 -2878 4879 2685 0 -2878 5001 4879 0 -2685 4879 4878 0 -2878 2685 5000 0 -2684 5950 4880 0 -4879 5001 2684 0 -4880 5950 2457 0 -4879 2684 5949 0 -5002 5950 2684 0 -5949 2684 4880 0 -5948 4712 4878 0 -4712 2686 4878 0 -5949 4878 4879 0 -5948 4878 5949 0 -4714 2457 2458 0 -4714 5948 5949 0 -4712 5948 2459 0 -4714 4713 5948 0 -2458 2457 4715 0 -5949 4880 4714 0 -4880 2457 4714 0 -4714 2458 4713 0 -2457 5950 4881 0 -4878 2686 2685 0 -5000 3067 2878 0 -5947 4712 5607 0 -5082 2874 5083 0 -4710 4876 2687 0 -3062 5106 2873 0 -5106 3062 2874 0 -2873 5106 3061 0 -3062 2873 5083 0 -5084 2872 2873 0 -3061 5107 5084 0 -2871 2681 2872 0 -5084 2873 3061 0 -5106 2874 3063 0 -2873 2872 2681 0 -2682 5003 2683 0 -5083 2873 2682 0 -5003 2682 2681 0 -2683 5083 2682 0 -5951 2681 5004 0 -2870 5004 2681 0 -2679 2680 5004 0 -2681 5951 5003 0 -2873 2681 2682 0 -5951 5004 2680 0 -2872 5084 2871 0 -5003 4881 5950 0 -5084 3059 2871 0 -3060 5108 3059 0 -5085 3059 5109 0 -2871 3059 5085 0 -3452 3250 5109 0 -5161 5110 3250 0 -5085 3250 3058 0 -5109 3250 5085 0 -5107 3060 5084 0 -5111 3250 5110 0 -5086 2870 2869 0 -2870 2681 2871 0 -2870 2871 5085 0 -5004 2870 2679 0 -3058 5111 2869 0 -2870 5085 3058 0 -5086 2869 2678 0 -2868 5087 2869 0 -5087 2868 2677 0 -3058 2869 2870 0 -3250 5111 3058 0 -5086 2679 2870 0 -5111 2868 2869 0 -5084 3060 3059 0 -3059 3251 5109 0 -5087 2678 2869 0 -4881 5951 2456 0 -5951 4881 5003 0 -2456 5951 5952 0 -4881 2456 4715 0 -5952 2679 5953 0 -5952 5951 2680 0 -5953 2679 2678 0 -2456 5952 4882 0 -2680 2679 5952 0 -4882 5952 5953 0 -2456 2235 4715 0 -2456 4716 2235 0 -2235 4716 2234 0 -2458 4715 5604 0 -2234 4716 4718 0 -2456 2455 4716 0 -4883 2455 4882 0 -2455 4718 4716 0 -2456 4882 2455 0 -2235 5604 4715 0 -5005 4882 5953 0 -4715 2457 4881 0 -5005 2678 5954 0 -5005 4883 4882 0 -5954 2678 5006 0 -5005 5954 4883 0 -5955 5006 5007 0 -2677 5006 2678 0 -2677 5007 5006 0 -5006 5955 5954 0 -2678 5087 2677 0 -5954 5955 4884 0 -4719 4718 4883 0 -4883 4718 2455 0 -2454 4883 4884 0 -4719 4883 2454 0 -4884 4885 2454 0 -2454 5956 4719 0 -4718 2453 4717 0 -5600 5601 2453 0 -2453 4719 5600 0 -4719 2453 4718 0 -4883 5954 4884 0 -4718 4717 2234 0 -4535 5601 5600 0 -5005 5953 2678 0 -2679 5086 2678 0 -5603 2235 2234 0 -5111 5110 3057 0 -5083 2874 3062 0 -5106 3254 3061 0 -5601 4717 2453 0 -5608 2238 4527 0 -2460 5608 4711 0 -2238 5608 2460 0 -2238 2037 4527 0 -4528 2460 5607 0 -5607 2460 5947 0 -2237 5607 2459 0 -2460 4528 2238 0 -5946 4711 5608 0 -4528 5607 2237 0 -2238 4337 2037 0 -4337 2238 4528 0 -4337 4528 2036 0 -4337 1842 4144 0 -1842 2036 4338 0 -2036 1842 4337 0 -1841 4338 2036 0 -1842 4338 4145 0 -4528 2237 2036 0 -4144 1842 1663 0 -2036 2237 4529 0 -1843 2037 4337 0 -2459 4713 5606 0 -4713 2459 5948 0 -5606 4713 2236 0 -2035 2237 5606 0 -5605 2236 5604 0 -2236 4713 2458 0 -5604 2235 2034 0 -5606 2236 5605 0 -5604 2236 2458 0 -2034 5605 5604 0 -4529 2035 1841 0 -4529 2237 2035 0 -1841 2035 4530 0 -4529 1841 2036 0 -1840 4530 2035 0 -4532 1840 5605 0 -4530 1661 1841 0 -1661 4530 1840 0 -1661 4531 4340 0 -1840 2035 5605 0 -2035 5606 5605 0 -4338 1841 1662 0 -1661 1840 4531 0 -5606 2237 2459 0 -5607 4712 2459 0 -4339 1841 1661 0 -5796 1663 4145 0 -5796 1487 1663 0 -1663 1842 4145 0 -5796 4145 1486 0 -1486 1662 4146 0 -1662 1486 4145 0 -6146 5795 4146 0 -5796 1486 6145 0 -4338 1662 4145 0 -5795 1486 4146 0 -6145 5795 3935 0 -5795 6145 1486 0 -3935 5795 1319 0 -3935 6144 6145 0 -1319 5794 3936 0 -5794 1319 5795 0 -3936 5794 5793 0 -3935 1319 3729 0 -5794 5795 6146 0 -3936 3730 1319 0 -4146 1662 4339 0 -6145 6144 5796 0 -4146 1485 6146 0 -1485 4146 4339 0 -4339 1661 1485 0 -6146 1485 4147 0 -4147 4340 1484 0 -4340 4147 1485 0 -5791 5792 1484 0 -4147 1484 5792 0 -1661 4340 1485 0 -4147 5793 5794 0 -3938 3937 5793 0 -3937 3936 5793 0 -5792 5793 4147 0 -6147 5793 5792 0 -6147 3939 3938 0 -5791 3939 6147 0 -3937 3938 3732 0 -6148 3938 3939 0 -6148 3939 3940 0 -6147 3938 5793 0 -5791 6147 5792 0 -6148 3732 3938 0 -4341 1484 4340 0 -5794 6146 4147 0 -1662 1841 4339 0 -3937 3731 3936 0 -5605 2034 4532 0 -5946 5608 2461 0 -4346 1839 4532 0 -5603 4532 2034 0 -2033 4532 5603 0 -1839 4346 4345 0 -4347 4346 2033 0 -2033 4346 4532 0 -4533 2033 5603 0 -4345 4346 4154 0 -5603 5602 4533 0 -1838 4345 4154 0 -4343 1660 4531 0 -1660 4340 4531 0 -4531 1840 1839 0 -4343 4531 1839 0 -4343 1838 4344 0 -1838 4343 1839 0 -4344 1838 4153 0 -4152 4343 4344 0 -1839 4345 1838 0 -1660 4343 4341 0 -4533 4534 2033 0 -1840 4532 1839 0 -5602 4534 4533 0 -4717 5601 5602 0 -4534 5602 2233 0 -2033 4534 4347 0 -2233 2451 4349 0 -4535 4536 2233 0 -2233 4349 4534 0 -4535 2233 5602 0 -5601 4535 5602 0 -4534 4349 4348 0 -4153 4154 5785 0 -4154 4346 4347 0 -2032 4154 4347 0 -4154 4153 1838 0 -2032 4348 4155 0 -4348 2032 4347 0 -2031 5785 4155 0 -5785 4154 4155 0 -4155 4348 2231 0 -4155 4154 2032 0 -4347 4534 4348 0 -4344 4153 1837 0 -2232 4348 4349 0 -5603 4717 5602 0 -2235 5603 2034 0 -5785 1837 4153 0 -4148 4341 4342 0 -4148 1484 4341 0 -4342 4341 1659 0 -4149 4148 4342 0 -1659 4151 4150 0 -1659 4341 4343 0 -4150 4151 5788 0 -1659 4150 4342 0 -4151 1659 4343 0 -4149 4342 4150 0 -5791 1483 3940 0 -5791 4148 1483 0 -3940 1483 5790 0 -5791 3940 3939 0 -5790 4149 5789 0 -4149 5790 1483 0 -3734 3940 1482 0 -5789 1482 5790 0 -5789 3941 1482 0 -5790 1482 3940 0 -4148 4149 1483 0 -3941 3735 1482 0 -4150 5789 4149 0 -5791 1484 4148 0 -4150 5788 1658 0 -1658 5789 4150 0 -5788 4152 5787 0 -3942 1658 5788 0 -5787 3943 5788 0 -5786 5787 1837 0 -3944 3943 5787 0 -5787 4152 1837 0 -4343 4152 4151 0 -3942 5788 3943 0 -3941 3737 1481 0 -5789 1658 3942 0 -3941 3942 3737 0 -3941 5789 3942 0 -1657 3738 3537 0 -1657 3737 3942 0 -3737 3736 1481 0 -3737 1657 3736 0 -3738 3943 1836 0 -1657 3942 3738 0 -3943 3738 3942 0 -3537 3536 1657 0 -3738 3739 3537 0 -4151 4152 5788 0 -4344 1837 4152 0 -6148 3940 3734 0 -1837 5785 5786 0 -4340 1660 4341 0 -4717 5603 2234 0 -6144 3935 3934 0 -4884 5955 4885 0 -3260 4466 5152 0 -5263 4254 3249 0 -4254 5263 5164 0 -5679 4254 5164 0 -4254 5679 3249 0 -4060 5221 3249 0 -5221 4060 5164 0 -3850 5221 5164 0 -5221 3850 3249 0 -5679 5164 4060 0 -5679 4060 3249 0 -3249 3645 3447 0 -3645 3249 3850 0 -3447 3645 5164 0 -3249 3447 3247 0 -3247 3248 3249 0 -3248 3247 5164 0 -5175 3248 5164 0 -3248 5175 3249 0 -3447 5164 3247 0 -3249 5175 5126 0 -3850 5164 3645 0 -5110 5161 3249 0 -5126 5163 3249 0 -5163 5126 5164 0 -5051 5163 5164 0 -5163 5051 3249 0 -4967 5213 3249 0 -5213 4967 5164 0 -5411 5213 5164 0 -5213 5411 3249 0 -5051 5164 4967 0 -5051 4967 3249 0 -3249 5410 3057 0 -5260 5410 5214 0 -4804 5260 5214 0 -3057 5410 5260 0 -3057 4804 5513 0 -5219 5513 5214 0 -3057 5219 4622 0 -4440 4622 5261 0 -5219 5214 4622 0 -3057 5513 5219 0 -4804 5214 5513 0 -3057 5260 4804 0 -5411 5214 5410 0 -5411 5410 3249 0 -5175 5164 5126 0 -3057 4622 4440 0 -5220 4253 5112 0 -4253 5220 5218 0 -5680 4253 5218 0 -4253 5680 5112 0 -4059 5174 5112 0 -5174 4059 5113 0 -3849 5174 5113 0 -5174 3849 5112 0 -5680 5172 4059 0 -5680 4059 5112 0 -4439 5088 2868 0 -2868 5111 3057 0 -3057 4803 2868 0 -5088 2676 2677 0 -5112 3644 3446 0 -3057 4440 5112 0 -3446 3644 5113 0 -5112 4802 3057 0 -3849 3644 5112 0 -3246 5112 3446 0 -3849 5113 3644 0 -5112 4440 5220 0 -3246 3056 5112 0 -3056 3246 5113 0 -5125 3056 5113 0 -3056 5125 5112 0 -5050 4966 5112 0 -4966 5050 5113 0 -5412 4966 5113 0 -4966 5412 5112 0 -5125 5113 5050 0 -5125 5050 5112 0 -4802 4803 3057 0 -4803 4802 5113 0 -5514 4803 5113 0 -4803 5514 2868 0 -4621 4439 2868 0 -4439 4621 5515 0 -4252 5681 5088 0 -5681 4252 4620 0 -4439 4620 4252 0 -4439 4252 5088 0 -5514 5515 4621 0 -5514 4621 2868 0 -5412 5113 4802 0 -5412 4802 5112 0 -3446 5113 3246 0 -5088 5681 4058 0 -4440 5261 5220 0 -3057 5110 3249 0 -5214 5165 5261 0 -5215 5218 5261 0 -5261 5218 5220 0 -5261 4622 5214 0 -5218 5172 5680 0 -5124 5172 5216 0 -5173 5172 5124 0 -4059 5172 5113 0 -5172 5218 5216 0 -5113 5172 5173 0 -5113 4801 5515 0 -4801 5113 5413 0 -5515 4801 5516 0 -4620 4439 5515 0 -4801 5413 5516 0 -4965 5413 5113 0 -5049 5113 5124 0 -5516 5413 4964 0 -5113 5173 5124 0 -4620 5515 5516 0 -5165 5214 5164 0 -5113 5515 5514 0 -5215 5166 5216 0 -5217 5216 5166 0 -5114 5124 5216 0 -5216 5218 5215 0 -5114 5217 5171 0 -5167 5171 5217 0 -5168 5048 5123 0 -5114 5171 5123 0 -5168 5123 5171 0 -5216 5217 5114 0 -4965 5049 5114 0 -5049 4965 5113 0 -5124 5114 5049 0 -4965 5114 4964 0 -4800 4963 5116 0 -4963 5414 5123 0 -4056 5516 4964 0 -4964 5114 5414 0 -4963 4800 5414 0 -5114 5123 5414 0 -4963 5123 5048 0 -4964 5413 4965 0 -5166 5215 5165 0 -5215 5261 5165 0 -5263 4441 5164 0 -5517 4964 5414 0 -4058 3848 5088 0 -5089 3848 4058 0 -3848 5089 3643 0 -3848 3643 5088 0 -5089 3445 3643 0 -5089 4058 5681 0 -4620 5516 4438 0 -5089 3245 3445 0 -5681 4620 5089 0 -3643 3445 5088 0 -3245 3055 5088 0 -5089 3055 3245 0 -5089 2867 3055 0 -3055 2867 5088 0 -2866 3244 3054 0 -3847 5089 4057 0 -5090 3054 3244 0 -5090 2866 3054 0 -2866 3444 3244 0 -5088 2867 2676 0 -5089 4620 4438 0 -3445 3245 5088 0 -5682 4056 4057 0 -4251 5089 4438 0 -5682 4251 4056 0 -5682 5089 4251 0 -5517 4800 4619 0 -4056 4438 5516 0 -4619 4800 4436 0 -4438 4056 4251 0 -5517 4056 4964 0 -4056 5517 4619 0 -5089 3847 3642 0 -5090 3642 3847 0 -5090 3444 3642 0 -5089 3642 3444 0 -4437 4436 5683 0 -4250 4619 4437 0 -4056 4619 4250 0 -5683 4250 4437 0 -4436 4055 5683 0 -4057 5090 3847 0 -4619 4436 4437 0 -3444 5090 3244 0 -4436 4249 4055 0 -5089 5682 4057 0 -5414 4800 5517 0 -5683 4056 4250 0 -4436 4800 5518 0 -5214 5411 5164 0 -5127 5165 5164 0 -5089 3444 2866 0 -5956 4720 4719 0 -4886 5956 4885 0 -4720 5956 5957 0 -4720 5600 4719 0 -5957 5599 2452 0 -5956 4886 5957 0 -2675 4885 5955 0 -4886 4885 2675 0 -2675 5955 5007 0 -4720 5957 2452 0 -5600 2452 4536 0 -5600 4720 2452 0 -4536 2452 5958 0 -2451 2233 4536 0 -2450 2451 5960 0 -4536 4537 2451 0 -5958 4537 4536 0 -2450 4349 2451 0 -2452 5599 5958 0 -4349 2450 2232 0 -4886 2675 2674 0 -2454 4885 5956 0 -4721 5599 5957 0 -5957 4886 4721 0 -5599 4721 2673 0 -5958 5599 2672 0 -2673 2672 5599 0 -2673 4886 2674 0 -4538 2671 2672 0 -2673 4721 4886 0 -5007 2676 2865 0 -2672 4537 5958 0 -4350 2450 5960 0 -4537 5959 2451 0 -5959 4537 2671 0 -2451 5959 5960 0 -5960 2671 5961 0 -2671 5960 5959 0 -4157 4156 2449 0 -2449 4350 5961 0 -2449 5961 4351 0 -5961 4350 5960 0 -4537 2672 2671 0 -2450 4350 4156 0 -5961 2671 4538 0 -2675 5007 2674 0 -2676 5007 2677 0 -4156 4350 2449 0 -2031 3947 5785 0 -2232 2231 4348 0 -5785 3947 3946 0 -5786 5785 3946 0 -5786 3945 3944 0 -3945 5786 3946 0 -1836 3945 3740 0 -3944 3945 1836 0 -2231 5784 4155 0 -3943 3944 1836 0 -3538 3537 3739 0 -3738 1836 3739 0 -3739 1836 3740 0 -3537 3538 1480 0 -1656 3340 1480 0 -3539 3538 3739 0 -1479 3340 1656 0 -1656 1480 3538 0 -1655 1656 3538 0 -3536 3537 1480 0 -3945 3946 3741 0 -5787 5786 3944 0 -5784 3947 2031 0 -5784 2231 2230 0 -3947 5784 3948 0 -2031 4155 5784 0 -5784 5783 3948 0 -4156 4157 2231 0 -2029 3741 3948 0 -4157 2230 2231 0 -2231 2232 4156 0 -3947 3948 2030 0 -3740 3945 3741 0 -3741 2030 3948 0 -3740 3741 1835 0 -3946 2030 3741 0 -1654 1655 3539 0 -1655 3538 3539 0 -1655 3341 1656 0 -1654 3341 1655 0 -3539 1835 1834 0 -3739 1835 3539 0 -1835 3741 3540 0 -3739 3740 1835 0 -5783 5784 2230 0 -3946 3947 2030 0 -2232 2450 4156 0 -3341 1479 1656 0 -2672 2673 5598 0 -5600 4536 4535 0 -2865 5008 5007 0 -2865 2676 5089 0 -5008 2865 5009 0 -5007 5008 2674 0 -4888 4887 2864 0 -5008 4887 2674 0 -5008 2864 4887 0 -4887 4888 2863 0 -2865 5089 2866 0 -4887 4722 2673 0 -5598 4722 2862 0 -5598 2673 4722 0 -2862 4722 2863 0 -2861 5598 2862 0 -4723 5597 2862 0 -4723 2863 4888 0 -5597 4723 3048 0 -4723 2862 2863 0 -4722 4887 2863 0 -2861 2862 5597 0 -4888 3049 4723 0 -5598 4538 2672 0 -3243 5090 4056 0 -2865 2866 5090 0 -3846 3641 3243 0 -3443 3243 3641 0 -5683 3641 3846 0 -3846 4056 5683 0 -3641 4055 3443 0 -4056 3846 3243 0 -4055 3641 5683 0 -3443 4055 3052 0 -5010 5009 3052 0 -3052 5090 3053 0 -5009 5090 3052 0 -5009 2864 5008 0 -3243 3443 3052 0 -3243 3052 3053 0 -2860 3048 5596 0 -3048 4723 4724 0 -2864 5012 4888 0 -2864 5009 5010 0 -3053 5090 3243 0 -4889 3049 4888 0 -3052 4055 5010 0 -5090 5009 2865 0 -4057 4056 5090 0 -5597 3048 2860 0 -2448 4351 5962 0 -4157 2449 4351 0 -2670 4351 4538 0 -4351 2448 4157 0 -4351 2670 5962 0 -2670 4538 2861 0 -4352 5962 2670 0 -4158 2448 5962 0 -4538 5598 2861 0 -5783 2230 5782 0 -2029 3540 3741 0 -2230 4157 2448 0 -2448 5782 2230 0 -5783 2229 3948 0 -1835 3540 1834 0 -3949 2029 3948 0 -3343 3342 1654 0 -1654 3539 3343 0 -3948 2229 3949 0 -3343 3539 1834 0 -5783 5782 2229 0 -3341 1654 1653 0 -2670 2861 4539 0 -5961 4538 4351 0 -5962 4352 4158 0 -2447 2448 4158 0 -4352 4539 2669 0 -4158 4352 5963 0 -4352 2669 5963 0 -2669 4539 2860 0 -2669 2860 4540 0 -4158 5963 4159 0 -2861 5597 4539 0 -2447 4158 4159 0 -1833 3343 3344 0 -1834 3344 3343 0 -3540 2029 3742 0 -3540 3541 1834 0 -5781 2229 5782 0 -5782 2448 2447 0 -3540 3742 2028 0 -2447 5781 5782 0 -3742 3949 3950 0 -3742 2029 3949 0 -2229 2228 3949 0 -3343 1833 3342 0 -5963 2669 4353 0 -4352 2670 4539 0 -5597 2860 4539 0 -1653 3342 3150 0 -1833 3150 3342 0 -2674 4887 2673 0 -2676 2867 5089 0 -3342 1653 1654 0 -5048 5115 4963 0 -2868 5088 2677 0 -3249 4441 5263 0 -3735 3941 1481 0 -5166 5257 5298 0 -5286 4459 5285 0 -6157 1480 3340 0 -3501 2711 4496 0 -4496 2711 3302 0 -3301 2711 4309 0 -4309 2711 3299 0 -2711 3301 3302 0 -4308 3106 5627 0 -3105 4308 5627 0 -4116 3104 5627 0 -3103 4116 5627 0 -3105 5627 3104 0 -3299 5627 3106 0 -3101 3102 5627 0 -3100 3101 5627 0 -2908 3907 5627 0 -4507 2908 5627 0 -3100 5627 3907 0 -5922 2713 5627 0 -3703 5922 5627 0 -3503 2483 2258 0 -3304 4684 2258 0 -3503 2258 4684 0 -3703 5627 2483 0 -4507 5627 2713 0 -3103 5627 3102 0 -3303 4679 2258 0 -4498 3303 2258 0 -4497 5813 2258 0 -3300 4497 2258 0 -4498 2258 5813 0 -2916 3107 5628 0 -4310 2916 5628 0 -2914 2915 5628 0 -4118 2914 5628 0 -4310 5628 2915 0 -3300 2258 3107 0 -2912 2913 5628 0 -2911 2912 5628 0 -2909 4117 5628 0 -2714 2909 5628 0 -2911 5628 4117 0 -4506 3908 5628 0 -5921 4506 5628 0 -2484 3704 2259 0 -3504 4683 2259 0 -2484 2259 4683 0 -5921 5628 3704 0 -2714 5628 3908 0 -4118 5628 2913 0 -3304 2258 4679 0 -5810 3305 2259 0 -5811 5810 2259 0 -5629 5812 4499 0 -5629 3109 5812 0 -5811 2259 4499 0 -5629 3108 4311 0 -5629 2917 3108 0 -5629 2722 4119 0 -5629 2721 2722 0 -4119 2917 5629 0 -4311 3109 5629 0 -5627 4322 2258 0 -2711 5627 3299 0 -2258 4322 2053 0 -3704 5628 2259 0 -4131 2053 4321 0 -4321 2053 4322 0 -1855 4321 5630 0 -4131 4321 1855 0 -4322 2254 4321 0 -2258 2053 4320 0 -2716 2721 5629 0 -3504 2259 3305 0 -2259 5628 4320 0 -5629 4499 2259 0 -4320 4131 2054 0 -4320 2053 4131 0 -4319 2259 2054 0 -4131 4130 2054 0 -1855 1856 4131 0 -4320 2054 2259 0 -5628 2258 4320 0 -1856 4130 4131 0 -3921 4130 1856 0 -3107 2258 5628 0 -2483 5627 2258 0 -3910 2720 5629 0 -2719 3910 5629 0 -2717 2718 5629 0 -2715 2717 5629 0 -2719 5629 2718 0 -3909 4505 5629 0 -5920 3909 5629 0 -3705 2485 5629 0 -4682 3705 2260 0 -5920 5629 2485 0 -2715 5629 4505 0 -4682 2260 3505 0 -2260 3705 5629 0 -2260 5629 4319 0 -4681 3505 2260 0 -2054 2055 4319 0 -4319 5629 2259 0 -4504 5809 2260 0 -2260 4319 4318 0 -2720 2716 5629 0 -4130 4129 2055 0 -4681 2260 3306 0 -3306 2260 5809 0 -4500 4504 3112 0 -4504 4500 5809 0 -4312 3112 4504 0 -4129 4130 1857 0 -4318 4319 2055 0 -1857 4130 3921 0 -2055 2054 4130 0 -3920 4129 1857 0 -5627 2256 4322 0 -3110 3111 4504 0 -4120 3110 4504 0 -2918 2919 4504 0 -3911 2918 4504 0 -4120 4504 2919 0 -2723 4503 4504 0 -5918 2723 4504 0 -5911 5917 4504 0 -3706 5911 4504 0 -5918 4504 5917 0 -3911 4504 4503 0 -5919 5912 4504 0 -2486 5919 4504 0 -4502 5916 4504 0 -5914 4502 4504 0 -2486 4504 5916 0 -3506 5915 4504 0 -3307 3506 4504 0 -3114 4501 4504 0 -3113 4313 4317 0 -3114 2261 4313 0 -3307 4504 4501 0 -5914 4504 5915 0 -3706 4504 5912 0 -2922 4121 4317 0 -2921 2922 4317 0 -2920 3912 4317 0 -3707 2920 4317 0 -2921 4317 3912 0 -4316 2725 4125 0 -2724 4316 4125 0 -5910 3507 4125 0 -2497 5910 4125 0 -2724 4125 3507 0 -3707 4125 2725 0 -2489 4315 4125 0 -3308 2489 4125 0 -2495 2487 4125 0 -2490 2495 4125 0 -3308 4125 2487 0 -2493 2494 4123 0 -2491 2493 4123 0 -3115 2492 4123 0 -5814 4314 4123 0 -3115 4123 4314 0 -2491 4123 2492 0 -2490 4123 2494 0 -2497 4125 4315 0 -3113 4317 4121 0 -3707 4317 4125 0 -4125 4123 2490 0 -4313 2261 4317 0 -4125 4317 4126 0 -4317 4127 4126 0 -4128 4127 4317 0 -4128 4317 2261 0 -4125 4126 2262 0 -4504 4318 2261 0 -4124 4126 4127 0 -4123 4122 5814 0 -4123 4125 2262 0 -3914 4123 2262 0 -2923 4122 4123 0 -4124 3916 3915 0 -4124 2262 4126 0 -3710 3915 3916 0 -2262 4124 3709 0 -4127 3916 4124 0 -3509 4124 3915 0 -4127 3711 3916 0 -3114 4504 2261 0 -2056 4128 2261 0 -2056 4318 4129 0 -3918 4128 2056 0 -2056 2261 4318 0 -4129 1858 3919 0 -3920 1858 4129 0 -3919 1858 3714 0 -4129 3919 2056 0 -3111 4312 4504 0 -3918 2056 3919 0 -2057 3511 3711 0 -3711 4127 2057 0 -2057 4128 3917 0 -3917 3511 2057 0 -3713 3712 3919 0 -3713 3919 3714 0 -3712 3713 1859 0 -3712 3918 3919 0 -3713 3513 3313 0 -3918 3712 3917 0 -4128 3918 3917 0 -3711 3510 3916 0 -3714 1858 3715 0 -2057 4127 4128 0 -4318 4504 2260 0 -3917 3712 3312 0 -3913 2923 3914 0 -2728 3913 3914 0 -2727 3914 3709 0 -3914 2727 2728 0 -3914 2262 3709 0 -2727 3709 3708 0 -2923 4123 3914 0 -2726 3708 3709 0 -3509 2502 2726 0 -3509 2501 3508 0 -3310 2500 2501 0 -3508 2502 3509 0 -2726 3709 3509 0 -3916 3510 3710 0 -3118 3711 3511 0 -3510 3711 3311 0 -3710 3510 3310 0 -3312 3712 3119 0 -3312 3511 3917 0 -1859 3713 3512 0 -3119 1859 3512 0 -3710 3509 3915 0 -2927 3118 3511 0 -3710 3310 3509 0 -3310 3510 3117 0 -3311 3117 3510 0 -3509 3310 2501 0 -3512 2928 3119 0 -3311 3711 3118 0 -3118 2058 3311 0 -3312 3119 5818 0 -5817 3512 3313 0 -2927 3511 3312 0 -5817 2928 3512 0 -2500 3310 3309 0 -3713 3313 3512 0 -3509 3709 4124 0 -3712 1859 3119 0 -3119 2928 1860 0 -3713 3714 3513 0 -2055 4129 4318 0 -1856 1855 4132 0 -4132 1673 4133 0 -1673 4132 1855 0 -4133 1673 1672 0 -4132 4133 1674 0 -5805 1495 4133 0 -5805 1672 1671 0 -3925 1495 3926 0 -5805 4133 1672 0 -1855 5630 1673 0 -4133 1495 3923 0 -3717 3921 3922 0 -3922 3921 1856 0 -1674 3922 4132 0 -1674 3717 3922 0 -3923 3925 3924 0 -3923 1674 4133 0 -3719 3924 3925 0 -3717 1674 3923 0 -1495 3925 3923 0 -3924 3718 3923 0 -3926 1495 5805 0 -4132 3922 1856 0 -5804 1494 5805 0 -1671 1670 5804 0 -3927 1494 5804 0 -5805 1494 3926 0 -1493 1492 3722 0 -5804 1670 1493 0 -6140 6139 3722 0 -3927 5804 1493 0 -1492 1493 3928 0 -3927 1493 3722 0 -1494 3719 3926 0 -1494 3927 3720 0 -1494 3720 3719 0 -3926 3719 3925 0 -3720 6139 3519 0 -6140 3721 6139 0 -3519 6138 3720 0 -3720 6138 3719 0 -3519 6139 3721 0 -3720 3927 3722 0 -3722 6139 3720 0 -3924 3719 1496 0 -3721 1324 1325 0 -5804 5805 1671 0 -1673 4134 1672 0 -6138 3518 3719 0 -3920 1675 3716 0 -3920 3921 1675 0 -1497 3716 1675 0 -3920 3716 1676 0 -3718 3517 1675 0 -3717 3923 3718 0 -3318 3517 6137 0 -3718 1675 3717 0 -3921 3717 1675 0 -1497 1675 3517 0 -3515 3715 1676 0 -1676 3715 3920 0 -1676 3716 3516 0 -3515 1676 3516 0 -1498 1497 3318 0 -1498 3516 3716 0 -3318 1497 3517 0 -3516 1498 3317 0 -1497 1498 3716 0 -3317 1498 3318 0 -3517 3718 1496 0 -3715 3513 3714 0 -3719 3518 1496 0 -6138 3519 3518 0 -3319 3518 3519 0 -6137 1496 3518 0 -3520 1325 3321 0 -1325 3520 3721 0 -3128 3321 1173 0 -3519 3520 1327 0 -3721 3520 3519 0 -3520 3321 1327 0 -6137 3319 3126 0 -3319 6137 3518 0 -3126 3319 3320 0 -3318 6137 6136 0 -3320 1327 3127 0 -3320 3319 3519 0 -3128 2936 3321 0 -1327 2936 3127 0 -2936 1327 3321 0 -3126 3320 2935 0 -3519 1327 3320 0 -2935 3320 3127 0 -3321 1325 1326 0 -1496 6137 3517 0 -3924 1496 3718 0 -6136 6137 3126 0 -6140 3722 1492 0 -3715 1858 3920 0 -3723 6141 3724 0 -1492 1491 3723 0 -3724 6141 3931 0 -3723 3724 1323 0 -3725 3931 1322 0 -6141 3930 3931 0 -1322 3931 3932 0 -3931 3725 3724 0 -1491 6141 3723 0 -3724 3725 1323 0 -3521 1324 3721 0 -6140 3723 3521 0 -1324 3521 3522 0 -1324 3322 1325 0 -3522 1323 3523 0 -1323 3522 3521 0 -3521 3723 1323 0 -1324 3522 3323 0 -3725 3523 1323 0 -3523 1169 3522 0 -3725 3524 3523 0 -3723 6140 1492 0 -1322 3933 3726 0 -1322 3932 3933 0 -3727 3726 3933 0 -3726 3524 3725 0 -3727 3933 3934 0 -1321 6144 3934 0 -3728 3934 1320 0 -3934 3933 1321 0 -3932 1321 3933 0 -3727 3934 3728 0 -1168 3524 3525 0 -1168 3523 3524 0 -3525 3524 1167 0 -3326 1168 3525 0 -1167 3727 3526 0 -1167 3524 3726 0 -3329 1022 3328 0 -3526 3328 1167 0 -3727 3728 3526 0 -1167 3328 3525 0 -3726 3727 1167 0 -1022 3525 3328 0 -3327 884 3134 0 -3725 1322 3726 0 -6142 3932 3931 0 -3324 3523 1168 0 -3322 1170 3129 0 -1170 3322 3323 0 -1025 3129 1170 0 -3322 3129 1326 0 -3323 1169 3130 0 -3323 3522 1169 0 -3130 2744 1025 0 -3323 3130 1170 0 -1324 3323 3322 0 -1025 1170 3130 0 -2938 1173 1171 0 -1173 3321 1326 0 -1171 1173 1326 0 -3128 1173 2937 0 -2743 1171 1025 0 -1171 1326 3129 0 -1025 2744 2939 0 -2938 1171 2743 0 -3129 1025 1171 0 -2939 2743 1025 0 -3130 1169 3131 0 -1325 3322 1326 0 -3132 3324 3325 0 -3324 1168 3325 0 -1023 3325 3326 0 -3132 3325 1023 0 -3326 1022 3327 0 -3326 3325 1168 0 -3136 3327 1022 0 -1023 3326 3133 0 -3523 3324 1169 0 -3134 3326 3327 0 -3131 1024 2940 0 -1024 3131 3132 0 -2745 2940 1024 0 -2744 3130 3131 0 -2746 3132 3133 0 -3133 3132 1023 0 -885 3134 2943 0 -3134 885 3133 0 -3133 3326 3134 0 -1024 3132 2941 0 -3131 3324 3132 0 -3133 885 2942 0 -884 2943 3134 0 -3324 3131 1169 0 -3525 1022 3326 0 -2940 2744 3131 0 -1892 6110 2299 0 -6140 3521 3721 0 -4135 1671 1672 0 -2937 1173 2938 0 -3315 3514 3515 0 -3514 3513 3715 0 -3515 3516 3316 0 -3315 3515 3316 0 -3318 3124 3317 0 -3316 3516 3317 0 -2737 2932 3124 0 -3316 3317 3123 0 -6136 3124 3318 0 -3123 3317 3124 0 -3514 3314 3513 0 -1677 3314 3514 0 -3315 1677 3514 0 -3513 3314 3120 0 -3123 2931 3316 0 -3122 3315 3316 0 -2932 3123 3124 0 -2932 2736 3123 0 -3122 3316 2931 0 -3122 2930 3315 0 -3124 6136 3125 0 -3514 3715 3515 0 -3126 2934 3125 0 -3125 6136 3126 0 -2739 2934 2935 0 -3125 2934 2933 0 -2516 2740 2936 0 -1328 3127 2740 0 -2741 3128 2937 0 -2936 3128 2741 0 -2935 2934 3126 0 -2935 3127 1328 0 -2934 2738 2933 0 -2738 2934 2514 0 -2739 2514 2934 0 -2737 3124 2933 0 -1328 2740 2515 0 -2516 2936 2741 0 -2516 6118 2740 0 -2741 6117 2516 0 -2741 2937 2517 0 -1328 2515 2739 0 -2935 1328 2739 0 -2517 2292 2741 0 -2085 2517 6116 0 -3125 2933 3124 0 -2936 2740 3127 0 -2933 2738 2513 0 -2929 3120 3314 0 -2929 3314 1677 0 -1677 3315 3121 0 -1677 2734 2929 0 -2930 3122 2735 0 -2930 3121 3315 0 -2512 2932 6121 0 -2931 3123 2736 0 -3122 2931 1499 0 -1677 3121 2733 0 -2733 3121 2930 0 -2735 3122 2511 0 -1499 2511 3122 0 -2929 2508 3120 0 -2512 2736 2932 0 -2737 6121 2932 0 -2513 6135 2737 0 -2736 2512 1499 0 -1499 2931 2736 0 -2080 1499 2512 0 -2933 2513 2737 0 -2732 3313 3120 0 -2514 2739 6119 0 -6120 2738 2289 0 -2515 2740 6118 0 -2083 2290 2515 0 -1885 2084 1174 0 -2516 1174 2291 0 -2292 6117 2741 0 -2516 2291 6118 0 -2516 6117 1174 0 -1174 6117 1885 0 -6119 2082 2514 0 -2738 2514 2289 0 -6119 2515 2290 0 -6135 2513 6120 0 -1174 2084 2291 0 -2292 1885 6117 0 -1353 2085 1886 0 -2292 2085 1699 0 -1886 2085 6116 0 -1884 6118 2291 0 -2083 2515 6118 0 -1521 2291 2084 0 -2292 2517 2085 0 -6120 2513 2738 0 -2739 2515 6119 0 -2286 2930 2735 0 -2517 2937 6116 0 -3120 3313 3513 0 -2518 2742 1172 0 -2742 6116 2937 0 -2939 2744 2519 0 -2743 2518 1172 0 -6114 2519 2744 0 -2519 2743 2939 0 -2940 2745 6113 0 -2744 2940 2520 0 -2743 1172 2938 0 -1026 2743 2519 0 -6116 2742 2293 0 -2518 2743 1026 0 -6115 2518 1026 0 -2518 2293 2742 0 -2520 2295 2744 0 -2519 6115 1026 0 -2520 2940 6113 0 -2519 6114 2294 0 -6114 2744 2295 0 -2520 6113 887 0 -6113 2745 2296 0 -2742 2937 1172 0 -3132 2746 2941 0 -2746 3133 2942 0 -2522 2746 2942 0 -2941 2746 6112 0 -2523 2747 2943 0 -2747 2942 885 0 -6110 755 2748 0 -6111 2942 2747 0 -885 2943 2747 0 -755 2523 2943 0 -2941 6112 2521 0 -2746 886 6112 0 -2297 6112 886 0 -2296 2745 2521 0 -2942 6111 2522 0 -2298 6111 2747 0 -2522 6111 2090 0 -2747 2523 2298 0 -2523 755 6110 0 -886 2522 2090 0 -2746 2522 886 0 -6112 2089 2521 0 -2748 755 2943 0 -2941 2521 2745 0 -1172 2937 2938 0 -6110 2091 2523 0 -2293 2518 2086 0 -2086 2518 6115 0 -6115 2519 2294 0 -1701 1887 6115 0 -2520 2088 2295 0 -2520 887 2088 0 -2088 887 1889 0 -6114 2087 2294 0 -2295 2087 6114 0 -1702 1888 2295 0 -1354 1027 2086 0 -2086 1027 2293 0 -2086 6115 1887 0 -1886 1027 1700 0 -2295 1888 2087 0 -2088 1702 2295 0 -1703 1356 1889 0 -2088 1889 1525 0 -1703 1889 2296 0 -1524 2294 2087 0 -1701 6115 2294 0 -1888 6277 2087 0 -887 6113 1889 0 -2293 1027 6116 0 -2521 2089 2296 0 -6112 2297 1890 0 -2089 6112 1890 0 -2296 2089 1526 0 -2090 6111 1891 0 -2090 2297 886 0 -2091 2298 2523 0 -756 2298 2091 0 -2298 1891 6111 0 -1527 1704 2297 0 -2297 1704 1890 0 -2090 1527 2297 0 -1891 1358 2090 0 -2089 1890 1357 0 -6273 756 1892 0 -1705 1891 2298 0 -1705 6274 1891 0 -756 1528 1705 0 -1706 1892 2299 0 -2298 756 1705 0 -2091 1892 756 0 -757 1890 1704 0 -2091 6110 1892 0 -2296 1526 1703 0 -2296 1889 6113 0 -1887 6278 2086 0 -1529 1201 1706 0 -6116 1027 1886 0 -1024 2941 2745 0 -1329 6119 2290 0 -3526 3329 3328 0 -1857 3921 3920 0 -3932 6143 1321 0 -1892 1706 6273 0 -3309 3310 2498 0 -2272 2498 3117 0 -2271 3116 3117 0 -2270 2271 2926 0 -2272 3117 3116 0 -2269 2263 2926 0 -2268 2269 2926 0 -2925 2488 2926 0 -2924 2925 5819 0 -2268 2926 2488 0 -2270 2926 2263 0 -2926 5819 2925 0 -2058 2926 3117 0 -2058 5819 2926 0 -2924 5819 2279 0 -5818 1860 2731 0 -1860 5818 3119 0 -2058 2927 5818 0 -2058 3117 3311 0 -3118 2927 2058 0 -2282 2058 5818 0 -5819 2730 2280 0 -2729 2924 2279 0 -2506 2730 2058 0 -2280 2730 2506 0 -2507 2282 2731 0 -5818 2731 2282 0 -2284 1860 2077 0 -1860 2284 2507 0 -2507 2731 1860 0 -2506 2058 2282 0 -5819 2058 2730 0 -2284 2075 2507 0 -2927 3312 5818 0 -2926 2271 3117 0 -3310 3117 2498 0 -2504 2505 2279 0 -2503 2504 2279 0 -2503 2279 2278 0 -2505 2729 2279 0 -2279 2280 6122 0 -2278 2279 2277 0 -2280 2279 5819 0 -2277 2279 2276 0 -2276 2279 2499 0 -2067 2273 2279 0 -2273 2499 2279 0 -6123 6122 2073 0 -2275 2279 6123 0 -2279 6122 6123 0 -2280 2281 6122 0 -2073 2281 2283 0 -2281 2280 2506 0 -2283 2506 2282 0 -2282 2073 2283 0 -2075 2077 2076 0 -2074 2507 2075 0 -1876 2076 2077 0 -2075 2076 2071 0 -2077 2075 2284 0 -2282 2507 2074 0 -2275 2073 2274 0 -2275 6123 2073 0 -2068 2274 2073 0 -2275 2274 2279 0 -2074 2072 2070 0 -2072 2074 2075 0 -2071 2069 2070 0 -2071 2070 2072 0 -2072 2075 2071 0 -2074 2070 2073 0 -2073 2282 2074 0 -2274 2059 2279 0 -2071 2076 1876 0 -2073 6122 2281 0 -2283 2281 2506 0 -2073 2070 1865 0 -2077 1860 1876 0 -3313 2732 5817 0 -2735 2511 2079 0 -2732 3120 2508 0 -2508 1860 2732 0 -2733 2930 2509 0 -2733 2734 1677 0 -2511 1499 2510 0 -2929 1678 2508 0 -1678 2929 2734 0 -2511 1880 2079 0 -2285 1678 2078 0 -1678 2285 2508 0 -2285 2078 1878 0 -2508 2285 1877 0 -2286 2509 2930 0 -2735 2079 2286 0 -1694 2509 2286 0 -1678 2733 2509 0 -2734 2733 1678 0 -2079 1694 2286 0 -1880 2511 2510 0 -1877 1860 2508 0 -6121 6135 2288 0 -1881 2512 6121 0 -2081 2288 6135 0 -1881 6121 1695 0 -2514 2082 2289 0 -6119 1329 2082 0 -1696 1882 6135 0 -6135 2289 1519 0 -6120 2289 6135 0 -1883 2082 1329 0 -1881 2080 2512 0 -2287 1499 2080 0 -1880 2287 2080 0 -1880 2510 2287 0 -6135 1882 2081 0 -2289 2082 1519 0 -6126 2081 1882 0 -2082 1883 1518 0 -1883 1329 1697 0 -1695 2288 2081 0 -6121 2288 1695 0 -1697 6127 1883 0 -1329 1520 1697 0 -2287 2510 1499 0 -5817 1860 2928 0 -1881 6133 2080 0 -2078 1879 1878 0 -1877 2285 1878 0 -1879 1678 1694 0 -1875 1878 1879 0 -2079 1500 1694 0 -1694 1678 2509 0 -1693 1500 1880 0 -6125 1694 1691 0 -1500 2079 1880 0 -6125 1879 1694 0 -1871 1876 1877 0 -1860 1877 1876 0 -1873 1877 1874 0 -2069 2071 1876 0 -1508 1874 1875 0 -1874 1877 1878 0 -1874 1878 1875 0 -1873 1874 1687 0 -1879 6125 1875 0 -1875 6125 1872 0 -1692 1694 1500 0 -2078 1678 1879 0 -1695 2081 6126 0 -6133 1881 1695 0 -1696 6135 1519 0 -1519 6134 1696 0 -1520 1351 1350 0 -1351 1520 1329 0 -1350 1351 1329 0 -2082 1518 1519 0 -6127 1697 1520 0 -1350 1348 1520 0 -1515 6133 6126 0 -6133 1695 6126 0 -1882 1517 6126 0 -2080 6133 1693 0 -1518 6127 6134 0 -1883 6127 1518 0 -6134 1346 1517 0 -6127 1346 6134 0 -1520 1348 6127 0 -6134 1517 1696 0 -6134 1519 1518 0 -1347 6127 1348 0 -1330 1348 1191 0 -2080 1693 1880 0 -1882 1696 1517 0 -6126 1517 1516 0 -1348 1350 1191 0 -2732 1860 5817 0 -6121 2737 6135 0 -1871 1877 1873 0 -2066 2067 2279 0 -2059 2065 2066 0 -2073 1867 1868 0 -1868 2068 2073 0 -2068 1868 2059 0 -2279 2059 2066 0 -2274 2068 2059 0 -1867 2059 1868 0 -2059 2063 2064 0 -2059 2062 2063 0 -1866 2059 1867 0 -2062 2059 2265 0 -1865 1866 1867 0 -1865 2059 1866 0 -2059 2064 2065 0 -2073 1865 1867 0 -1876 1869 2069 0 -1876 1871 1684 0 -1869 1876 1686 0 -1862 2069 1869 0 -1873 1870 1871 0 -1872 1689 1875 0 -1689 1872 1688 0 -1873 1687 1870 0 -1691 1872 6125 0 -1683 1684 1871 0 -2070 1861 2061 0 -1869 1686 1862 0 -1876 1685 1686 0 -1685 1862 1686 0 -1875 1689 1507 0 -1680 1871 1870 0 -1691 1690 1510 0 -1870 1687 1680 0 -1691 1688 1872 0 -1685 1876 1684 0 -1874 1509 1687 0 -1864 2059 1865 0 -1691 1692 1690 0 -2070 1864 1865 0 -2070 2069 1861 0 -1862 1685 1684 0 -1864 2060 2059 0 -2059 2264 2265 0 -1861 2059 2060 0 -2059 158 2264 0 -2069 1862 1861 0 -1684 1683 1862 0 -1683 1871 1682 0 -2060 2061 1861 0 -2060 1864 2061 0 -1863 1683 1682 0 -1861 1862 1863 0 -2061 1864 2070 0 -1863 1862 1683 0 -1861 1863 1679 0 -1680 1681 1679 0 -1679 1682 1680 0 -1679 1681 1505 0 -1679 1863 1682 0 -1680 1509 1681 0 -2059 1861 1501 0 -1871 1680 1682 0 -1679 1501 1861 0 -1681 1508 1507 0 -1874 1508 1509 0 -1875 1507 1508 0 -1505 1681 1507 0 -1505 1506 1688 0 -1506 1507 1689 0 -1688 1691 1510 0 -1501 1688 1510 0 -1509 1680 1687 0 -1506 1505 1507 0 -1688 1501 1505 0 -1510 1502 1501 0 -1501 1502 1503 0 -1505 1501 1679 0 -1338 1503 1510 0 -1338 1339 1337 0 -1501 1503 1504 0 -1337 1503 1338 0 -1339 1690 1337 0 -1503 1502 1510 0 -1338 1510 1339 0 -1504 1503 1337 0 -1690 1339 1510 0 -1508 1681 1509 0 -1688 1506 1689 0 -1501 1177 2059 0 -1690 1513 1337 0 -1513 1514 1512 0 -1514 1513 1692 0 -1513 1512 6128 0 -1513 6128 1337 0 -1514 1693 1511 0 -1693 1514 1692 0 -1511 1693 1340 0 -1514 1511 1512 0 -1694 1692 1691 0 -1512 1511 6128 0 -6128 1511 1504 0 -1504 1337 6128 0 -1504 1511 1340 0 -6132 1504 1340 0 -6282 6283 6130 0 -1515 6282 1340 0 -6130 6283 6129 0 -1340 6282 6132 0 -6282 1344 6283 0 -6130 6131 6282 0 -1515 1340 1693 0 -1690 1692 1513 0 -1515 1516 1344 0 -1693 6133 1515 0 -1343 1344 1516 0 -6283 1344 1343 0 -1345 1517 1346 0 -1345 1516 1517 0 -1347 1346 6127 0 -1189 1346 1347 0 -1516 1515 6126 0 -6281 1516 1345 0 -1336 6129 6283 0 -6283 1343 1336 0 -1336 1343 1342 0 -1332 6129 1336 0 -1516 1342 1343 0 -1516 6281 1342 0 -1341 6281 1336 0 -6281 1341 1342 0 -1345 1346 1187 0 -1342 1341 1336 0 -6281 1345 1185 0 -1332 1336 6281 0 -1347 1330 1189 0 -1344 6282 1515 0 -1348 1330 1347 0 -6130 6129 6132 0 -6130 6132 6131 0 -6281 1185 1183 0 -6281 1183 1332 0 -6131 6132 6282 0 -1182 1186 1187 0 -1186 1185 1345 0 -1188 1187 1346 0 -1183 1185 1186 0 -1345 1187 1186 0 -1188 1184 1187 0 -1183 1334 1332 0 -1334 1183 1335 0 -1332 1334 1335 0 -1332 6132 6129 0 -1331 1182 1184 0 -1335 1186 1182 0 -1182 1332 1335 0 -1331 1332 1182 0 -1184 1188 1038 0 -1335 1183 1186 0 -1187 1184 1182 0 -1038 1331 1184 0 -1188 1189 1038 0 -6132 1332 1331 0 -1189 1039 1036 0 -1039 1189 1190 0 -1035 1036 1039 0 -1036 1180 1038 0 -1190 1041 1040 0 -1191 1041 1190 0 -1035 1039 1040 0 -1190 1040 1039 0 -1189 1330 1190 0 -1037 1040 1041 0 -1179 1180 1331 0 -1179 1038 1180 0 -1331 1180 1181 0 -1179 1331 1038 0 -1331 1035 1177 0 -1035 1181 1036 0 -1035 1037 1177 0 -1033 1032 1037 0 -1035 1040 1037 0 -1035 1331 1181 0 -1180 1036 1181 0 -1331 1177 6132 0 -1037 1041 1033 0 -1038 1189 1036 0 -1188 1346 1189 0 -1177 1501 6132 0 -1191 1190 1330 0 -6132 1501 1504 0 -1693 1692 1500 0 -1037 1032 1178 0 -1350 1329 1698 0 -2291 1698 1884 0 -1884 2083 6118 0 -1521 1350 1698 0 -1698 1329 1884 0 -2085 1353 1522 0 -1699 2085 1522 0 -1175 1885 1699 0 -2291 1521 1698 0 -2083 1329 2290 0 -1175 1699 1522 0 -1352 1175 6280 0 -1521 2084 1352 0 -1352 6280 1191 0 -1352 1191 1350 0 -1353 1349 1522 0 -2084 1175 1352 0 -6279 1353 1886 0 -1349 6280 1175 0 -1175 1522 1349 0 -1353 6279 1192 0 -1885 2292 1699 0 -1352 1350 1521 0 -1027 1354 1523 0 -1523 1700 1027 0 -1195 1523 1194 0 -1700 1523 1195 0 -888 1701 1524 0 -6278 1887 888 0 -2087 1355 1524 0 -888 1524 1355 0 -1700 6279 1886 0 -2086 6278 1354 0 -6278 1194 1354 0 -1523 1354 1194 0 -6278 888 1196 0 -1196 1193 6278 0 -1048 1355 1047 0 -1355 2087 6277 0 -908 1046 6277 0 -1355 6277 1047 0 -1888 1197 6277 0 -1048 1196 888 0 -1355 1048 888 0 -1047 1196 1048 0 -1701 2294 1524 0 -1700 1195 6279 0 -1175 2084 1885 0 -1195 1028 6279 0 -1191 1349 1176 0 -1349 1191 6280 0 -1176 1349 1192 0 -1176 1042 1041 0 -1192 1028 1044 0 -1192 1349 1353 0 -1044 1028 1193 0 -1176 1192 1043 0 -1028 1192 6279 0 -1043 1192 1044 0 -901 1034 1033 0 -1033 1041 901 0 -1041 1042 901 0 -902 901 1042 0 -1043 903 1042 0 -1042 903 902 0 -1029 1043 1044 0 -1029 903 1043 0 -1176 1043 1042 0 -902 898 901 0 -1044 905 1029 0 -1176 1041 1191 0 -1193 1047 1045 0 -1193 1194 6278 0 -906 1044 1045 0 -1045 1044 1193 0 -1045 1046 907 0 -1047 6277 1046 0 -889 1046 777 0 -1046 1045 1047 0 -1195 1194 1028 0 -907 1046 889 0 -905 906 890 0 -1044 906 905 0 -890 906 775 0 -904 1029 905 0 -889 776 907 0 -776 889 777 0 -761 775 776 0 -775 906 776 0 -777 760 776 0 -776 906 907 0 -1045 907 906 0 -773 905 890 0 -1046 908 777 0 -1193 1028 1194 0 -1047 1193 1196 0 -890 775 774 0 -908 6277 1197 0 -1032 1033 1178 0 -1889 1356 1525 0 -1525 1702 2088 0 -1049 1525 759 0 -1702 1525 1049 0 -2089 1357 1526 0 -757 6275 1890 0 -1050 1526 1357 0 -1703 6276 1356 0 -1702 1197 1888 0 -1357 1890 6275 0 -6276 759 1356 0 -1525 1356 759 0 -1703 1198 6276 0 -1198 759 6276 0 -1050 6275 1199 0 -1050 1198 1526 0 -1199 6275 757 0 -910 1050 1199 0 -6275 1050 1357 0 -910 1198 1050 0 -757 1051 1199 0 -1702 1049 1197 0 -1891 6274 1358 0 -1358 1527 2090 0 -1358 6274 912 0 -1527 1358 1051 0 -756 1359 1528 0 -756 6273 1359 0 -1052 1528 1359 0 -1705 1200 6274 0 -1527 757 1704 0 -913 1359 6273 0 -1358 912 1051 0 -912 6274 1200 0 -911 912 1200 0 -758 1199 1051 0 -1359 913 1052 0 -1052 1200 1528 0 -633 1201 1053 0 -1201 781 913 0 -1706 1201 6273 0 -634 1200 1052 0 -1201 913 6273 0 -634 1052 913 0 -1201 1529 1053 0 -1527 1051 757 0 -1526 1198 1703 0 -911 1051 912 0 -908 909 759 0 -908 1197 909 0 -759 909 1049 0 -760 908 759 0 -778 910 758 0 -758 1051 911 0 -910 1199 758 0 -910 778 759 0 -759 1198 910 0 -760 759 778 0 -651 652 636 0 -652 651 760 0 -636 536 651 0 -776 760 650 0 -538 652 653 0 -652 760 778 0 -758 653 652 0 -636 652 537 0 -778 758 652 0 -652 538 537 0 -758 911 779 0 -908 760 777 0 -779 634 653 0 -634 779 911 0 -653 634 780 0 -779 653 758 0 -781 633 780 0 -633 781 1201 0 -653 780 655 0 -781 780 634 0 -634 913 781 0 -655 780 633 0 -654 540 539 0 -655 654 635 0 -654 539 538 0 -654 538 635 0 -540 655 6368 0 -655 540 654 0 -6284 6368 6369 0 -6368 6284 540 0 -655 633 6368 0 -540 6284 539 0 -635 653 655 0 -539 6284 6369 0 -6368 633 541 0 -635 538 653 0 -634 911 1200 0 -651 650 760 0 -633 1053 782 0 -909 1197 1049 0 -1887 1701 888 0 -523 538 539 0 -1178 1034 1177 0 -1034 1178 1033 0 -1177 1034 898 0 -1178 1177 1037 0 -902 899 898 0 -898 1034 901 0 -899 903 900 0 -1177 898 1031 0 -903 899 902 0 -1031 898 899 0 -1031 894 895 0 -894 1031 899 0 -894 899 895 0 -1031 895 896 0 -900 897 896 0 -904 897 900 0 -896 897 1031 0 -900 896 895 0 -899 900 895 0 -891 1031 897 0 -900 903 904 0 -762 1177 1031 0 -772 773 769 0 -772 904 905 0 -772 769 897 0 -904 772 897 0 -773 890 774 0 -772 905 773 0 -761 776 650 0 -769 773 767 0 -903 1029 904 0 -761 650 649 0 -767 768 766 0 -768 767 773 0 -766 768 770 0 -769 767 892 0 -771 770 774 0 -774 770 773 0 -650 637 647 0 -771 775 648 0 -651 536 637 0 -770 771 766 0 -768 773 770 0 -535 637 536 0 -650 651 637 0 -897 769 891 0 -761 648 775 0 -893 767 766 0 -892 893 891 0 -893 892 767 0 -893 766 891 0 -892 891 769 0 -771 648 644 0 -771 774 775 0 -644 648 643 0 -771 644 891 0 -766 771 891 0 -891 644 762 0 -643 763 762 0 -763 643 764 0 -762 763 764 0 -643 762 644 0 -764 765 645 0 -765 764 643 0 -645 765 643 0 -764 645 762 0 -643 648 645 0 -762 645 646 0 -648 649 645 0 -2059 762 271 0 -650 646 649 0 -650 647 646 0 -639 646 647 0 -649 646 645 0 -647 530 642 0 -530 647 535 0 -530 535 531 0 -642 530 641 0 -637 535 647 0 -639 647 642 0 -639 640 638 0 -640 639 641 0 -640 530 638 0 -639 638 646 0 -531 532 638 0 -530 531 638 0 -532 525 638 0 -532 535 536 0 -531 535 532 0 -641 530 640 0 -639 642 641 0 -526 532 533 0 -536 533 532 0 -646 638 762 0 -648 761 649 0 -762 1031 891 0 -536 524 533 0 -430 762 638 0 -6386 528 529 0 -528 533 529 0 -529 533 534 0 -6386 529 534 0 -537 6371 524 0 -6371 537 523 0 -534 524 6384 0 -534 533 524 0 -528 526 533 0 -6384 524 6371 0 -526 527 525 0 -527 526 528 0 -525 527 6386 0 -526 525 532 0 -534 6385 6386 0 -6385 534 6384 0 -6392 525 6385 0 -6386 6385 525 0 -527 528 6386 0 -6384 6392 6385 0 -6371 6383 6384 0 -536 537 524 0 -539 522 6370 0 -6370 523 539 0 -6381 6370 522 0 -6370 6393 6371 0 -6380 6369 521 0 -6369 6380 522 0 -441 521 520 0 -6381 522 6380 0 -537 538 523 0 -440 6380 521 0 -6383 6393 6382 0 -6383 6371 6393 0 -437 6382 6393 0 -6383 6391 6384 0 -6380 440 6381 0 -440 521 441 0 -429 440 428 0 -440 429 6381 0 -440 355 428 0 -6393 6381 437 0 -6393 6370 6381 0 -429 437 6381 0 -520 521 6369 0 -6371 523 6370 0 -522 539 6369 0 -6389 6383 6382 0 -6391 6390 6392 0 -6390 6391 6383 0 -6392 6390 6389 0 -6391 6392 6384 0 -6388 6387 6392 0 -6389 6382 6388 0 -436 6387 6382 0 -6388 6392 6389 0 -6390 6383 6389 0 -6392 6387 436 0 -436 437 435 0 -437 436 6382 0 -435 437 438 0 -436 435 430 0 -431 434 432 0 -438 434 435 0 -431 430 434 0 -430 431 432 0 -434 438 439 0 -434 430 435 0 -429 438 437 0 -436 430 6392 0 -6388 6382 6387 0 -430 271 762 0 -433 439 430 0 -433 432 434 0 -439 433 434 0 -439 351 430 0 -429 351 439 0 -429 352 351 0 -352 428 353 0 -351 352 350 0 -438 429 439 0 -351 350 430 0 -346 349 347 0 -349 346 350 0 -346 347 354 0 -346 430 350 0 -353 354 349 0 -354 353 428 0 -348 349 354 0 -348 347 349 0 -354 347 348 0 -349 350 353 0 -350 352 353 0 -346 354 278 0 -428 278 354 0 -433 430 432 0 -429 428 352 0 -638 6392 430 0 -427 440 441 0 -638 525 6392 0 -636 537 536 0 -2059 1177 762 0 -520 6369 6368 0 -1329 2083 1884 0 -1705 1528 1200 0 -346 271 430 0 -3526 3728 3527 0 -3729 3527 3728 0 -1320 3935 3729 0 -3527 3729 3528 0 -3527 3329 3526 0 -3527 3528 1166 0 -3528 3729 3730 0 -3528 3730 1318 0 -1166 3528 3330 0 -1320 3934 3935 0 -3527 1166 3329 0 -3327 3136 884 0 -1022 3329 3136 0 -1021 3136 3329 0 -884 3136 3135 0 -3136 3137 3135 0 -1021 3137 3136 0 -3330 1021 3329 0 -3135 3137 2946 0 -3329 1166 3330 0 -884 3135 2944 0 -3528 3529 3330 0 -884 2748 2943 0 -3331 3529 3530 0 -3331 3330 3529 0 -3529 3528 1318 0 -3331 1021 3330 0 -1318 3732 3530 0 -3732 1318 3731 0 -3530 3732 1317 0 -1318 3530 3529 0 -3730 3936 3731 0 -1165 3331 3332 0 -3137 883 2946 0 -3137 1021 3138 0 -3137 3138 883 0 -3135 2946 2749 0 -3138 1165 1020 0 -3138 1021 3331 0 -882 2947 2948 0 -1020 2947 3138 0 -1020 1165 3139 0 -3138 2947 883 0 -3331 1165 3138 0 -883 2947 2751 0 -2948 2947 1020 0 -3730 3731 1318 0 -3729 1319 3730 0 -2945 2946 883 0 -2748 2944 2524 0 -2944 2748 884 0 -2524 2944 754 0 -2748 2524 2299 0 -2944 2749 754 0 -2944 3135 2749 0 -2749 2945 2525 0 -2524 754 6109 0 -2945 2749 2946 0 -6109 754 2749 0 -2524 2092 2299 0 -2524 6109 2092 0 -1893 2092 6109 0 -2299 2092 1529 0 -2300 2525 2093 0 -2300 6109 2749 0 -6108 2093 2525 0 -1893 6109 2300 0 -2525 2300 2749 0 -2093 1530 2300 0 -2525 2945 6108 0 -2299 1529 1706 0 -2750 2751 2526 0 -2750 2945 883 0 -6107 2526 2751 0 -2750 2526 2301 0 -2527 882 6106 0 -882 2527 2751 0 -2303 6106 2752 0 -6107 2751 2527 0 -883 2751 2750 0 -2302 2527 6106 0 -1894 6108 2301 0 -6108 2750 2301 0 -2301 2526 2094 0 -6108 1894 2093 0 -2527 2302 6107 0 -6106 2303 2095 0 -6107 2302 1895 0 -2302 6106 2095 0 -2095 2303 1896 0 -6107 1895 2094 0 -6107 2094 2526 0 -2095 1532 2302 0 -2752 6106 882 0 -2750 6108 2945 0 -882 2751 2947 0 -2094 753 2301 0 -3530 3332 3331 0 -2299 6110 2748 0 -1164 3332 3530 0 -1164 3530 1317 0 -3733 1317 3732 0 -3531 1317 3733 0 -3333 3531 1316 0 -6149 1316 3531 0 -1315 1314 3333 0 -1317 3531 1164 0 -3531 3733 6149 0 -1316 1315 3333 0 -3139 1164 1163 0 -1164 3139 3332 0 -1163 1164 3333 0 -3139 1163 1019 0 -1163 3333 1314 0 -1019 3140 1162 0 -3140 1019 1163 0 -1163 1314 3140 0 -1164 3531 3333 0 -3139 1019 2948 0 -1315 1316 6152 0 -3332 3139 1165 0 -1316 3532 6151 0 -1315 6152 1314 0 -3734 6149 3733 0 -6150 3532 6149 0 -6151 3533 6154 0 -3532 3533 6151 0 -6152 6151 3334 0 -6151 6152 1316 0 -6150 6149 3734 0 -3334 6151 6154 0 -1162 1314 3141 0 -1314 6152 6153 0 -3141 1314 6153 0 -1314 1162 3140 0 -1313 3141 6153 0 -6153 1312 1313 0 -1313 1162 3141 0 -1162 1313 1161 0 -1313 3142 1161 0 -6153 3334 6154 0 -6152 3334 6153 0 -1019 1162 2950 0 -1312 6153 6154 0 -3532 1316 6149 0 -3732 6148 3733 0 -1018 1162 1161 0 -2752 881 2528 0 -881 2752 2948 0 -2528 2096 2303 0 -2948 2752 882 0 -881 2949 2753 0 -881 2948 1019 0 -880 2753 2529 0 -881 2753 2528 0 -2949 881 1019 0 -2528 2753 6105 0 -2096 1897 751 0 -1897 2096 6105 0 -1897 1711 751 0 -1896 2303 2096 0 -2753 2304 6105 0 -2304 2753 880 0 -2304 880 2097 0 -6105 2304 1897 0 -2096 2528 6105 0 -2304 1711 1897 0 -2753 2949 879 0 -2528 2303 2752 0 -2753 879 2529 0 -879 2949 2950 0 -879 2950 1018 0 -6104 2529 879 0 -2754 1018 2530 0 -1018 2754 879 0 -6103 2530 878 0 -6104 879 2754 0 -2949 1019 2950 0 -2305 2754 2530 0 -2097 6104 1898 0 -6104 2097 2529 0 -1898 6104 2305 0 -2097 1711 2304 0 -2530 2098 2305 0 -2305 6104 2754 0 -1712 2305 2098 0 -1899 2098 6103 0 -1899 2306 1713 0 -1898 2305 1712 0 -6103 2098 2530 0 -1365 2097 1898 0 -2755 2530 1018 0 -2529 2097 880 0 -1018 2950 1162 0 -1899 749 2098 0 -1899 6103 2306 0 -3139 2948 1020 0 -3732 3731 3937 0 -1710 2096 751 0 -2092 1360 1529 0 -1360 2092 1893 0 -6272 1360 1893 0 -1053 1360 914 0 -2300 1530 1707 0 -1707 1893 2300 0 -2093 1361 1530 0 -6272 1893 1707 0 -1361 2093 1894 0 -1202 1707 1530 0 -914 632 6272 0 -632 1360 6272 0 -6272 1707 1202 0 -1202 914 6272 0 -915 1054 1361 0 -1054 1202 1530 0 -6271 1361 1894 0 -6271 915 1361 0 -1054 1530 1361 0 -782 1202 1054 0 -1894 1708 6271 0 -914 1360 632 0 -1708 753 1531 0 -1894 2301 1708 0 -1362 1531 753 0 -1708 1531 1203 0 -2095 1363 1532 0 -2302 1532 1709 0 -1709 6270 1895 0 -753 1895 6270 0 -753 1708 2301 0 -1204 1709 1532 0 -1531 1055 1203 0 -1055 1531 1362 0 -916 1055 1362 0 -631 6271 1203 0 -1709 1204 6270 0 -6270 1362 753 0 -916 6270 1204 0 -1204 1532 1056 0 -1363 917 1056 0 -916 1362 6270 0 -1056 1532 1363 0 -784 1204 1056 0 -1895 2302 1709 0 -1708 1203 6271 0 -2094 1895 753 0 -631 1203 1055 0 -782 656 633 0 -1053 914 782 0 -914 1202 782 0 -782 1054 656 0 -657 1054 783 0 -631 657 783 0 -631 542 657 0 -656 1054 657 0 -915 783 1054 0 -656 657 541 0 -520 6367 6379 0 -520 6368 6285 0 -6379 427 441 0 -6368 541 6285 0 -6285 542 6367 0 -542 6285 541 0 -6286 6367 542 0 -6285 6367 520 0 -541 657 542 0 -519 6379 6367 0 -783 915 631 0 -541 633 656 0 -1055 784 631 0 -631 915 6271 0 -916 1204 784 0 -631 784 658 0 -1056 658 784 0 -1056 659 658 0 -752 1056 917 0 -658 659 630 0 -1056 785 659 0 -631 658 543 0 -6378 6286 6366 0 -6286 542 543 0 -6366 6286 543 0 -6367 6286 519 0 -6366 630 544 0 -630 6366 543 0 -6377 6287 6365 0 -6287 6378 6365 0 -6366 544 6365 0 -6365 6378 6366 0 -543 658 630 0 -6378 519 6286 0 -659 660 630 0 -543 542 631 0 -916 784 1055 0 -6287 444 6378 0 -1363 6269 917 0 -6379 441 520 0 -751 1533 1710 0 -1710 1896 2096 0 -1710 1533 1205 0 -1896 1710 6269 0 -6268 1364 1711 0 -1364 1533 751 0 -1711 2097 1534 0 -6268 1711 1534 0 -1364 751 1711 0 -1533 1364 1057 0 -1533 1057 1205 0 -1205 6269 1710 0 -1205 1057 785 0 -1205 752 917 0 -918 6268 1206 0 -918 1057 1364 0 -1206 6268 1534 0 -786 918 1206 0 -918 1364 6268 0 -1057 918 786 0 -1534 1365 1206 0 -1205 917 6269 0 -1365 1898 6267 0 -1365 1534 2097 0 -1712 2098 1535 0 -1535 1207 1712 0 -6266 1366 749 0 -749 1366 1535 0 -1535 2098 749 0 -6266 749 1713 0 -1899 1713 749 0 -1207 1535 1366 0 -1058 6267 750 0 -6267 1058 1365 0 -750 6267 1207 0 -786 1206 1058 0 -1366 1059 1207 0 -1207 6267 1712 0 -919 1207 1059 0 -920 1059 6266 0 -788 920 1208 0 -750 1207 919 0 -6266 1059 1366 0 -662 1058 750 0 -1208 920 6266 0 -1365 1058 1206 0 -6267 1898 1712 0 -920 787 1059 0 -661 628 659 0 -752 785 1056 0 -785 752 1205 0 -659 785 1057 0 -659 628 660 0 -1057 661 659 0 -545 661 662 0 -628 661 545 0 -661 1057 786 0 -660 628 544 0 -6364 6377 6365 0 -6365 629 6364 0 -6377 6364 6376 0 -544 629 6365 0 -6363 545 546 0 -6363 629 544 0 -629 6376 6364 0 -6376 629 6363 0 -6363 627 6376 0 -544 628 6363 0 -628 545 6363 0 -6377 6376 6288 0 -786 1058 661 0 -544 630 660 0 -662 661 1058 0 -546 545 662 0 -919 1059 787 0 -787 920 662 0 -663 662 920 0 -920 664 663 0 -664 788 748 0 -547 626 663 0 -788 664 920 0 -662 663 626 0 -6376 627 6288 0 -6363 546 627 0 -627 546 6362 0 -6288 627 6375 0 -6361 6375 6362 0 -626 6362 546 0 -6288 6375 447 0 -6361 6290 6375 0 -6361 6362 626 0 -6362 6375 627 0 -546 662 626 0 -6377 6288 517 0 -663 664 748 0 -787 662 750 0 -919 787 750 0 -6375 6290 448 0 -1536 6266 1713 0 -6269 1363 1896 0 -1363 2095 1896 0 -444 6287 6377 0 -1161 2951 1018 0 -3728 1320 3729 0 -6150 3735 3533 0 -3735 6150 3734 0 -3735 1481 3533 0 -3533 3335 6154 0 -3533 3534 6155 0 -3533 1481 3534 0 -1481 6156 3534 0 -1311 6155 3336 0 -1482 3735 3734 0 -3533 6155 3335 0 -3143 3142 1312 0 -1312 6154 3335 0 -1311 1312 3335 0 -1160 1161 3142 0 -1160 1017 2951 0 -2951 1161 1160 0 -3142 3143 1160 0 -1312 1311 3143 0 -3335 6155 1311 0 -1312 3142 1313 0 -6155 3534 3336 0 -2951 2755 1018 0 -3534 1310 3336 0 -1310 3534 6156 0 -1310 6156 3535 0 -3336 1310 3337 0 -3535 3337 1310 0 -3338 3337 3535 0 -1309 3337 3338 0 -3535 6156 3536 0 -6156 3736 3536 0 -3336 3337 1159 0 -2952 3143 1158 0 -1311 1159 3143 0 -1158 3143 1159 0 -3143 2952 1160 0 -1158 3144 1308 0 -3144 1159 3337 0 -1157 2757 2953 0 -2953 2952 1158 0 -2954 2953 1158 0 -1158 1159 3144 0 -1309 3144 3337 0 -2952 2953 2757 0 -3535 3536 6157 0 -1159 1311 3336 0 -1481 3736 6156 0 -1017 1160 2952 0 -878 2755 2531 0 -878 2530 2755 0 -2531 2755 1017 0 -2099 878 2531 0 -2756 6102 2531 0 -1017 2952 2756 0 -6102 2756 2307 0 -2756 2531 1017 0 -2755 2951 1017 0 -2099 2531 6102 0 -6102 1900 2099 0 -2099 2306 878 0 -2099 1900 1536 0 -2099 1536 1713 0 -1714 2307 2100 0 -2307 1714 1900 0 -2532 2100 2307 0 -1537 1714 2100 0 -2307 1900 6102 0 -1900 1714 1367 0 -2307 2756 2532 0 -2306 6103 878 0 -6101 2757 2308 0 -2757 6101 2532 0 -6101 2308 1901 0 -6101 1901 2100 0 -2757 1157 2533 0 -2533 1157 6100 0 -1016 2757 2533 0 -2308 2757 1016 0 -2532 2756 2757 0 -2101 2533 6100 0 -2308 1715 1901 0 -1715 2308 1016 0 -1715 1016 2101 0 -1537 2100 1901 0 -6100 1902 2101 0 -2101 1016 2533 0 -1538 2101 1902 0 -1716 1902 2309 0 -2309 2102 1716 0 -1715 2101 1538 0 -2309 1902 6100 0 -1716 1369 1902 0 -6100 2758 2309 0 -6101 2100 2532 0 -2952 2757 2756 0 -1715 1368 1901 0 -3145 3144 1309 0 -1713 2306 2099 0 -3535 3339 3338 0 -3339 6157 1479 0 -6158 3339 3148 0 -3338 3339 6158 0 -6158 1478 3147 0 -1479 3341 3148 0 -1478 6158 3148 0 -3148 3339 1479 0 -3536 1480 6157 0 -6158 3147 3338 0 -3144 3145 1308 0 -3145 3338 6159 0 -1158 1308 2954 0 -1309 3338 3145 0 -1307 1308 3146 0 -3145 3146 1308 0 -6159 3146 3145 0 -1308 1307 2954 0 -3338 3147 6159 0 -1157 2954 1307 0 -3147 1477 6159 0 -2953 2954 1157 0 -1477 1478 3149 0 -1653 1478 3148 0 -3149 1478 1653 0 -2957 6159 1477 0 -3149 1652 2958 0 -2959 1652 3149 0 -1476 2958 2540 0 -3149 2958 1477 0 -2959 3149 1653 0 -1477 2958 2957 0 -3146 6160 2955 0 -6160 3146 2957 0 -2955 6160 2956 0 -3146 2955 1307 0 -2957 1476 2956 0 -3146 6159 2957 0 -2955 2956 1306 0 -2762 6161 2956 0 -2761 2760 6161 0 -2957 2956 6160 0 -2958 1476 2957 0 -1307 2955 2759 0 -1373 1906 1720 0 -1477 3147 1478 0 -3341 1653 3148 0 -2956 6161 2760 0 -2758 2759 2534 0 -2758 1157 1307 0 -2534 2102 2309 0 -2534 2309 2758 0 -6099 2759 2310 0 -2759 6099 2534 0 -2103 2310 2535 0 -6099 2310 1903 0 -2759 2758 1307 0 -2534 6099 2102 0 -2310 1717 1903 0 -1903 2102 6099 0 -1539 2102 1903 0 -1716 2102 1539 0 -1540 1156 2103 0 -1156 1717 2310 0 -6098 1904 2103 0 -1904 1540 2103 0 -1156 2310 2103 0 -1540 1717 1156 0 -2535 2310 2759 0 -6100 1157 2758 0 -2535 1306 6098 0 -1306 2535 2759 0 -2311 6098 1306 0 -2311 1904 6098 0 -2536 2761 6097 0 -2761 2536 2760 0 -2536 2311 2760 0 -1306 2760 2311 0 -2759 2955 1306 0 -2104 2536 6097 0 -1718 2311 2104 0 -1718 1904 2311 0 -2311 2536 2104 0 -1541 1718 2104 0 -1905 2312 1719 0 -6097 2761 2312 0 -1541 2104 1905 0 -2312 2105 1719 0 -2105 2312 2537 0 -1905 2104 6097 0 -2312 1905 6097 0 -1906 1542 2105 0 -2105 6162 1906 0 -6098 2103 2535 0 -2760 1306 2956 0 -1370 1903 1717 0 -2956 1476 1475 0 -3339 3535 6157 0 -3340 1479 6157 0 -1718 1371 1904 0 -1714 6265 1367 0 -1367 1536 1900 0 -1367 6265 1060 0 -1536 1367 1208 0 -1537 1368 877 0 -1537 1901 1368 0 -877 1368 1061 0 -6265 1537 1209 0 -6265 1714 1537 0 -1209 1537 877 0 -6265 921 1060 0 -1060 1208 1367 0 -1060 921 748 0 -1060 748 788 0 -1209 1061 789 0 -1061 1209 877 0 -665 789 1061 0 -1209 789 921 0 -1209 921 6265 0 -921 789 665 0 -1368 6264 1061 0 -1208 6266 1536 0 -6264 1538 1210 0 -6264 1368 1715 0 -1210 1538 1369 0 -6264 1210 922 0 -1716 6263 1369 0 -1369 1538 1902 0 -6263 1539 1211 0 -1210 1369 1062 0 -1539 6263 1716 0 -1062 1369 6263 0 -1210 790 922 0 -1210 1062 790 0 -666 790 1062 0 -665 1061 922 0 -923 1211 1015 0 -923 1062 6263 0 -923 1015 667 0 -791 1015 1211 0 -791 1211 1063 0 -666 1062 923 0 -1211 923 6263 0 -791 667 1015 0 -667 791 1063 0 -6264 922 1061 0 -1538 6264 1715 0 -666 922 790 0 -547 748 548 0 -547 663 748 0 -548 748 665 0 -6360 547 548 0 -747 6359 548 0 -6372 747 6358 0 -6360 548 6359 0 -549 747 548 0 -921 665 748 0 -6373 6359 747 0 -6361 625 6374 0 -625 6361 6360 0 -449 6374 625 0 -6361 6374 6290 0 -6359 6373 6360 0 -6373 747 6372 0 -6373 6372 746 0 -6360 6373 625 0 -6361 547 6360 0 -6373 449 625 0 -548 665 922 0 -547 6361 626 0 -923 876 549 0 -549 666 923 0 -6358 747 876 0 -876 747 549 0 -551 550 668 0 -876 923 550 0 -667 668 550 0 -876 550 6358 0 -549 922 666 0 -6358 550 551 0 -450 746 451 0 -6358 6357 6372 0 -746 6372 875 0 -6373 746 624 0 -6356 451 875 0 -875 6372 6357 0 -450 451 623 0 -6356 452 451 0 -6356 875 6357 0 -875 451 746 0 -6358 551 6357 0 -452 367 451 0 -6357 551 1014 0 -548 922 549 0 -550 923 667 0 -6290 6374 449 0 -1370 1211 1539 0 -788 1208 1060 0 -1717 6262 1370 0 -1370 1539 1903 0 -1212 6262 1540 0 -1370 6262 1063 0 -1718 6261 1371 0 -1371 1540 1904 0 -926 6261 1213 0 -1212 1540 1371 0 -6261 1718 1541 0 -1064 1371 6261 0 -6262 924 1063 0 -924 6262 1212 0 -924 1212 925 0 -1063 924 668 0 -925 1064 926 0 -1064 925 1212 0 -1064 1212 1371 0 -670 925 926 0 -6261 926 1064 0 -924 925 792 0 -6261 1541 1213 0 -667 1063 668 0 -1372 1065 1213 0 -1372 1213 1541 0 -1372 1541 1905 0 -6260 1372 1719 0 -1214 6260 1542 0 -1542 6260 1719 0 -1542 1719 2105 0 -1214 1542 1373 0 -1906 1373 1542 0 -1065 1372 6260 0 -927 793 1065 0 -1065 793 1213 0 -1305 1065 6260 0 -927 1065 1305 0 -1214 1066 794 0 -1066 1214 1373 0 -793 927 554 0 -794 927 1214 0 -1066 671 794 0 -1214 927 1305 0 -1305 6260 1214 0 -671 927 794 0 -1066 1373 6259 0 -1213 793 926 0 -1540 6262 1717 0 -670 926 793 0 -551 552 1014 0 -6356 6357 1014 0 -552 668 669 0 -6355 1014 552 0 -669 670 553 0 -669 792 925 0 -552 669 1155 0 -792 669 668 0 -668 924 792 0 -1155 6355 552 0 -452 453 745 0 -452 6356 453 0 -453 6356 874 0 -368 745 453 0 -6355 1013 874 0 -6354 1013 6355 0 -874 873 453 0 -873 874 1013 0 -1013 454 873 0 -6355 874 6356 0 -6356 1014 6355 0 -367 452 745 0 -553 1155 669 0 -668 552 551 0 -553 554 1155 0 -554 553 670 0 -670 793 554 0 -6354 1155 554 0 -554 1304 555 0 -554 927 1304 0 -1304 671 672 0 -556 6353 555 0 -925 670 669 0 -1154 554 555 0 -1012 454 1154 0 -454 1013 6354 0 -1154 454 6354 0 -454 1012 873 0 -6353 1011 1012 0 -6353 1154 555 0 -1011 1153 455 0 -1153 1011 6353 0 -1153 6353 556 0 -6353 1012 1154 0 -6354 554 1154 0 -1012 1011 873 0 -555 1304 1303 0 -6354 6355 1155 0 -1304 927 671 0 -873 1011 872 0 -1152 455 1153 0 -1211 1370 1063 0 -1372 1905 1719 0 -873 368 453 0 -1652 2764 2958 0 -3533 3532 6150 0 -3734 3733 6148 0 -624 746 450 0 -355 427 442 0 -355 440 427 0 -442 427 519 0 -355 442 356 0 -356 442 426 0 -443 442 519 0 -519 6378 443 0 -357 442 443 0 -427 6379 519 0 -355 356 344 0 -276 277 279 0 -346 278 277 0 -279 277 278 0 -277 276 346 0 -279 344 280 0 -344 279 278 0 -344 215 280 0 -276 279 275 0 -428 344 278 0 -275 279 280 0 -344 356 282 0 -355 344 428 0 -357 518 358 0 -357 426 442 0 -358 518 444 0 -283 357 358 0 -425 359 445 0 -444 359 358 0 -445 359 444 0 -283 358 359 0 -443 6378 518 0 -360 284 359 0 -282 270 344 0 -282 426 343 0 -270 282 220 0 -282 356 426 0 -283 284 343 0 -284 283 359 0 -343 220 282 0 -220 343 284 0 -220 342 269 0 -343 426 357 0 -357 283 343 0 -270 220 269 0 -359 425 360 0 -357 443 518 0 -518 6378 444 0 -215 344 270 0 -273 274 345 0 -274 273 276 0 -345 274 275 0 -273 345 276 0 -214 345 280 0 -215 270 216 0 -214 215 213 0 -280 345 275 0 -274 276 275 0 -345 214 271 0 -213 272 271 0 -272 213 211 0 -281 271 272 0 -213 271 214 0 -211 212 216 0 -212 211 213 0 -212 213 215 0 -272 211 281 0 -215 216 212 0 -216 281 211 0 -280 215 214 0 -2059 271 158 0 -6452 219 6451 0 -219 6452 281 0 -6452 6451 6453 0 -6452 6419 271 0 -219 269 218 0 -281 216 270 0 -6448 218 269 0 -6451 219 6450 0 -281 270 219 0 -6450 219 218 0 -6418 210 6419 0 -210 6418 6454 0 -6419 210 217 0 -6418 6419 6452 0 -6450 217 6453 0 -217 6450 218 0 -6454 6453 217 0 -6454 6418 6452 0 -217 210 6454 0 -6450 6453 6451 0 -6452 6453 6454 0 -165 6419 217 0 -217 218 165 0 -6452 271 281 0 -269 219 270 0 -159 271 6419 0 -342 220 284 0 -276 345 346 0 -445 517 446 0 -517 445 444 0 -517 6288 446 0 -361 445 446 0 -516 447 363 0 -447 516 446 0 -363 447 6289 0 -286 516 287 0 -6377 517 444 0 -362 446 516 0 -360 361 285 0 -425 361 360 0 -285 361 424 0 -221 284 285 0 -424 362 286 0 -362 424 361 0 -286 362 516 0 -222 424 286 0 -446 362 361 0 -285 424 221 0 -516 363 287 0 -361 425 445 0 -364 6289 448 0 -6289 447 6375 0 -6291 448 449 0 -6291 364 448 0 -449 624 365 0 -449 6373 624 0 -514 365 6292 0 -449 365 6291 0 -6288 447 446 0 -364 6291 365 0 -288 423 287 0 -423 286 287 0 -363 515 287 0 -288 287 515 0 -289 288 365 0 -289 365 514 0 -423 288 224 0 -288 364 365 0 -289 513 225 0 -364 288 515 0 -364 515 363 0 -422 288 289 0 -514 513 289 0 -6289 364 363 0 -6289 6375 448 0 -341 286 423 0 -6447 342 221 0 -342 284 221 0 -268 221 222 0 -6447 221 268 0 -286 341 222 0 -423 223 341 0 -6446 222 341 0 -268 222 6446 0 -424 222 221 0 -267 341 223 0 -6448 6447 6421 0 -6448 269 6447 0 -6447 268 6421 0 -205 6448 6421 0 -6446 267 6422 0 -268 6446 6422 0 -6422 267 6445 0 -268 168 6421 0 -341 267 6446 0 -268 6422 168 0 -223 340 267 0 -269 342 6447 0 -340 6444 6445 0 -340 6445 267 0 -223 423 340 0 -224 6444 340 0 -225 421 224 0 -225 422 289 0 -513 421 225 0 -225 224 422 0 -224 340 423 0 -421 339 224 0 -169 266 170 0 -169 6445 266 0 -170 266 339 0 -169 170 127 0 -224 339 6444 0 -339 421 6443 0 -338 170 339 0 -338 339 6443 0 -337 171 338 0 -339 266 6444 0 -6445 6444 266 0 -170 338 171 0 -6443 421 420 0 -6445 169 6422 0 -224 288 422 0 -6448 166 218 0 -338 6443 420 0 -284 360 285 0 -6290 449 448 0 -169 6423 6422 0 -208 165 164 0 -164 165 166 0 -162 164 163 0 -209 164 162 0 -163 166 6449 0 -166 163 164 0 -166 6448 6449 0 -163 6449 207 0 -165 218 166 0 -162 163 209 0 -208 163 207 0 -209 208 164 0 -208 209 163 0 -208 207 159 0 -119 121 120 0 -6449 161 207 0 -159 119 120 0 -161 119 207 0 -119 161 122 0 -159 207 119 0 -161 6449 122 0 -165 208 6419 0 -121 122 123 0 -123 122 6449 0 -167 123 205 0 -121 123 167 0 -204 205 168 0 -123 6448 205 0 -168 6422 6423 0 -167 205 124 0 -6449 6448 123 0 -6423 204 168 0 -159 124 84 0 -167 159 120 0 -83 84 85 0 -120 121 167 0 -124 204 86 0 -125 204 6423 0 -86 85 124 0 -124 85 84 0 -85 82 83 0 -124 159 167 0 -205 204 124 0 -204 125 86 0 -125 126 87 0 -122 121 119 0 -168 205 6421 0 -84 83 160 0 -159 160 118 0 -160 159 84 0 -160 83 118 0 -118 158 159 0 -81 82 86 0 -82 81 118 0 -82 85 86 0 -158 118 81 0 -82 118 83 0 -86 54 81 0 -54 53 81 0 -53 54 203 0 -52 53 203 0 -53 52 81 0 -51 50 81 0 -50 51 55 0 -80 50 55 0 -50 80 81 0 -52 203 51 0 -52 51 81 0 -86 203 54 0 -80 117 81 0 -117 55 158 0 -117 80 55 0 -158 55 32 0 -117 158 81 0 -32 55 202 0 -203 56 55 0 -55 51 203 0 -31 32 202 0 -203 86 125 0 -158 32 31 0 -31 30 158 0 -202 30 31 0 -202 29 30 0 -30 29 158 0 -49 79 158 0 -33 79 49 0 -116 157 158 0 -33 157 116 0 -116 79 33 0 -79 116 158 0 -49 29 33 0 -29 49 158 0 -87 56 203 0 -158 157 2264 0 -87 203 125 0 -271 159 158 0 -125 6423 126 0 -156 2264 157 0 -57 202 89 0 -56 87 88 0 -202 33 29 0 -202 55 56 0 -88 6424 128 0 -127 6424 126 0 -56 89 202 0 -88 87 6424 0 -6423 169 126 0 -88 89 56 0 -33 15 14 0 -201 15 33 0 -201 14 15 0 -14 13 157 0 -28 13 201 0 -14 201 13 0 -201 33 57 0 -28 157 13 0 -33 202 57 0 -33 14 157 0 -128 6424 127 0 -157 28 48 0 -6425 128 129 0 -265 128 127 0 -264 128 265 0 -6425 89 88 0 -128 264 129 0 -264 265 171 0 -264 171 130 0 -91 129 92 0 -169 127 126 0 -6425 129 91 0 -34 201 58 0 -90 57 89 0 -201 57 90 0 -28 201 16 0 -90 91 6426 0 -90 89 6425 0 -58 201 6426 0 -59 58 6426 0 -59 6426 91 0 -6426 201 90 0 -6425 91 90 0 -200 34 6427 0 -129 264 92 0 -6425 88 128 0 -265 127 170 0 -16 201 34 0 -78 157 48 0 -78 48 16 0 -115 78 16 0 -115 157 78 0 -156 16 5 0 -16 156 115 0 -200 4 5 0 -156 5 4 0 -28 16 48 0 -156 157 115 0 -4 12 156 0 -200 27 12 0 -6 27 200 0 -12 27 156 0 -47 77 156 0 -77 47 6 0 -155 6 2 0 -6 155 77 0 -47 27 6 0 -77 155 156 0 -200 12 4 0 -27 47 156 0 -5 16 200 0 -2264 155 154 0 -2 11 155 0 -11 199 26 0 -26 199 46 0 -11 26 155 0 -76 46 3 0 -46 199 3 0 -199 11 2 0 -46 76 155 0 -199 2 6 0 -26 46 155 0 -113 6469 155 0 -6469 113 3 0 -154 6469 3 0 -6469 154 155 0 -1 3 10 0 -3 1 154 0 -10 25 1 0 -45 25 10 0 -75 10 3 0 -1 25 154 0 -113 76 3 0 -154 153 2264 0 -17 6 200 0 -76 113 155 0 -34 200 16 0 -156 155 2264 0 -34 58 6427 0 -6424 87 126 0 -171 265 170 0 -6419 208 159 0 -6292 365 624 0 -45 154 25 0 -513 514 290 0 -421 513 226 0 -366 6292 624 0 -6293 6292 366 0 -6292 6293 290 0 -622 6293 366 0 -6294 226 6293 0 -513 290 226 0 -624 450 366 0 -226 290 6293 0 -172 420 419 0 -226 512 421 0 -420 421 6442 0 -172 337 338 0 -419 6442 511 0 -6442 421 512 0 -512 511 6442 0 -6442 419 420 0 -512 226 6294 0 -418 172 419 0 -623 6394 366 0 -338 420 172 0 -622 6394 744 0 -622 366 6394 0 -744 6394 367 0 -291 6293 622 0 -622 621 291 0 -743 621 622 0 -6295 6296 6441 0 -291 621 6294 0 -745 744 367 0 -291 6294 6293 0 -173 511 510 0 -227 512 6294 0 -512 227 6441 0 -511 173 419 0 -227 6295 6441 0 -620 6295 227 0 -6441 510 511 0 -6296 510 6441 0 -509 173 510 0 -6441 511 512 0 -6294 621 227 0 -418 173 509 0 -227 621 620 0 -6394 623 367 0 -450 623 366 0 -418 419 173 0 -92 130 131 0 -130 171 337 0 -131 130 337 0 -92 263 91 0 -93 131 336 0 -131 93 92 0 -418 336 131 0 -262 93 335 0 -337 172 131 0 -92 93 263 0 -58 59 6427 0 -91 263 59 0 -263 93 59 0 -6427 59 262 0 -6428 35 261 0 -262 35 6427 0 -261 35 262 0 -35 6428 17 0 -93 262 59 0 -35 17 200 0 -93 336 335 0 -35 200 6427 0 -336 94 335 0 -60 262 335 0 -336 132 94 0 -416 335 94 0 -417 95 94 0 -509 132 418 0 -417 132 509 0 -417 94 132 0 -417 133 95 0 -334 335 416 0 -260 261 36 0 -261 262 60 0 -334 261 60 0 -6428 261 18 0 -334 61 333 0 -334 60 335 0 -37 260 36 0 -333 36 334 0 -333 61 62 0 -334 36 261 0 -334 416 61 0 -332 36 333 0 -94 95 416 0 -418 132 336 0 -172 418 131 0 -18 261 260 0 -743 622 744 0 -514 6292 290 0 -292 743 744 0 -292 368 872 0 -871 743 292 0 -743 6417 621 0 -743 742 6417 0 -871 742 743 0 -742 741 228 0 -6417 742 620 0 -873 872 368 0 -621 6417 620 0 -6295 6440 6296 0 -228 620 742 0 -228 619 6295 0 -174 510 6296 0 -6297 6440 618 0 -6440 6295 619 0 -619 229 6440 0 -6440 6297 6296 0 -619 228 741 0 -6296 6297 174 0 -872 871 292 0 -228 6295 620 0 -293 871 870 0 -871 872 369 0 -870 871 369 0 -293 741 742 0 -369 1009 870 0 -1152 1010 455 0 -740 739 229 0 -1010 369 455 0 -872 455 369 0 -293 870 741 0 -618 175 6297 0 -6440 229 618 0 -175 618 617 0 -741 6416 619 0 -6416 740 229 0 -6416 741 870 0 -618 739 617 0 -229 739 618 0 -617 176 616 0 -229 619 6416 0 -6416 6415 740 0 -175 617 6298 0 -370 369 1010 0 -293 742 871 0 -745 368 744 0 -6297 175 6298 0 -133 508 95 0 -417 509 133 0 -508 133 6468 0 -508 134 95 0 -134 416 95 0 -509 174 6297 0 -134 507 96 0 -509 6468 133 0 -510 174 509 0 -416 134 96 0 -61 415 62 0 -61 96 415 0 -415 96 507 0 -62 415 97 0 -332 414 63 0 -62 414 332 0 -414 62 97 0 -37 36 332 0 -415 135 97 0 -62 332 333 0 -134 508 507 0 -96 61 416 0 -6468 6298 6299 0 -6298 6468 509 0 -6299 6298 616 0 -507 6468 6299 0 -6300 6467 615 0 -616 6467 6299 0 -616 615 6467 0 -6467 6300 6299 0 -6297 6298 509 0 -507 6299 6300 0 -414 136 98 0 -97 135 506 0 -414 98 63 0 -415 507 135 0 -136 414 506 0 -135 507 6300 0 -412 99 413 0 -505 413 98 0 -6300 506 135 0 -414 97 506 0 -506 6301 136 0 -98 413 63 0 -505 98 136 0 -507 508 6468 0 -617 616 6298 0 -63 413 331 0 -6415 6416 870 0 -368 292 744 0 -451 367 623 0 -259 332 63 0 -45 75 112 0 -75 198 153 0 -112 75 6470 0 -45 112 154 0 -6470 75 153 0 -3 199 7 0 -3 7 198 0 -45 10 75 0 -17 199 6 0 -112 6470 153 0 -153 8 9 0 -198 8 153 0 -9 8 24 0 -153 9 24 0 -24 44 74 0 -44 24 8 0 -197 6471 74 0 -24 74 153 0 -197 44 8 0 -153 74 111 0 -17 6428 199 0 -112 153 154 0 -198 7 6429 0 -7 199 6428 0 -6429 7 18 0 -20 8 198 0 -260 19 6429 0 -6429 18 260 0 -6430 19 259 0 -198 6429 20 0 -37 19 260 0 -20 6429 19 0 -111 74 6471 0 -8 20 6431 0 -197 74 44 0 -111 6471 153 0 -6430 38 21 0 -259 38 6430 0 -6431 197 8 0 -21 6431 6430 0 -21 257 6432 0 -6430 6431 20 0 -20 19 6430 0 -22 6431 21 0 -38 257 21 0 -3 198 75 0 -18 7 6428 0 -153 6471 152 0 -197 22 23 0 -22 197 6431 0 -197 23 43 0 -152 197 153 0 -22 73 43 0 -73 22 110 0 -43 73 197 0 -22 43 23 0 -152 6471 197 0 -197 73 6472 0 -6472 6433 151 0 -6433 6472 110 0 -6472 151 196 0 -110 6472 73 0 -151 6433 196 0 -6433 110 6432 0 -6433 41 42 0 -196 6433 2264 0 -6433 6432 41 0 -6472 196 197 0 -6432 110 22 0 -197 196 2264 0 -41 109 72 0 -72 42 41 0 -72 109 6433 0 -42 72 6433 0 -6473 109 255 0 -41 255 109 0 -257 40 6432 0 -195 6473 255 0 -41 6432 40 0 -109 6473 150 0 -150 195 6434 0 -150 6473 195 0 -6434 195 254 0 -150 6434 6433 0 -254 70 71 0 -255 254 195 0 -71 108 254 0 -108 71 70 0 -108 70 6474 0 -254 108 6474 0 -255 70 254 0 -6434 254 6433 0 -41 40 255 0 -109 150 6433 0 -6432 22 21 0 -6433 254 2264 0 -37 259 19 0 -197 2264 153 0 -257 66 39 0 -38 65 258 0 -256 40 39 0 -40 257 39 0 -258 100 66 0 -64 258 65 0 -258 66 257 0 -258 257 38 0 -259 64 65 0 -256 39 329 0 -69 326 70 0 -70 255 69 0 -69 255 68 0 -70 326 6474 0 -69 327 326 0 -67 68 256 0 -327 69 68 0 -68 255 256 0 -40 256 255 0 -149 6474 326 0 -329 39 66 0 -38 259 65 0 -100 64 330 0 -64 259 63 0 -330 64 331 0 -100 329 66 0 -330 412 101 0 -99 412 330 0 -100 330 329 0 -99 330 331 0 -37 332 259 0 -329 330 101 0 -327 105 326 0 -67 328 68 0 -105 327 104 0 -329 67 256 0 -327 103 104 0 -410 328 67 0 -104 407 105 0 -408 407 104 0 -328 103 327 0 -327 68 328 0 -329 101 67 0 -105 407 406 0 -410 67 101 0 -258 64 100 0 -64 63 331 0 -106 326 105 0 -149 6435 194 0 -106 325 326 0 -194 6435 253 0 -149 194 6474 0 -326 325 253 0 -253 6435 326 0 -325 106 107 0 -253 325 194 0 -149 326 6435 0 -325 254 194 0 -107 406 6475 0 -6475 406 148 0 -148 405 325 0 -107 6475 325 0 -193 6436 405 0 -193 148 406 0 -252 405 6436 0 -405 252 324 0 -6436 406 252 0 -193 405 148 0 -193 406 6436 0 -148 325 6475 0 -107 106 406 0 -325 405 2264 0 -145 146 405 0 -145 324 406 0 -496 146 145 0 -146 147 405 0 -146 496 147 0 -144 406 407 0 -192 147 496 0 -144 145 406 0 -324 252 406 0 -147 192 405 0 -495 192 6437 0 -6437 192 496 0 -251 6437 496 0 -495 6437 251 0 -495 323 404 0 -323 495 251 0 -495 404 494 0 -6456 404 323 0 -323 251 496 0 -6455 404 6456 0 -6456 323 496 0 -495 405 192 0 -496 145 144 0 -145 405 324 0 -406 106 105 0 -405 495 2264 0 -144 407 143 0 -194 254 6474 0 -331 413 99 0 -254 325 2264 0 -506 6300 6301 0 -92 264 130 0 -1011 455 872 0 -271 346 345 0 -928 671 1066 0 -1360 1053 1529 0 -1657 3536 3736 0 -2264 495 494 0 -4724 4723 3049 0 -5168 5171 5167 0 -5167 5166 5168 0 -5168 5169 5048 0 -5115 5116 4963 0 -5116 5115 5415 0 -5115 5048 5169 0 -5169 5168 5166 0 -5117 5116 5415 0 -5166 5170 5169 0 -4800 5116 5117 0 -3845 5091 5684 0 -4055 4249 5091 0 -5684 5091 4249 0 -3845 5092 4055 0 -4618 4249 5518 0 -5518 4800 5117 0 -5117 5118 5518 0 -4249 4618 5684 0 -5518 4249 4436 0 -3640 3845 5684 0 -5169 5170 5115 0 -5091 3845 4055 0 -5117 5415 5118 0 -5415 5115 5170 0 -5415 5170 5122 0 -5118 5415 5519 0 -5519 5415 4799 0 -5047 5415 5122 0 -5122 5170 5166 0 -4435 5519 4799 0 -5166 5047 5122 0 -5118 5519 4618 0 -5684 5119 4054 0 -5684 4618 5119 0 -4618 5519 5119 0 -4054 5119 4248 0 -5120 4435 5121 0 -5120 5119 5519 0 -4054 4248 3640 0 -5121 4248 5120 0 -5121 4435 4617 0 -5120 4248 5119 0 -4435 5120 5519 0 -3640 5684 4054 0 -5047 4799 5415 0 -4618 5518 5118 0 -5166 4962 5047 0 -5121 3051 4248 0 -3050 5011 5096 0 -4055 5092 5093 0 -5010 4055 5093 0 -3050 2864 5011 0 -5096 5011 5010 0 -5094 5010 5093 0 -3051 5094 5093 0 -5096 5012 3050 0 -5093 5092 3051 0 -5012 2864 3050 0 -5015 4890 4889 0 -4889 4888 5012 0 -5013 4889 5012 0 -4891 3049 4890 0 -4892 4891 5015 0 -4890 5015 4891 0 -4889 5013 5014 0 -4892 4724 3049 0 -5013 5012 5096 0 -4892 3049 4891 0 -5095 5010 5094 0 -3049 4889 4890 0 -3242 3241 5092 0 -3241 3242 3442 0 -3051 3241 3442 0 -3241 3051 5092 0 -3051 3442 4248 0 -3442 3242 5092 0 -5121 5095 3051 0 -4248 3442 3640 0 -5092 3640 3442 0 -5096 5010 5095 0 -5015 5016 4892 0 -5013 5096 5097 0 -5014 5013 5097 0 -4889 5014 5015 0 -5016 5015 3239 0 -5014 3239 5015 0 -3238 4893 5016 0 -3238 4894 4893 0 -3239 5014 5097 0 -4893 4892 5016 0 -5121 5096 5095 0 -4893 4894 4724 0 -5096 3240 5097 0 -5094 3051 5095 0 -5092 3845 3640 0 -4892 4893 4724 0 -5096 5121 5046 0 -2864 5010 5011 0 -4617 5685 5121 0 -4617 4799 4962 0 -5685 4617 4247 0 -5121 5685 5046 0 -4798 4247 4617 0 -4798 4962 4961 0 -3639 4247 3844 0 -4798 4617 4962 0 -4799 5047 4962 0 -5685 4247 5046 0 -5046 3440 3240 0 -3440 5046 3441 0 -3240 3440 3441 0 -3240 5098 5097 0 -5046 3638 3441 0 -3638 5046 3639 0 -3844 3638 3639 0 -3240 3441 3844 0 -5046 4247 3639 0 -3638 3844 3441 0 -4798 4434 4247 0 -3240 5096 5046 0 -5416 4434 4798 0 -4962 5166 4961 0 -4434 5416 5520 0 -4434 3844 4247 0 -4246 5520 4797 0 -4797 5520 5416 0 -5166 5416 4961 0 -4434 5520 4053 0 -4797 5416 5166 0 -5521 4616 4797 0 -4434 4053 3844 0 -5520 5686 4053 0 -4960 4053 5686 0 -5045 3844 4053 0 -4052 4246 4616 0 -5686 5520 4246 0 -5687 4245 3843 0 -4245 5687 4616 0 -4616 5521 4245 0 -5686 4246 3637 0 -4797 4616 4246 0 -3844 5045 3240 0 -5419 4796 3437 0 -4798 4961 5416 0 -5166 5521 4797 0 -4052 4616 5687 0 -5044 3239 5098 0 -3239 5097 5098 0 -5045 5098 3240 0 -5044 5098 5045 0 -5044 4960 4959 0 -4960 5044 5045 0 -3438 4959 5417 0 -3239 5044 5099 0 -4053 4960 5045 0 -5043 5044 4959 0 -3238 5099 5043 0 -3238 5016 5099 0 -5099 5044 5043 0 -5100 3238 5043 0 -3237 4958 5418 0 -4958 5043 4959 0 -3438 5418 4959 0 -5043 4958 5042 0 -4959 5418 4958 0 -5042 4958 3237 0 -4959 4960 5417 0 -5016 3239 5099 0 -3635 3636 3842 0 -3637 3636 4960 0 -3842 3636 3637 0 -3636 3635 4960 0 -3842 4052 3843 0 -4052 3842 3637 0 -3843 4052 5687 0 -3635 3842 3439 0 -5686 3637 4960 0 -4796 3842 3843 0 -4796 3438 5417 0 -3439 3842 5417 0 -3438 4796 5418 0 -5418 4957 3237 0 -5419 4957 5418 0 -5419 3236 5420 0 -3237 4957 5041 0 -5420 4957 5419 0 -3045 5420 5421 0 -5419 5418 4796 0 -3842 4796 5417 0 -4960 3439 5417 0 -4796 3843 3839 0 -3635 3439 4960 0 -4246 4052 3637 0 -3238 5017 4894 0 -4433 3843 4245 0 -4799 4617 4435 0 -5166 5522 5521 0 -4956 4957 5420 0 -4726 3048 4725 0 -4725 4894 4726 0 -4726 4727 3048 0 -2860 5596 5595 0 -5595 5596 4895 0 -5596 3048 4727 0 -4727 4726 4894 0 -5594 5595 4895 0 -4725 4724 4894 0 -5594 2860 5595 0 -4896 4541 4540 0 -4540 2860 5594 0 -5593 4540 5594 0 -4542 2669 4541 0 -5592 4542 2668 0 -4541 4896 4542 0 -4540 5593 4729 0 -4542 5592 4353 0 -5593 5594 2859 0 -4542 4353 2669 0 -4727 4728 5596 0 -2669 4540 4541 0 -4728 3047 4895 0 -4728 4727 3047 0 -4895 3047 5018 0 -2859 5594 4895 0 -5018 5042 5101 0 -5017 3238 5100 0 -3047 5042 5018 0 -5100 3047 5017 0 -4894 3047 4727 0 -4895 5018 2859 0 -4896 2668 4542 0 -4896 4729 2859 0 -2668 4896 5019 0 -4896 4540 4729 0 -2859 5101 5019 0 -5101 2859 5018 0 -5019 5020 2668 0 -5039 5020 5019 0 -5019 5101 5039 0 -2859 5019 4896 0 -5593 2859 4729 0 -2668 5020 4898 0 -3047 5100 5042 0 -4895 5596 4728 0 -4894 5017 3047 0 -5592 2668 4730 0 -4543 4354 5964 0 -4353 4897 4354 0 -4543 4159 5963 0 -4543 5963 4354 0 -4731 5591 5964 0 -4543 5964 5591 0 -4897 4353 4730 0 -5591 4731 4159 0 -4353 5592 4730 0 -4543 5591 4159 0 -4544 2447 4355 0 -4355 4159 4732 0 -2447 4544 5781 0 -2229 5781 2228 0 -4544 5590 2446 0 -4544 4355 4732 0 -5589 2446 5590 0 -4544 2446 5781 0 -4544 4732 5590 0 -5781 2446 5589 0 -4159 4731 4732 0 -2447 4159 4355 0 -4732 5964 4899 0 -4897 5964 4354 0 -4898 5964 4897 0 -4733 5590 4732 0 -4899 4160 4732 0 -4897 4730 2668 0 -4734 4356 4160 0 -4899 5964 4898 0 -4898 4897 2668 0 -4732 4160 4733 0 -5588 5589 4545 0 -5589 5590 4733 0 -4545 5589 4733 0 -5589 5588 5781 0 -4546 5780 5588 0 -4734 4545 4356 0 -5587 5780 5586 0 -5587 5588 5780 0 -5588 4545 4546 0 -4545 4733 4356 0 -4733 4160 4356 0 -5781 5588 2228 0 -4160 4899 5965 0 -4732 4731 5964 0 -4898 2667 4899 0 -5587 2228 5588 0 -4898 5020 2667 0 -5963 4353 4354 0 -5039 5040 2858 0 -5039 5101 3046 0 -2858 5021 5020 0 -2858 5020 5039 0 -5040 3046 4956 0 -5041 3046 5042 0 -4956 3046 5041 0 -3046 5040 5039 0 -3237 5041 5042 0 -5022 2858 5038 0 -5965 4899 4900 0 -5020 5021 2667 0 -4899 2667 4900 0 -5965 4900 4901 0 -5022 4901 4900 0 -5022 2667 5021 0 -4901 5022 5023 0 -5022 4900 2667 0 -5021 2858 5022 0 -5965 4901 4161 0 -2858 5040 5038 0 -4734 4160 5965 0 -5038 5037 2857 0 -4955 5038 4956 0 -5038 2857 5023 0 -5023 5022 5038 0 -5037 4955 4954 0 -4955 4956 5420 0 -5025 4903 5024 0 -4955 5037 5038 0 -5041 4957 4956 0 -5024 2857 5036 0 -4901 4902 4161 0 -5023 2666 4901 0 -4901 2666 4902 0 -4161 4902 5966 0 -2666 5024 4903 0 -5024 2666 5023 0 -4361 5966 4358 0 -5966 4902 4903 0 -4162 4903 5025 0 -4903 4902 2666 0 -5023 2857 5024 0 -4161 5966 4357 0 -2857 5037 5036 0 -5038 5040 4956 0 -5043 5042 5100 0 -5965 4161 4735 0 -4546 4735 4547 0 -4735 4546 4734 0 -4546 4547 2445 0 -2445 5780 4546 0 -4735 4357 4736 0 -4735 4734 5965 0 -5779 4736 4737 0 -4547 4735 4548 0 -4357 4735 4161 0 -4548 4735 4736 0 -5585 5586 4548 0 -5586 5780 2445 0 -4548 5586 2445 0 -3951 5587 5586 0 -4736 4549 4548 0 -4549 4736 5779 0 -5584 5585 4549 0 -4548 4549 5585 0 -2445 4547 4548 0 -4550 4549 5779 0 -4736 4357 4359 0 -4546 4545 4734 0 -4360 4737 4736 0 -4359 5966 4360 0 -4737 4360 4361 0 -4737 2444 5779 0 -4361 4166 4362 0 -4361 4360 5966 0 -4738 4362 4166 0 -4361 4362 4737 0 -4357 5966 4359 0 -4737 4362 2444 0 -4550 2444 4551 0 -4550 5779 2444 0 -2444 4362 4551 0 -5584 4549 4550 0 -4363 4738 5778 0 -4551 4362 4363 0 -4364 4363 5778 0 -4363 4364 4552 0 -5778 4738 4167 0 -4363 4552 4551 0 -4362 4738 4363 0 -5584 4550 4551 0 -4162 4358 4903 0 -4360 4736 4359 0 -4358 5966 4903 0 -5583 4551 4552 0 -3045 4954 4955 0 -5101 5042 3046 0 -5420 3045 4955 0 -3951 5586 5585 0 -4433 4245 5521 0 -3048 4724 4725 0 -4051 4433 5688 0 -5522 4433 5521 0 -5688 4433 5522 0 -4051 3841 3843 0 -4050 5688 4244 0 -4244 5688 5522 0 -4432 4244 5522 0 -5688 4050 4051 0 -5522 4615 4432 0 -5690 4050 4244 0 -3634 3839 3840 0 -3839 3843 3841 0 -3840 3839 3841 0 -3634 4796 3839 0 -3841 4050 3840 0 -4050 3841 4049 0 -4050 4049 4051 0 -3840 4050 3634 0 -3841 4051 4049 0 -4050 4795 3634 0 -5690 4244 4432 0 -3634 4795 4796 0 -4615 4431 4243 0 -4431 4615 4614 0 -4046 4243 4431 0 -4615 4243 5690 0 -4614 4613 4430 0 -4615 5522 5166 0 -4242 4430 4240 0 -4431 4614 4046 0 -4614 4615 5166 0 -4430 4242 4614 0 -5690 5689 4048 0 -5689 5690 4243 0 -4048 5689 4047 0 -5690 4048 5523 0 -5689 4046 4047 0 -5689 4243 4046 0 -4047 4046 3633 0 -3838 4046 4242 0 -4242 5524 3838 0 -4048 4047 5523 0 -4614 4242 4046 0 -4050 5690 4795 0 -4430 4613 4240 0 -4615 5690 4432 0 -4614 5166 5298 0 -3838 3633 4046 0 -4793 4794 3437 0 -3437 4796 4795 0 -5523 3437 4795 0 -4794 3236 5419 0 -3436 5523 5524 0 -5523 4795 5690 0 -5524 5523 3633 0 -4793 3437 5523 0 -4047 3633 5523 0 -4793 5523 3436 0 -3236 4793 5421 0 -4793 3236 4794 0 -5421 4793 3235 0 -3045 5421 4954 0 -3044 5422 5423 0 -3235 5422 5421 0 -5423 5422 3235 0 -5421 5422 4954 0 -3235 4793 4792 0 -3044 4954 5422 0 -4793 3436 4792 0 -5419 3437 4794 0 -5525 5524 4242 0 -5524 3633 3838 0 -5691 4240 4613 0 -5692 4240 5691 0 -5692 3837 3632 0 -4045 3837 5692 0 -3632 3837 4045 0 -5525 4240 5692 0 -4045 5692 5691 0 -5525 5692 3632 0 -5423 4792 3234 0 -4792 3436 5525 0 -3435 4792 5525 0 -4792 5423 3235 0 -3234 4791 4790 0 -3435 4791 4792 0 -5424 3234 5425 0 -5424 5423 3234 0 -4790 4791 3435 0 -3234 4792 4791 0 -3435 5525 5526 0 -3044 5423 4952 0 -5526 5525 3632 0 -5525 3436 5524 0 -4240 5525 4242 0 -5424 3043 5423 0 -5691 4613 4612 0 -3236 5421 5420 0 -5691 4429 4045 0 -4429 5691 4612 0 -4429 4612 4241 0 -4429 4241 3632 0 -4238 4241 4611 0 -4611 4241 4612 0 -4428 4611 4426 0 -3632 4241 5526 0 -4612 4613 5298 0 -4428 4238 4611 0 -4238 5693 3631 0 -4238 4428 5693 0 -3631 5693 4044 0 -5526 4241 4238 0 -4044 4239 3836 0 -4044 5693 4428 0 -3836 4239 3631 0 -4044 3836 3631 0 -4239 4044 4428 0 -3631 4239 4237 0 -4428 4237 4239 0 -5527 4238 3631 0 -4428 4426 4237 0 -4426 4611 4610 0 -5694 4426 4610 0 -5694 4237 4426 0 -4043 4236 4427 0 -4427 4236 4610 0 -4609 4427 4610 0 -4425 4427 4609 0 -4614 5298 4613 0 -4236 5694 4610 0 -4427 3835 4043 0 -4043 5694 4236 0 -4043 3835 3630 0 -5528 5694 4043 0 -3630 4425 5529 0 -5695 4425 4609 0 -4235 4607 4608 0 -5695 4609 4607 0 -4609 4608 4607 0 -4425 3630 3835 0 -4425 3835 4427 0 -5694 5528 4237 0 -4611 4612 5298 0 -4237 5527 3631 0 -4611 5298 4610 0 -5528 4043 3630 0 -5526 3434 4790 0 -3434 5526 5527 0 -4790 3434 4789 0 -4790 3435 5526 0 -3434 3433 4789 0 -5528 5527 4237 0 -4788 4789 3433 0 -5528 3434 5527 0 -5526 4238 5527 0 -4790 4789 3233 0 -5425 3233 5426 0 -3233 5425 4790 0 -5426 3233 3232 0 -3043 5424 5425 0 -3232 5427 5426 0 -4787 3232 4788 0 -5427 3232 5428 0 -3233 4789 3232 0 -4789 4788 3232 0 -3042 5425 5426 0 -5528 3433 3434 0 -5425 3234 4790 0 -5529 5695 4042 0 -5529 3433 5528 0 -5695 4607 4042 0 -5529 4042 3629 0 -3834 4235 3629 0 -3834 4042 4607 0 -5529 3629 5530 0 -3834 3629 4042 0 -4235 3834 4607 0 -5530 3629 4235 0 -4787 5428 3232 0 -3433 5529 4787 0 -5428 4787 3231 0 -4787 4788 3433 0 -3432 5530 4786 0 -4787 5529 3432 0 -5428 3231 4785 0 -3231 4787 4786 0 -4786 5530 3431 0 -4786 4787 3432 0 -5529 5530 3432 0 -4785 3231 4786 0 -5530 4235 4424 0 -5528 3630 5529 0 -5529 4425 5695 0 -3041 5426 5427 0 -4786 3430 4785 0 -4429 3632 4045 0 -4609 4610 5298 0 -3041 5427 5428 0 -5024 5036 5025 0 -5036 4954 4953 0 -5025 5036 5035 0 -4904 4162 5025 0 -5035 2856 5034 0 -4953 2856 5036 0 -4952 2856 4953 0 -2856 5035 5036 0 -3044 4953 4954 0 -5025 5035 2665 0 -4358 4904 4166 0 -4162 4904 4358 0 -4167 4166 4904 0 -4167 4738 4166 0 -5967 4904 4905 0 -2665 4163 4904 0 -5026 2665 5035 0 -5026 4163 2665 0 -5025 2665 4904 0 -5967 4167 4904 0 -5035 5034 5026 0 -5037 4954 5036 0 -2664 5034 5033 0 -2855 5034 4952 0 -5034 2664 5027 0 -5026 5034 5027 0 -4952 4951 2855 0 -4951 4952 3043 0 -4951 3043 4950 0 -4164 2664 5028 0 -4953 3044 4952 0 -5034 2855 5033 0 -4905 4163 4165 0 -4905 4904 4163 0 -4906 4165 4163 0 -4905 4165 5773 0 -4165 4906 5773 0 -4164 5027 2664 0 -4906 4164 5774 0 -4164 4906 5027 0 -5774 4164 5029 0 -5027 4906 4163 0 -5026 5027 4163 0 -5967 4905 4169 0 -5774 3963 2663 0 -5034 2856 4952 0 -4952 5423 3043 0 -5773 5772 4905 0 -5967 4169 4168 0 -4170 4169 4905 0 -4168 4169 4739 0 -4739 5778 4168 0 -4170 5777 4171 0 -5772 5777 4170 0 -4740 4171 4172 0 -4170 4171 4740 0 -4168 4167 5967 0 -4169 4170 4739 0 -5968 4364 4365 0 -4364 5778 4739 0 -4365 4364 4739 0 -4364 5968 4552 0 -4365 4740 4366 0 -4365 4739 4170 0 -4740 4367 4366 0 -4365 4366 5968 0 -4170 4740 4365 0 -4552 5968 4553 0 -4172 4171 5777 0 -5778 4167 4168 0 -5772 4906 5771 0 -5772 4170 4905 0 -5771 4906 2663 0 -5770 5777 5771 0 -5770 2663 5769 0 -2663 5770 5771 0 -5768 5769 4907 0 -5770 5769 4172 0 -4906 5774 2663 0 -5770 4172 5777 0 -4172 4173 4741 0 -4172 5769 4173 0 -2662 4741 4173 0 -4367 4740 4172 0 -5768 5776 5775 0 -2663 4907 5769 0 -4174 5775 3962 0 -4174 4173 5775 0 -5775 5776 5767 0 -5775 4173 5768 0 -5769 5768 4173 0 -4367 4172 4741 0 -5768 3965 5776 0 -5771 5777 5772 0 -5773 4906 5772 0 -4554 5968 4366 0 -4951 4950 2855 0 -4361 4358 4166 0 -5032 5033 2854 0 -4950 5033 2855 0 -2854 5033 4950 0 -5033 5032 5028 0 -4950 4949 2854 0 -4949 4950 3042 0 -3042 5426 4949 0 -2854 4949 5031 0 -3043 5425 4950 0 -5032 2854 5031 0 -5028 2853 5029 0 -5028 5032 2853 0 -5029 2853 5030 0 -5029 3963 5774 0 -5031 3039 5030 0 -4947 3039 5031 0 -5030 3039 4946 0 -5031 5030 2853 0 -5032 5031 2853 0 -5029 5030 2852 0 -5031 4949 3040 0 -5033 5028 2664 0 -3041 5429 4948 0 -5429 3041 3230 0 -4948 5429 3040 0 -4948 3040 4949 0 -3230 4785 4784 0 -3230 3041 5428 0 -4784 3428 5431 0 -3230 4784 5429 0 -5426 3041 4949 0 -5429 4784 3229 0 -4947 4946 3039 0 -4947 3040 3229 0 -4946 4947 3228 0 -4947 5031 3040 0 -5430 3229 5431 0 -3229 5430 4947 0 -3228 5431 4944 0 -5431 3228 5430 0 -3229 4784 5431 0 -5430 3228 4947 0 -3040 5429 3229 0 -4946 3228 4945 0 -3427 4944 5431 0 -4948 4949 3041 0 -5425 3042 4950 0 -5030 4946 3038 0 -3963 3964 4907 0 -3963 5029 3964 0 -4907 3964 3965 0 -3963 4907 2663 0 -3966 3965 2852 0 -2852 3965 3964 0 -2852 5030 4908 0 -3966 5776 3965 0 -2852 3964 5029 0 -4908 3967 2852 0 -3962 5767 3757 0 -5775 5767 3962 0 -3757 5767 5766 0 -4174 3962 4742 0 -3967 5766 5767 0 -3967 3966 2852 0 -3757 5766 4175 0 -3968 5765 5766 0 -3968 5766 3967 0 -3967 5767 3966 0 -3966 5767 5776 0 -5765 4743 5766 0 -3967 4908 3968 0 -5768 4907 3965 0 -3970 4909 3038 0 -5030 4909 4908 0 -3038 4946 4910 0 -3971 3038 4910 0 -3227 4945 4944 0 -3227 4910 4946 0 -4944 4945 3228 0 -3227 4944 4912 0 -3968 4908 4909 0 -4910 3227 4911 0 -4909 3970 3969 0 -3969 3968 4909 0 -5765 3969 5764 0 -3968 3969 5765 0 -3973 4910 4911 0 -4912 4911 3227 0 -5763 3970 3971 0 -3971 4910 3972 0 -4911 4912 3975 0 -3971 3970 3038 0 -3972 4910 3973 0 -5764 3969 3970 0 -4912 3976 3975 0 -3038 4909 5030 0 -4946 4945 3227 0 -4742 3962 3757 0 -3426 4912 4944 0 -5029 4164 5028 0 -5428 4785 3230 0 -3971 3972 5763 0 -3429 4784 4785 0 -3843 4433 4051 0 -4608 4609 5298 0 -4173 4174 4368 0 -3951 3743 3950 0 -2028 3742 3743 0 -3950 3949 2228 0 -3951 3950 2228 0 -5585 2227 3951 0 -3951 2228 5587 0 -3744 3951 2227 0 -3743 3951 3744 0 -5584 2227 5585 0 -2227 3745 3744 0 -3541 2028 3542 0 -3541 3540 2028 0 -3542 2028 2226 0 -2027 3541 3542 0 -2226 2225 3542 0 -3745 2226 3744 0 -2441 2225 2226 0 -2226 2028 3744 0 -2028 3743 3744 0 -2225 2027 3542 0 -3952 2227 5584 0 -3742 3950 3743 0 -2227 2443 3745 0 -2442 2226 3745 0 -2443 3952 5583 0 -5969 3746 3745 0 -2443 5582 5969 0 -5582 5583 4552 0 -3953 5582 4553 0 -5582 2443 5583 0 -3952 5584 5583 0 -2443 5969 3745 0 -2225 2441 3543 0 -2226 2442 2441 0 -2441 2442 5970 0 -3543 2441 2440 0 -3544 5970 5971 0 -5970 2442 3745 0 -3543 2440 2224 0 -3544 2440 5970 0 -5971 5970 3746 0 -5970 2440 2441 0 -3745 3746 5970 0 -2224 2440 3544 0 -3544 2439 3347 0 -2227 3952 2443 0 -5584 4551 5583 0 -2224 2225 3543 0 -1832 3150 3151 0 -3150 1833 3344 0 -3345 3150 3344 0 -3150 1832 2959 0 -3345 3151 3150 0 -2027 2225 3345 0 -3152 2960 3151 0 -3345 3344 2027 0 -3344 3541 2027 0 -1832 3151 2960 0 -2540 2764 1651 0 -2960 2764 2959 0 -2541 1476 2540 0 -2958 2764 2540 0 -2961 2962 2765 0 -2960 1831 2961 0 -1651 2764 2961 0 -2960 2961 2764 0 -2959 1832 2960 0 -2765 1651 2961 0 -3151 3345 2026 0 -2959 1653 3150 0 -2026 3346 3152 0 -2026 3345 3346 0 -2223 3152 3346 0 -3153 2960 3152 0 -2025 3152 2223 0 -3347 2224 3544 0 -3347 2223 3346 0 -3347 3346 2224 0 -3345 2224 3346 0 -2025 2223 3348 0 -1831 3153 2962 0 -2960 3153 1831 0 -2962 3153 3154 0 -2765 2962 2542 0 -3154 3153 3349 0 -3349 2025 3348 0 -3154 1830 2962 0 -1830 2766 2962 0 -3349 2024 3154 0 -3349 3153 2025 0 -3152 2025 3153 0 -2962 2961 1831 0 -3348 3547 3349 0 -3152 3151 2026 0 -2225 2224 3345 0 -2766 2542 2962 0 -3953 5969 5582 0 -1834 3541 3344 0 -4553 4554 5581 0 -4553 5968 4554 0 -5580 5581 4554 0 -5581 5969 3953 0 -4554 4555 3954 0 -4555 4554 4367 0 -4555 4367 2662 0 -2661 3954 5579 0 -4553 5582 4552 0 -5580 4554 3954 0 -3746 3747 5971 0 -5580 5969 5581 0 -2661 5969 5580 0 -5971 3545 3544 0 -3956 5971 3747 0 -2661 3747 5969 0 -3748 3747 2661 0 -3545 5971 3749 0 -5580 3954 2661 0 -3544 3545 2439 0 -3954 4555 5579 0 -5581 3953 4553 0 -4368 4556 4555 0 -2662 4173 4368 0 -4556 4368 4369 0 -4555 4556 5579 0 -3955 4369 4557 0 -4369 4368 4174 0 -4557 5806 3955 0 -4369 3955 4556 0 -4367 4741 2662 0 -4556 3955 5806 0 -3748 5579 5578 0 -5806 5579 4556 0 -5578 5579 5806 0 -3748 3956 3747 0 -3748 2660 3956 0 -5972 5971 3960 0 -3960 5971 3956 0 -3960 3956 2660 0 -2438 3959 3754 0 -5578 5806 3961 0 -3961 5806 4557 0 -2661 5579 3748 0 -5578 3756 3748 0 -4368 4555 2662 0 -4367 4554 4366 0 -5972 3749 5971 0 -3347 3546 2223 0 -3347 2439 3546 0 -3546 2439 3545 0 -3547 2223 3546 0 -3546 2438 2222 0 -2438 3546 3545 0 -3545 3957 2438 0 -2222 3549 3548 0 -3545 3749 3957 0 -3547 3546 2222 0 -2024 3350 3154 0 -3349 3547 3548 0 -3548 2024 3349 0 -3154 3350 2963 0 -2024 3548 3550 0 -3548 3547 2222 0 -2963 3350 2767 0 -2024 3352 3350 0 -5807 3155 3350 0 -3155 2544 3350 0 -2222 2438 3753 0 -3547 3348 2223 0 -3958 3753 2438 0 -3959 2438 3957 0 -3553 3750 2438 0 -3958 2438 3750 0 -3958 3160 3753 0 -3750 3160 3958 0 -3352 3157 5807 0 -2222 3552 3751 0 -3959 3957 3357 0 -2968 3752 3751 0 -3550 3352 2024 0 -2222 3752 3551 0 -3549 2222 3551 0 -3550 3548 3549 0 -3550 2771 3352 0 -3549 2771 3550 0 -5807 2547 3351 0 -2547 3156 3351 0 -3353 3549 3551 0 -5807 3350 3352 0 -3551 3752 2968 0 -3155 5807 2964 0 -3753 3552 2222 0 -3752 2222 3751 0 -3357 3957 3749 0 -1830 3154 2963 0 -3756 5578 3961 0 -3747 3746 5969 0 -4174 4742 4369 0 -5807 3351 2768 0 -2763 2541 6093 0 -2763 1476 2541 0 -6093 2541 1651 0 -2763 6093 2316 0 -2316 1651 2109 0 -1651 2316 6093 0 -1910 2109 6092 0 -2316 2109 2763 0 -2541 2540 1651 0 -2763 2109 2539 0 -6094 1475 1476 0 -2539 2109 6094 0 -1475 6094 2315 0 -1476 2539 6094 0 -2315 1910 2108 0 -2315 6094 2109 0 -1909 2108 1910 0 -2315 2108 1475 0 -1910 2315 2109 0 -2762 1475 2108 0 -2109 1651 2542 0 -2956 1475 2762 0 -2766 2317 6092 0 -6092 2542 2766 0 -6092 2317 1650 0 -6092 1650 1910 0 -2543 1830 6091 0 -2543 2766 1830 0 -6091 2318 1911 0 -2317 2543 2110 0 -1651 2765 2542 0 -2110 2543 6091 0 -1724 2110 1725 0 -2110 1724 1650 0 -1909 1910 1724 0 -1650 1724 1910 0 -1911 1549 1725 0 -2110 6091 1911 0 -1550 1649 1549 0 -1912 1549 1911 0 -1911 2318 1912 0 -1911 1725 2110 0 -1650 2317 2110 0 -1723 1724 1725 0 -1726 1549 1912 0 -6092 2109 2542 0 -2766 2543 2317 0 -1549 1649 1725 0 -2538 1909 6095 0 -2314 6095 1909 0 -6095 2761 6161 0 -6095 6161 2538 0 -2314 1723 2107 0 -1908 2107 1723 0 -2107 1908 2761 0 -2314 2107 2761 0 -2538 2762 1909 0 -6095 2314 2761 0 -2537 6162 2105 0 -2761 1908 2537 0 -6162 2537 6096 0 -6162 1720 1906 0 -6096 2537 1908 0 -1908 2313 6096 0 -2313 2106 6162 0 -6096 2313 6162 0 -2313 1722 2106 0 -1720 6162 2106 0 -2313 1908 1474 0 -2312 2761 2537 0 -1725 1546 1723 0 -1723 1909 1724 0 -1546 1474 1908 0 -1546 1908 1723 0 -1547 1649 1548 0 -1649 1547 1546 0 -1550 1548 1649 0 -1547 1548 1376 0 -1649 1546 1725 0 -1546 1547 1722 0 -2106 1722 1907 0 -2313 1474 1722 0 -1474 1546 1722 0 -1907 1722 1545 0 -1907 1545 1721 0 -1547 1545 1722 0 -1376 1377 1473 0 -1547 1376 1545 0 -1377 1376 1548 0 -1545 1376 1473 0 -1377 1648 1378 0 -1378 6257 1377 0 -1648 1377 1548 0 -2314 1909 1723 0 -2762 2108 1909 0 -1543 2106 1907 0 -6091 1830 2318 0 -6161 2762 2538 0 -2318 2767 2111 0 -2318 1830 2963 0 -2111 2767 2544 0 -2111 1726 1912 0 -3155 6090 2544 0 -2319 6090 2964 0 -2112 2544 6090 0 -2111 2544 2112 0 -2319 2964 2113 0 -1913 6090 2319 0 -1726 2112 1829 0 -2112 1726 2111 0 -1829 2112 1913 0 -1726 1829 1550 0 -2319 1914 1913 0 -1913 2112 6090 0 -1914 2113 1915 0 -1829 1913 1727 0 -2113 1914 2319 0 -1914 1728 1913 0 -3350 2544 2767 0 -1726 1550 1549 0 -2768 2023 2113 0 -2964 5807 2768 0 -2023 2768 2545 0 -2113 2023 2114 0 -3351 6089 2545 0 -2545 2768 3351 0 -6089 3156 2320 0 -2545 6089 2023 0 -6090 3155 2964 0 -2023 6089 2115 0 -1916 1915 2114 0 -1915 2113 2114 0 -2114 2023 2115 0 -2115 1916 2114 0 -2320 2321 2116 0 -2965 2321 2320 0 -1916 2115 2116 0 -2117 2116 2321 0 -2117 2321 2322 0 -2320 2116 2115 0 -6089 2320 2115 0 -1728 1914 1915 0 -3156 2965 2320 0 -2113 2964 2768 0 -2318 2963 2767 0 -2117 1917 2116 0 -1550 1727 1551 0 -1727 1550 1829 0 -1551 1727 1728 0 -1550 1551 1379 0 -1728 1729 1552 0 -1729 1728 1915 0 -1828 1552 1553 0 -1728 1552 1551 0 -1727 1913 1728 0 -1551 1552 1828 0 -6254 1648 1380 0 -1648 1548 1379 0 -1380 1648 1379 0 -1648 6254 1378 0 -1380 1828 1381 0 -1380 1379 1551 0 -1827 6253 1381 0 -6253 6254 1381 0 -1381 1828 1382 0 -1381 6254 1380 0 -1551 1828 1380 0 -6256 1378 6254 0 -1729 1553 1552 0 -1379 1548 1550 0 -1729 1730 1553 0 -1730 1729 1916 0 -1730 1916 2022 0 -1554 1553 1730 0 -2022 1917 1731 0 -1917 2022 1916 0 -1918 1731 1917 0 -1730 2022 1554 0 -1916 2116 1917 0 -1731 1554 2022 0 -6253 1827 6252 0 -1827 1381 1382 0 -1827 1382 1383 0 -6252 1827 2020 0 -1554 2021 1383 0 -2021 1554 1731 0 -1383 2020 1827 0 -1384 2020 1383 0 -1383 2021 2217 0 -1554 1383 1382 0 -1382 1553 1554 0 -6251 6252 2020 0 -2021 1731 1555 0 -1553 1382 1828 0 -1916 1729 1915 0 -1647 6254 6253 0 -1918 1917 2117 0 -2111 1912 2318 0 -3351 3156 6089 0 -6252 1219 6253 0 -4742 3758 4369 0 -1476 2763 2539 0 -3758 4175 3759 0 -3757 4175 3758 0 -3759 4175 2851 0 -3759 4370 4369 0 -2851 4743 3760 0 -4743 2851 4175 0 -3761 4370 3760 0 -3759 2851 4370 0 -4742 3757 3758 0 -4370 2851 3760 0 -3756 4557 3555 0 -4557 3756 3961 0 -5577 3555 4558 0 -2660 3748 3555 0 -3555 5577 3556 0 -4557 4558 3555 0 -4370 4558 4557 0 -3557 3556 5577 0 -4369 4370 4557 0 -3555 3556 2660 0 -3760 4743 4176 0 -4369 3758 3759 0 -3761 4176 4177 0 -4176 4743 5765 0 -4177 4176 5764 0 -3761 4558 4370 0 -4177 4744 3037 0 -4744 4177 5764 0 -5763 3037 4744 0 -3761 4177 4371 0 -4744 5764 5763 0 -4372 4177 3037 0 -4558 4371 3762 0 -4371 4558 3761 0 -3762 4371 4177 0 -4558 3762 4559 0 -3763 4372 4559 0 -3763 3762 4177 0 -5576 5577 4559 0 -3764 5576 4559 0 -5576 4560 2850 0 -3763 4559 3762 0 -4177 4372 3763 0 -4559 5577 4558 0 -3764 4559 4372 0 -3761 3760 4176 0 -4175 5766 4743 0 -3558 5577 5576 0 -5972 3755 3554 0 -5972 3960 3755 0 -3557 3554 3755 0 -5972 3554 3356 0 -3356 3558 3357 0 -3755 2660 3557 0 -3356 3357 5972 0 -3554 3558 3356 0 -3960 2660 3755 0 -3357 3754 3959 0 -3355 3750 3553 0 -3754 3357 3553 0 -3355 3160 3750 0 -2438 3754 3553 0 -3354 3552 5973 0 -3552 3753 3160 0 -3355 3553 3358 0 -5973 3552 3160 0 -3355 3358 3160 0 -3159 3751 3354 0 -3358 3553 3357 0 -3354 3751 3552 0 -3559 3357 3558 0 -3557 5577 3558 0 -3357 3559 3358 0 -3359 3160 3358 0 -3559 2850 3560 0 -3559 3558 5576 0 -3561 2659 3560 0 -3559 3560 3358 0 -2850 3559 5576 0 -3358 3560 2659 0 -3161 3162 5973 0 -3162 3161 3360 0 -5973 3162 3163 0 -3161 5973 3160 0 -3359 2659 3360 0 -2659 3359 3358 0 -3360 3361 3162 0 -3361 3163 3162 0 -3360 2659 3361 0 -3360 3161 3359 0 -3160 3359 3161 0 -3354 5973 3159 0 -2850 3561 3560 0 -3554 3557 3558 0 -2660 3556 3557 0 -3163 2969 5973 0 -4178 4372 3037 0 -3748 3756 3555 0 -5763 5762 4178 0 -5763 3972 5762 0 -4178 5762 4745 0 -4178 3764 4372 0 -4745 3973 5761 0 -3973 4745 5762 0 -5761 3973 3974 0 -4178 4745 4373 0 -5764 3970 5763 0 -4179 4745 5761 0 -3764 3765 4560 0 -3765 3764 4373 0 -4560 3765 3766 0 -4560 5575 2850 0 -4373 4179 3766 0 -4373 3764 4178 0 -4374 3766 4179 0 -4373 3766 3765 0 -4745 4179 4373 0 -5575 4560 3766 0 -4179 5761 3226 0 -5576 3764 4560 0 -3226 3974 4746 0 -3974 3226 5761 0 -4746 3974 5760 0 -4180 3226 4746 0 -5759 5760 3975 0 -3975 5760 3974 0 -3976 4747 3975 0 -4746 5760 4180 0 -3973 4911 3974 0 -3975 4747 5759 0 -3767 4561 4374 0 -4561 3766 4374 0 -4374 4179 4180 0 -3767 4374 4180 0 -5759 4747 4181 0 -4375 4180 4181 0 -3767 4375 3768 0 -3767 4180 4375 0 -4181 4182 4375 0 -4180 5760 4181 0 -5760 5759 4181 0 -3036 3766 4561 0 -4375 3769 4562 0 -4179 3226 4180 0 -4911 3975 3974 0 -5574 4561 3767 0 -2659 3562 3361 0 -2659 3561 3562 0 -3562 3561 5575 0 -3361 3562 2849 0 -3563 2849 3562 0 -3036 3562 5575 0 -2849 3563 3564 0 -3036 3563 3562 0 -2850 5575 3561 0 -3361 2849 3362 0 -3165 3164 2658 0 -3362 3164 3361 0 -2658 3164 3362 0 -2970 3163 3164 0 -3166 2658 3364 0 -3363 2658 3362 0 -3363 3364 2658 0 -2658 3166 3165 0 -3362 2849 3363 0 -2972 3164 3165 0 -3363 2849 3364 0 -3164 3163 3361 0 -5574 3564 3036 0 -3036 5575 3766 0 -3036 4561 5574 0 -3225 5574 3768 0 -5573 3225 3768 0 -3769 5573 4562 0 -3769 3225 5573 0 -5573 3768 4562 0 -3767 3768 5574 0 -3564 5574 3225 0 -2848 3167 3364 0 -3167 3166 3364 0 -2849 3564 3565 0 -3364 2849 3565 0 -3035 3365 3364 0 -3566 3035 3565 0 -3365 2848 3364 0 -3366 2848 3365 0 -2848 3169 3168 0 -3035 3364 3565 0 -3564 3225 3565 0 -3167 2848 3168 0 -3035 3366 3365 0 -3563 3036 3564 0 -4375 4562 3768 0 -2973 3165 3166 0 -4182 4181 4747 0 -4178 3037 5763 0 -5764 4176 5765 0 -2974 3166 3167 0 -2967 3549 3158 0 -2437 3158 3353 0 -2967 2771 3549 0 -2966 5807 3157 0 -2966 3157 2221 0 -2437 2967 3158 0 -2771 3157 3352 0 -2771 2221 3157 0 -3159 2968 3751 0 -2770 5807 2966 0 -2769 2220 2321 0 -2547 2769 2965 0 -2220 2769 2546 0 -2220 2322 2321 0 -2769 2548 2546 0 -2547 2770 2221 0 -2547 2965 3156 0 -2770 2547 5807 0 -2966 2221 2770 0 -2220 2546 6088 0 -2437 3551 2968 0 -2321 2965 2769 0 -2437 2771 2967 0 -2437 2968 2969 0 -2772 2771 2437 0 -3353 3551 2437 0 -5973 2969 2968 0 -3163 2970 2969 0 -2970 2971 2437 0 -2969 2970 2437 0 -3164 2971 2970 0 -2772 2437 2971 0 -2548 2547 2773 0 -2221 2772 2773 0 -2547 2221 2773 0 -2547 2548 2769 0 -2548 2436 2549 0 -2773 2772 2971 0 -6088 2548 2549 0 -2436 2550 2549 0 -2774 2436 2548 0 -2774 2548 2773 0 -2773 2972 2774 0 -2546 2548 6088 0 -2972 2773 2971 0 -2772 2221 2771 0 -5973 2968 3159 0 -2435 2549 2550 0 -2117 2219 1918 0 -2117 2322 2219 0 -2219 2322 2323 0 -1919 1918 2219 0 -2219 2323 2118 0 -2323 2322 6088 0 -2324 2118 2323 0 -1919 2118 2119 0 -2322 2220 6088 0 -1919 2219 2118 0 -1384 1383 2217 0 -2021 1555 2217 0 -1555 1731 2218 0 -1732 1555 2218 0 -2433 1555 1732 0 -1919 1732 2218 0 -2433 1732 1919 0 -1555 2433 2217 0 -2218 1918 1919 0 -1384 2217 1385 0 -2323 6088 6087 0 -1731 1918 2218 0 -2324 2325 2434 0 -6086 2324 6087 0 -2324 2434 2119 0 -2119 2118 2324 0 -2325 6086 6085 0 -2435 6086 6087 0 -2435 6085 6086 0 -6086 2325 2324 0 -6088 2549 6087 0 -1920 2434 2120 0 -1556 2433 1733 0 -2119 1920 1919 0 -2433 1919 1733 0 -1556 1385 2217 0 -1385 1556 5978 0 -1733 1919 1920 0 -1384 1385 6250 0 -2432 1385 5978 0 -1920 1734 1733 0 -1734 1556 1733 0 -2119 2434 1920 0 -1556 2217 2433 0 -2120 2434 2325 0 -2324 2323 6087 0 -2549 2435 6087 0 -1384 6250 2020 0 -2435 2550 2551 0 -3353 3158 3549 0 -5974 2775 2774 0 -5974 2972 2973 0 -2775 5974 2776 0 -2775 2436 2774 0 -2974 2776 5974 0 -2974 2973 3166 0 -2552 2776 2777 0 -2974 5974 2973 0 -2972 3165 2973 0 -2775 2776 2436 0 -2551 2552 2435 0 -2550 2436 2551 0 -2436 2776 2551 0 -2552 6084 2435 0 -6083 6084 5976 0 -6084 6085 2435 0 -2552 5976 6084 0 -6084 6083 2326 0 -2776 2552 2551 0 -6084 2326 2325 0 -2777 2776 2974 0 -2972 5974 2774 0 -2777 2975 2778 0 -2975 2777 2974 0 -2778 2975 2976 0 -2777 2778 5975 0 -2976 2657 3169 0 -3168 2657 2975 0 -3169 2657 3168 0 -2657 2976 2975 0 -2974 3167 2975 0 -2778 2976 2779 0 -6081 6082 5976 0 -5976 2552 2553 0 -2554 5976 2553 0 -5976 6082 6083 0 -5975 2779 2554 0 -2779 5975 2778 0 -6081 5976 2554 0 -2555 2554 2779 0 -2779 2976 2977 0 -5975 2554 2553 0 -2552 5975 2553 0 -6081 2554 2555 0 -2555 2779 2780 0 -2552 2777 5975 0 -2971 3164 2972 0 -6083 6082 2327 0 -1921 2120 2121 0 -2120 2325 2326 0 -2121 2120 2326 0 -1921 1734 1920 0 -2121 2327 5977 0 -2327 2121 2326 0 -1922 5977 2122 0 -2121 5977 1921 0 -6083 2327 2326 0 -1921 5977 1922 0 -1557 1735 5978 0 -1735 1557 1734 0 -5978 1386 2432 0 -1557 1556 1734 0 -1558 1735 1923 0 -1558 5978 1735 0 -1386 1558 1387 0 -1386 5978 1558 0 -1735 1922 1923 0 -1734 1921 1735 0 -1921 1922 1735 0 -1556 1557 5978 0 -5977 2327 2122 0 -2120 1921 1920 0 -2123 2122 2328 0 -2328 2122 2327 0 -2328 2327 6082 0 -2329 2328 6081 0 -2654 2123 2329 0 -2329 2123 2328 0 -6080 2329 6081 0 -2654 2329 6080 0 -6080 6081 2655 0 -2122 2123 1923 0 -1387 1558 5979 0 -1558 1923 1736 0 -1736 1559 1558 0 -6247 1387 5979 0 -1559 1736 1924 0 -2654 1736 1923 0 -1388 5979 1559 0 -1560 1388 1559 0 -1924 1736 2654 0 -1558 1559 5979 0 -1923 2123 2654 0 -2431 1386 1387 0 -6080 2330 2654 0 -1922 2122 1923 0 -6081 2328 6082 0 -2432 1386 6249 0 -6081 2555 2655 0 -2325 6085 6084 0 -3168 2975 3167 0 -1385 2432 6249 0 -3566 3565 3225 0 -3357 3749 5972 0 -5762 3972 3973 0 -6247 5979 1388 0 -4235 4608 4424 0 -5217 5166 5167 0 -5696 4606 4041 0 -4606 5696 4424 0 -5696 4041 3628 0 -5696 5531 5530 0 -4606 4423 4234 0 -4423 4606 4422 0 -4041 4234 3833 0 -4606 4234 4041 0 -4424 4608 4606 0 -5697 4234 4423 0 -5530 5531 3431 0 -5531 5696 3628 0 -3833 3628 4041 0 -3628 3833 4234 0 -4605 5531 5697 0 -5531 3628 4234 0 -4040 5697 4423 0 -3431 5531 3626 0 -5697 5531 4234 0 -4040 4605 5697 0 -4423 4422 4040 0 -5532 4786 3431 0 -5698 4422 3832 0 -4422 5698 4040 0 -3832 4422 3627 0 -3832 4605 4040 0 -4605 3627 4233 0 -4233 3627 4422 0 -4232 4233 4422 0 -4039 4233 4232 0 -5298 4606 4608 0 -3832 3627 4605 0 -3626 4605 4421 0 -4421 4233 4039 0 -4605 4233 4421 0 -4605 3626 5531 0 -3831 5699 3830 0 -3831 4039 4232 0 -3829 4421 3830 0 -3830 4421 3831 0 -3829 3830 4420 0 -3831 4421 4039 0 -4232 5699 3831 0 -3626 4421 4603 0 -4232 4231 5699 0 -3832 4040 5698 0 -4606 5298 4422 0 -3431 3626 5532 0 -3429 4785 5533 0 -4784 3429 4783 0 -3430 4786 5532 0 -3625 3430 5532 0 -3624 5533 5534 0 -3625 5533 3430 0 -3624 4782 4783 0 -3429 5533 4783 0 -5532 4604 3625 0 -4783 5533 3624 0 -3427 5431 5432 0 -3428 5432 5431 0 -3428 4784 4783 0 -5432 3428 4783 0 -3622 5432 5433 0 -3623 5432 4783 0 -3623 5433 5432 0 -3427 5432 4943 0 -3623 4783 4782 0 -4943 5432 3622 0 -5534 5533 3625 0 -5533 4785 3430 0 -3828 3625 4603 0 -4604 3626 4603 0 -3828 5535 3625 0 -4603 3625 4604 0 -3625 5535 5534 0 -4602 3828 4603 0 -4602 4603 3829 0 -3827 5535 5536 0 -5532 3626 4604 0 -5535 3827 3624 0 -5433 5434 3825 0 -5433 3623 3826 0 -3622 5433 4942 0 -3826 3623 4782 0 -3826 4781 4780 0 -3827 4781 4782 0 -3826 5434 5433 0 -5435 5434 3826 0 -5536 5535 3828 0 -4781 3826 4782 0 -3624 3827 4782 0 -3825 5434 5435 0 -4781 3827 4780 0 -3624 5534 5535 0 -3829 4603 4421 0 -4942 5433 3825 0 -3830 5699 4420 0 -4943 4944 3427 0 -4231 4037 4036 0 -4037 4231 4038 0 -4036 4037 4038 0 -4231 4036 4420 0 -4419 4038 5700 0 -5700 4038 4231 0 -4230 5700 4231 0 -4038 4419 4036 0 -4231 4232 5344 0 -4419 5700 4230 0 -4420 4035 4602 0 -4420 4036 4035 0 -4602 4035 4601 0 -4420 4602 3829 0 -4419 5705 4601 0 -5704 5705 4419 0 -5706 4601 5705 0 -4419 4601 4035 0 -4036 4419 4035 0 -4602 4601 4034 0 -4419 4230 5704 0 -4231 4420 5699 0 -5703 5702 5704 0 -5702 5703 4230 0 -5704 5702 5701 0 -5703 5704 4230 0 -4418 5701 4229 0 -5701 5702 4230 0 -5298 4232 4422 0 -5704 5701 4418 0 -4230 4229 5701 0 -4228 4227 4229 0 -4223 4418 4224 0 -4224 4229 4225 0 -4418 4229 4224 0 -4600 5705 4418 0 -4226 4227 4417 0 -4227 4226 4229 0 -4224 4417 4223 0 -4417 4224 4226 0 -4227 4228 4417 0 -4226 4224 4225 0 -4225 4229 4226 0 -4600 4418 4223 0 -5539 4599 4221 0 -5704 4418 5705 0 -5344 4230 4231 0 -4599 4223 4417 0 -4033 4780 3827 0 -4033 5536 5537 0 -4780 4033 4779 0 -4033 3827 5536 0 -5537 4779 4033 0 -4034 4601 5537 0 -4031 4779 4778 0 -5537 5536 4034 0 -5536 4602 4034 0 -4780 4779 4032 0 -3824 5435 5436 0 -5435 3826 4032 0 -5436 5435 4032 0 -5435 3824 3825 0 -3823 5436 5437 0 -5436 4032 4779 0 -5437 5436 4031 0 -5436 3823 3824 0 -4779 4031 5436 0 -3824 3823 4939 0 -4779 5537 5707 0 -4032 3826 4780 0 -5706 4600 5538 0 -5537 4601 5706 0 -5538 4600 4222 0 -5538 5707 5537 0 -4222 4599 5539 0 -4222 4600 4223 0 -5539 5709 4777 0 -4222 5539 5538 0 -4223 4599 4222 0 -5538 5539 5708 0 -4778 5437 4031 0 -5707 5538 4778 0 -5437 4778 4030 0 -4778 4779 5707 0 -4777 4030 4778 0 -4777 5708 5539 0 -4030 5438 5437 0 -5438 4030 4777 0 -3821 4937 5438 0 -4777 4778 5708 0 -5538 5708 4778 0 -3823 5437 4938 0 -4029 5438 4777 0 -5538 5537 5706 0 -5705 4600 5706 0 -3822 5437 5438 0 -4410 4599 4417 0 -4602 5536 3828 0 -4230 5344 4229 0 -3825 3824 4941 0 -4747 3976 3425 0 -3426 4913 4912 0 -4912 4913 3976 0 -5758 3425 3976 0 -3976 4913 3977 0 -4913 3426 4943 0 -3978 4913 4914 0 -3977 4913 3978 0 -4944 4943 3426 0 -5758 3976 3977 0 -4182 5758 4748 0 -5758 4182 3425 0 -5758 3977 4748 0 -4183 4182 4748 0 -4748 3424 5757 0 -3424 4748 3978 0 -3424 3978 4749 0 -5757 3424 4749 0 -3977 3978 4748 0 -5757 4183 4748 0 -4913 4943 3621 0 -4376 4375 4182 0 -3621 4942 4914 0 -4942 3621 4943 0 -4914 4942 3620 0 -3979 3978 4914 0 -4915 3620 3619 0 -4941 3620 4942 0 -3981 4915 4916 0 -3620 4915 4914 0 -4942 3825 4941 0 -4914 4915 3979 0 -4749 3979 5756 0 -4749 3978 3979 0 -5756 3979 3423 0 -5757 4749 4183 0 -5756 3423 5755 0 -3423 4915 3980 0 -5755 3980 4750 0 -4750 3980 4915 0 -3981 4750 4915 0 -5755 3423 3980 0 -3979 4915 3423 0 -5754 4750 3981 0 -5754 3981 4751 0 -4914 4913 3621 0 -4943 3622 4942 0 -5756 4183 4749 0 -3769 4376 4563 0 -3769 4375 4376 0 -4563 4376 4183 0 -3769 4563 3770 0 -5756 4377 4183 0 -5756 4184 4377 0 -4563 4377 3770 0 -4183 4377 4563 0 -4376 4182 4183 0 -4564 4377 4184 0 -5572 3771 3224 0 -5572 3770 4377 0 -5572 3224 3567 0 -5572 3225 3770 0 -3771 4564 5571 0 -3771 5572 4377 0 -4378 5571 4564 0 -3771 5571 3224 0 -4564 3771 4377 0 -3568 3224 5571 0 -4564 4184 4378 0 -3225 5572 3566 0 -4378 5755 4185 0 -5755 4378 4184 0 -5755 4750 4185 0 -4565 4378 4185 0 -4379 5754 4186 0 -5754 4379 4185 0 -4751 4186 5754 0 -4379 4186 4566 0 -4750 5754 4185 0 -4565 4185 4379 0 -3772 4565 3223 0 -4565 3772 4378 0 -3223 4565 3773 0 -5571 3772 3569 0 -4379 3773 4565 0 -4379 5570 3773 0 -3773 5570 3570 0 -3774 5570 4566 0 -3774 4566 3222 0 -3773 3570 3223 0 -4379 4566 5570 0 -3223 3569 3772 0 -4566 4186 4380 0 -4378 3772 5571 0 -5756 5755 4184 0 -5570 3774 3570 0 -4941 3619 3620 0 -3770 3225 3769 0 -3619 4916 4915 0 -3619 4940 4939 0 -4916 3619 4917 0 -4916 3422 3981 0 -3618 4917 3619 0 -3618 4939 4938 0 -3421 3983 4917 0 -3618 3619 4939 0 -3824 4939 4940 0 -4916 4917 3982 0 -3422 3982 4751 0 -3422 4916 3982 0 -4751 3982 5753 0 -5753 4186 4751 0 -4187 5753 3983 0 -3983 5753 3982 0 -3982 4917 3983 0 -4752 5752 3983 0 -3983 3421 4752 0 -5753 4380 4186 0 -4917 3618 4918 0 -4941 4940 3619 0 -4918 4938 3617 0 -4938 4939 3823 0 -4937 4938 3822 0 -4918 3421 4917 0 -3617 4937 4919 0 -4937 3617 4938 0 -3819 4754 4919 0 -3617 4919 4918 0 -3822 5438 4937 0 -4918 4919 3616 0 -4918 3984 3421 0 -4918 4753 3984 0 -5751 3984 4753 0 -4752 3421 5752 0 -5750 3985 3616 0 -3616 3985 4753 0 -5750 3616 4754 0 -4754 3616 4919 0 -4754 3819 3986 0 -3985 4189 4753 0 -4918 3616 4753 0 -3984 5752 3421 0 -4190 3986 5749 0 -4918 3618 4938 0 -4941 3824 4940 0 -3986 4190 4754 0 -4380 4187 4567 0 -4187 4380 5753 0 -4567 4187 4381 0 -4380 4567 3222 0 -4381 5752 4188 0 -5752 4381 4187 0 -5752 3984 4188 0 -4567 4381 3776 0 -4187 3983 5752 0 -4568 4381 4188 0 -3222 3775 5569 0 -3222 4567 3775 0 -3774 5569 3571 0 -5569 3774 3222 0 -3420 4568 3777 0 -3420 5568 4381 0 -3777 3776 5568 0 -3776 3775 4567 0 -5568 3420 3777 0 -4381 5568 3776 0 -4568 3420 4381 0 -3775 3221 5569 0 -4188 4382 4568 0 -3222 4566 4380 0 -4382 5751 4189 0 -4382 3777 4568 0 -5751 4753 4189 0 -4569 4382 4189 0 -5750 4190 4383 0 -4190 5750 4754 0 -4383 4190 3615 0 -5750 4383 4189 0 -3984 5751 4188 0 -4569 4189 4383 0 -4382 5567 3777 0 -5567 4382 4569 0 -5567 4569 3778 0 -3419 3777 5567 0 -4190 5566 3615 0 -5566 4190 4570 0 -3614 3778 5566 0 -5566 3778 3615 0 -5566 4570 3779 0 -3615 3778 4569 0 -4569 4383 3615 0 -3419 5567 3778 0 -4570 4190 4384 0 -4382 4188 5751 0 -5750 4189 3985 0 -3221 3775 3776 0 -4937 3820 4919 0 -3422 4751 3981 0 -3822 4938 5437 0 -3419 3776 3777 0 -4417 4228 4412 0 -5530 4424 5696 0 -4412 4413 4411 0 -4413 4412 4228 0 -4411 4413 4414 0 -4412 4411 4416 0 -4416 4414 4415 0 -4415 4414 4228 0 -4595 4415 4596 0 -4414 4416 4411 0 -4413 4228 4414 0 -4594 4415 4595 0 -4412 4416 4410 0 -4416 4415 4594 0 -4416 4594 4409 0 -4598 4410 4416 0 -4409 4593 4597 0 -4593 4594 4595 0 -4597 4593 4592 0 -4593 4409 4594 0 -4595 4592 4593 0 -4598 4416 4409 0 -4595 4596 4592 0 -4410 4221 4599 0 -5545 5544 5546 0 -5544 5545 4596 0 -5545 5546 4597 0 -4596 5545 4597 0 -5544 5543 4591 0 -5543 5544 4596 0 -4591 5543 5542 0 -5546 5544 5542 0 -4228 5344 4415 0 -5542 5544 4591 0 -5546 4408 4597 0 -5546 5542 4408 0 -4774 4408 5542 0 -4409 4597 5541 0 -5547 4590 4773 0 -4772 4590 5547 0 -4407 4773 5442 0 -4773 4407 5547 0 -4590 4772 4773 0 -5547 4407 5542 0 -5543 5547 5542 0 -4774 5542 4407 0 -5547 5543 4772 0 -4596 4597 4592 0 -5344 4228 4229 0 -5541 4597 4408 0 -4221 4598 5540 0 -4598 4221 4410 0 -5540 4598 4220 0 -5540 5709 5539 0 -4220 5541 4775 0 -4220 4598 4409 0 -4775 5541 4219 0 -4220 4775 5540 0 -5541 4220 4409 0 -5540 4775 5710 0 -4777 4776 4029 0 -4777 5709 4776 0 -5709 5540 4776 0 -4029 4776 5439 0 -4776 4028 5439 0 -5440 4028 4776 0 -5710 5440 4776 0 -5439 4028 4931 0 -5540 5710 4776 0 -5439 3821 5438 0 -5710 4775 5440 0 -5539 4221 5540 0 -4219 4774 5441 0 -4774 4219 5541 0 -5441 4774 4218 0 -5441 5711 4775 0 -4929 5441 4218 0 -5442 4218 4774 0 -4935 4930 4217 0 -5441 4929 4217 0 -4408 4774 5541 0 -5441 4217 4930 0 -4931 5440 5712 0 -5440 4775 5711 0 -4930 5440 5711 0 -5440 4931 4028 0 -4930 4216 5712 0 -4930 5711 5441 0 -5712 4934 4931 0 -4934 5712 4216 0 -4216 4923 4934 0 -4930 5712 5440 0 -4930 4935 4216 0 -4931 4934 5713 0 -4405 4218 5442 0 -4775 4219 5441 0 -4407 5442 4774 0 -5439 4931 4027 0 -4773 4406 5442 0 -5439 5438 4029 0 -4772 5548 4589 0 -5548 4772 4771 0 -4589 5548 4771 0 -4589 5443 4773 0 -5443 4771 5549 0 -4771 5443 4589 0 -5549 4771 4770 0 -5443 5549 5444 0 -5543 4596 5344 0 -5443 4406 4773 0 -4928 4405 5442 0 -4928 4406 4588 0 -4405 4928 4587 0 -4928 5442 4406 0 -4927 4928 4588 0 -4588 4406 5443 0 -5551 4588 5444 0 -4928 4927 4587 0 -5443 5444 4588 0 -4936 4405 4587 0 -5549 4769 5444 0 -4929 4218 4405 0 -4770 5459 4769 0 -4596 4415 5344 0 -4769 5459 4768 0 -4769 5445 5444 0 -4768 5550 4769 0 -5550 5445 4769 0 -5445 5551 5444 0 -4588 4767 4927 0 -4588 5551 4767 0 -4927 4767 4926 0 -4926 5552 4927 0 -4766 4587 5446 0 -5446 4587 5552 0 -4587 4927 5552 0 -4769 5549 4770 0 -4772 5543 5344 0 -4925 4587 4766 0 -4404 4935 4217 0 -4404 4929 4936 0 -4924 4935 4404 0 -4404 4217 4929 0 -4936 4925 4586 0 -4925 4936 4587 0 -5451 4586 5448 0 -4936 4586 4404 0 -4929 4405 4936 0 -4924 4404 4586 0 -4403 4923 4216 0 -4403 4935 4924 0 -4403 4924 4585 0 -4923 4403 5452 0 -4403 4585 5452 0 -4585 4924 5451 0 -4585 5451 4762 0 -5452 4585 4760 0 -4924 4586 5451 0 -4923 5452 4402 0 -4586 4925 5449 0 -4403 4216 4935 0 -5449 4925 5450 0 -5553 4586 5449 0 -5448 4586 4765 0 -4761 5451 5448 0 -4765 4586 5553 0 -4762 5554 4585 0 -5451 4761 4762 0 -5554 4763 4585 0 -4764 4585 4763 0 -4764 5559 4760 0 -4764 4760 4585 0 -4760 5559 5558 0 -5450 4925 5447 0 -5447 4925 4766 0 -5452 4760 4584 0 -4772 4589 4773 0 -4771 4772 5344 0 -4934 4923 4215 0 -4932 3820 4937 0 -3821 5439 4932 0 -4920 3820 4932 0 -4920 4919 3820 0 -4933 4026 4932 0 -4933 4027 5713 0 -4921 4920 4026 0 -4933 4932 4027 0 -4027 4932 5439 0 -4920 4932 4026 0 -3987 3819 4755 0 -3819 4919 4920 0 -4025 3819 4920 0 -3819 3987 5749 0 -4755 4025 3988 0 -4025 4755 3819 0 -3988 4025 4756 0 -3987 4755 4191 0 -4025 4920 4921 0 -5748 4755 3988 0 -4026 4933 5714 0 -3986 3819 5749 0 -4922 5714 4933 0 -4922 5713 4215 0 -5714 4922 4214 0 -4026 5714 4921 0 -5453 4214 4922 0 -5453 4215 4402 0 -4758 4214 4401 0 -5453 4922 4215 0 -4934 4215 5713 0 -5714 4214 5454 0 -4025 3989 4756 0 -5715 4921 5454 0 -5715 3989 4025 0 -5715 4025 4921 0 -5454 4758 4213 0 -5454 4921 5714 0 -3990 5715 4213 0 -3990 4757 5715 0 -4213 5743 5562 0 -4213 5715 5454 0 -4214 4758 5454 0 -3988 4756 4192 0 -4214 5453 4401 0 -4922 4933 5713 0 -5713 4027 4931 0 -4757 3989 5715 0 -5749 4191 4384 0 -5749 3987 4191 0 -3818 4384 4191 0 -5749 4384 4190 0 -3988 4192 5748 0 -4192 4756 5747 0 -4192 5747 4024 0 -4191 5748 4571 0 -4755 5748 4191 0 -4192 4385 5748 0 -3818 4571 5565 0 -3818 4191 4571 0 -3781 5565 4571 0 -4384 3818 3780 0 -5564 4385 4024 0 -4571 5748 4385 0 -4386 4572 4024 0 -4571 4385 3781 0 -4192 4024 4385 0 -5564 4024 4572 0 -4024 5747 4386 0 -4570 4384 3780 0 -4757 5746 3989 0 -4757 3990 5746 0 -5716 5746 3990 0 -3989 5746 4193 0 -3990 3991 5745 0 -3990 4213 3991 0 -3991 5562 4212 0 -5745 3991 4212 0 -5562 3991 4213 0 -4194 3990 5745 0 -4386 4193 5716 0 -4193 4386 3989 0 -4193 5746 5716 0 -5563 4386 5716 0 -4575 4387 4194 0 -4212 4194 5745 0 -4387 3783 4573 0 -4387 4573 5716 0 -4212 4575 4194 0 -4387 5716 3990 0 -4194 4387 3990 0 -4572 4386 3782 0 -5562 4195 4212 0 -3989 4386 5747 0 -3989 5747 4756 0 -5563 5716 4573 0 -4213 4758 4400 0 -4937 3821 4932 0 -4759 4401 5453 0 -4759 4402 4584 0 -4401 4759 4583 0 -5561 4758 4401 0 -5560 4583 4759 0 -5560 4584 5555 0 -4577 4583 4581 0 -5560 4759 4584 0 -5452 4584 4402 0 -4401 4583 5561 0 -5742 4576 4400 0 -5561 4400 4758 0 -4398 4400 5561 0 -4400 4576 5743 0 -5738 4392 4398 0 -4577 4398 5561 0 -4394 4398 4577 0 -5742 4400 4398 0 -5561 4583 4577 0 -5742 4398 4392 0 -4583 5560 4579 0 -5743 4213 4400 0 -5558 5557 4584 0 -5557 5556 4584 0 -5555 4578 5560 0 -4578 4579 5560 0 -5556 5555 4584 0 -4580 4582 4583 0 -4582 4581 4583 0 -4393 4394 4577 0 -4395 4396 4398 0 -4394 4395 4398 0 -4581 4393 4577 0 -4579 4580 4583 0 -4215 4923 4402 0 -5738 4398 4396 0 -5744 4399 4195 0 -4388 4212 4195 0 -5744 5562 5743 0 -5743 4399 5744 0 -4208 4196 4197 0 -4576 4399 5743 0 -4399 4576 4197 0 -4208 4197 5742 0 -5742 4197 4576 0 -4197 4196 4399 0 -4388 3993 4574 0 -4574 4212 4388 0 -4399 4391 4195 0 -3993 4388 4195 0 -4389 4196 3994 0 -4389 4399 4196 0 -4391 4399 4390 0 -4389 3994 4390 0 -4208 4200 4201 0 -4389 4390 4399 0 -4208 4201 4196 0 -4575 4212 4574 0 -4392 4208 5742 0 -5562 5744 4195 0 -5739 4198 5738 0 -4199 4208 4198 0 -4392 5738 4198 0 -5741 5738 4203 0 -5741 4204 5738 0 -4204 5739 5738 0 -4198 5739 4205 0 -5731 4200 4199 0 -4200 4208 4199 0 -4207 4198 4205 0 -4198 4207 5732 0 -5732 4199 4198 0 -5731 5730 4200 0 -5732 5731 4199 0 -4201 4200 5737 0 -4208 4392 4198 0 -4203 5738 4396 0 -4196 4201 3994 0 -4759 5453 4402 0 -4760 5558 4584 0 -3780 3818 5565 0 -4410 4417 4412 0 -4770 4771 5344 0 -4200 5730 5737 0 -3035 3567 3366 0 -3035 3566 3567 0 -3569 3568 5571 0 -3366 3567 3367 0 -3368 3367 3034 0 -3034 3367 3567 0 -3568 3034 3567 0 -3569 3368 3034 0 -3568 3567 3224 0 -3367 3169 2848 0 -3367 3170 3169 0 -3367 2847 3170 0 -3170 2847 3171 0 -3169 3170 2977 0 -3171 3369 3172 0 -2847 3369 3171 0 -3368 3369 2847 0 -3171 3172 2846 0 -3367 3368 2847 0 -3170 3171 2978 0 -3569 3034 3568 0 -2848 3366 3367 0 -3569 3570 3033 0 -5569 3221 3571 0 -3370 3033 3570 0 -3569 3033 3369 0 -3571 3221 3572 0 -3571 3570 3774 0 -3572 3221 3573 0 -3370 3570 3571 0 -3570 3569 3223 0 -3371 3571 3572 0 -3032 3172 3370 0 -3369 3033 3370 0 -3172 3032 3173 0 -3370 3172 3369 0 -3370 3371 3032 0 -3370 3571 3371 0 -3371 3174 3173 0 -3372 3371 3220 0 -3372 3373 3219 0 -3371 3173 3032 0 -3220 3371 3572 0 -3174 3371 3372 0 -3372 3220 3373 0 -3569 3369 3368 0 -3566 5572 3567 0 -3172 3173 2980 0 -2977 2978 2780 0 -2979 2978 3171 0 -2781 2780 2978 0 -2555 2780 2656 0 -2782 2979 2846 0 -2846 2979 3171 0 -2980 2846 3172 0 -2978 2979 2781 0 -2978 2977 3170 0 -2782 2846 2980 0 -2656 2781 2782 0 -2781 2656 2780 0 -2781 2979 2782 0 -2557 2656 2782 0 -2784 2844 2783 0 -2783 2844 2782 0 -2783 2782 2980 0 -2784 2783 2845 0 -2845 2783 2980 0 -2558 2782 2844 0 -2980 2981 2845 0 -2656 2556 2555 0 -2981 3031 2982 0 -2981 2980 3173 0 -2982 3031 3175 0 -2981 2982 3030 0 -3372 3175 3174 0 -3175 3372 3219 0 -2982 3175 2983 0 -3174 3175 3031 0 -3173 3031 2981 0 -3218 2983 3175 0 -3029 3030 2785 0 -3029 2784 2845 0 -3215 3029 2785 0 -2844 2784 3028 0 -3030 2983 2786 0 -2983 3030 2982 0 -2785 3216 3215 0 -2786 3216 3030 0 -2786 2983 3217 0 -3030 3216 2785 0 -3030 3029 2845 0 -3028 2784 3029 0 -3176 3175 3219 0 -3030 2845 2981 0 -3174 3031 3173 0 -3215 3216 2786 0 -3220 3572 3573 0 -2779 2977 2780 0 -3573 3419 3574 0 -3221 3776 3573 0 -3574 3419 3575 0 -3418 3573 3574 0 -3613 3575 3576 0 -3575 3419 3778 0 -3576 3575 3614 0 -3574 3575 3418 0 -3614 3575 3778 0 -3613 3418 3575 0 -3176 3373 3417 0 -3374 3373 3418 0 -3417 3373 3374 0 -3176 3218 3175 0 -3375 3417 3374 0 -3375 3418 3613 0 -3417 3375 3612 0 -3375 3374 3418 0 -3373 3573 3418 0 -3177 3176 3417 0 -3613 3376 3375 0 -3176 3219 3373 0 -3614 3817 3576 0 -3780 5565 3781 0 -3817 3779 3780 0 -3613 3576 3816 0 -3577 3817 3578 0 -3781 3817 3780 0 -4023 3578 3817 0 -3817 3577 3576 0 -3779 4570 3780 0 -3576 3577 3816 0 -3815 3612 3376 0 -3612 3375 3376 0 -3377 3376 3816 0 -3815 3376 3377 0 -3378 3816 4022 0 -3578 3816 3577 0 -3379 3815 4021 0 -3815 3377 3378 0 -3815 3378 4021 0 -3378 3377 3816 0 -3613 3816 3376 0 -3178 3417 3612 0 -3817 3781 4023 0 -3614 3779 3817 0 -3419 3573 3776 0 -3179 3612 3815 0 -2984 3416 2985 0 -2984 3218 3176 0 -2984 2985 3415 0 -3218 2984 3217 0 -3177 3178 3416 0 -3177 3417 3178 0 -2985 3416 2986 0 -3416 2984 3176 0 -3177 3416 3176 0 -3611 2986 3416 0 -2787 3415 2788 0 -3415 2787 3217 0 -3413 2786 2787 0 -3217 2787 2786 0 -2789 3415 3610 0 -3415 3217 2984 0 -3610 3415 2986 0 -3415 2789 2788 0 -2985 2986 3415 0 -3413 2787 2788 0 -3416 3178 3611 0 -3218 3217 2983 0 -2987 3611 3814 0 -3179 3611 3178 0 -3814 3611 3179 0 -2987 3610 2986 0 -3180 3379 3814 0 -3180 3179 3815 0 -2987 3814 2988 0 -3180 3814 3179 0 -3612 3179 3178 0 -4020 3181 3814 0 -2790 3813 2791 0 -3813 2790 3610 0 -3609 2789 2790 0 -3610 2790 2789 0 -2988 3181 3813 0 -3181 2988 3814 0 -3813 2989 2791 0 -4019 2989 3813 0 -3813 3181 4019 0 -3813 3610 2987 0 -2987 2988 3813 0 -3812 2790 2791 0 -3814 3379 4020 0 -2987 2986 3611 0 -3379 3180 3815 0 -3414 2788 2789 0 -4022 3816 3578 0 -3373 3220 3573 0 -5566 3779 3614 0 -3812 2791 2989 0 -6079 2655 2557 0 -2655 2555 2556 0 -2557 2655 2556 0 -6079 2330 6080 0 -2557 2558 2843 0 -2558 2557 2782 0 -6077 6078 2843 0 -6079 2557 6078 0 -2556 2656 2557 0 -6078 2557 2843 0 -2124 2330 2125 0 -2124 2654 2330 0 -2125 2330 2331 0 -2124 2125 1924 0 -2331 6078 2332 0 -2331 2330 6079 0 -6077 2332 6078 0 -2331 2332 2125 0 -6079 6078 2331 0 -2842 2125 2332 0 -2843 2558 2559 0 -2655 6079 6080 0 -2844 2560 2559 0 -2559 2558 2844 0 -2560 3028 2561 0 -2559 2560 3027 0 -2561 3215 2562 0 -3215 2561 3028 0 -2562 3215 3413 0 -2561 2562 3026 0 -3029 3215 3028 0 -2560 2561 3027 0 -6076 2333 2332 0 -6076 6077 3027 0 -2333 6076 6075 0 -6076 2332 6077 0 -3027 3026 6075 0 -3026 3027 2561 0 -2333 6075 2127 0 -3026 2334 6075 0 -6074 3026 3214 0 -3027 6075 6076 0 -2843 3027 6077 0 -6074 2334 3026 0 -6074 6073 2335 0 -3027 2843 2559 0 -2844 3028 2560 0 -2126 2332 2333 0 -2125 1737 1924 0 -2125 1925 1737 0 -1560 1737 1925 0 -1924 1737 1559 0 -2842 2126 1926 0 -2842 1925 2125 0 -1739 1926 2841 0 -1738 1925 2842 0 -2842 2332 2126 0 -1561 2842 1926 0 -1560 1738 2653 0 -1738 1560 1925 0 -2653 1738 1561 0 -1560 1389 1388 0 -1561 1739 2652 0 -1561 1738 2842 0 -2652 1739 1562 0 -2653 1561 1390 0 -1926 1739 1561 0 -1389 1560 2653 0 -2126 2333 1926 0 -1560 1559 1737 0 -2841 2127 1927 0 -2841 1926 2333 0 -1927 2127 2334 0 -2841 1927 1562 0 -6074 2128 2334 0 -2128 6074 2335 0 -1927 2128 1740 0 -2334 2128 1927 0 -2127 2841 2333 0 -1928 2128 2335 0 -1562 1740 2839 0 -1562 1927 1740 0 -2839 1740 1563 0 -1562 2839 2652 0 -2840 1928 1563 0 -1928 2840 2128 0 -1563 1741 2839 0 -1741 1563 1928 0 -1741 1928 2129 0 -2840 1563 1740 0 -2128 2840 1740 0 -2839 1741 3024 0 -2129 1928 2335 0 -2841 1562 1739 0 -2334 2127 6075 0 -1391 2652 2839 0 -2562 3214 3026 0 -2124 1924 2654 0 -2562 2563 3214 0 -6073 6074 3214 0 -2563 3413 2564 0 -3214 2563 2564 0 -3412 2564 2565 0 -3414 2564 3413 0 -6071 3412 3608 0 -2564 3412 3214 0 -2786 3413 3215 0 -3214 3412 6072 0 -2129 6073 2336 0 -2129 2335 6073 0 -2336 6073 6072 0 -3025 2129 2336 0 -3412 2337 6072 0 -6072 6073 3214 0 -2339 2337 6071 0 -2336 6072 2130 0 -6071 2337 3412 0 -6072 2337 2130 0 -2565 2564 3414 0 -2562 3413 2563 0 -2565 3609 2566 0 -3609 2565 3414 0 -2566 3609 3812 0 -2565 2566 3608 0 -2567 3812 2792 0 -3812 2567 2566 0 -2792 3812 2989 0 -2567 2792 3607 0 -2789 3609 3414 0 -2566 2567 3608 0 -6071 6070 2338 0 -6071 3608 6070 0 -6070 3608 6069 0 -3411 2338 6070 0 -6069 3607 2568 0 -6069 6068 6070 0 -6067 6069 2568 0 -6069 2341 6068 0 -3607 2792 2568 0 -6069 3608 3607 0 -3608 2567 3607 0 -2338 2339 6071 0 -2792 3606 2568 0 -3608 3412 2565 0 -2788 3414 3413 0 -6068 2340 6070 0 -3025 2130 1929 0 -2130 3025 2336 0 -1930 1929 2130 0 -1742 1741 1929 0 -1931 2131 3213 0 -2131 2130 2337 0 -2131 2337 3213 0 -1931 3213 2339 0 -2339 3213 2337 0 -1930 2130 2131 0 -3024 1742 1930 0 -1741 1742 3024 0 -1930 1742 1929 0 -3024 1930 3212 0 -3212 1931 1744 0 -1743 1930 2131 0 -1743 3212 1930 0 -3212 1743 1931 0 -1931 2132 1744 0 -2131 1931 1743 0 -2339 2132 1931 0 -3024 3212 1565 0 -2338 2132 2339 0 -3025 1929 1741 0 -2132 2340 1744 0 -3411 6070 2340 0 -2340 6068 2133 0 -1932 2340 2133 0 -2134 2341 3405 0 -2133 6068 2341 0 -6069 3405 2341 0 -2134 3405 6067 0 -2338 3411 2132 0 -2133 2341 1933 0 -1744 1932 3410 0 -1932 1744 2340 0 -3410 1932 1933 0 -1744 3410 3212 0 -3406 2134 2135 0 -1933 1932 2133 0 -1933 3406 3410 0 -3406 1933 2134 0 -2134 2342 2135 0 -2341 2134 1933 0 -6067 2342 2134 0 -3410 3406 1934 0 -6067 2568 2342 0 -2340 2132 3411 0 -3405 6069 6067 0 -3212 3410 1745 0 -2568 6065 2342 0 -2129 3025 1741 0 -2790 3812 3609 0 -1564 2839 3024 0 -2989 3811 2792 0 -2976 3169 2977 0 -4023 5564 3782 0 -5564 4023 3781 0 -5564 4572 3782 0 -4023 3782 5717 0 -5717 5563 3783 0 -5563 5717 3782 0 -5563 4573 3783 0 -3784 5717 4211 0 -5563 3782 4386 0 -4023 5717 3579 0 -4022 3579 3580 0 -4022 3578 3579 0 -3580 3579 5718 0 -3580 4021 3378 0 -3581 5718 5721 0 -3784 5718 3579 0 -3784 5721 5718 0 -5718 3581 3580 0 -3579 5717 3784 0 -3580 3581 5719 0 -4211 5717 3783 0 -3580 3378 4022 0 -4211 3992 3993 0 -3992 3783 4387 0 -3993 3992 4575 0 -4211 3785 3784 0 -4210 3993 4391 0 -3993 4575 4574 0 -4391 4390 3994 0 -4211 3993 4210 0 -3992 4387 4575 0 -3994 4210 4391 0 -3785 3786 5721 0 -3785 4211 3786 0 -5721 3786 5722 0 -3785 5721 3784 0 -3994 3995 4210 0 -3995 3994 4209 0 -5722 3995 3788 0 -5722 3786 3995 0 -3995 5723 3788 0 -3995 3786 4210 0 -4211 4210 3786 0 -3581 5721 3582 0 -3994 4202 4209 0 -4211 3783 3992 0 -3993 4195 4391 0 -3787 5721 5722 0 -3580 3380 4021 0 -3580 5719 3380 0 -3380 5719 3381 0 -3380 4020 3379 0 -3583 3381 5720 0 -5719 5720 3381 0 -5720 5719 3581 0 -4017 3381 4016 0 -3582 5720 3581 0 -3380 3381 4017 0 -3182 4019 3181 0 -4020 3380 3182 0 -2990 4019 3182 0 -2989 4019 2990 0 -3183 4018 3182 0 -4016 3183 4017 0 -4018 3183 3808 0 -3183 3182 4017 0 -3380 4017 3182 0 -4018 2990 3182 0 -4016 3381 3583 0 -3380 3379 4021 0 -3584 5720 4014 0 -3787 5720 3582 0 -4014 5720 3787 0 -3584 4016 3583 0 -4014 3788 3585 0 -3788 4014 3787 0 -3585 3788 4013 0 -4014 3585 3584 0 -3582 5721 3787 0 -3584 3585 4015 0 -3808 3382 3383 0 -3382 3183 4016 0 -3383 3382 4016 0 -3382 3808 3183 0 -3383 4015 3586 0 -3383 4016 3584 0 -3384 3807 3586 0 -3586 3807 3383 0 -4015 3585 3586 0 -3808 3383 3185 0 -3584 4015 3383 0 -2991 4018 3808 0 -3585 3805 3586 0 -3583 5720 3584 0 -5722 3788 3787 0 -3383 3807 3185 0 -4209 3996 3995 0 -3182 3181 4020 0 -4201 5737 4202 0 -3994 4201 4202 0 -5737 5729 5728 0 -4005 4209 5737 0 -5728 5736 5737 0 -5736 5740 5737 0 -5737 5740 5727 0 -3789 5723 3798 0 -3995 3996 5723 0 -3996 3798 5723 0 -3789 4013 3788 0 -5726 5737 5733 0 -4006 4209 4005 0 -5727 5733 5737 0 -5726 4005 5737 0 -4209 4202 5737 0 -4006 3996 4209 0 -4006 4007 3996 0 -4008 3997 3996 0 -3997 5735 3996 0 -4007 4008 3996 0 -4009 5734 3996 0 -5734 5724 3996 0 -4004 3798 3996 0 -3799 3800 3789 0 -3798 3799 3789 0 -5724 4004 3996 0 -5735 4009 3996 0 -3789 3800 3998 0 -3801 4002 3789 0 -3801 3789 3998 0 -4002 4003 3789 0 -4012 3789 4003 0 -3797 3789 4012 0 -3797 3593 3789 0 -3586 3791 3806 0 -3805 3585 3790 0 -3592 3791 3805 0 -3791 3586 3805 0 -3592 3805 3790 0 -3790 4013 3593 0 -3594 3790 3593 0 -3789 3593 4013 0 -3587 3806 3791 0 -3790 3585 4013 0 -3594 3595 3790 0 -3595 3999 3790 0 -3802 4001 3790 0 -4001 3803 3790 0 -3999 3802 3790 0 -3796 3592 3790 0 -3592 3391 3791 0 -4000 3596 3791 0 -3598 3794 3791 0 -3596 3598 3791 0 -3391 4000 3791 0 -3803 3796 3790 0 -3384 3586 3806 0 -3789 3788 5723 0 -5737 5730 5729 0 -3791 3794 3795 0 -2793 3606 2792 0 -2793 3811 2990 0 -2569 3606 2793 0 -2568 3606 2569 0 -3810 2794 2793 0 -3810 2990 2991 0 -2794 3810 3809 0 -3810 2793 2990 0 -2989 2990 3811 0 -3605 2793 2794 0 -6066 6063 3404 0 -3404 2568 6066 0 -2568 2569 6066 0 -2569 6063 6066 0 -2569 2570 2571 0 -2570 2569 3605 0 -2795 2570 3605 0 -3403 2571 2570 0 -2793 3605 2569 0 -2571 6064 2569 0 -3605 2794 3604 0 -2568 3404 6065 0 -2794 3809 2992 0 -2991 3808 3184 0 -3809 2991 3184 0 -3604 2794 2992 0 -2993 3809 3185 0 -3185 3809 3184 0 -3602 3185 3384 0 -3809 2993 2992 0 -3808 3185 3184 0 -2993 3185 3602 0 -2795 3604 2796 0 -2795 3605 3604 0 -2796 3604 2993 0 -2572 2570 2795 0 -3602 3603 2993 0 -3603 3602 3186 0 -3603 3401 2796 0 -2994 3603 3186 0 -3186 3399 2994 0 -3603 2796 2993 0 -3604 2992 2993 0 -3402 2795 2796 0 -3602 3384 3186 0 -3810 2991 3809 0 -2991 2990 4018 0 -2994 3401 3603 0 -6063 2343 3404 0 -2569 6064 6063 0 -2136 6063 6064 0 -3404 2343 6065 0 -3403 2573 6062 0 -3403 2570 2573 0 -2345 2344 6062 0 -2571 2344 6064 0 -3403 2344 2571 0 -6062 2344 3403 0 -2135 2343 3204 0 -2343 2135 6065 0 -3204 2343 2136 0 -2135 3204 3406 0 -2136 2344 3203 0 -2136 2343 6063 0 -2345 3203 2344 0 -2136 3203 3204 0 -6064 2344 2136 0 -3204 3203 2137 0 -6062 2573 2345 0 -6065 2135 2342 0 -6061 2572 3402 0 -2573 2570 2572 0 -2798 3402 2797 0 -2574 3402 2798 0 -2799 2797 3401 0 -2797 3402 2796 0 -2995 3401 2994 0 -2797 2575 2798 0 -2796 3401 2797 0 -2996 3401 2995 0 -6061 2345 2573 0 -6061 3402 2574 0 -6061 2574 6060 0 -3202 2345 6061 0 -2576 2799 2996 0 -2798 2575 6060 0 -3201 6060 2575 0 -2574 2798 6060 0 -2575 2799 6059 0 -2797 2799 2575 0 -3401 2996 2799 0 -3203 2345 3202 0 -2800 2996 2995 0 -6061 2573 2572 0 -2795 3402 2572 0 -6061 6060 3202 0 -2994 3400 2995 0 -3811 2793 2792 0 -3806 3587 3384 0 -3384 3185 3807 0 -3392 3591 3791 0 -3384 3587 3601 0 -3591 3587 3791 0 -3591 3390 3587 0 -3587 3390 3792 0 -3601 3385 3399 0 -3601 3587 3385 0 -3187 3399 3385 0 -3399 3186 3601 0 -3393 3587 3394 0 -3389 3385 3587 0 -3587 3792 3394 0 -3600 3587 3393 0 -3399 3400 2994 0 -3600 3793 3587 0 -3793 3192 3587 0 -3590 3389 3587 0 -3389 3193 3385 0 -3192 3590 3587 0 -3191 3194 3385 0 -3194 3195 3385 0 -3588 3397 3385 0 -3000 3398 3385 0 -3397 3000 3385 0 -3195 3588 3385 0 -3193 3191 3385 0 -3589 3385 3398 0 -3001 3385 3388 0 -3190 3385 3001 0 -3190 3002 3187 0 -3002 2999 3187 0 -3190 3187 3385 0 -2799 2576 6059 0 -3400 2800 2995 0 -2996 2800 2576 0 -2576 3200 6059 0 -3387 3188 3187 0 -3188 2997 3400 0 -3189 2997 3188 0 -2997 2800 3400 0 -2997 2998 2800 0 -3188 3400 3187 0 -3399 3187 3400 0 -2801 2576 2800 0 -3196 3187 2999 0 -3385 3589 3388 0 -3196 2802 3187 0 -2802 3197 3187 0 -3386 3198 3187 0 -3198 3387 3187 0 -3197 3386 3187 0 -3189 2803 2997 0 -2803 2998 2997 0 -2801 2577 2576 0 -3200 2576 3006 0 -2577 3006 2576 0 -2998 2801 2800 0 -3387 3189 3188 0 -3201 2575 6059 0 -3384 3601 3186 0 -3392 3791 3795 0 -3006 3003 3200 0 -3578 4023 3579 0 -5564 3781 4385 0 -3406 3204 1935 0 -4747 3425 4182 0 -5344 5459 4770 0 -2652 1390 1561 0 -1720 1543 6259 0 -1720 2106 1543 0 -6259 1543 1215 0 -928 1066 6259 0 -1215 1067 6163 0 -1374 1215 1543 0 -1374 1543 1907 0 -1374 1067 1215 0 -1374 1907 1721 0 -6259 1215 928 0 -672 928 6163 0 -672 671 928 0 -928 1215 6163 0 -672 6163 795 0 -796 795 931 0 -929 795 6163 0 -1068 795 929 0 -795 796 672 0 -6163 1067 929 0 -673 672 796 0 -1374 1721 6258 0 -672 673 1304 0 -1216 6258 1544 0 -1544 6258 1721 0 -1473 1544 1721 0 -1375 1544 1473 0 -1216 1375 6257 0 -1375 1216 1544 0 -6257 1375 1473 0 -1216 6257 1217 0 -1377 6257 1473 0 -6258 1216 1068 0 -1068 931 795 0 -1067 6258 1068 0 -931 1068 930 0 -1068 929 1067 0 -1218 1472 1069 0 -1069 930 1068 0 -930 1472 931 0 -1472 930 1069 0 -932 931 1472 0 -1069 1068 1217 0 -1216 1217 1068 0 -799 931 932 0 -1069 1217 1218 0 -6258 1067 1374 0 -1545 1473 1721 0 -6164 796 931 0 -674 556 1303 0 -6352 1153 556 0 -1303 1304 673 0 -674 1303 673 0 -797 6164 674 0 -6164 797 796 0 -674 6165 1302 0 -797 674 673 0 -673 796 797 0 -556 674 1302 0 -6352 6351 1152 0 -6351 6352 557 0 -1152 6351 456 0 -456 1010 1152 0 -557 1301 6351 0 -6165 557 1302 0 -6350 1301 557 0 -557 6352 1302 0 -6352 556 1302 0 -458 6351 1301 0 -674 6164 798 0 -555 1303 556 0 -675 6165 674 0 -675 798 799 0 -558 6165 675 0 -558 557 6165 0 -799 1471 676 0 -799 798 931 0 -800 676 1471 0 -675 799 559 0 -799 932 1471 0 -676 559 799 0 -6166 6350 558 0 -6350 557 558 0 -559 558 675 0 -6166 558 559 0 -6166 6349 6167 0 -6349 6166 559 0 -459 6167 6168 0 -459 6350 6167 0 -6167 6349 1469 0 -6167 6350 6166 0 -6349 559 560 0 -6350 459 1301 0 -560 559 676 0 -674 798 675 0 -6164 931 798 0 -1300 1301 459 0 -6256 1217 6257 0 -1153 6352 1152 0 -6256 6255 1218 0 -6256 6254 6255 0 -1218 6255 1070 0 -1218 932 1472 0 -1071 1070 1219 0 -1647 1070 6255 0 -1219 1070 1647 0 -1218 1070 1072 0 -6256 6257 1378 0 -1072 1070 1071 0 -932 1072 800 0 -1072 932 1218 0 -800 1072 933 0 -800 560 676 0 -933 1073 1646 0 -1073 933 1072 0 -801 1646 1073 0 -677 933 1646 0 -1072 1071 1073 0 -561 800 933 0 -1073 1071 1074 0 -1218 1217 6256 0 -1071 1220 1074 0 -1219 6252 1220 0 -1220 6252 1826 0 -1826 1074 1220 0 -2019 1221 2216 0 -6251 1221 1826 0 -6250 1221 6251 0 -1826 1221 1075 0 -1647 6253 1219 0 -2019 1075 1221 0 -801 1074 934 0 -801 1073 1074 0 -1074 1826 934 0 -801 934 1825 0 -1075 935 802 0 -935 1075 2019 0 -801 1825 562 0 -1825 934 802 0 -935 2018 802 0 -802 934 1075 0 -1826 1075 934 0 -801 677 1646 0 -935 936 803 0 -1071 1219 1220 0 -6255 6254 1647 0 -802 678 1825 0 -560 561 1470 0 -561 560 800 0 -1470 561 1645 0 -1470 1469 6349 0 -1645 677 6347 0 -677 1645 561 0 -677 801 6347 0 -461 1645 1644 0 -561 933 677 0 -1470 1645 6348 0 -460 6168 6167 0 -460 1469 1468 0 -460 374 6168 0 -6167 1469 460 0 -6348 461 1468 0 -461 6348 1645 0 -460 1468 374 0 -6348 1468 1469 0 -1469 1470 6348 0 -1468 461 374 0 -1645 6347 1644 0 -6349 560 1470 0 -6346 562 678 0 -6347 801 562 0 -678 802 563 0 -6346 678 563 0 -679 2018 803 0 -679 563 802 0 -803 2018 935 0 -563 679 565 0 -1825 678 562 0 -564 565 679 0 -1643 1467 1644 0 -1467 461 1644 0 -6346 1644 562 0 -1824 1644 6346 0 -1824 565 6345 0 -1824 6346 563 0 -462 1467 1643 0 -6345 1643 1824 0 -6345 565 1823 0 -1824 1643 1644 0 -563 565 1824 0 -1467 374 461 0 -564 679 803 0 -1644 6347 562 0 -679 802 2018 0 -1466 1643 6345 0 -935 2019 1222 0 -800 1471 932 0 -1826 6252 6251 0 -459 6168 373 0 -1009 370 371 0 -370 1010 456 0 -1151 370 456 0 -1009 294 870 0 -457 371 370 0 -457 1151 458 0 -295 371 1150 0 -457 370 1151 0 -6351 1151 456 0 -1009 371 294 0 -294 869 6415 0 -869 294 1008 0 -6415 869 868 0 -6415 870 294 0 -1008 6414 869 0 -869 6414 868 0 -1008 294 371 0 -1007 1008 295 0 -371 295 1008 0 -6415 868 230 0 -371 457 1150 0 -230 740 6415 0 -457 372 1150 0 -295 1150 1149 0 -372 458 1300 0 -296 1150 372 0 -1300 373 1299 0 -1300 459 373 0 -1298 296 1299 0 -1300 1299 372 0 -6351 458 1151 0 -296 372 1299 0 -1006 6414 1007 0 -1008 1007 6414 0 -295 1149 1007 0 -6413 1007 1149 0 -1148 6413 1149 0 -1298 1148 296 0 -6412 1006 6413 0 -1006 1007 6413 0 -6412 6413 1147 0 -1149 296 1148 0 -296 1149 1150 0 -868 6414 231 0 -1147 6413 1148 0 -457 458 372 0 -1300 458 1301 0 -231 6414 1006 0 -176 739 738 0 -739 740 230 0 -867 739 230 0 -616 176 737 0 -739 867 738 0 -867 230 231 0 -867 231 1005 0 -738 867 232 0 -230 868 231 0 -176 738 737 0 -6466 6301 6300 0 -6300 614 6466 0 -6302 6301 6466 0 -615 614 6300 0 -737 177 615 0 -615 177 614 0 -615 616 737 0 -233 737 232 0 -738 232 737 0 -6466 614 613 0 -867 866 232 0 -136 6301 505 0 -6411 233 232 0 -1004 6411 866 0 -233 6411 865 0 -233 177 737 0 -866 1005 1004 0 -6411 1004 865 0 -1005 866 867 0 -864 233 865 0 -1006 1005 231 0 -234 233 864 0 -614 178 613 0 -177 233 736 0 -614 736 178 0 -614 177 736 0 -179 735 734 0 -735 178 736 0 -178 179 613 0 -179 178 735 0 -735 234 734 0 -736 233 735 0 -234 735 233 0 -6302 6466 613 0 -6412 1004 1005 0 -232 866 6411 0 -1006 6412 1005 0 -179 612 613 0 -297 1299 373 0 -176 617 739 0 -1299 297 1298 0 -373 374 6169 0 -373 6169 297 0 -1298 297 6170 0 -375 6170 297 0 -375 6169 374 0 -6170 375 1465 0 -375 297 6169 0 -374 462 375 0 -1298 6170 1297 0 -1146 1147 1296 0 -1147 1148 1297 0 -1296 1147 1297 0 -1146 1004 6412 0 -1146 1296 299 0 -1296 1297 298 0 -6171 1296 298 0 -299 1296 1295 0 -1297 6170 298 0 -1146 299 1004 0 -298 6170 6171 0 -1146 6412 1147 0 -1465 463 376 0 -1465 375 1466 0 -376 6171 6170 0 -376 6170 1465 0 -463 1464 376 0 -1466 462 1643 0 -1464 463 464 0 -463 1465 1466 0 -1466 6344 463 0 -376 1464 6171 0 -377 300 1296 0 -1296 6171 377 0 -377 6171 378 0 -1296 300 1295 0 -378 1463 6172 0 -465 1463 378 0 -6173 300 6172 0 -300 377 6172 0 -1294 1295 300 0 -6172 377 378 0 -6171 1464 378 0 -6173 6172 1463 0 -378 1464 464 0 -462 1466 375 0 -6168 374 373 0 -299 1295 1145 0 -865 6410 864 0 -865 1003 6410 0 -863 864 6410 0 -863 234 864 0 -1002 1003 1144 0 -1145 1003 1004 0 -1001 1002 1143 0 -1003 1002 6410 0 -299 1145 1004 0 -863 6410 1002 0 -734 863 235 0 -734 234 863 0 -235 863 862 0 -734 235 733 0 -235 862 733 0 -862 863 1001 0 -862 1001 6409 0 -733 862 861 0 -863 1002 1001 0 -734 733 179 0 -1144 1003 1145 0 -865 1004 1003 0 -301 1144 1294 0 -1294 1144 1145 0 -1294 1145 1295 0 -1293 1294 6173 0 -1292 1142 301 0 -1293 301 1294 0 -6174 1293 6173 0 -1293 1292 301 0 -6174 6173 379 0 -1144 301 1143 0 -861 6409 1000 0 -1143 1142 1001 0 -6409 1001 1000 0 -6409 861 862 0 -999 1000 302 0 -1000 1001 1142 0 -860 6408 999 0 -999 6408 1000 0 -302 1000 1142 0 -1000 6408 861 0 -1143 301 1142 0 -733 861 236 0 -6174 6175 1293 0 -1143 1002 1144 0 -6173 1294 300 0 -6408 732 861 0 -1463 379 6173 0 -1148 1298 1297 0 -462 374 1467 0 -612 179 733 0 -1466 6345 6344 0 -6259 1373 1720 0 -2216 1222 2019 0 -2216 6250 6249 0 -1222 2216 6249 0 -936 935 1222 0 -1222 1223 1076 0 -2430 1223 1222 0 -2430 1222 6249 0 -1076 1223 2215 0 -1386 2431 6249 0 -1222 1076 936 0 -936 804 2017 0 -804 936 1077 0 -682 2017 804 0 -936 2017 680 0 -1223 1077 2215 0 -937 1077 2214 0 -1077 937 804 0 -1077 936 1076 0 -1076 2215 1077 0 -2016 804 937 0 -1077 1223 2214 0 -680 803 936 0 -2430 6248 2429 0 -2430 6249 6248 0 -2429 6248 2428 0 -2429 2214 1223 0 -6247 2428 6248 0 -2431 1387 6247 0 -1225 2428 6246 0 -6247 6248 2431 0 -6249 2431 6248 0 -2429 2428 1224 0 -2429 938 1078 0 -1078 2214 2429 0 -937 1078 2016 0 -2214 1078 937 0 -1079 1224 939 0 -1225 1224 2428 0 -938 1079 2213 0 -1224 1225 939 0 -1080 941 2427 0 -1224 1079 938 0 -1224 938 2429 0 -1078 938 805 0 -2428 6247 6246 0 -2429 1223 2430 0 -6250 1385 6249 0 -1225 1080 939 0 -566 564 680 0 -564 803 680 0 -2017 682 566 0 -680 2017 566 0 -2016 805 681 0 -2016 1078 805 0 -684 682 681 0 -2016 681 682 0 -804 2016 682 0 -2015 682 684 0 -567 1822 1823 0 -1822 6345 1823 0 -566 1823 564 0 -2015 1823 566 0 -2014 567 685 0 -2015 567 1823 0 -2015 685 567 0 -1822 567 6343 0 -566 682 2015 0 -6343 567 2014 0 -684 681 805 0 -1822 6344 6345 0 -2213 807 683 0 -807 2213 806 0 -2212 683 807 0 -683 684 805 0 -809 806 939 0 -806 2213 1079 0 -2427 939 1080 0 -807 806 810 0 -938 2213 805 0 -939 2427 809 0 -2212 685 2015 0 -684 683 2212 0 -685 2212 2013 0 -2212 2015 684 0 -686 810 2211 0 -2212 807 810 0 -687 2013 686 0 -2013 2212 686 0 -810 809 2211 0 -686 2212 810 0 -806 809 810 0 -685 2013 2014 0 -809 2427 808 0 -683 805 2213 0 -939 806 1079 0 -2014 2013 568 0 -940 1080 1225 0 -1221 6250 2216 0 -1226 6246 5980 0 -1225 1226 1081 0 -5980 6246 1389 0 -2426 1226 5980 0 -5980 6245 2426 0 -6245 1389 5981 0 -1227 6245 2425 0 -6245 5980 1389 0 -6247 1388 6246 0 -1227 2426 6245 0 -940 941 1080 0 -1225 1081 940 0 -943 940 1081 0 -944 941 940 0 -2426 1082 1083 0 -1082 2426 1227 0 -1086 1083 1082 0 -942 1081 2426 0 -1081 1226 2426 0 -2426 1083 942 0 -1082 1227 1085 0 -941 808 2427 0 -5981 1390 6244 0 -1390 5981 1389 0 -6244 1390 2651 0 -6244 2425 6245 0 -1392 2651 2838 0 -1391 2651 1390 0 -1392 2650 5982 0 -2651 1392 6244 0 -1390 2652 1391 0 -6244 1392 5982 0 -2425 1229 1084 0 -1229 2425 1228 0 -1087 1084 1229 0 -1227 2425 1085 0 -1231 1228 5982 0 -1228 2425 6244 0 -1234 1230 1233 0 -6243 1230 5982 0 -6243 5982 2650 0 -1229 1228 1232 0 -6244 5982 1228 0 -5982 1230 1231 0 -1230 6243 1233 0 -6244 6245 5981 0 -1389 2653 1390 0 -1085 2425 1084 0 -941 811 808 0 -941 944 811 0 -2210 811 944 0 -808 811 2211 0 -946 943 942 0 -944 940 943 0 -942 1083 945 0 -944 943 2210 0 -1081 942 943 0 -942 945 946 0 -2211 812 2012 0 -2211 811 812 0 -688 2012 812 0 -2211 2012 686 0 -946 945 2210 0 -812 811 2210 0 -947 2210 1089 0 -2210 947 812 0 -943 946 2210 0 -947 2011 812 0 -2210 945 1089 0 -808 2211 809 0 -1089 1086 1085 0 -945 1083 1086 0 -1084 1087 1088 0 -1085 1084 1088 0 -2424 1232 1231 0 -1087 1229 1232 0 -1234 1231 1230 0 -1087 1232 1090 0 -1082 1085 1086 0 -1234 2424 1231 0 -1089 1088 2209 0 -1088 1089 1085 0 -2209 1088 1090 0 -2209 947 1089 0 -2424 1235 1090 0 -1235 2424 1234 0 -2209 1090 948 0 -2208 1090 1235 0 -2208 1235 1237 0 -1090 1088 1087 0 -1232 2424 1090 0 -947 2209 948 0 -1091 2208 1238 0 -1089 945 1086 0 -1231 1232 1228 0 -1090 2208 948 0 -2838 2651 1391 0 -1225 6246 1226 0 -6251 2020 6250 0 -687 686 2012 0 -1642 464 463 0 -1642 6344 6343 0 -464 1642 6343 0 -465 378 464 0 -1641 6343 1821 0 -6343 6344 1822 0 -6343 2014 1821 0 -6343 1641 464 0 -2013 687 568 0 -1821 569 1641 0 -379 465 1462 0 -1463 465 379 0 -1462 465 6342 0 -6175 379 1462 0 -1462 1461 466 0 -569 6342 1641 0 -6342 1461 1462 0 -6342 465 1641 0 -464 1641 465 0 -6175 1462 466 0 -569 1821 568 0 -6174 379 6175 0 -570 569 1820 0 -1820 569 568 0 -687 1820 568 0 -570 1820 687 0 -689 570 1819 0 -1819 570 687 0 -1819 687 2012 0 -689 1819 688 0 -2012 688 1819 0 -569 570 1640 0 -6342 6341 1461 0 -6342 1640 6341 0 -6341 1640 1639 0 -1461 6341 1638 0 -1639 689 571 0 -1639 1640 570 0 -6340 1638 572 0 -571 1638 1639 0 -571 689 1817 0 -1639 1638 6341 0 -570 689 1639 0 -466 1461 6176 0 -689 688 1818 0 -1640 6342 569 0 -1821 2014 568 0 -6340 1461 1638 0 -1292 6175 380 0 -1292 1293 6175 0 -380 6176 1291 0 -1292 380 1291 0 -1291 6176 381 0 -1290 381 6177 0 -381 1290 1140 0 -1291 302 1142 0 -6175 466 380 0 -1141 1291 381 0 -1141 303 999 0 -1141 302 1291 0 -303 1141 1140 0 -303 998 999 0 -997 998 1140 0 -1140 998 303 0 -1140 1141 381 0 -382 997 1140 0 -1290 382 1140 0 -999 302 1141 0 -1290 468 382 0 -1142 1292 1291 0 -467 6177 381 0 -467 6176 1460 0 -6177 467 6178 0 -468 1290 6177 0 -6339 6178 1460 0 -1460 6178 467 0 -6340 1460 6176 0 -1459 6339 1460 0 -466 6176 380 0 -6177 6178 468 0 -469 383 1139 0 -1139 997 382 0 -382 468 1289 0 -1139 382 1289 0 -1289 6339 1288 0 -6339 1289 468 0 -470 383 469 0 -1288 469 1289 0 -1288 6339 6179 0 -1289 469 1139 0 -468 6178 6339 0 -1288 1287 469 0 -1459 1460 6340 0 -6176 467 381 0 -1461 6340 6176 0 -860 999 998 0 -571 1637 1638 0 -6344 1642 463 0 -813 1818 688 0 -2010 813 2011 0 -2009 1818 813 0 -689 1818 690 0 -690 1818 2009 0 -2010 814 813 0 -948 2010 2011 0 -815 1817 690 0 -2011 947 948 0 -690 1817 689 0 -1638 1637 572 0 -1817 691 571 0 -571 691 1637 0 -572 1637 692 0 -691 1636 692 0 -1816 691 1817 0 -1635 692 1636 0 -691 692 1637 0 -691 816 1636 0 -1459 572 692 0 -813 814 2009 0 -688 2011 813 0 -2009 950 815 0 -2009 815 690 0 -814 2010 1091 0 -2009 814 949 0 -1091 2008 949 0 -2008 1091 1238 0 -950 2009 949 0 -949 814 1091 0 -2010 948 1091 0 -950 949 2008 0 -816 951 817 0 -951 816 1816 0 -817 1636 816 0 -816 691 1816 0 -950 1815 951 0 -1093 1815 950 0 -1095 1814 952 0 -951 952 817 0 -1094 952 951 0 -950 951 1816 0 -1816 815 950 0 -817 1635 1636 0 -950 2008 1092 0 -1817 815 1816 0 -948 2208 1091 0 -953 817 952 0 -6179 1459 573 0 -6179 6339 1459 0 -573 1459 693 0 -574 6179 573 0 -693 1635 694 0 -1635 693 692 0 -694 695 1458 0 -573 693 1458 0 -1459 692 693 0 -694 1458 693 0 -6337 6338 6180 0 -6338 6179 574 0 -6180 6338 574 0 -6338 6337 1288 0 -6180 1458 575 0 -1458 6180 574 0 -6337 575 6181 0 -695 575 1458 0 -575 695 1456 0 -6180 575 6337 0 -573 1458 574 0 -1456 1455 575 0 -694 1635 819 0 -6179 6338 1288 0 -819 818 1634 0 -817 818 1635 0 -1634 818 953 0 -819 1457 694 0 -953 954 1634 0 -954 953 1814 0 -1633 954 1813 0 -1634 954 1633 0 -817 953 818 0 -819 1634 820 0 -696 695 1632 0 -695 694 1457 0 -820 695 1457 0 -695 696 1456 0 -820 1633 1632 0 -820 1457 819 0 -697 1631 1810 0 -1631 696 1632 0 -1631 1632 821 0 -1632 695 820 0 -1634 1633 820 0 -696 1631 697 0 -1813 954 1814 0 -1635 818 819 0 -952 1814 953 0 -6337 1287 1288 0 -951 1815 1094 0 -1459 6340 572 0 -688 812 2011 0 -997 1139 383 0 -1235 1234 1237 0 -565 564 1823 0 -6246 1388 1389 0 -1456 696 576 0 -505 504 137 0 -504 505 6302 0 -412 137 504 0 -137 412 413 0 -504 6303 503 0 -6302 613 6465 0 -502 138 503 0 -6465 504 6302 0 -505 6301 6302 0 -138 504 503 0 -411 410 101 0 -411 412 138 0 -410 411 501 0 -102 328 410 0 -410 501 102 0 -502 501 411 0 -138 502 411 0 -102 501 139 0 -412 504 138 0 -102 103 328 0 -6303 504 6465 0 -412 411 101 0 -6464 6303 6304 0 -612 6303 6465 0 -611 6303 612 0 -502 503 6464 0 -6303 180 6304 0 -180 6303 611 0 -6305 180 610 0 -6304 180 6305 0 -733 611 612 0 -6464 6304 502 0 -500 139 6306 0 -501 502 6463 0 -139 501 6463 0 -102 139 409 0 -6463 6305 6306 0 -6463 502 6304 0 -6462 500 6306 0 -6307 6462 6306 0 -6308 6462 6307 0 -6306 139 6463 0 -6304 6305 6463 0 -500 6462 6308 0 -180 611 610 0 -6464 503 6303 0 -613 612 6465 0 -409 139 500 0 -408 142 407 0 -103 102 408 0 -142 408 141 0 -407 142 143 0 -141 498 142 0 -141 409 499 0 -498 6459 497 0 -141 408 409 0 -102 409 408 0 -142 498 497 0 -6457 496 497 0 -144 143 497 0 -496 144 497 0 -6312 6456 6457 0 -6458 6311 6457 0 -497 143 142 0 -6310 6311 6458 0 -6458 6457 497 0 -497 6459 6458 0 -6312 6457 6311 0 -498 141 6460 0 -103 408 104 0 -6460 499 6461 0 -140 499 409 0 -6461 499 140 0 -6460 6459 498 0 -604 6309 6461 0 -6461 6309 6460 0 -6308 6461 140 0 -605 604 6461 0 -409 500 140 0 -6460 6309 6310 0 -6310 187 6311 0 -6459 6460 6310 0 -187 6310 603 0 -6310 6458 6459 0 -186 604 603 0 -6310 6309 186 0 -244 602 603 0 -602 187 603 0 -604 724 603 0 -603 6310 186 0 -6309 604 186 0 -188 6311 187 0 -184 6461 6308 0 -6460 141 499 0 -500 6308 140 0 -188 187 602 0 -608 6306 6305 0 -6457 6456 496 0 -610 236 732 0 -236 610 611 0 -236 861 732 0 -610 732 237 0 -729 609 237 0 -237 609 610 0 -731 237 732 0 -730 729 237 0 -732 860 731 0 -610 609 181 0 -6306 608 6307 0 -608 6305 181 0 -181 609 608 0 -6307 608 182 0 -608 607 182 0 -238 607 608 0 -238 608 729 0 -6307 182 606 0 -609 729 608 0 -607 606 182 0 -237 731 730 0 -610 181 6305 0 -6407 730 731 0 -6407 860 859 0 -730 6407 858 0 -6406 729 730 0 -859 304 858 0 -859 860 998 0 -305 857 858 0 -859 858 6407 0 -304 859 998 0 -730 858 6406 0 -728 239 607 0 -728 238 6406 0 -239 728 855 0 -728 607 238 0 -857 6405 728 0 -857 6406 858 0 -855 6404 854 0 -855 728 6405 0 -6405 857 856 0 -728 6406 857 0 -729 6406 238 0 -239 855 854 0 -855 6405 6404 0 -731 860 6407 0 -733 236 611 0 -727 607 239 0 -184 606 605 0 -606 184 6307 0 -605 606 726 0 -604 605 185 0 -605 726 725 0 -726 606 183 0 -726 183 727 0 -242 725 852 0 -607 183 606 0 -605 725 185 0 -723 724 243 0 -603 724 723 0 -243 724 851 0 -6399 723 243 0 -242 851 724 0 -724 725 242 0 -990 851 242 0 -724 185 725 0 -185 724 604 0 -850 243 851 0 -241 725 726 0 -6461 184 605 0 -241 726 853 0 -854 726 727 0 -726 854 240 0 -852 241 853 0 -854 992 240 0 -6402 240 992 0 -240 6402 853 0 -853 726 240 0 -727 239 854 0 -991 853 6402 0 -990 242 6401 0 -852 853 6401 0 -990 989 851 0 -242 852 6401 0 -312 990 1131 0 -991 990 6401 0 -989 312 988 0 -312 989 990 0 -1132 990 991 0 -989 6400 851 0 -853 991 6401 0 -6400 989 849 0 -312 1131 988 0 -852 725 241 0 -607 727 183 0 -244 603 723 0 -997 858 304 0 -184 6308 6307 0 -732 6408 860 0 -851 6400 850 0 -404 191 494 0 -191 6455 6312 0 -6438 191 6312 0 -494 191 6438 0 -250 6312 322 0 -6312 250 6438 0 -322 6312 403 0 -6438 250 494 0 -6456 6312 6455 0 -494 250 322 0 -322 493 494 0 -493 322 403 0 -493 403 189 0 -6313 494 493 0 -189 188 190 0 -6439 190 601 0 -190 6439 493 0 -189 190 493 0 -403 6312 189 0 -249 493 6439 0 -188 189 6312 0 -191 404 6455 0 -602 245 601 0 -601 188 602 0 -246 492 601 0 -601 249 6439 0 -245 722 600 0 -722 245 244 0 -244 723 722 0 -600 722 6397 0 -6312 6311 188 0 -245 246 601 0 -249 321 6313 0 -249 601 321 0 -6313 321 402 0 -249 6313 493 0 -402 6314 6313 0 -6314 402 492 0 -6314 492 246 0 -492 402 601 0 -321 601 402 0 -6397 6396 600 0 -600 247 245 0 -6313 6314 599 0 -6397 6398 720 0 -601 190 188 0 -602 244 245 0 -494 6313 2264 0 -248 600 320 0 -401 320 600 0 -248 599 6314 0 -246 247 6314 0 -599 320 401 0 -600 491 401 0 -6315 600 6396 0 -599 401 491 0 -248 247 600 0 -248 320 599 0 -491 598 599 0 -598 491 6315 0 -598 6315 6396 0 -719 599 598 0 -6395 720 319 0 -6395 6396 6397 0 -6395 319 598 0 -6396 6395 598 0 -491 600 6315 0 -719 598 319 0 -246 245 247 0 -248 6314 247 0 -400 490 719 0 -490 400 720 0 -719 490 6316 0 -400 719 319 0 -718 6316 597 0 -720 6316 490 0 -597 6316 720 0 -6316 718 719 0 -400 319 720 0 -846 719 718 0 -317 318 718 0 -317 597 720 0 -318 317 316 0 -318 399 718 0 -399 846 718 0 -318 847 399 0 -6317 846 489 0 -6317 489 847 0 -847 489 399 0 -399 489 846 0 -6317 847 596 0 -718 597 317 0 -720 316 317 0 -846 6317 596 0 -6397 720 6395 0 -599 719 2264 0 -6398 6397 722 0 -6313 599 2264 0 -6398 6399 721 0 -6398 722 6399 0 -721 6399 314 0 -721 315 720 0 -721 314 315 0 -849 314 6399 0 -849 6399 850 0 -847 315 848 0 -723 6399 722 0 -720 315 316 0 -846 717 845 0 -846 596 717 0 -845 717 397 0 -398 846 845 0 -847 397 717 0 -847 316 315 0 -396 397 847 0 -717 596 847 0 -847 318 316 0 -398 845 397 0 -848 315 314 0 -720 6398 721 0 -395 848 313 0 -849 848 314 0 -313 848 849 0 -848 395 396 0 -313 988 987 0 -313 849 989 0 -987 394 986 0 -313 987 395 0 -850 6400 849 0 -395 987 986 0 -985 986 485 0 -986 985 396 0 -486 985 485 0 -397 396 398 0 -484 1126 485 0 -484 986 394 0 -1126 1127 6322 0 -1127 1126 484 0 -484 394 1127 0 -484 485 986 0 -396 395 986 0 -396 985 398 0 -1129 987 988 0 -848 396 847 0 -988 313 989 0 -487 485 1126 0 -488 6318 846 0 -6318 488 985 0 -6318 985 595 0 -984 846 6318 0 -984 595 716 0 -716 595 985 0 -716 985 844 0 -6318 595 984 0 -486 844 985 0 -716 844 984 0 -984 983 486 0 -983 984 844 0 -486 983 844 0 -984 486 487 0 -487 6319 984 0 -6319 1126 594 0 -594 715 984 0 -1126 6319 487 0 -1126 715 594 0 -6319 594 984 0 -487 486 485 0 -984 715 1125 0 -398 985 488 0 -488 846 398 0 -1125 843 982 0 -843 1126 982 0 -982 1126 1124 0 -1125 982 1124 0 -1124 6321 1125 0 -1124 1126 6321 0 -6321 6322 6320 0 -1125 6321 6320 0 -715 1126 843 0 -843 1125 715 0 -593 714 1125 0 -6320 6322 593 0 -714 593 1274 0 -593 1125 6320 0 -1272 1125 981 0 -981 842 1274 0 -1123 1272 981 0 -1273 1272 1123 0 -981 1274 1123 0 -981 714 842 0 -1274 842 714 0 -981 1125 714 0 -6322 1274 593 0 -6322 6321 1126 0 -6322 1127 483 0 -6399 243 850 0 -6402 1132 991 0 -137 413 505 0 -305 1138 996 0 -384 305 997 0 -305 996 856 0 -305 856 857 0 -996 385 995 0 -1138 384 470 0 -385 996 1138 0 -1138 305 384 0 -383 384 997 0 -996 995 306 0 -6404 994 993 0 -6404 6405 994 0 -6404 993 992 0 -6404 992 854 0 -995 994 306 0 -994 995 307 0 -994 307 993 0 -306 994 856 0 -856 996 306 0 -1134 993 307 0 -995 1136 307 0 -305 858 997 0 -1138 1137 385 0 -1137 1138 470 0 -1137 470 471 0 -1285 385 1137 0 -1286 471 470 0 -1286 1287 6182 0 -1285 471 6183 0 -1286 470 1287 0 -383 470 384 0 -1285 1137 471 0 -1135 1136 386 0 -1135 307 1136 0 -386 1136 1285 0 -1135 386 1284 0 -1284 1283 1135 0 -472 386 1285 0 -1283 387 6187 0 -387 1283 1284 0 -387 6186 6187 0 -472 1284 386 0 -1136 385 1285 0 -1134 307 1135 0 -6186 387 1284 0 -1136 995 385 0 -997 304 998 0 -308 1135 1283 0 -6402 1133 1132 0 -992 993 6403 0 -1133 992 6403 0 -311 990 1132 0 -310 1133 1281 0 -6403 1282 1133 0 -1134 1282 6403 0 -1133 310 1132 0 -993 1134 6403 0 -1132 310 1280 0 -393 1131 1130 0 -1131 990 311 0 -1130 1131 311 0 -988 393 1129 0 -311 392 1130 0 -392 311 1280 0 -1279 392 1280 0 -1129 393 1130 0 -311 1132 1280 0 -1278 1130 392 0 -1280 310 391 0 -992 1133 6402 0 -1133 1282 1281 0 -1282 1134 308 0 -308 1283 1282 0 -1281 1282 309 0 -309 6187 6188 0 -6187 309 1282 0 -390 6189 6188 0 -1281 309 6189 0 -1283 6187 1282 0 -6189 309 6188 0 -6192 480 1278 0 -1280 391 1279 0 -1278 392 1279 0 -1130 1278 1129 0 -1279 6190 6191 0 -391 6190 1279 0 -479 6191 1445 0 -6191 479 1279 0 -6190 391 6189 0 -1279 479 1278 0 -310 6189 391 0 -479 6192 1278 0 -6188 6187 388 0 -310 1281 6189 0 -1134 1135 308 0 -480 481 1278 0 -1286 6183 471 0 -393 988 1131 0 -6182 6336 6183 0 -6336 6182 6181 0 -6183 472 1285 0 -6183 1286 6182 0 -6336 1455 1454 0 -1455 6336 6181 0 -6335 1454 1629 0 -6336 1454 6183 0 -575 1455 6181 0 -6183 1454 1453 0 -6184 473 1284 0 -6184 472 1453 0 -473 6184 6185 0 -1284 473 6186 0 -6185 1453 1452 0 -6335 1453 1454 0 -1452 1453 6335 0 -1453 6185 6184 0 -472 6183 1453 0 -473 6185 1451 0 -1454 1455 1629 0 -6182 1287 6181 0 -576 1630 1629 0 -576 696 1630 0 -1629 1630 577 0 -576 1629 1455 0 -577 697 1809 0 -697 577 1630 0 -578 1809 698 0 -1629 577 1628 0 -696 697 1630 0 -1628 577 1809 0 -1451 6334 1627 0 -6334 6185 1452 0 -1627 6334 1452 0 -6334 1451 6185 0 -578 1627 1452 0 -578 1628 1809 0 -1625 6333 1626 0 -6333 1451 1627 0 -1627 578 1808 0 -1452 1628 578 0 -1452 1629 1628 0 -473 1451 474 0 -6333 1627 1626 0 -1452 6335 1629 0 -1456 576 1455 0 -1450 1451 6333 0 -388 1450 1449 0 -474 388 6186 0 -1448 388 1449 0 -6188 388 1448 0 -389 1449 1623 0 -1450 388 474 0 -474 1451 1450 0 -1447 1448 389 0 -473 474 6186 0 -1448 1449 389 0 -390 1448 6190 0 -390 6188 1448 0 -1446 6190 1447 0 -478 6191 6190 0 -478 1446 1445 0 -1446 478 6190 0 -1445 1446 6328 0 -1445 6191 478 0 -1447 6190 1448 0 -6190 6189 390 0 -475 1449 1450 0 -6187 6186 388 0 -476 1623 475 0 -1623 1447 389 0 -475 1450 1624 0 -476 475 1624 0 -476 1624 6331 0 -6332 1624 1625 0 -1805 6332 1625 0 -6329 1800 1619 0 -1450 1625 1624 0 -1623 476 477 0 -1620 1446 477 0 -1447 1623 477 0 -1446 1620 6328 0 -477 1446 1447 0 -1621 6329 1620 0 -1801 6329 1621 0 -1619 1620 6329 0 -1620 1619 6328 0 -1619 1799 585 0 -1621 1620 477 0 -476 1621 477 0 -1445 6328 1444 0 -476 1622 1621 0 -1623 1449 475 0 -1625 1450 6333 0 -6327 479 1445 0 -2001 1809 697 0 -472 6184 1284 0 -470 469 1287 0 -1619 1444 6328 0 -1128 394 987 0 -987 1129 1128 0 -1128 1129 481 0 -394 1128 482 0 -1128 1276 482 0 -1276 1128 481 0 -1276 481 6325 0 -482 1276 6324 0 -1278 481 1129 0 -482 483 1127 0 -1274 6323 590 0 -1275 1274 483 0 -591 1274 590 0 -1274 6322 483 0 -1275 6324 6323 0 -1275 483 482 0 -590 6323 589 0 -1275 6323 1274 0 -482 6324 1275 0 -6195 6323 6324 0 -1276 588 6324 0 -1123 1274 1273 0 -6325 6193 6194 0 -6193 6325 1277 0 -6325 6194 588 0 -6325 588 1276 0 -1277 6326 6193 0 -1277 480 6326 0 -6326 1443 6193 0 -6194 6193 587 0 -480 1277 481 0 -6194 1442 588 0 -6196 589 710 0 -590 589 6196 0 -710 589 1441 0 -711 6196 710 0 -6195 588 1442 0 -589 6323 6195 0 -837 1440 709 0 -1440 710 1441 0 -6195 1442 1441 0 -1441 589 6195 0 -6195 6324 588 0 -712 710 1440 0 -6326 6192 6327 0 -6325 481 1277 0 -6192 6326 480 0 -713 590 6196 0 -1273 591 592 0 -1273 1274 591 0 -592 590 713 0 -1273 592 713 0 -6196 980 841 0 -711 6197 6196 0 -841 980 1273 0 -713 841 1273 0 -841 713 6196 0 -1273 980 1271 0 -1122 6198 1271 0 -1122 6196 6198 0 -1271 6198 6197 0 -1122 1271 980 0 -6197 711 712 0 -6197 6198 6196 0 -711 710 712 0 -6197 712 840 0 -980 6196 1122 0 -1271 6197 1273 0 -592 591 590 0 -979 1121 6197 0 -840 1440 979 0 -979 1440 1121 0 -1121 1270 6197 0 -6199 1270 1438 0 -1270 1121 1440 0 -1270 1440 1438 0 -6199 6197 1270 0 -712 1440 840 0 -840 979 6197 0 -1439 1438 1440 0 -1440 838 1439 0 -1439 838 839 0 -1438 1439 6199 0 -839 978 1439 0 -839 837 978 0 -1269 1439 1120 0 -839 838 837 0 -978 837 1120 0 -1120 1439 978 0 -1120 1614 1269 0 -1439 1269 6200 0 -1614 1120 837 0 -6199 1439 6197 0 -1440 837 838 0 -709 1440 1441 0 -394 482 1127 0 -6193 1443 587 0 -587 1442 6194 0 -1617 1616 587 0 -1616 1442 587 0 -1444 586 1443 0 -6327 1445 1444 0 -586 1444 1618 0 -1443 6326 1444 0 -1444 6326 6327 0 -587 1443 1617 0 -1614 1615 975 0 -1614 837 1615 0 -976 1614 975 0 -1614 6200 1269 0 -1615 1616 836 0 -1616 1615 709 0 -708 836 1616 0 -1615 836 975 0 -709 1442 1616 0 -975 836 1795 0 -1617 1443 586 0 -1442 709 1441 0 -586 1798 707 0 -1798 586 1618 0 -1618 1444 1619 0 -1798 1618 585 0 -1797 834 1796 0 -707 1797 1617 0 -707 1617 586 0 -707 1988 1797 0 -1798 585 1799 0 -1617 1797 708 0 -974 1794 1795 0 -708 1796 836 0 -1794 975 1795 0 -1794 974 1117 0 -1796 835 1795 0 -834 835 1796 0 -1795 973 974 0 -973 1795 835 0 -973 1985 974 0 -1795 836 1796 0 -708 1797 1796 0 -1986 1985 973 0 -706 707 1798 0 -708 1616 1617 0 -6192 479 6327 0 -1268 975 1794 0 -6200 1437 1612 0 -6200 1614 1437 0 -1437 1614 1612 0 -6200 1612 1439 0 -1613 976 977 0 -1613 1612 1614 0 -975 1119 977 0 -1613 977 1119 0 -1614 976 1613 0 -1612 1613 1439 0 -1794 6201 1268 0 -1268 1119 975 0 -6201 1436 1613 0 -1119 1268 1613 0 -1436 1611 1613 0 -1611 1794 1792 0 -1792 1793 1613 0 -1794 1611 1436 0 -1794 1793 1792 0 -1611 1792 1613 0 -1436 6201 1794 0 -6201 1613 1268 0 -976 975 977 0 -974 1267 1118 0 -1118 1117 974 0 -1118 1267 1793 0 -1117 1118 1793 0 -6202 1985 1435 0 -6202 1267 974 0 -1435 1985 1610 0 -1435 1610 1793 0 -1793 1794 1117 0 -1267 6202 1793 0 -1791 1983 1793 0 -1610 1985 1791 0 -1791 1985 1983 0 -1791 1793 1610 0 -1984 1265 1266 0 -1984 1983 1985 0 -1266 6203 1984 0 -1116 6203 1266 0 -1266 1265 1116 0 -1984 6203 1434 0 -1984 1985 1265 0 -1983 1984 1793 0 -1985 1116 1265 0 -1435 1793 6202 0 -974 1985 6202 0 -835 1986 973 0 -837 709 1615 0 -1618 1619 585 0 -6331 1624 6332 0 -856 994 6405 0 -6181 1287 6337 0 -1633 955 1632 0 -369 370 1009 0 -2650 6242 6241 0 -2650 1392 1393 0 -2650 6241 6240 0 -6240 6243 2650 0 -6242 2837 1396 0 -2837 2650 1393 0 -2650 2837 6242 0 -3023 1393 2838 0 -2838 1393 1392 0 -6241 6242 6237 0 -5983 1233 6240 0 -1233 6243 6240 0 -5983 6240 6241 0 -6237 5983 6241 0 -6238 6237 6242 0 -6242 6239 6238 0 -2649 6239 1396 0 -6235 6238 6239 0 -6242 1396 6239 0 -6234 6237 6238 0 -3023 2838 1564 0 -2838 1391 1564 0 -1394 3023 3211 0 -1565 3023 1564 0 -1566 3023 1565 0 -1394 2837 1393 0 -3211 1567 1394 0 -3211 1566 1745 0 -3022 1567 1746 0 -3211 3023 1566 0 -1566 3212 1745 0 -1394 1567 3022 0 -1395 1398 1399 0 -3022 1395 2837 0 -1396 1395 6236 0 -2837 1395 1396 0 -1400 2836 1568 0 -3022 1568 1397 0 -1398 1397 2836 0 -1398 1395 1397 0 -1568 2836 1397 0 -1397 1395 3022 0 -2837 1394 3022 0 -1399 1398 1402 0 -1568 3022 1570 0 -1394 1393 3023 0 -3212 1566 1565 0 -6236 1395 1399 0 -1237 5983 1236 0 -5983 1237 1233 0 -5983 6237 1236 0 -2423 1237 1236 0 -6238 6235 6234 0 -1236 6237 6234 0 -5984 6234 6235 0 -6233 6234 5984 0 -2649 6235 6239 0 -2423 1236 6234 0 -1092 1238 2207 0 -2423 1238 1237 0 -6233 1238 2423 0 -1092 2008 1238 0 -2207 6233 1239 0 -6233 2207 1238 0 -2422 1239 6233 0 -2207 1239 1092 0 -2423 6234 6233 0 -2007 1092 1239 0 -6236 6235 2649 0 -1238 2208 1237 0 -6235 1403 5984 0 -1403 6235 6236 0 -6236 1399 1403 0 -1404 5984 1403 0 -1402 2836 1401 0 -1403 1399 1402 0 -1400 1401 2836 0 -1403 1402 2648 0 -2649 1396 6236 0 -1402 1576 2648 0 -2422 6232 2206 0 -6232 2422 1404 0 -6231 2206 6232 0 -2422 2206 1239 0 -5985 1404 1577 0 -2648 1404 1403 0 -1405 2421 6232 0 -5985 1405 6232 0 -1577 1404 2648 0 -5985 6232 1404 0 -6233 1404 2422 0 -6231 6232 2421 0 -1401 1576 1402 0 -1404 6233 5984 0 -2836 1402 1398 0 -1240 1239 2206 0 -1746 1567 3211 0 -1092 1093 950 0 -1746 3210 3022 0 -3211 3409 1746 0 -3210 1570 3022 0 -1569 1570 3210 0 -1746 1747 3210 0 -1934 3409 1745 0 -1569 3210 1749 0 -3409 1747 1746 0 -3406 1935 1934 0 -3408 1749 3210 0 -1571 1400 1568 0 -1568 1570 1571 0 -1571 1570 3021 0 -1575 1400 1571 0 -1569 3021 1570 0 -3021 1569 1573 0 -1753 1572 1749 0 -1572 1573 1569 0 -1569 1749 1572 0 -1574 1571 3021 0 -1934 3407 3409 0 -3211 1745 3409 0 -3408 1747 1936 0 -3407 1747 3409 0 -1936 1747 3407 0 -3408 1748 1749 0 -3205 1936 3407 0 -3205 1935 2137 0 -3206 1936 2138 0 -3205 3407 1935 0 -3407 1934 1935 0 -3408 1936 3206 0 -1748 1751 3209 0 -1751 1748 1750 0 -1752 3209 1751 0 -1748 3209 1749 0 -3207 1937 1940 0 -3206 1937 1750 0 -1751 1750 1941 0 -3207 1750 1937 0 -1937 3206 1939 0 -1750 1748 3408 0 -3408 3206 1750 0 -3207 1941 1750 0 -1936 3205 2138 0 -3408 3210 1747 0 -1934 1745 3410 0 -3209 1753 1749 0 -1575 2835 1576 0 -1575 1576 1400 0 -1574 1575 1571 0 -1574 2835 1575 0 -1573 1754 1755 0 -1573 1572 1754 0 -1756 1755 3020 0 -1573 1755 1574 0 -1574 3021 1573 0 -1755 2835 1574 0 -2647 1405 1577 0 -5985 1577 1405 0 -2835 1577 1576 0 -1756 1577 2835 0 -2647 1756 1578 0 -1756 2647 1577 0 -2834 1578 1756 0 -1405 2647 5986 0 -2835 1755 1756 0 -2421 1405 1406 0 -1753 1755 1754 0 -2648 1576 1577 0 -1753 1752 1943 0 -1754 1572 1753 0 -1943 1752 1942 0 -1753 1943 3020 0 -1942 1941 2145 0 -1942 1752 1751 0 -2145 1941 2144 0 -1943 1942 1944 0 -1751 1941 1942 0 -1944 1942 2145 0 -2834 3019 1757 0 -2834 1756 1945 0 -2834 1757 1578 0 -1756 3020 1945 0 -3208 1945 3020 0 -3208 1944 2145 0 -1945 3019 2834 0 -1946 3019 1945 0 -1945 3208 1946 0 -3208 3020 1944 0 -3020 1943 1944 0 -2833 1757 1758 0 -2145 2146 3208 0 -3020 1755 1753 0 -3209 1752 1753 0 -1578 5986 2647 0 -2144 1941 3207 0 -1401 1400 1576 0 -1564 3024 1565 0 -2646 1578 1757 0 -1094 2006 1095 0 -2007 1094 1093 0 -1813 1814 1095 0 -952 1094 1095 0 -2006 1243 1096 0 -2007 1241 1094 0 -1095 2006 2005 0 -1094 1242 2006 0 -1092 2007 1093 0 -2005 2006 1096 0 -1632 956 1811 0 -955 1633 1813 0 -1812 955 1813 0 -1811 821 1632 0 -2005 1096 1812 0 -2005 1812 1813 0 -956 1812 2004 0 -1812 956 955 0 -1813 1095 2005 0 -1811 956 2003 0 -1812 1096 2004 0 -1815 1093 1094 0 -1242 1241 6230 0 -1240 1241 2007 0 -1241 1240 2206 0 -1241 1242 1094 0 -2420 6230 6229 0 -6230 1241 2206 0 -6229 6230 6231 0 -1242 6230 2205 0 -6230 2206 6231 0 -1243 6230 2420 0 -2203 2003 2004 0 -1096 2204 2004 0 -1097 2004 2204 0 -2003 956 2004 0 -1244 2204 1243 0 -2205 6230 1243 0 -2202 957 2003 0 -2419 1097 2204 0 -2420 1244 1243 0 -1096 1243 2204 0 -2205 1243 1242 0 -2004 1097 2203 0 -2419 2204 1244 0 -1242 1243 2006 0 -1239 1240 2007 0 -1811 2003 957 0 -2001 697 1810 0 -1810 821 822 0 -1809 2001 698 0 -698 1808 578 0 -698 2001 2000 0 -2001 1810 822 0 -1999 2001 823 0 -2000 2001 1999 0 -1811 822 821 0 -698 2000 1808 0 -579 1807 1626 0 -1807 579 1808 0 -1625 1626 1806 0 -1626 1627 579 0 -1808 699 1807 0 -1808 2000 699 0 -699 1999 824 0 -1806 1626 1807 0 -2000 1999 699 0 -700 1807 699 0 -2001 822 2002 0 -821 1810 1631 0 -823 2002 6124 0 -823 2001 2002 0 -6124 2002 958 0 -6124 824 1999 0 -2201 958 2002 0 -2202 2201 957 0 -958 2201 2200 0 -2201 2002 957 0 -822 957 2002 0 -6124 958 2200 0 -1807 700 1997 0 -699 824 700 0 -700 824 1998 0 -700 701 1997 0 -959 1998 824 0 -1100 959 2200 0 -1998 825 700 0 -960 825 1998 0 -1998 959 960 0 -959 824 2200 0 -824 6124 2200 0 -825 701 700 0 -1100 2200 2201 0 -1999 823 6124 0 -1811 957 822 0 -1806 1807 1997 0 -2419 1244 2420 0 -956 1632 955 0 -5987 6229 1406 0 -6229 6231 2421 0 -6229 2421 1406 0 -5987 1406 5986 0 -5987 1407 6228 0 -1407 5987 5986 0 -2644 5988 6228 0 -5987 5988 2420 0 -1578 1407 5986 0 -5988 5987 6228 0 -2419 1245 2203 0 -2419 2420 1245 0 -2203 1245 2418 0 -2419 2203 1097 0 -2418 5989 1246 0 -5988 6227 2418 0 -2203 2418 1098 0 -5988 2418 1245 0 -5988 1245 2420 0 -2417 2418 1246 0 -6228 1407 2645 0 -2420 6229 5987 0 -2645 2832 1408 0 -2645 1407 1579 0 -1408 2644 6228 0 -1579 1407 2646 0 -2832 1579 1580 0 -1579 2646 1757 0 -1580 1579 2833 0 -1579 2832 2645 0 -1757 2833 1579 0 -2831 1408 1409 0 -6227 5989 2418 0 -6227 2644 2831 0 -5989 6227 6226 0 -6227 5988 2644 0 -2643 6226 1411 0 -2831 6226 6227 0 -2642 6225 1412 0 -6225 5989 2643 0 -6226 2831 1411 0 -2643 5989 6226 0 -2644 1408 2831 0 -5989 6225 1246 0 -1408 2832 1409 0 -1408 6228 2645 0 -1578 2646 1407 0 -1246 6225 5990 0 -1099 2202 2417 0 -1098 2202 2203 0 -2417 2202 1098 0 -1099 1100 2201 0 -1100 1099 2416 0 -2416 1099 2417 0 -2416 2417 1247 0 -1101 2415 1102 0 -2418 2417 1098 0 -2415 1100 2416 0 -825 961 826 0 -825 960 961 0 -961 960 2199 0 -827 826 961 0 -1101 2199 960 0 -1101 1100 2415 0 -2199 962 961 0 -962 2199 1101 0 -962 1101 1102 0 -1101 960 1100 0 -959 1100 960 0 -962 827 961 0 -1248 2415 2416 0 -2201 2202 1099 0 -5991 1247 6224 0 -5990 1247 2417 0 -6224 1247 5990 0 -5991 1248 2416 0 -6224 2641 5991 0 -5991 2641 6223 0 -2642 6224 5990 0 -2640 5992 6223 0 -1246 5990 2417 0 -5991 6223 5992 0 -963 1102 2414 0 -1102 2415 1248 0 -5992 1102 1248 0 -1102 963 962 0 -5992 1249 2414 0 -2640 1249 5992 0 -2414 1103 963 0 -5993 1103 2414 0 -5993 2414 1249 0 -5992 2414 1102 0 -1248 5991 5992 0 -963 827 962 0 -2642 1413 6224 0 -2416 1247 5991 0 -6225 2642 5990 0 -1103 2413 963 0 -6225 2643 1412 0 -2203 2202 2003 0 -5986 1406 1405 0 -825 826 701 0 -1757 3019 1758 0 -1808 579 1627 0 -2138 3014 3206 0 -3205 3013 2138 0 -3206 3014 1939 0 -1938 1939 3014 0 -2138 2139 3014 0 -2346 3013 2137 0 -1938 3014 2141 0 -3013 2139 2138 0 -3203 2346 2137 0 -2814 2141 3014 0 -2143 1940 1939 0 -1940 1937 1939 0 -1939 1938 3015 0 -3015 2143 1939 0 -2352 2142 2141 0 -2142 3015 1938 0 -2140 2351 2141 0 -2352 3015 2142 0 -2141 2142 1938 0 -2351 6053 2141 0 -3013 2346 3012 0 -1940 2144 3207 0 -3013 3012 2139 0 -2346 3202 2347 0 -2346 2347 3012 0 -2348 2139 3012 0 -3011 2347 6058 0 -3201 2347 3202 0 -6058 2347 3201 0 -2347 3011 3012 0 -3202 6060 3201 0 -2348 3012 3011 0 -2815 2140 2350 0 -2140 2141 2814 0 -2350 2140 2814 0 -2140 2815 2351 0 -2813 2350 2814 0 -6057 2813 2348 0 -6052 2350 2589 0 -2349 2350 2813 0 -2589 2350 2349 0 -2813 2814 2348 0 -2814 2139 2348 0 -2351 2815 6054 0 -2348 3011 6057 0 -2814 3014 2139 0 -1935 3204 2137 0 -6054 2815 2350 0 -2144 2354 2146 0 -2354 2144 2143 0 -2354 3016 3208 0 -2354 3208 2146 0 -2353 2352 2355 0 -2353 2143 3015 0 -2354 2353 3016 0 -2143 2353 2354 0 -2144 1940 2143 0 -2355 3016 2353 0 -1758 3019 3017 0 -3019 1946 3017 0 -1946 3208 3016 0 -1947 3017 1946 0 -1759 3018 1758 0 -2817 1759 3017 0 -2818 3018 1759 0 -1759 1758 3017 0 -3016 1947 1946 0 -3018 1580 2833 0 -3016 2355 2147 0 -2833 1758 3018 0 -2147 2352 6053 0 -2352 2141 6053 0 -6053 2351 2356 0 -2816 6053 2356 0 -2148 6054 6052 0 -6052 6054 2350 0 -2589 2357 6052 0 -6054 2148 2356 0 -3015 2352 2353 0 -2590 2148 6052 0 -3016 2816 1947 0 -3016 2147 2816 0 -2147 6053 2816 0 -1947 2816 1948 0 -1948 2816 2590 0 -1948 2817 1947 0 -1760 1759 2817 0 -2591 1760 2817 0 -2591 2817 1948 0 -2590 2816 2148 0 -2356 2148 2816 0 -2817 3017 1947 0 -6052 2357 2590 0 -2147 2355 2352 0 -2356 2351 6054 0 -1760 2818 1759 0 -2349 2813 6055 0 -2137 3013 3205 0 -3004 6058 3200 0 -3010 3011 6058 0 -3007 3200 3003 0 -2804 3200 3007 0 -2804 3004 3200 0 -3004 2578 6058 0 -6058 2578 3010 0 -6055 2589 2349 0 -2813 6057 2812 0 -2812 6056 2813 0 -6055 2813 6056 0 -3010 2579 6057 0 -2812 6057 2579 0 -2812 2585 6056 0 -6056 2580 2588 0 -3010 6057 3011 0 -2582 6056 2588 0 -2578 2805 3010 0 -6058 3201 3200 0 -2805 3008 3010 0 -3008 2809 3010 0 -2808 3005 3010 0 -3005 2579 3010 0 -2809 2808 3010 0 -2811 2806 2812 0 -2806 2810 2812 0 -2807 2584 2812 0 -2585 2580 6056 0 -2584 2585 2812 0 -2810 2807 2812 0 -2579 2811 2812 0 -2588 2580 2587 0 -2149 6046 2590 0 -2590 2357 2149 0 -2149 2357 6055 0 -2582 2149 6055 0 -6051 2358 2149 0 -2582 2588 6051 0 -6051 2588 2358 0 -6051 2149 2582 0 -6056 2582 6055 0 -6046 2149 2358 0 -1951 1950 6045 0 -2591 1948 1949 0 -1949 6045 2591 0 -2591 1950 1760 0 -1949 2150 6045 0 -1950 2591 6045 0 -6046 2150 1949 0 -2360 2150 6046 0 -1949 2590 6046 0 -2592 1760 1950 0 -2358 2359 6046 0 -1949 1948 2590 0 -2588 2583 2586 0 -2588 2581 2583 0 -2588 2359 2358 0 -2586 2359 2588 0 -2586 6050 2359 0 -2359 6050 6049 0 -6047 6046 6048 0 -6048 2359 6049 0 -2360 6046 6047 0 -6045 2150 2360 0 -2361 2151 6045 0 -2360 2361 6045 0 -6045 2151 1951 0 -2359 6048 6046 0 -2588 2587 2581 0 -2592 1950 1951 0 -2357 2589 6055 0 -3201 6059 3200 0 -6044 1951 2151 0 -1410 1409 5816 0 -2832 1580 5816 0 -1409 2832 5816 0 -1410 5815 2831 0 -1410 1582 5815 0 -1581 5816 1580 0 -5816 1582 1410 0 -5815 2821 1583 0 -3018 1581 1580 0 -5815 1411 2831 0 -2643 2830 1412 0 -2643 1411 2830 0 -2830 1411 1583 0 -1412 2830 1584 0 -1412 1584 2829 0 -2822 1584 2830 0 -2822 2830 1583 0 -2823 2829 1584 0 -1411 5815 1583 0 -2829 2642 1412 0 -5816 1581 2820 0 -2829 1413 2642 0 -1761 2820 1581 0 -1761 2818 2592 0 -2820 1761 1762 0 -2820 1582 5816 0 -2819 1762 1952 0 -2592 1762 1761 0 -1952 1762 2592 0 -1762 2819 2820 0 -2818 1760 2592 0 -2820 2819 1763 0 -1764 1583 2821 0 -2821 1582 1763 0 -1583 1764 2822 0 -2821 5815 1582 0 -2595 1764 2821 0 -1953 2595 1763 0 -2822 2596 1765 0 -1764 2596 2822 0 -2823 1765 2597 0 -2595 2821 1763 0 -1582 2820 1763 0 -2822 1765 1584 0 -1763 2819 1953 0 -1581 2818 1761 0 -3018 2818 1581 0 -2823 1584 1765 0 -1414 2640 6223 0 -1414 2641 2828 0 -2640 1414 2827 0 -1414 6223 2641 0 -1414 1586 2827 0 -2828 1413 1585 0 -1586 1414 2828 0 -2828 2641 1413 0 -1413 2641 6224 0 -2640 2827 6222 0 -5993 1250 5994 0 -1250 5993 2639 0 -5994 1250 2638 0 -5993 5994 1103 0 -1250 6221 2638 0 -1415 2639 6222 0 -6221 1250 2639 0 -2639 5993 6222 0 -1249 6222 5993 0 -5994 2638 1251 0 -6222 2827 1415 0 -6222 1249 2640 0 -1586 2828 2824 0 -2827 1586 2825 0 -1585 1413 2829 0 -1766 1585 2823 0 -1767 2824 2598 0 -1766 2824 1585 0 -1766 2598 2824 0 -2824 1767 1586 0 -1766 2823 2597 0 -1586 1767 2825 0 -6221 2639 2826 0 -2639 1415 2826 0 -1415 2827 2825 0 -1587 1415 2825 0 -1416 2826 2600 0 -1587 2826 1415 0 -5909 1416 2601 0 -5909 6221 1416 0 -2600 2826 1587 0 -1416 6221 2826 0 -1587 2825 2599 0 -6221 5909 2638 0 -2599 2825 1767 0 -2828 1585 2824 0 -2823 1585 2829 0 -2638 5909 6220 0 -1954 1764 2595 0 -1410 2831 1409 0 -2819 2593 1953 0 -2593 2819 1952 0 -2152 1952 6044 0 -1953 2594 2595 0 -1952 2152 2593 0 -2152 6044 2362 0 -1953 2593 2364 0 -6044 2151 2362 0 -2594 1953 2365 0 -1765 1955 2597 0 -2596 1764 1954 0 -6038 2596 1954 0 -1955 1765 2596 0 -2596 6038 1955 0 -2154 1954 2594 0 -6038 1954 2366 0 -2155 1955 6038 0 -2594 1954 2595 0 -6037 2597 1955 0 -6043 2593 2152 0 -1952 2592 6044 0 -6043 2363 2593 0 -2363 2364 2593 0 -2153 2365 1953 0 -2365 6041 2594 0 -2364 2153 1953 0 -6042 2154 2594 0 -2154 2366 1954 0 -6040 6039 6038 0 -2155 2367 1955 0 -6039 2155 6038 0 -2366 6040 6038 0 -6041 6042 2594 0 -6037 1955 2367 0 -1767 1957 2599 0 -1766 1956 2598 0 -6036 2598 1956 0 -1957 1767 2598 0 -2598 6036 1957 0 -2157 6036 1956 0 -2156 1956 6037 0 -6035 1957 1958 0 -2597 6037 1956 0 -2599 1957 6035 0 -1416 1588 2601 0 -1587 1768 2600 0 -6034 2600 1768 0 -1588 1416 2600 0 -6034 1588 2600 0 -1769 1768 6035 0 -1588 6033 2601 0 -1770 6033 1588 0 -1588 6034 1770 0 -1769 6034 1768 0 -2599 6035 1768 0 -2601 6033 1589 0 -1957 6036 1958 0 -1768 1587 2599 0 -2368 2156 6037 0 -2156 2157 1956 0 -2370 1958 6036 0 -1958 1959 6035 0 -2157 2370 6036 0 -2159 1769 6035 0 -1769 1960 6034 0 -2372 1770 6034 0 -1961 2161 6033 0 -1770 1961 6033 0 -1960 2372 6034 0 -1959 2159 6035 0 -6037 2367 2368 0 -6033 2161 1771 0 -1956 1766 2597 0 -2592 1951 6044 0 -5909 2601 1417 0 -2144 2146 2145 0 -1391 2839 1564 0 -1103 5994 2413 0 -1995 1803 1804 0 -1804 476 6331 0 -6331 1995 1804 0 -1804 1622 476 0 -6331 1805 1996 0 -1806 1997 580 0 -1994 582 1995 0 -580 1805 1806 0 -1805 6331 6332 0 -1803 1995 582 0 -1622 1802 6330 0 -1802 1622 1803 0 -6330 1802 1993 0 -6330 1801 1621 0 -1803 1994 1993 0 -1994 1803 582 0 -1993 1994 2195 0 -1803 1993 1802 0 -1622 1804 1803 0 -1992 6330 1993 0 -1996 1805 580 0 -1625 1806 1805 0 -6331 581 1995 0 -581 6331 1996 0 -581 1996 701 0 -1995 581 702 0 -581 2197 702 0 -2197 581 701 0 -2197 701 828 0 -702 2197 2196 0 -580 1997 1996 0 -1995 702 2196 0 -703 2195 2194 0 -703 1993 2195 0 -2194 2195 2409 0 -703 2194 2193 0 -2410 2409 2195 0 -2410 2196 2197 0 -2408 2409 5999 0 -829 2409 2410 0 -829 2410 2411 0 -2410 2195 2196 0 -1994 2196 2195 0 -2194 2409 2408 0 -2410 2197 2411 0 -1994 1995 2196 0 -1996 1997 701 0 -1992 1993 703 0 -584 1800 1991 0 -1800 6329 1801 0 -1992 1800 1801 0 -1800 584 1799 0 -1800 583 1991 0 -583 1800 1992 0 -583 1992 2193 0 -705 1991 2192 0 -6330 1992 1801 0 -584 1991 1990 0 -706 1990 1989 0 -706 1798 1990 0 -1988 706 1989 0 -706 1988 707 0 -1989 2190 833 0 -705 1990 1991 0 -2191 1989 705 0 -705 1989 1990 0 -1799 584 1990 0 -1988 1989 833 0 -1991 583 2192 0 -1799 1619 1800 0 -2192 2193 704 0 -2193 2192 583 0 -704 2193 830 0 -2192 704 2191 0 -830 2408 2407 0 -2408 830 2193 0 -2407 2408 6000 0 -704 830 2406 0 -1992 703 2193 0 -2406 830 2407 0 -2190 2191 832 0 -2190 1989 2191 0 -832 2191 2405 0 -2190 832 2404 0 -831 2405 2191 0 -6002 831 2406 0 -968 2404 2405 0 -2404 832 2405 0 -2405 831 6003 0 -704 831 2191 0 -704 2406 831 0 -833 2190 2189 0 -2406 2407 967 0 -2191 705 2192 0 -2408 2193 2194 0 -2404 970 2190 0 -829 5999 2409 0 -6330 1621 1622 0 -2411 828 2412 0 -2198 828 701 0 -2412 828 2198 0 -829 2411 5999 0 -2412 965 2411 0 -2412 827 964 0 -965 2412 5998 0 -2412 2198 827 0 -701 826 2198 0 -2411 965 5999 0 -5999 966 6000 0 -966 5999 1106 0 -6001 6000 966 0 -6000 2408 5999 0 -1106 2633 2632 0 -1105 1106 5998 0 -1106 2632 966 0 -5998 1106 5999 0 -5999 965 5998 0 -6001 966 2632 0 -2412 5997 5998 0 -2407 6000 6001 0 -964 2413 5996 0 -964 827 963 0 -5996 2413 1104 0 -2412 5996 5997 0 -5996 5995 1253 0 -5995 1104 5994 0 -2634 2635 6216 0 -5995 5996 1104 0 -2413 5994 1104 0 -2635 1253 5995 0 -1107 2632 2633 0 -1106 1105 2633 0 -1105 5998 5997 0 -1253 1105 5997 0 -2634 2633 1105 0 -2634 1253 2635 0 -2633 1254 1107 0 -1254 2633 2634 0 -2634 5837 1254 0 -2634 1105 1253 0 -5997 5996 1253 0 -1107 1254 1255 0 -5837 2634 6216 0 -964 5996 2412 0 -827 2198 826 0 -1107 1108 2632 0 -6002 967 1108 0 -6002 2406 967 0 -1108 967 6001 0 -6003 831 6002 0 -1108 2631 2630 0 -2631 1108 1107 0 -2630 2631 1256 0 -1108 2630 6002 0 -6001 2632 1108 0 -6002 2630 1109 0 -2404 968 969 0 -2405 6003 968 0 -1110 968 2629 0 -969 968 6004 0 -968 1110 6004 0 -6003 2629 968 0 -1109 2629 6003 0 -2628 6004 1110 0 -6003 6002 1109 0 -970 2404 969 0 -1109 2630 1257 0 -2407 6001 967 0 -1256 1255 5836 0 -1255 1256 2631 0 -1255 1254 5836 0 -1256 5836 6214 0 -6214 6215 5837 0 -6215 6214 5836 0 -5836 1254 6215 0 -6214 5837 5866 0 -1107 1255 2631 0 -6214 5835 2630 0 -1257 2629 1109 0 -2630 5835 1257 0 -1257 5835 5834 0 -2629 1257 5834 0 -5834 5866 5865 0 -5866 5834 5835 0 -5833 1258 5864 0 -5865 1258 5834 0 -5865 5866 6212 0 -5834 2628 1110 0 -5835 6214 5866 0 -5834 1110 2629 0 -6213 5866 5837 0 -1256 6214 2630 0 -6215 1254 5837 0 -1258 5833 5834 0 -1252 2635 5995 0 -2411 2197 828 0 -2413 964 963 0 -1111 969 6004 0 -1115 1986 972 0 -1987 972 835 0 -1986 835 972 0 -1115 1116 1985 0 -1987 971 972 0 -834 1797 1988 0 -834 971 1987 0 -972 971 2187 0 -834 1987 835 0 -1115 972 2187 0 -1434 1116 2186 0 -1434 6203 1116 0 -2186 1116 1114 0 -1609 1434 2186 0 -2186 1114 1264 0 -1116 1115 1114 0 -1114 2187 1263 0 -1264 1114 2401 0 -1115 2187 1114 0 -1264 6205 2186 0 -834 1988 2189 0 -1985 1986 1115 0 -971 2189 2188 0 -2189 971 834 0 -2188 2189 970 0 -2188 1113 2187 0 -2402 970 2403 0 -970 969 2403 0 -1112 2403 6005 0 -970 2402 2188 0 -2189 2190 970 0 -1112 2402 2403 0 -6206 2400 2401 0 -1263 2401 1114 0 -2400 1264 2401 0 -1431 2400 6206 0 -1263 1262 2401 0 -1113 2402 1262 0 -6206 6207 6008 0 -2401 6207 6206 0 -1262 1263 1113 0 -2187 1113 1263 0 -2402 1113 2188 0 -1608 1264 2400 0 -6006 2402 1112 0 -2187 971 2188 0 -1988 833 2189 0 -6008 1788 6206 0 -2186 1982 1790 0 -1790 1609 2186 0 -1790 1982 1984 0 -1609 1790 1984 0 -2184 6205 2185 0 -2184 1982 2186 0 -2185 6205 6204 0 -2184 2186 6205 0 -1982 2184 1984 0 -6204 1433 2185 0 -1608 1433 1264 0 -1789 1608 2400 0 -1433 1608 2185 0 -2400 2183 1981 0 -2400 1981 1789 0 -1981 2183 2185 0 -1789 1981 2185 0 -1433 6204 1264 0 -1608 1789 2185 0 -6205 1264 6204 0 -2185 2183 2398 0 -2398 1431 2399 0 -2398 2183 2400 0 -2399 1431 1432 0 -2398 2399 2185 0 -6206 1607 1432 0 -6206 1788 1607 0 -1607 1788 2399 0 -1432 1607 2399 0 -6206 1432 1431 0 -2399 1788 1980 0 -6008 2397 2182 0 -2182 1980 6008 0 -2397 6010 2399 0 -1980 2182 2399 0 -6010 1605 6009 0 -6010 2397 6008 0 -6009 1606 1787 0 -1606 6009 1605 0 -1606 1605 1430 0 -1788 6008 1980 0 -2397 2399 2182 0 -2400 1431 2398 0 -6008 1605 6010 0 -1605 6008 1430 0 -1434 1609 1984 0 -2402 6006 1262 0 -6005 1261 1112 0 -1112 1261 6006 0 -6007 1262 6006 0 -6005 2627 1261 0 -2403 969 1111 0 -2626 6006 1261 0 -1111 2627 6005 0 -2403 1111 6005 0 -6006 6208 6007 0 -6008 6007 1430 0 -6008 6207 6007 0 -6207 1262 6007 0 -1430 6007 2625 0 -2624 2625 1429 0 -2625 2624 1430 0 -1429 2625 5830 0 -1604 2624 1429 0 -6007 6208 2625 0 -1430 2624 1979 0 -2628 1260 1111 0 -6207 2401 1262 0 -1111 1260 2627 0 -1260 2628 1259 0 -1260 1259 5832 0 -2627 1260 5832 0 -5833 6210 5832 0 -5864 6210 5833 0 -6209 5832 5863 0 -5833 5832 1259 0 -2628 5834 1259 0 -2627 5832 6209 0 -1429 5830 5829 0 -6208 6006 2626 0 -5831 6208 2626 0 -5830 2625 6208 0 -5831 6209 1427 0 -6209 5831 2626 0 -5830 1428 5829 0 -5862 1428 5830 0 -5830 5831 5862 0 -5831 5830 6208 0 -2626 2627 6209 0 -5829 1428 1603 0 -1603 1428 5860 0 -2626 1261 2627 0 -5834 5833 1259 0 -6012 1429 5829 0 -1430 1979 1787 0 -2396 2181 2624 0 -1979 2181 6009 0 -1787 1979 6009 0 -2624 6011 2396 0 -2624 1604 2622 0 -2624 2622 6011 0 -2396 6011 6009 0 -2624 2181 1979 0 -2181 2396 6009 0 -1604 2623 2622 0 -2623 1604 1786 0 -2623 1786 1978 0 -2180 2395 2623 0 -1786 1429 1978 0 -2395 6012 2623 0 -6012 2395 1429 0 -2180 1429 2395 0 -2180 2623 1978 0 -1978 1429 2180 0 -2623 6012 2621 0 -1604 1429 1786 0 -6011 2622 6009 0 -5827 1785 5828 0 -5827 2621 5829 0 -2621 5827 2623 0 -1785 1603 1977 0 -1785 5827 5829 0 -1977 1603 2179 0 -1785 1977 5828 0 -6012 5829 2621 0 -5828 1977 2179 0 -1603 6013 2394 0 -2394 2179 1603 0 -2394 6013 5828 0 -2179 2394 5828 0 -2620 5826 5828 0 -1602 2620 6013 0 -2620 1602 5826 0 -1602 5858 5826 0 -5858 1784 5859 0 -6013 2620 5828 0 -1602 6013 1603 0 -5829 1603 1785 0 -5863 5832 6210 0 -1430 1787 1606 0 -1111 6004 2628 0 -5865 5864 1258 0 -1990 1798 1799 0 -2637 1252 5995 0 -2637 1251 6220 0 -1252 2637 2636 0 -1252 6216 2635 0 -2637 6219 2636 0 -5908 6219 2637 0 -5841 2636 6219 0 -6218 2636 5841 0 -1251 5995 5994 0 -1252 2636 6217 0 -6213 6216 5838 0 -6216 1252 6217 0 -5839 6216 6217 0 -6213 6212 5866 0 -5838 5839 1421 0 -6218 5839 6217 0 -5840 5839 6218 0 -5839 5838 6216 0 -2636 6218 6217 0 -1422 6213 5838 0 -5908 2637 6220 0 -5995 1251 2637 0 -6219 1418 5841 0 -1417 5908 6220 0 -2602 5908 1417 0 -6218 5841 1419 0 -2603 1418 1590 0 -2602 1418 5908 0 -2603 1591 1419 0 -1418 2603 5841 0 -1417 1589 2602 0 -5841 2603 1419 0 -5869 1421 1420 0 -1421 5839 5840 0 -1420 1421 5840 0 -5838 1421 1422 0 -5842 1420 5840 0 -1591 5842 1419 0 -1420 5870 5869 0 -1592 5870 1420 0 -1594 5870 1593 0 -5842 5840 1419 0 -6218 1419 5840 0 -5869 1422 1421 0 -1592 1420 5842 0 -6219 5908 1418 0 -2638 6220 1251 0 -1595 5869 5870 0 -5865 1423 5891 0 -6212 6213 5867 0 -1423 6212 5867 0 -5891 6211 5865 0 -5891 1423 5892 0 -1422 5868 5867 0 -5891 5892 1424 0 -5867 5868 1423 0 -6213 1422 5867 0 -5891 1424 5890 0 -1426 5864 5890 0 -5864 5865 6211 0 -5890 5864 6211 0 -1426 5863 6210 0 -1426 1425 5889 0 -5890 6211 5891 0 -5890 1424 1425 0 -1425 1426 5890 0 -1425 5894 1598 0 -1598 5889 1425 0 -1425 1424 5894 0 -5864 1426 6210 0 -5868 1595 5892 0 -1422 5869 5868 0 -1596 5892 1595 0 -5868 5892 1423 0 -1777 5871 1776 0 -1594 5871 1595 0 -1594 5844 5871 0 -1595 5871 5893 0 -1595 5868 5869 0 -1777 5893 5871 0 -1597 5894 1424 0 -1424 1596 1597 0 -1597 1596 1778 0 -1597 1779 1598 0 -1778 5893 5874 0 -5893 1778 1596 0 -1779 5874 1970 0 -5874 1779 1778 0 -5874 5893 1969 0 -1778 1779 1597 0 -1596 1595 5893 0 -1597 1598 5894 0 -5893 1777 1969 0 -1424 5892 1596 0 -5870 1594 1595 0 -1779 5895 1598 0 -1590 1418 2602 0 -6213 5837 6216 0 -1589 1771 6032 0 -6220 5909 1417 0 -6032 1771 2374 0 -6032 1590 2602 0 -2374 2162 6032 0 -2162 1962 6032 0 -6032 1962 1590 0 -2604 5842 1591 0 -2603 6031 1591 0 -5842 2604 1592 0 -5843 5870 1592 0 -6031 2163 1591 0 -2604 1591 1773 0 -5843 1592 1774 0 -2604 2165 1592 0 -6031 1590 1772 0 -5843 1593 5870 0 -1772 1590 1962 0 -2603 1590 6031 0 -1772 2375 6031 0 -2375 2376 6031 0 -1963 2163 6031 0 -2163 2164 1591 0 -2376 1963 6031 0 -1773 6030 2604 0 -6030 1964 2604 0 -2378 2165 2604 0 -1774 6029 5843 0 -2165 1774 1592 0 -1964 2378 2604 0 -2164 1773 1591 0 -5843 6029 1965 0 -5844 1776 5871 0 -5844 1593 1775 0 -2167 1776 5844 0 -1776 5872 1777 0 -2379 1593 5843 0 -2379 1775 1593 0 -5843 1965 2379 0 -5872 1776 1967 0 -5873 1970 1969 0 -1969 1970 5874 0 -1969 1777 5872 0 -1968 1969 5872 0 -2171 5873 2382 0 -1968 5873 1969 0 -1968 2382 5873 0 -5873 2171 1970 0 -1968 5872 2381 0 -5875 1779 1970 0 -5844 1775 1966 0 -1594 1593 5844 0 -1966 6028 5844 0 -6028 2380 5844 0 -2167 1967 1776 0 -1967 5845 5872 0 -2380 2167 5844 0 -6027 2168 5872 0 -2168 2381 5872 0 -2169 5846 1968 0 -2383 2382 1968 0 -5846 2383 1968 0 -2381 2169 1968 0 -5845 6027 5872 0 -2171 2382 2170 0 -2602 1589 6032 0 -1589 1417 2601 0 -5907 1970 2171 0 -5862 5861 1428 0 -5861 5860 1428 0 -5831 1427 5862 0 -1600 5861 5862 0 -1427 5889 5888 0 -1427 5863 1426 0 -5888 5889 1599 0 -1427 5888 5862 0 -5889 1427 1426 0 -1601 5861 5887 0 -1784 1602 1976 0 -1602 1603 5860 0 -1601 1602 5860 0 -1602 1784 5858 0 -1976 2393 2178 0 -1976 1602 2393 0 -5886 1602 1601 0 -2178 5859 1976 0 -5860 5861 1601 0 -1976 5859 1784 0 -1600 5862 5888 0 -5863 1427 6209 0 -5896 1599 1780 0 -1599 5889 1598 0 -1780 1599 5895 0 -5896 1600 5888 0 -5897 1780 1971 0 -1780 5895 1779 0 -1781 5897 1972 0 -1780 5897 5896 0 -1779 5875 1780 0 -5896 5897 1781 0 -5886 5887 1782 0 -1600 5887 5861 0 -1781 5887 1600 0 -5887 5886 1601 0 -5903 1782 5887 0 -1972 5903 1781 0 -1782 5904 5886 0 -5898 1782 5903 0 -5899 5883 5904 0 -5903 5887 1781 0 -1600 5896 1781 0 -2619 1602 5886 0 -1973 5904 1782 0 -5896 5888 1599 0 -5895 1599 1598 0 -5824 5886 5904 0 -1602 2619 6014 0 -6014 2393 1602 0 -6014 2619 5859 0 -2393 6014 5859 0 -5825 5886 5857 0 -5825 2619 5886 0 -1783 5886 1975 0 -5885 1783 1975 0 -5886 1783 5857 0 -2619 5825 5859 0 -1783 5885 5857 0 -5885 1975 2177 0 -5885 2177 2392 0 -5886 2392 2177 0 -5886 6015 2392 0 -6015 2618 5885 0 -2392 6015 5885 0 -1975 5886 2177 0 -5885 2618 5824 0 -5886 2618 6015 0 -5904 5856 5824 0 -2618 5886 5824 0 -5884 5856 1974 0 -5824 5856 5885 0 -2176 6016 2391 0 -6016 2176 2617 0 -2391 5884 2176 0 -1974 2176 5884 0 -5904 2176 1974 0 -5884 2391 6016 0 -5884 2617 5823 0 -2617 5884 6016 0 -5823 2617 5855 0 -5884 5823 5855 0 -5855 5883 5884 0 -2617 2176 5855 0 -5882 5902 5883 0 -5883 5855 5904 0 -5855 2176 5904 0 -2175 5882 5883 0 -5882 2175 5902 0 -1974 5856 5904 0 -1971 1780 5875 0 -2178 2393 5859 0 -1971 5907 2172 0 -1971 5875 1970 0 -2384 2172 5907 0 -1971 2172 5876 0 -6025 2171 2609 0 -6024 2171 6025 0 -1970 5907 1971 0 -5876 2172 2385 0 -1973 5898 5877 0 -1972 5898 5903 0 -2173 5898 1972 0 -5899 5904 1973 0 -2173 5877 5898 0 -6023 2173 5876 0 -2612 5877 2173 0 -2173 1972 5876 0 -5897 5876 1972 0 -2174 1973 5877 0 -6024 2384 2171 0 -5876 5897 1971 0 -2384 5907 2171 0 -2384 2385 2172 0 -6022 5906 5876 0 -5906 2610 5876 0 -2385 6022 5876 0 -6023 6021 2173 0 -6021 2612 2173 0 -5849 2611 5877 0 -5877 6020 2174 0 -2611 6020 5877 0 -2612 5849 5877 0 -2610 6023 5876 0 -2387 2174 6020 0 -2175 5899 2390 0 -2175 5883 5899 0 -6017 2390 5881 0 -5854 2175 2390 0 -5900 5881 5899 0 -2390 5899 5881 0 -6018 5853 2389 0 -5880 5900 2389 0 -1973 5900 5899 0 -5822 2390 6017 0 -2390 5822 5854 0 -5822 6017 2616 0 -5822 2616 5881 0 -5822 5881 5902 0 -2616 6017 5881 0 -5853 5901 5880 0 -5821 5901 5853 0 -5880 5881 5900 0 -2389 5853 5880 0 -5822 5902 5854 0 -2389 5900 6018 0 -5854 5902 2175 0 -5900 5851 6018 0 -5900 1973 2174 0 -5850 2174 2613 0 -6018 5821 5853 0 -5850 5851 2174 0 -5851 2388 6018 0 -6018 2388 2615 0 -5879 5821 2615 0 -2615 5821 6018 0 -6019 2615 2388 0 -2614 2615 6019 0 -2614 5820 2615 0 -5852 5879 2615 0 -5820 5852 2615 0 -5821 5879 5901 0 -5900 2174 5851 0 -2387 2613 2174 0 -1782 5898 1973 0 -2171 2170 2609 0 -1423 5865 6212 0 -6033 1771 1589 0 -1237 1234 1233 0 -3202 2346 3203 0 -1652 2959 2764 0 - -Edges -0 - -End -# vorpaline algo:delaunay=triangle -# vorpaline algo:hole_filling=loop_split -# vorpaline algo:nn_search=BNN -# vorpaline algo:parallel=true -# vorpaline algo:predicates=fast -# vorpaline algo:reconstruct=Co3Ne -# vorpaline biblio=false -# vorpaline biblio:command_line=false -# vorpaline convex_hull=false -# vorpaline debug=false -# vorpaline dimension=2 -# vorpaline log:features=* -# vorpaline log:features_exclude= -# vorpaline log:file_name= -# vorpaline log:pretty=true -# vorpaline log:quiet=false -# vorpaline nl:CUDA=false -# vorpaline nl:MKL=false -# vorpaline profile=scan -# vorpaline sys:FPE=false -# vorpaline sys:assert=throw -# vorpaline sys:cancel=false -# vorpaline sys:compression_level=3 -# vorpaline sys:lowmem=false -# vorpaline sys:max_threads=8 -# vorpaline sys:multithread=true -# vorpaline sys:stats=false -# vorpaline sys:use_doubles=false diff --git a/tests/out.meshb b/tests/out.meshb deleted file mode 100755 index d6c92844..00000000 Binary files a/tests/out.meshb and /dev/null differ diff --git a/tools/runpybot.sh.in b/tools/runpybot.sh.in index a3243120..70c40ab0 100755 --- a/tools/runpybot.sh.in +++ b/tools/runpybot.sh.in @@ -22,5 +22,5 @@ fi if [ "$VORPALINE_WITH_GCOV" = 1 ]; then echo "Generating coverage report" - gcovr --xml --output=coverage.xml --verbose $VORPALINE_BUILD_DIR > coverage.log + gcovr --xml --output=coverage.xml --verbose -r $VORPALINE_SOURCE_DIR/src $VORPALINE_BUILD_DIR > coverage.log fi