First
This commit is contained in:
+192
@@ -0,0 +1,192 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.16.5)
|
||||||
|
|
||||||
|
project(IUI)
|
||||||
|
|
||||||
|
include(${PROJECT_SOURCE_DIR}/../build-utils/cmake/compiler_settings.cmake)
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
add_definitions(/bigobj)
|
||||||
|
set(LIB_FOLDER "vs2022")
|
||||||
|
elseif (ANDROID)
|
||||||
|
set(LIB_FOLDER "Android")
|
||||||
|
elseif (APPLE)
|
||||||
|
set(LIB_FOLDER "macos")
|
||||||
|
elseif (UNIX)
|
||||||
|
set(LIB_FOLDER "linux")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT TARGET TL)
|
||||||
|
set(WITH_OPENSSL OFF)
|
||||||
|
set(WITH_ZLIB OFF)
|
||||||
|
add_subdirectory("${PROJECT_SOURCE_DIR}/../TL" "./TL")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT TARGET EASTL)
|
||||||
|
set(EASTL_DIR "${PROJECT_SOURCE_DIR}/../EASTL")
|
||||||
|
add_library(EASTL STATIC IMPORTED)
|
||||||
|
list(APPEND EASTL_INCLUDE_DIRS "${EASTL_DIR}/include"
|
||||||
|
"${EASTL_DIR}/test/packages/EAAssert/include"
|
||||||
|
"${EASTL_DIR}/test/packages/EABase/include/Common"
|
||||||
|
"${EASTL_DIR}/test/packages/EAMain/include"
|
||||||
|
"${EASTL_DIR}/test/packages/EAStdC/include"
|
||||||
|
"${EASTL_DIR}/test/packages/EATest/include"
|
||||||
|
"${EASTL_DIR}/test/packages/EAThread/include")
|
||||||
|
list(APPEND LIB_INCLUDE_DIRS "dir2")
|
||||||
|
set(EASTL_LIB_DIR "${EASTL_DIR}/release/lib/${LIB_FOLDER}")
|
||||||
|
set_target_properties(EASTL PROPERTIES
|
||||||
|
IMPORTED_LOCATION "${EASTL_LIB_DIR}/Release/${LIB_PREFIX}EASTL${LIB_SUFFIX}"
|
||||||
|
IMPORTED_LOCATION_DEBUG "${EASTL_LIB_DIR}/Debug/${LIB_PREFIX}EASTL${LIB_SUFFIX}"
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES "${EASTL_INCLUDE_DIRS}")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT TARGET VMath)
|
||||||
|
add_subdirectory("${PROJECT_SOURCE_DIR}/../VMath" "./VMath")
|
||||||
|
endif()
|
||||||
|
if(NOT TARGET FS)
|
||||||
|
add_subdirectory("${PROJECT_SOURCE_DIR}/../FS" "./FS")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT TARGET cppcoro)
|
||||||
|
add_subdirectory("${PROJECT_SOURCE_DIR}/../cppcoro" "./cppcoro")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if (NOT TARGET Kit)
|
||||||
|
add_subdirectory("${PROJECT_SOURCE_DIR}/../../libs/Kit" "./Kit")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(FREETYPE_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/../freetype/release/${LIB_FOLDER}/include/freetype2")
|
||||||
|
set(FREETYPE_LIB_DIR "${PROJECT_SOURCE_DIR}/../freetype/release/${LIB_FOLDER}/lib")
|
||||||
|
add_library(freetype STATIC IMPORTED)
|
||||||
|
set_target_properties(freetype PROPERTIES
|
||||||
|
IMPORTED_LOCATION "${FREETYPE_LIB_DIR}/${LIB_PREFIX}freetype${LIB_SUFFIX}"
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES ${FREETYPE_INCLUDE_DIR})
|
||||||
|
|
||||||
|
# FILAMENT
|
||||||
|
if (MSVC OR APPLE OR UNIX)
|
||||||
|
set(USE_STATIC_CRT OFF)
|
||||||
|
set(FILAMENT_SKIP_SAMPLES ON)
|
||||||
|
set(FILAMENT_SKIP_SDL2 ON)
|
||||||
|
if (MSVC)
|
||||||
|
set(FILAMENT_SUPPORTS_VULKAN ON)
|
||||||
|
endif()
|
||||||
|
if(NOT TARGET filament)
|
||||||
|
add_subdirectory("${PROJECT_SOURCE_DIR}/../filament" "./filament")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
set(FILAMENT_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/../filament/release/include")
|
||||||
|
set(FILAMENT_LIB_DIR "${PROJECT_SOURCE_DIR}/../filament/release/lib/${LIB_FOLDER}/")
|
||||||
|
|
||||||
|
add_library(filament_backend STATIC IMPORTED)
|
||||||
|
set_target_properties(filament_backend PROPERTIES IMPORTED_LOCATION "${FILAMENT_LIB_DIR}/Release/libbackend.a" IMPORTED_LOCATION_DEBUG "${FILAMENT_LIB_DIR}/Debug/libbackend.a" INTERFACE_INCLUDE_DIRECTORIES ${FILAMENT_INCLUDE_DIR})
|
||||||
|
add_library(filament_bluevk STATIC IMPORTED)
|
||||||
|
set_target_properties(filament_bluevk PROPERTIES IMPORTED_LOCATION "${FILAMENT_LIB_DIR}/Release/libbluevk.a" IMPORTED_LOCATION_DEBUG "${FILAMENT_LIB_DIR}/Debug/libbluevk.a" INTERFACE_INCLUDE_DIRECTORIES ${FILAMENT_INCLUDE_DIR})
|
||||||
|
add_library(filament_filabridge STATIC IMPORTED)
|
||||||
|
set_target_properties(filament_filabridge PROPERTIES IMPORTED_LOCATION "${FILAMENT_LIB_DIR}/Release/libfilabridge.a" IMPORTED_LOCATION_DEBUG "${FILAMENT_LIB_DIR}/Debug/libfilabridge.a")
|
||||||
|
add_library(filament_filaflat STATIC IMPORTED)
|
||||||
|
set_target_properties(filament_filaflat PROPERTIES IMPORTED_LOCATION "${FILAMENT_LIB_DIR}/Release/libfilaflat.a" IMPORTED_LOCATION_DEBUG "${FILAMENT_LIB_DIR}/Debug/libfilaflat.a")
|
||||||
|
add_library(filament_filamat STATIC IMPORTED)
|
||||||
|
set_target_properties(filament_filamat PROPERTIES IMPORTED_LOCATION "${FILAMENT_LIB_DIR}/Release/libfilamat.a" IMPORTED_LOCATION_DEBUG "${FILAMENT_LIB_DIR}/Debug/libfilamat.a")
|
||||||
|
add_library(filament_filament STATIC IMPORTED)
|
||||||
|
set_target_properties(filament_filament PROPERTIES IMPORTED_LOCATION "${FILAMENT_LIB_DIR}/Release/libfilament.a" IMPORTED_LOCATION_DEBUG "${FILAMENT_LIB_DIR}/Debug/libfilament.a")
|
||||||
|
add_library(filament_ibl STATIC IMPORTED)
|
||||||
|
set_target_properties(filament_ibl PROPERTIES IMPORTED_LOCATION "${FILAMENT_LIB_DIR}/Release/libibl.a" IMPORTED_LOCATION_DEBUG "${FILAMENT_LIB_DIR}/Debug/libibl.a")
|
||||||
|
add_library(filament_image STATIC IMPORTED)
|
||||||
|
set_target_properties(filament_image PROPERTIES IMPORTED_LOCATION "${FILAMENT_LIB_DIR}/Release/libimage.a" IMPORTED_LOCATION_DEBUG "${FILAMENT_LIB_DIR}/Debug/libimage.a")
|
||||||
|
add_library(filament_utils STATIC IMPORTED)
|
||||||
|
set_target_properties(filament_utils PROPERTIES IMPORTED_LOCATION "${FILAMENT_LIB_DIR}/Release/libutils.a" IMPORTED_LOCATION_DEBUG "${FILAMENT_LIB_DIR}/Debug/libutils.a")
|
||||||
|
add_library(filament_geometry STATIC IMPORTED)
|
||||||
|
set_target_properties(filament_geometry PROPERTIES IMPORTED_LOCATION "${FILAMENT_LIB_DIR}/Release/libgeometry.a" IMPORTED_LOCATION_DEBUG "${FILAMENT_LIB_DIR}/Debug/libgeometry.a")
|
||||||
|
add_library(filament_shaders STATIC IMPORTED)
|
||||||
|
set_target_properties(filament_shaders PROPERTIES IMPORTED_LOCATION "${FILAMENT_LIB_DIR}/Release/libshaders.a" IMPORTED_LOCATION_DEBUG "${FILAMENT_LIB_DIR}/Debug/libshaders.a")
|
||||||
|
add_library(filament_smol-v STATIC IMPORTED)
|
||||||
|
set_target_properties(filament_smol-v PROPERTIES IMPORTED_LOCATION "${FILAMENT_LIB_DIR}/Release/libsmol-v.a" IMPORTED_LOCATION_DEBUG "${FILAMENT_LIB_DIR}/Debug/libsmol-v.a")
|
||||||
|
add_library(filament_vkshaders STATIC IMPORTED)
|
||||||
|
set_target_properties(filament_vkshaders PROPERTIES IMPORTED_LOCATION "${FILAMENT_LIB_DIR}/Release/libvkshaders.a" IMPORTED_LOCATION_DEBUG "${FILAMENT_LIB_DIR}/Debug/libvkshaders.a")
|
||||||
|
add_library(filament INTERFACE IMPORTED)
|
||||||
|
set_property(TARGET filament PROPERTY INTERFACE_LINK_LIBRARIES
|
||||||
|
filament_backend
|
||||||
|
filament_bluevk
|
||||||
|
filament_filabridge
|
||||||
|
filament_filaflat
|
||||||
|
filament_filamat
|
||||||
|
filament_filament
|
||||||
|
filament_ibl
|
||||||
|
filament_image
|
||||||
|
filament_utils
|
||||||
|
filament_geometry
|
||||||
|
filament_smol-v
|
||||||
|
filament_shaders
|
||||||
|
filament_vkshaders)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
if (APPLE)
|
||||||
|
find_package(BZIP2 REQUIRED)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
file(GLOB_RECURSE SRC "src/*.cpp" "src/*.h" "src/*.inl" "src/*.c")
|
||||||
|
file(GLOB_RECURSE HEADERS "include/*.h" "include/*.inl" "include/*.hpp" "include/*.ipp")
|
||||||
|
file(GLOB_RECURSE NATVIS "natvis/*.natjmc" "natvis/*.natstepfilter" "natvis/*.natvis")
|
||||||
|
|
||||||
|
if (NOT UNIX)
|
||||||
|
#remove libbacktrace stuff on non-unixes
|
||||||
|
set (EXCLUDE_DIR "/libbacktrace/")
|
||||||
|
foreach (TMP_PATH ${SRC})
|
||||||
|
string (FIND ${TMP_PATH} ${EXCLUDE_DIR} EXCLUDE_DIR_FOUND)
|
||||||
|
if (NOT ${EXCLUDE_DIR_FOUND} EQUAL -1)
|
||||||
|
list (REMOVE_ITEM SRC ${TMP_PATH})
|
||||||
|
endif ()
|
||||||
|
endforeach(TMP_PATH)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
foreach(_source IN ITEMS ${HEADERS})
|
||||||
|
get_filename_component(_source_path "${_source}" PATH)
|
||||||
|
file(RELATIVE_PATH _source_path_rel "${PROJECT_SOURCE_DIR}" "${_source_path}")
|
||||||
|
string(REPLACE "/" "\\" _group_path "${_source_path_rel}")
|
||||||
|
source_group("${_group_path}" FILES "${_source}")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
foreach(_source IN ITEMS ${SRC})
|
||||||
|
get_filename_component(_source_path "${_source}" PATH)
|
||||||
|
file(RELATIVE_PATH _source_path_rel "${PROJECT_SOURCE_DIR}" "${_source_path}")
|
||||||
|
string(REPLACE "/" "\\" _group_path "${_source_path_rel}")
|
||||||
|
source_group("${_group_path}" FILES "${_source}")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
add_library(IUI STATIC ${SRC} ${HEADERS} ${NATVIS})
|
||||||
|
target_include_directories(IUI PUBLIC "include")
|
||||||
|
target_include_directories(IUI PUBLIC "include/IUI/util/ImGui")
|
||||||
|
target_include_directories(IUI PRIVATE "src")
|
||||||
|
target_include_directories(IUI PUBLIC "${PROJECT_SOURCE_DIR}/../Logger/include")
|
||||||
|
target_include_directories(IUI PUBLIC "${PROJECT_SOURCE_DIR}/../xsimd/include")
|
||||||
|
target_include_directories(IUI PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
target_link_libraries(IUI EASTL)
|
||||||
|
|
||||||
|
target_link_libraries(IUI VMath)
|
||||||
|
target_link_libraries(IUI FS)
|
||||||
|
target_link_libraries(IUI Kit)
|
||||||
|
target_link_libraries(IUI freetype)
|
||||||
|
target_link_libraries(IUI filament)
|
||||||
|
if (MSVC OR APPLE OR UNIX)
|
||||||
|
target_link_libraries(IUI filamat)
|
||||||
|
target_link_libraries(IUI geometry)
|
||||||
|
target_link_libraries(IUI filabridge)
|
||||||
|
target_link_libraries(IUI image)
|
||||||
|
target_link_libraries(IUI imageio)
|
||||||
|
if (MSVC)
|
||||||
|
target_link_libraries(IUI Ws2_32)
|
||||||
|
target_link_libraries(IUI Mswsock)
|
||||||
|
target_link_libraries(IUI Synchronization)
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
target_link_libraries(IUI -lz)
|
||||||
|
target_link_libraries(IUI -lGLESv3)
|
||||||
|
target_link_libraries(IUI -lEGL)
|
||||||
|
target_link_libraries(IUI -lvulkan)
|
||||||
|
endif()
|
||||||
|
if (APPLE)
|
||||||
|
target_link_libraries(IUI ${BZIP2_LIBRARY})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_precompile_headers(IUI PRIVATE "$<$<COMPILE_LANGUAGE:CXX>:${CMAKE_CURRENT_SOURCE_DIR}/src/StdAfx.h>")
|
||||||
|
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <tl/ptr.h>
|
||||||
|
#include <tl/flag_set.h>
|
||||||
|
#include <fs/IFilesystem.h>
|
||||||
|
#include <fs/AbsPath.h>
|
||||||
|
|
||||||
|
#include "IUI/ImGui/imgui.h"
|
||||||
|
#include "Kit/EngineManager.h"
|
||||||
|
|
||||||
|
namespace kit
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace icons
|
||||||
|
{
|
||||||
|
|
||||||
|
enum class Placement : uint8_t
|
||||||
|
{
|
||||||
|
HCenter = 1 << 0,
|
||||||
|
VCenter = 1 << 1,
|
||||||
|
Left = 1 << 2,
|
||||||
|
Right = 1 << 3,
|
||||||
|
Top = 1 << 4,
|
||||||
|
Bottom = 1 << 5,
|
||||||
|
|
||||||
|
Center = HCenter | VCenter,
|
||||||
|
TopLeft = Left | Top,
|
||||||
|
BottomRight = Right | Bottom
|
||||||
|
};
|
||||||
|
using PlacementSet = tl::flag_set<Placement>;
|
||||||
|
|
||||||
|
void init(tl::lent_ref<kit::EngineManager> engineManager, tl::lent_ref<fs::IFilesystem> filesystem, fs::AbsPath folderPath);
|
||||||
|
void shutdown();
|
||||||
|
|
||||||
|
struct Icon
|
||||||
|
{
|
||||||
|
tl::lent_ptr<filament::Texture> texture;
|
||||||
|
math::vec2f uv0;
|
||||||
|
math::vec2f uv1;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FontIcon
|
||||||
|
{
|
||||||
|
const char* icon = nullptr;
|
||||||
|
explicit operator bool() const
|
||||||
|
{
|
||||||
|
return icon != nullptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const Icon& getIcon(const tl::string& name);
|
||||||
|
const Icon& getIcon(const char* name);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,145 @@
|
|||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// DEAR IMGUI COMPILE-TIME OPTIONS
|
||||||
|
// 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 Dear ImGui, or maintain a patch/rebased branch with your modifications to it)
|
||||||
|
// B) or '#define IMGUI_USER_CONFIG "my_imgui_config.h"' in your project and then add directives in your own file without touching this template.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// 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 Dear 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 file to verify that the data structures your files are using are matching the ones imgui.cpp is using.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//---- Define assertion handler. Defaults to calling assert().
|
||||||
|
// If your macro uses multiple statements, make sure is enclosed in a 'do { .. } while (0)' block so it can be used as a single statement.
|
||||||
|
//#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
|
||||||
|
// Using Dear ImGui via a shared library is not recommended, because of function call overhead and because we don't guarantee backward nor forward ABI compatibility.
|
||||||
|
// DLL users: heaps and globals are not shared across DLL boundaries! You will need to call SetCurrentContext() + SetAllocatorFunctions()
|
||||||
|
// for each static/DLL boundary you are calling from. Read "Context and Memory Allocators" section of imgui.cpp for more details.
|
||||||
|
//#define IMGUI_API __declspec( dllexport )
|
||||||
|
//#define IMGUI_API __declspec( dllimport )
|
||||||
|
|
||||||
|
//---- Don't define obsolete functions/enums/behaviors. Consider enabling from time to time after updating to clean your code of obsolete function/names.
|
||||||
|
//#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||||
|
//#define IMGUI_DISABLE_OBSOLETE_KEYIO // 1.87+ disable legacy io.KeyMap[]+io.KeysDown[] in favor io.AddKeyEvent(). This is automatically done by IMGUI_DISABLE_OBSOLETE_FUNCTIONS.
|
||||||
|
|
||||||
|
//---- Disable all of Dear ImGui or don't implement standard windows/tools.
|
||||||
|
// It is very strongly recommended to NOT disable the demo windows and debug tool during development. They are extremely useful in day to day work. Please read comments in imgui_demo.cpp.
|
||||||
|
//#define IMGUI_DISABLE // Disable everything: all headers and source files will be empty.
|
||||||
|
//#define IMGUI_DISABLE_DEMO_WINDOWS // Disable demo windows: ShowDemoWindow()/ShowStyleEditor() will be empty.
|
||||||
|
//#define IMGUI_DISABLE_DEBUG_TOOLS // Disable metrics/debugger and other debug tools: ShowMetricsWindow(), ShowDebugLogWindow() and ShowIDStackToolWindow() will be empty.
|
||||||
|
|
||||||
|
//---- 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. (user32.lib/.a, kernel32.lib/.a)
|
||||||
|
//#define IMGUI_ENABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with Visual Studio] Implement default IME handler (require imm32.lib/.a, auto-link for Visual Studio, -limm32 on command-line for MinGW)
|
||||||
|
//#define IMGUI_DISABLE_WIN32_DEFAULT_IME_FUNCTIONS // [Win32] [Default with non-Visual Studio compilers] Don't implement default IME handler (won't require imm32.lib/.a)
|
||||||
|
//#define IMGUI_DISABLE_WIN32_FUNCTIONS // [Win32] Won't use and link with any Win32 function (clipboard, IME).
|
||||||
|
//#define IMGUI_ENABLE_OSX_DEFAULT_CLIPBOARD_FUNCTIONS // [OSX] Implement default OSX clipboard handler (need to link with '-framework ApplicationServices', this is why this is not the default).
|
||||||
|
//#define IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS // Don't implement ImFormatString/ImFormatStringV so you can implement them yourself (e.g. if you don't want to link with vsnprintf)
|
||||||
|
//#define IMGUI_DISABLE_DEFAULT_MATH_FUNCTIONS // Don't implement ImFabs/ImSqrt/ImPow/ImFmod/ImCos/ImSin/ImAcos/ImAtan2 so you can implement them yourself.
|
||||||
|
//#define IMGUI_DISABLE_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle at all (replace them with dummies)
|
||||||
|
//#define IMGUI_DISABLE_DEFAULT_FILE_FUNCTIONS // Don't implement ImFileOpen/ImFileClose/ImFileRead/ImFileWrite and ImFileHandle so you can implement them yourself if you don't want to link with fopen/fclose/fread/fwrite. This will also disable the LogToTTY() function.
|
||||||
|
//#define IMGUI_DISABLE_DEFAULT_ALLOCATORS // Don't implement default allocators calling malloc()/free() to avoid linking with them. You will need to call ImGui::SetAllocatorFunctions().
|
||||||
|
//#define IMGUI_DISABLE_SSE // Disable use of SSE intrinsics even if available
|
||||||
|
|
||||||
|
//---- Include imgui_user.h at the end of imgui.h as a convenience
|
||||||
|
// May be convenient for some users to only explicitly include vanilla imgui.h and have extra stuff included.
|
||||||
|
//#define IMGUI_INCLUDE_IMGUI_USER_H
|
||||||
|
//#define IMGUI_USER_H_FILENAME "my_folder/my_imgui_user.h"
|
||||||
|
|
||||||
|
//---- Pack colors to BGRA8 instead of RGBA8 (to avoid converting from one to another)
|
||||||
|
//#define IMGUI_USE_BGRA_PACKED_COLOR
|
||||||
|
|
||||||
|
//---- Use 32-bit for ImWchar (default is 16-bit) to support unicode planes 1-16. (e.g. point beyond 0xFFFF like emoticons, dingbats, symbols, shapes, ancient languages, etc...)
|
||||||
|
#define IMGUI_USE_WCHAR32
|
||||||
|
|
||||||
|
//---- 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 Dear ImGui sources 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_STB_SPRINTF_FILENAME "my_folder/stb_sprintf.h" // only used if IMGUI_USE_STB_SPRINTF is defined.
|
||||||
|
//#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
|
||||||
|
//#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
|
||||||
|
//#define IMGUI_DISABLE_STB_SPRINTF_IMPLEMENTATION // only disabled if IMGUI_USE_STB_SPRINTF is defined.
|
||||||
|
|
||||||
|
//---- Use stb_sprintf.h for a faster implementation of vsnprintf instead of the one from libc (unless IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS is defined)
|
||||||
|
// Compatibility checks of arguments and formats done by clang and GCC will be disabled in order to support the extra formats provided by stb_sprintf.h.
|
||||||
|
//#define IMGUI_USE_STB_SPRINTF
|
||||||
|
|
||||||
|
//---- Use FreeType to build and rasterize the font atlas (instead of stb_truetype which is embedded by default in Dear ImGui)
|
||||||
|
// Requires FreeType headers to be available in the include path. Requires program to be compiled with 'misc/freetype/imgui_freetype.cpp' (in this repository) + the FreeType library (not provided).
|
||||||
|
// On Windows you may use vcpkg with 'vcpkg install freetype --triplet=x64-windows' + 'vcpkg integrate install'.
|
||||||
|
#define IMGUI_ENABLE_FREETYPE
|
||||||
|
|
||||||
|
//---- Use FreeType+lunasvg library to render OpenType SVG fonts (SVGinOT)
|
||||||
|
// Requires lunasvg headers to be available in the include path + program to be linked with the lunasvg library (not provided).
|
||||||
|
// Only works in combination with IMGUI_ENABLE_FREETYPE.
|
||||||
|
// (implementation is based on Freetype's rsvg-port.c which is licensed under CeCILL-C Free Software License Agreement)
|
||||||
|
//#define IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||||
|
|
||||||
|
//---- Use stb_truetype to build and rasterize the font atlas (default)
|
||||||
|
// The only purpose of this define is if you want force compilation of the stb_truetype backend ALONG with the FreeType backend.
|
||||||
|
//#define IMGUI_ENABLE_STB_TRUETYPE
|
||||||
|
|
||||||
|
//---- Define constructor and implicit cast operators to convert back<>forth between your math types and ImVec2/ImVec4.
|
||||||
|
// This will be inlined as part of ImVec2 and ImVec4 class declarations.
|
||||||
|
/*
|
||||||
|
#define IM_VEC2_CLASS_EXTRA \
|
||||||
|
constexpr ImVec2(const MyVec2& f) : x(f.x), y(f.y) {} \
|
||||||
|
operator MyVec2() const { return MyVec2(x,y); }
|
||||||
|
|
||||||
|
#define IM_VEC4_CLASS_EXTRA \
|
||||||
|
constexpr 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); }
|
||||||
|
*/
|
||||||
|
//---- ...Or use Dear ImGui's own very basic math operators.
|
||||||
|
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||||
|
|
||||||
|
//---- Use 32-bit vertex indices (default is 16-bit) is one way to allow large meshes with more than 64K vertices.
|
||||||
|
// Your renderer backend will need to support it (most example renderer backends support both 16/32-bit indices).
|
||||||
|
// Another way to allow large meshes while keeping 16-bit indices is to handle ImDrawCmd::VtxOffset in your renderer.
|
||||||
|
// Read about ImGuiBackendFlags_RendererHasVtxOffset for details.
|
||||||
|
#define ImDrawIdx unsigned int
|
||||||
|
|
||||||
|
//---- Override ImDrawCallback signature (will need to modify renderer backends accordingly)
|
||||||
|
//struct ImDrawList;
|
||||||
|
//struct ImDrawCmd;
|
||||||
|
//typedef void (*MyImDrawCallback)(const ImDrawList* draw_list, const ImDrawCmd* cmd, void* my_renderer_user_data);
|
||||||
|
//#define ImDrawCallback MyImDrawCallback
|
||||||
|
|
||||||
|
//---- Debug Tools: Macro to break in Debugger (we provide a default implementation of this in the codebase)
|
||||||
|
// (use 'Metrics->Tools->Item Picker' to pick widgets with the mouse and break into them for easy debugging.)
|
||||||
|
//#define IM_DEBUG_BREAK IM_ASSERT(0)
|
||||||
|
//#define IM_DEBUG_BREAK __debugbreak()
|
||||||
|
|
||||||
|
//---- Debug Tools: Enable slower asserts
|
||||||
|
//#define IMGUI_DEBUG_PARANOID
|
||||||
|
|
||||||
|
//---- Tip: You can add extra functions within the ImGui:: namespace from anywhere (e.g. your own sources/header files)
|
||||||
|
/*
|
||||||
|
namespace ImGui
|
||||||
|
{
|
||||||
|
void MyFunction(const char* name, MyMatrix44* mtx);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
//#define IMGUI_USER_CONFIG "VMath.h"
|
||||||
|
#include "VMath.h"
|
||||||
|
|
||||||
|
#define IM_VEC2_CLASS_EXTRA \
|
||||||
|
template<typename T> \
|
||||||
|
ImVec2(math::vec2<T> v) noexcept { x = v.x; y = v.y; } \
|
||||||
|
operator math::vec2f() const noexcept { return math::vec2f(x, y); }
|
||||||
|
|
||||||
|
#define IM_VEC4_CLASS_EXTRA \
|
||||||
|
template<typename T> \
|
||||||
|
ImVec4(math::vec4<T> v) noexcept { x = v.x; y = v.y; z = v.z; w = v.w; }\
|
||||||
|
operator math::vec4f() const noexcept { return math::vec4f(x, y, z, w); }
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,212 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/*
|
||||||
|
Color definitions in ImGui are a good starting point,
|
||||||
|
but do not cover all the intricacies of Spectrum's possible colors
|
||||||
|
in controls and widgets.
|
||||||
|
|
||||||
|
One big difference is that ImGui communicates widget activity
|
||||||
|
(hover, pressed) with their background, while spectrum uses a mix
|
||||||
|
of background and border, with border being the most common choice.
|
||||||
|
|
||||||
|
Because of this, we reference extra colors in spectrum from
|
||||||
|
imgui.cpp and imgui_widgets.cpp directly, and to make that work,
|
||||||
|
we need to have them defined at here at compile time.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// Pick one, or have one defined already.
|
||||||
|
#if !defined(SPECTRUM_USE_LIGHT_THEME) && !defined(SPECTRUM_USE_DARK_THEME)
|
||||||
|
//#define SPECTRUM_USE_LIGHT_THEME
|
||||||
|
#define SPECTRUM_USE_DARK_THEME
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace ImGui {
|
||||||
|
namespace Spectrum {
|
||||||
|
// a list of changes introduced to change the look of the widgets.
|
||||||
|
// Collected here as const rather than being magic numbers spread
|
||||||
|
// around imgui.cpp and imgui_widgets.cpp.
|
||||||
|
const float CHECKBOX_BORDER_SIZE = 2.0f;
|
||||||
|
const float CHECKBOX_ROUNDING = 2.0f;
|
||||||
|
|
||||||
|
// Load SourceSansProRegular and sets it as a default font.
|
||||||
|
// You may want to call ImGui::GetIO().Fonts->Clear() before this
|
||||||
|
void LoadFont(float size = 16.0f);
|
||||||
|
|
||||||
|
// Sets the ImGui style to Spectrum
|
||||||
|
void StyleColorsSpectrum();
|
||||||
|
|
||||||
|
namespace { // Unnamed namespace, since we only use this here.
|
||||||
|
unsigned int Color(unsigned int c) {
|
||||||
|
// add alpha.
|
||||||
|
// also swap red and blue channel for some reason.
|
||||||
|
// todo: figure out why, and fix it.
|
||||||
|
const short a = 0xFF;
|
||||||
|
const short r = (c >> 16) & 0xFF;
|
||||||
|
const short g = (c >> 8) & 0xFF;
|
||||||
|
const short b = (c >> 0) & 0xFF;
|
||||||
|
return(a << 24)
|
||||||
|
| (r << 0)
|
||||||
|
| (g << 8)
|
||||||
|
| (b << 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// all colors are from http://spectrum.corp.adobe.com/color.html
|
||||||
|
|
||||||
|
inline unsigned int color_alpha(unsigned int alpha, unsigned int c) {
|
||||||
|
return ((alpha & 0xFF) << 24) | (c & 0x00FFFFFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Static { // static colors
|
||||||
|
const unsigned int NONE = 0x00000000; // transparent
|
||||||
|
const unsigned int WHITE = Color(0xFFFFFF);
|
||||||
|
const unsigned int BLACK = Color(0x000000);
|
||||||
|
const unsigned int GRAY200 = Color(0xF4F4F4);
|
||||||
|
const unsigned int GRAY300 = Color(0xEAEAEA);
|
||||||
|
const unsigned int GRAY400 = Color(0xD3D3D3);
|
||||||
|
const unsigned int GRAY500 = Color(0xBCBCBC);
|
||||||
|
const unsigned int GRAY600 = Color(0x959595);
|
||||||
|
const unsigned int GRAY700 = Color(0x767676);
|
||||||
|
const unsigned int GRAY800 = Color(0x505050);
|
||||||
|
const unsigned int GRAY900 = Color(0x323232);
|
||||||
|
const unsigned int BLUE400 = Color(0x378EF0);
|
||||||
|
const unsigned int BLUE500 = Color(0x2680EB);
|
||||||
|
const unsigned int BLUE600 = Color(0x1473E6);
|
||||||
|
const unsigned int BLUE700 = Color(0x0D66D0);
|
||||||
|
const unsigned int RED400 = Color(0xEC5B62);
|
||||||
|
const unsigned int RED500 = Color(0xE34850);
|
||||||
|
const unsigned int RED600 = Color(0xD7373F);
|
||||||
|
const unsigned int RED700 = Color(0xC9252D);
|
||||||
|
const unsigned int ORANGE400 = Color(0xF29423);
|
||||||
|
const unsigned int ORANGE500 = Color(0xE68619);
|
||||||
|
const unsigned int ORANGE600 = Color(0xDA7B11);
|
||||||
|
const unsigned int ORANGE700 = Color(0xCB6F10);
|
||||||
|
const unsigned int GREEN400 = Color(0x33AB84);
|
||||||
|
const unsigned int GREEN500 = Color(0x2D9D78);
|
||||||
|
const unsigned int GREEN600 = Color(0x268E6C);
|
||||||
|
const unsigned int GREEN700 = Color(0x12805C);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SPECTRUM_USE_LIGHT_THEME
|
||||||
|
const unsigned int GRAY50 = Color(0xFFFFFF);
|
||||||
|
const unsigned int GRAY75 = Color(0xFAFAFA);
|
||||||
|
const unsigned int GRAY100 = Color(0xF5F5F5);
|
||||||
|
const unsigned int GRAY200 = Color(0xEAEAEA);
|
||||||
|
const unsigned int GRAY300 = Color(0xE1E1E1);
|
||||||
|
const unsigned int GRAY400 = Color(0xCACACA);
|
||||||
|
const unsigned int GRAY500 = Color(0xB3B3B3);
|
||||||
|
const unsigned int GRAY600 = Color(0x8E8E8E);
|
||||||
|
const unsigned int GRAY700 = Color(0x707070);
|
||||||
|
const unsigned int GRAY800 = Color(0x4B4B4B);
|
||||||
|
const unsigned int GRAY900 = Color(0x2C2C2C);
|
||||||
|
const unsigned int BLUE400 = Color(0x2680EB);
|
||||||
|
const unsigned int BLUE500 = Color(0x1473E6);
|
||||||
|
const unsigned int BLUE600 = Color(0x0D66D0);
|
||||||
|
const unsigned int BLUE700 = Color(0x095ABA);
|
||||||
|
const unsigned int RED400 = Color(0xE34850);
|
||||||
|
const unsigned int RED500 = Color(0xD7373F);
|
||||||
|
const unsigned int RED600 = Color(0xC9252D);
|
||||||
|
const unsigned int RED700 = Color(0xBB121A);
|
||||||
|
const unsigned int ORANGE400 = Color(0xE68619);
|
||||||
|
const unsigned int ORANGE500 = Color(0xDA7B11);
|
||||||
|
const unsigned int ORANGE600 = Color(0xCB6F10);
|
||||||
|
const unsigned int ORANGE700 = Color(0xBD640D);
|
||||||
|
const unsigned int GREEN400 = Color(0x2D9D78);
|
||||||
|
const unsigned int GREEN500 = Color(0x268E6C);
|
||||||
|
const unsigned int GREEN600 = Color(0x12805C);
|
||||||
|
const unsigned int GREEN700 = Color(0x107154);
|
||||||
|
const unsigned int INDIGO400 = Color(0x6767EC);
|
||||||
|
const unsigned int INDIGO500 = Color(0x5C5CE0);
|
||||||
|
const unsigned int INDIGO600 = Color(0x5151D3);
|
||||||
|
const unsigned int INDIGO700 = Color(0x4646C6);
|
||||||
|
const unsigned int CELERY400 = Color(0x44B556);
|
||||||
|
const unsigned int CELERY500 = Color(0x3DA74E);
|
||||||
|
const unsigned int CELERY600 = Color(0x379947);
|
||||||
|
const unsigned int CELERY700 = Color(0x318B40);
|
||||||
|
const unsigned int MAGENTA400 = Color(0xD83790);
|
||||||
|
const unsigned int MAGENTA500 = Color(0xCE2783);
|
||||||
|
const unsigned int MAGENTA600 = Color(0xBC1C74);
|
||||||
|
const unsigned int MAGENTA700 = Color(0xAE0E66);
|
||||||
|
const unsigned int YELLOW400 = Color(0xDFBF00);
|
||||||
|
const unsigned int YELLOW500 = Color(0xD2B200);
|
||||||
|
const unsigned int YELLOW600 = Color(0xC4A600);
|
||||||
|
const unsigned int YELLOW700 = Color(0xB79900);
|
||||||
|
const unsigned int FUCHSIA400 = Color(0xC038CC);
|
||||||
|
const unsigned int FUCHSIA500 = Color(0xB130BD);
|
||||||
|
const unsigned int FUCHSIA600 = Color(0xA228AD);
|
||||||
|
const unsigned int FUCHSIA700 = Color(0x93219E);
|
||||||
|
const unsigned int SEAFOAM400 = Color(0x1B959A);
|
||||||
|
const unsigned int SEAFOAM500 = Color(0x16878C);
|
||||||
|
const unsigned int SEAFOAM600 = Color(0x0F797D);
|
||||||
|
const unsigned int SEAFOAM700 = Color(0x096C6F);
|
||||||
|
const unsigned int CHARTREUSE400 = Color(0x85D044);
|
||||||
|
const unsigned int CHARTREUSE500 = Color(0x7CC33F);
|
||||||
|
const unsigned int CHARTREUSE600 = Color(0x73B53A);
|
||||||
|
const unsigned int CHARTREUSE700 = Color(0x6AA834);
|
||||||
|
const unsigned int PURPLE400 = Color(0x9256D9);
|
||||||
|
const unsigned int PURPLE500 = Color(0x864CCC);
|
||||||
|
const unsigned int PURPLE600 = Color(0x7A42BF);
|
||||||
|
const unsigned int PURPLE700 = Color(0x6F38B1);
|
||||||
|
#endif
|
||||||
|
#ifdef SPECTRUM_USE_DARK_THEME
|
||||||
|
const unsigned int GRAY50 = Color(0x252525);
|
||||||
|
const unsigned int GRAY75 = Color(0x2F2F2F);
|
||||||
|
const unsigned int GRAY100 = Color(0x323232);
|
||||||
|
const unsigned int GRAY200 = Color(0x393939);
|
||||||
|
const unsigned int GRAY300 = Color(0x3E3E3E);
|
||||||
|
const unsigned int GRAY400 = Color(0x4D4D4D);
|
||||||
|
const unsigned int GRAY500 = Color(0x5C5C5C);
|
||||||
|
const unsigned int GRAY600 = Color(0x7B7B7B);
|
||||||
|
const unsigned int GRAY700 = Color(0x999999);
|
||||||
|
const unsigned int GRAY800 = Color(0xCDCDCD);
|
||||||
|
const unsigned int GRAY900 = Color(0xFFFFFF);
|
||||||
|
const unsigned int BLUE400 = Color(0x2680EB);
|
||||||
|
const unsigned int BLUE500 = Color(0x378EF0);
|
||||||
|
const unsigned int BLUE600 = Color(0x4B9CF5);
|
||||||
|
const unsigned int BLUE700 = Color(0x5AA9FA);
|
||||||
|
const unsigned int RED400 = Color(0xE34850);
|
||||||
|
const unsigned int RED500 = Color(0xEC5B62);
|
||||||
|
const unsigned int RED600 = Color(0xF76D74);
|
||||||
|
const unsigned int RED700 = Color(0xFF7B82);
|
||||||
|
const unsigned int ORANGE400 = Color(0xE68619);
|
||||||
|
const unsigned int ORANGE500 = Color(0xF29423);
|
||||||
|
const unsigned int ORANGE600 = Color(0xF9A43F);
|
||||||
|
const unsigned int ORANGE700 = Color(0xFFB55B);
|
||||||
|
const unsigned int GREEN400 = Color(0x2D9D78);
|
||||||
|
const unsigned int GREEN500 = Color(0x33AB84);
|
||||||
|
const unsigned int GREEN600 = Color(0x39B990);
|
||||||
|
const unsigned int GREEN700 = Color(0x3FC89C);
|
||||||
|
const unsigned int INDIGO400 = Color(0x6767EC);
|
||||||
|
const unsigned int INDIGO500 = Color(0x7575F1);
|
||||||
|
const unsigned int INDIGO600 = Color(0x8282F6);
|
||||||
|
const unsigned int INDIGO700 = Color(0x9090FA);
|
||||||
|
const unsigned int CELERY400 = Color(0x44B556);
|
||||||
|
const unsigned int CELERY500 = Color(0x4BC35F);
|
||||||
|
const unsigned int CELERY600 = Color(0x51D267);
|
||||||
|
const unsigned int CELERY700 = Color(0x58E06F);
|
||||||
|
const unsigned int MAGENTA400 = Color(0xD83790);
|
||||||
|
const unsigned int MAGENTA500 = Color(0xE2499D);
|
||||||
|
const unsigned int MAGENTA600 = Color(0xEC5AAA);
|
||||||
|
const unsigned int MAGENTA700 = Color(0xF56BB7);
|
||||||
|
const unsigned int YELLOW400 = Color(0xDFBF00);
|
||||||
|
const unsigned int YELLOW500 = Color(0xEDCC00);
|
||||||
|
const unsigned int YELLOW600 = Color(0xFAD900);
|
||||||
|
const unsigned int YELLOW700 = Color(0xFFE22E);
|
||||||
|
const unsigned int FUCHSIA400 = Color(0xC038CC);
|
||||||
|
const unsigned int FUCHSIA500 = Color(0xCF3EDC);
|
||||||
|
const unsigned int FUCHSIA600 = Color(0xD951E5);
|
||||||
|
const unsigned int FUCHSIA700 = Color(0xE366EF);
|
||||||
|
const unsigned int SEAFOAM400 = Color(0x1B959A);
|
||||||
|
const unsigned int SEAFOAM500 = Color(0x20A3A8);
|
||||||
|
const unsigned int SEAFOAM600 = Color(0x23B2B8);
|
||||||
|
const unsigned int SEAFOAM700 = Color(0x26C0C7);
|
||||||
|
const unsigned int CHARTREUSE400 = Color(0x85D044);
|
||||||
|
const unsigned int CHARTREUSE500 = Color(0x8EDE49);
|
||||||
|
const unsigned int CHARTREUSE600 = Color(0x9BEC54);
|
||||||
|
const unsigned int CHARTREUSE700 = Color(0xA3F858);
|
||||||
|
const unsigned int PURPLE400 = Color(0x9256D9);
|
||||||
|
const unsigned int PURPLE500 = Color(0x9D64E1);
|
||||||
|
const unsigned int PURPLE600 = Color(0xA873E9);
|
||||||
|
const unsigned int PURPLE700 = Color(0xB483F0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
// dear imgui, v1.86 WIP
|
||||||
|
// (stack layout headers)
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Index of this file:
|
||||||
|
// [SECTION] Stack Layout API
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// [SECTION] Stack Layout API
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace ImGui
|
||||||
|
{
|
||||||
|
IMGUI_API void BeginHorizontal(const char* str_id, const ImVec2& size = ImVec2(0, 0), float align = -1.0f);
|
||||||
|
IMGUI_API void BeginHorizontal(const void* ptr_id, const ImVec2& size = ImVec2(0, 0), float align = -1.0f);
|
||||||
|
IMGUI_API void BeginHorizontal(int id, const ImVec2& size = ImVec2(0, 0), float align = -1);
|
||||||
|
IMGUI_API void EndHorizontal();
|
||||||
|
IMGUI_API void BeginVertical(const char* str_id, const ImVec2& size = ImVec2(0, 0), float align = -1.0f);
|
||||||
|
IMGUI_API void BeginVertical(const void* ptr_id, const ImVec2& size = ImVec2(0, 0), float align = -1.0f);
|
||||||
|
IMGUI_API void BeginVertical(int id, const ImVec2& size = ImVec2(0, 0), float align = -1);
|
||||||
|
IMGUI_API void EndVertical();
|
||||||
|
IMGUI_API void Spring(float weight = 1.0f, float spacing = -1.0f);
|
||||||
|
IMGUI_API void SuspendLayout();
|
||||||
|
IMGUI_API void ResumeLayout();
|
||||||
|
|
||||||
|
} // namespace ImGui
|
||||||
|
|
||||||
|
|
||||||
|
#endif // #ifndef IMGUI_DISABLE
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
// dear imgui, v1.86 WIP
|
||||||
|
// (stack layout headers)
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
Index of this file:
|
||||||
|
// [SECTION] Stack Layout Internal API
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "imgui_internal.h"
|
||||||
|
#include "imgui_stacklayout.h"
|
||||||
|
|
||||||
|
#ifndef IMGUI_DISABLE
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// [SECTION] Stack Layout Internal API
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace ImGuiInternal
|
||||||
|
{
|
||||||
|
IMGUI_API ImGuiLayoutType GetCurrentLayoutType(ImGuiID window_id);
|
||||||
|
IMGUI_API void UpdateItemRect(ImGuiID window_id, const ImVec2& min, const ImVec2& max);
|
||||||
|
|
||||||
|
} // namespace ImGuiInternal
|
||||||
|
|
||||||
|
|
||||||
|
#endif // #ifndef IMGUI_DISABLE
|
||||||
@@ -0,0 +1,627 @@
|
|||||||
|
// [DEAR IMGUI]
|
||||||
|
// This is a slightly modified version of stb_rect_pack.h 1.01.
|
||||||
|
// Grep for [DEAR IMGUI] to find the changes.
|
||||||
|
//
|
||||||
|
// stb_rect_pack.h - v1.01 - public domain - rectangle packing
|
||||||
|
// Sean Barrett 2014
|
||||||
|
//
|
||||||
|
// Useful for e.g. packing rectangular textures into an atlas.
|
||||||
|
// Does not do rotation.
|
||||||
|
//
|
||||||
|
// Before #including,
|
||||||
|
//
|
||||||
|
// #define STB_RECT_PACK_IMPLEMENTATION
|
||||||
|
//
|
||||||
|
// in the file that you want to have the implementation.
|
||||||
|
//
|
||||||
|
// 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
|
||||||
|
// Fabian Giesen
|
||||||
|
//
|
||||||
|
// Version history:
|
||||||
|
//
|
||||||
|
// 1.01 (2021-07-11) always use large rect mode, expose STBRP__MAXVAL in public section
|
||||||
|
// 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles
|
||||||
|
// 0.99 (2019-02-07) warning fixes
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
typedef int stbrp_coord;
|
||||||
|
|
||||||
|
#define STBRP__MAXVAL 0x7fffffff
|
||||||
|
// Mostly for internal use, but this is the maximum supported coordinate value.
|
||||||
|
|
||||||
|
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 <stdlib.h>
|
||||||
|
#define STBRP_SORT qsort
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef STBRP_ASSERT
|
||||||
|
#include <assert.h>
|
||||||
|
#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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
context->extra[1].y = (1<<30);
|
||||||
|
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);
|
||||||
|
|
||||||
|
// if it can't possibly fit, bail immediately
|
||||||
|
if (width > c->width || height > c->height) {
|
||||||
|
fr.prev_link = NULL;
|
||||||
|
fr.x = fr.y = 0;
|
||||||
|
return fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
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); [DEAR IMGUI]
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,21 @@
|
|||||||
|
// dear imgui: wrappers for C++ standard library (STL) types (std::string, etc.)
|
||||||
|
// This is also an example of how you may wrap your own similar types.
|
||||||
|
|
||||||
|
// Changelog:
|
||||||
|
// - v0.10: Initial version. Added InputText() / InputTextMultiline() calls with std::string
|
||||||
|
|
||||||
|
// See more C++ related extension (fmt, RAII, syntaxis sugar) on Wiki:
|
||||||
|
// https://github.com/ocornut/imgui/wiki/Useful-Extensions#cness
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <tl/string.h>
|
||||||
|
|
||||||
|
namespace ImGui
|
||||||
|
{
|
||||||
|
// ImGui::InputText() with std::string
|
||||||
|
// Because text input needs dynamic resizing, we need to setup a callback to grow the capacity
|
||||||
|
IMGUI_API bool InputText(const char* label, eastl::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
|
||||||
|
IMGUI_API bool InputTextMultiline(const char* label, eastl::string* str, const ImVec2& size = ImVec2(0, 0), ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
|
||||||
|
IMGUI_API bool InputTextWithHint(const char* label, const char* hint, eastl::string* str, ImGuiInputTextFlags flags = 0, ImGuiInputTextCallback callback = NULL, void* user_data = NULL);
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
// dear imgui: FreeType font builder (used as a replacement for the stb_truetype builder)
|
||||||
|
// (headers)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "IUI/ImGui/imgui.h" // IMGUI_API
|
||||||
|
|
||||||
|
// Forward declarations
|
||||||
|
struct ImFontAtlas;
|
||||||
|
struct ImFontBuilderIO;
|
||||||
|
|
||||||
|
// Hinting greatly impacts visuals (and glyph sizes).
|
||||||
|
// - By default, hinting is enabled and the font's native hinter is preferred over the auto-hinter.
|
||||||
|
// - When disabled, FreeType generates blurrier glyphs, more or less matches the stb_truetype.h
|
||||||
|
// - The Default hinting mode usually looks good, but may distort glyphs in an unusual way.
|
||||||
|
// - The Light hinting mode generates fuzzier glyphs but better matches Microsoft's rasterizer.
|
||||||
|
// You can set those flags globaly in ImFontAtlas::FontBuilderFlags
|
||||||
|
// You can set those flags on a per font basis in ImFontConfig::FontBuilderFlags
|
||||||
|
enum ImGuiFreeTypeBuilderFlags
|
||||||
|
{
|
||||||
|
ImGuiFreeTypeBuilderFlags_NoHinting = 1 << 0, // Disable hinting. This generally generates 'blurrier' bitmap glyphs when the glyph are rendered in any of the anti-aliased modes.
|
||||||
|
ImGuiFreeTypeBuilderFlags_NoAutoHint = 1 << 1, // Disable auto-hinter.
|
||||||
|
ImGuiFreeTypeBuilderFlags_ForceAutoHint = 1 << 2, // Indicates that the auto-hinter is preferred over the font's native hinter.
|
||||||
|
ImGuiFreeTypeBuilderFlags_LightHinting = 1 << 3, // A lighter hinting algorithm for gray-level modes. Many generated glyphs are fuzzier but better resemble their original shape. This is achieved by snapping glyphs to the pixel grid only vertically (Y-axis), as is done by Microsoft's ClearType and Adobe's proprietary font renderer. This preserves inter-glyph spacing in horizontal text.
|
||||||
|
ImGuiFreeTypeBuilderFlags_MonoHinting = 1 << 4, // Strong hinting algorithm that should only be used for monochrome output.
|
||||||
|
ImGuiFreeTypeBuilderFlags_Bold = 1 << 5, // Styling: Should we artificially embolden the font?
|
||||||
|
ImGuiFreeTypeBuilderFlags_Oblique = 1 << 6, // Styling: Should we slant the font, emulating italic style?
|
||||||
|
ImGuiFreeTypeBuilderFlags_Monochrome = 1 << 7, // Disable anti-aliasing. Combine this with MonoHinting for best results!
|
||||||
|
ImGuiFreeTypeBuilderFlags_LoadColor = 1 << 8, // Enable FreeType color-layered glyphs
|
||||||
|
ImGuiFreeTypeBuilderFlags_Bitmap = 1 << 9 // Enable FreeType bitmap glyphs
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace ImGuiFreeType
|
||||||
|
{
|
||||||
|
// This is automatically assigned when using '#define IMGUI_ENABLE_FREETYPE'.
|
||||||
|
// If you need to dynamically select between multiple builders:
|
||||||
|
// - you can manually assign this builder with 'atlas->FontBuilderIO = ImGuiFreeType::GetBuilderForFreeType()'
|
||||||
|
// - prefer deep-copying this into your own ImFontBuilderIO instance if you use hot-reloading that messes up static data.
|
||||||
|
IMGUI_API const ImFontBuilderIO* GetBuilderForFreeType();
|
||||||
|
|
||||||
|
// Override allocators. By default ImGuiFreeType will use IM_ALLOC()/IM_FREE()
|
||||||
|
// However, as FreeType does lots of allocations we provide a way for the user to redirect it to a separate memory heap if desired.
|
||||||
|
IMGUI_API void SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data = nullptr);
|
||||||
|
|
||||||
|
// Obsolete names (will be removed soon)
|
||||||
|
// Prefer using '#define IMGUI_ENABLE_FREETYPE'
|
||||||
|
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
|
||||||
|
//static inline bool BuildFontAtlas(ImFontAtlas* atlas, unsigned int flags = 0) { atlas->FontBuilderIO = GetBuilderForFreeType(); atlas->FontBuilderFlags = flags; return atlas->Build(); } // Prefer using '#define IMGUI_ENABLE_FREETYPE'
|
||||||
|
#endif
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "IUI/ImGui/imgui.h"
|
||||||
|
#include "tl/narrow_cast.h"
|
||||||
|
#include "Kit/util/Color.h"
|
||||||
|
#include "IUI/Icons.h"
|
||||||
|
#include "Kit/util/IdentifierUtils.h"
|
||||||
|
#include "extras/imguiwrap.dear.h"
|
||||||
|
#include "tl/enum.h"
|
||||||
|
#include "tl/variant.h"
|
||||||
|
|
||||||
|
namespace ImGui
|
||||||
|
{
|
||||||
|
|
||||||
|
inline void PushID(size_t id) { PushID(static_cast<int>(id)); }
|
||||||
|
|
||||||
|
ImU32 ColorU32(kit::color::sRGBAi c);
|
||||||
|
ImU32 ColorU32(kit::color::sRGBi c);
|
||||||
|
|
||||||
|
void AddDottedLine(ImDrawList& list, ImVec2 p1, ImVec2 p2, ImU32 col, float dotLength, float thickness = 1.0f);
|
||||||
|
|
||||||
|
ImVec2 GetWindowContentPos();
|
||||||
|
ImVec2 GetWindowContentSize();
|
||||||
|
ImVec2 GetWindowContentBottomRightSide();
|
||||||
|
|
||||||
|
int8_t GetMouseButtonClicked();
|
||||||
|
int8_t GetMouseButtonDown();
|
||||||
|
int8_t GetMouseButtonReleased();
|
||||||
|
|
||||||
|
bool ColorEditor(kit::color::lRGBAhdr& io_color, bool& saveUndo, bool isHDR, bool hasAlpha, const tl::string& label, float alignNameX) noexcept;
|
||||||
|
bool CheckBoxTristate(const char* label, int* v_tristate);
|
||||||
|
|
||||||
|
bool BeginIconMenu(kit::icons::Icon icon, const char* label);
|
||||||
|
bool BeginIconMenu(kit::icons::FontIcon icon, const char* label);
|
||||||
|
bool MenuIconItem(kit::icons::Icon icon, const char* label, bool* selected = nullptr);
|
||||||
|
bool MenuIconItem(kit::icons::FontIcon icon, const char* label, bool* selected = nullptr);
|
||||||
|
|
||||||
|
void Icon(kit::icons::Icon icon, ImVec2 frameSize, ImVec2 size = ImVec2(0, 0), ImVec4 tint_col = ImVec4(1,1,1,1), ImVec4 border_col = ImVec4(0,0,0,0));
|
||||||
|
void Icon(kit::icons::FontIcon icon, ImVec2 frameSize, ImVec2 size = ImVec2(0, 0), ImVec4 tint_col = ImVec4(1,1,1,1), ImVec4 border_col = ImVec4(0,0,0,0));
|
||||||
|
bool IconButton(const char* str_id, kit::icons::Icon icon, ImVec2 size, bool active = false, ImVec4 bg_col = ImVec4(0,0,0,0), ImVec4 tint_col = ImVec4(1,1,1,1)); // <0 frame_padding uses default frame padding settings. 0 for no padding
|
||||||
|
bool IconButton(const char* str_id, kit::icons::FontIcon icon, const char* label, bool active = false);
|
||||||
|
|
||||||
|
bool ToggleButton(const char* str_id, bool* v);
|
||||||
|
bool Splitter(bool split_vertically, float thickness, float* size1, float* size2, float min_size1, float min_size2, float splitter_long_axis_size = -1.0f);
|
||||||
|
|
||||||
|
void BeginGroupPanel(const char* name, const ImVec2& size);
|
||||||
|
void EndGroupPanel();
|
||||||
|
|
||||||
|
template<typename Enum>
|
||||||
|
bool EnumCombo(const char* str_id, Enum& crtValue, bool humanReadable)
|
||||||
|
{
|
||||||
|
bool changed = false;
|
||||||
|
const tl::string preview = humanReadable ? kit::util::identifierToHumanReadableString(tl::string(tl::enum_name(crtValue))) : tl::string(tl::enum_name(crtValue));
|
||||||
|
if (const dear::Combo combo(str_id, preview.c_str(), ImGuiComboFlags_None); combo)
|
||||||
|
{
|
||||||
|
for (auto e: tl::enum_entries<Enum>())
|
||||||
|
{
|
||||||
|
tl::string label = humanReadable ? kit::util::identifierToHumanReadableString(tl::string(e.second)) : tl::string(e.second);
|
||||||
|
//int32_t value = static_cast<int32_t>(e.first);
|
||||||
|
PushID(tl::narrow<int>(e.first));
|
||||||
|
const bool itemSelected = crtValue == e.first;
|
||||||
|
if (Selectable(label.c_str(), itemSelected))
|
||||||
|
{
|
||||||
|
crtValue = e.first;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
if (itemSelected)
|
||||||
|
SetItemDefaultFocus();
|
||||||
|
PopID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddUnderLine(ImColor col);
|
||||||
|
void TextURL(const char* name, const char* url);
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Kit/EngineManager.h"
|
||||||
|
#include <tl/vector.h>
|
||||||
|
|
||||||
|
#include <filament/IndexBuffer.h>
|
||||||
|
#include <filament/Material.h>
|
||||||
|
#include <filament/MaterialInstance.h>
|
||||||
|
#include <filament/Texture.h>
|
||||||
|
#include <filament/VertexBuffer.h>
|
||||||
|
#include <utils/Entity.h>
|
||||||
|
|
||||||
|
#include "IUI/ImGui/imgui.h"
|
||||||
|
|
||||||
|
struct ImDrawData;
|
||||||
|
struct ImGuiIO;
|
||||||
|
struct ImGuiContext;
|
||||||
|
|
||||||
|
namespace kit
|
||||||
|
{
|
||||||
|
|
||||||
|
// Translates ImGui's draw commands into Filament primitives, textures, vertex buffers, etc.
|
||||||
|
// Creates a UI-specific Scene object and populates it with a Renderable. Does not handle
|
||||||
|
// event processing; clients can simply call ImGui::GetIO() directly and set the mouse state.
|
||||||
|
class ImGuiFilamentBridge
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// The constructor creates its own Scene and places it in the given View.
|
||||||
|
ImGuiFilamentBridge(tl::lent_ref<EngineManager> engineManager, tl::lent_ref<filament::Scene> flmScene);
|
||||||
|
~ImGuiFilamentBridge();
|
||||||
|
|
||||||
|
// Low-level alternative to render() that consumes an ImGui command list and translates it into
|
||||||
|
// various Filament calls. This includes updating the vertex buffer, setting up material
|
||||||
|
// instances, and rebuilding the Renderable component that encompasses the entire UI. Since this
|
||||||
|
// makes Filament calls, it must be called from the main thread.
|
||||||
|
void processImGuiCommands(ImDrawData& commands, const ImGuiIO& io);
|
||||||
|
|
||||||
|
// Helper method called after resolving fontPath; public so fonts can be added by caller.
|
||||||
|
ImTextureID createAtlasTexture();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void createBuffers(int numRequiredBuffers);
|
||||||
|
void populateVertexData(size_t bufferIndex,
|
||||||
|
size_t vbSizeInBytes,
|
||||||
|
void* vbData,
|
||||||
|
size_t ibSizeInBytes,
|
||||||
|
void* ibData);
|
||||||
|
tl::unique_ref<filament::VertexBuffer> createVertexBuffer(size_t capacity) const;
|
||||||
|
tl::unique_ref<filament::IndexBuffer> createIndexBuffer(size_t capacity) const;
|
||||||
|
tl::lent_ref<EngineManager> m_engineManager;
|
||||||
|
tl::lent_ref<filament::Scene> m_scene;
|
||||||
|
tl::unique_ptr<filament::Material> m_material;
|
||||||
|
tl::unique_ptr<filament::Texture> m_texture;
|
||||||
|
|
||||||
|
tl::vector<tl::unique_ref<filament::VertexBuffer>> m_vertexBuffers;
|
||||||
|
tl::vector<tl::unique_ref<filament::IndexBuffer>> m_indexBuffers;
|
||||||
|
tl::vector<tl::unique_ref<filament::MaterialInstance>> m_materialInstances;
|
||||||
|
utils::Entity m_renderable;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef FILAGUI_IMGUIMATH_H_
|
||||||
|
#define FILAGUI_IMGUIMATH_H_
|
||||||
|
|
||||||
|
#include <imgui.h>
|
||||||
|
|
||||||
|
static ImVec2 operator+(const ImVec2& lhs, const ImVec2& rhs) {
|
||||||
|
return { lhs.x+rhs.x, lhs.y+rhs.y };
|
||||||
|
}
|
||||||
|
|
||||||
|
static ImVec2 operator-(const ImVec2& lhs, const ImVec2& rhs) {
|
||||||
|
return { lhs.x-rhs.x, lhs.y-rhs.y };
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* FILAGUI_IMGUIMATH_H_ */
|
||||||
@@ -0,0 +1,451 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <utility> // std::forward etc
|
||||||
|
#include "IUI/ImGui/imgui.h"
|
||||||
|
#include <tl/string.h>
|
||||||
|
#include "imguiwrap.helpers.h"
|
||||||
|
|
||||||
|
namespace dear
|
||||||
|
{
|
||||||
|
static const ImVec2 Zero(0.0f, 0.0f);
|
||||||
|
|
||||||
|
// EditTableFlags provides a window with checkboxes/selects for all of the
|
||||||
|
// ImGuiTableFlags options so that a flags property can be edited in real-time.
|
||||||
|
extern void
|
||||||
|
EditTableFlags(const char* editWindowTitle, bool* showing, ImGuiTableFlags* flags) noexcept;
|
||||||
|
|
||||||
|
// EditWindowFlags presents a window for selecting text input field flags.
|
||||||
|
extern void
|
||||||
|
EditInputTextFlags(const char* title, bool* showing, ImGuiInputTextFlags* flags) noexcept;
|
||||||
|
|
||||||
|
// EditWindowFlags provides a window which checkboxes for all of the
|
||||||
|
// ImGuiWindowFlags options so that a flags property can be edited in real-time.
|
||||||
|
extern void
|
||||||
|
EditWindowFlags(const char* editWindowTitle, bool* showing, ImGuiWindowFlags* flags) noexcept;
|
||||||
|
|
||||||
|
// SetHostWindowSize lets you alter the native window dimensions from within your `imgui_main`
|
||||||
|
// callback. Change is applied after control returns to the imgui_main function, and the
|
||||||
|
// sizing from the last call within a frame is used.
|
||||||
|
extern void SetHostWindowSize(int x, int y) noexcept;
|
||||||
|
|
||||||
|
// scoped_effect is a helper that uses automatic object lifetime to control
|
||||||
|
// the invocation of a callable after potentially calling additional code,
|
||||||
|
// allowing for easy inline creation of scope guards.
|
||||||
|
//
|
||||||
|
// On its own, it does nothing but call the supplied function when it is
|
||||||
|
// destroyed;
|
||||||
|
template<typename Base, bool ForceDtor = false>
|
||||||
|
struct ScopeWrapper
|
||||||
|
{
|
||||||
|
using wrapped_type = Base;
|
||||||
|
using self_type = ScopeWrapper<Base>;
|
||||||
|
|
||||||
|
static constexpr bool force_dtor = ForceDtor;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
const bool ok_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// constructor takes a predicate that may be used to determine if
|
||||||
|
// additional calls can be made, and a function/lambda/callable to
|
||||||
|
// be invoked from the destructor.
|
||||||
|
constexpr ScopeWrapper(bool ok) noexcept : ok_{ok} {}
|
||||||
|
|
||||||
|
// destructor always invokes the supplied destructor function.
|
||||||
|
~ScopeWrapper() noexcept
|
||||||
|
{
|
||||||
|
if constexpr (!force_dtor) {
|
||||||
|
if (!ok_)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Base::dtor();
|
||||||
|
}
|
||||||
|
|
||||||
|
// operator&& will excute 'code' if the predicate supplied during
|
||||||
|
// construction was true.
|
||||||
|
template<typename PassthruFn>
|
||||||
|
constexpr bool operator&&(PassthruFn passthru) const noexcept
|
||||||
|
{
|
||||||
|
if (ok_)
|
||||||
|
passthru();
|
||||||
|
return ok_;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr operator bool() const noexcept { return ok_; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ScopeWrapper(const ScopeWrapper&) = delete;
|
||||||
|
ScopeWrapper& operator=(const ScopeWrapper&) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wrapper for ImGui::Begin ... End, which will always call End.
|
||||||
|
struct Begin : public ScopeWrapper<Begin, true>
|
||||||
|
{
|
||||||
|
// Invoke Begin and guarantee that 'End' will be called.
|
||||||
|
Begin(const char* title, bool* open = nullptr, ImGuiWindowFlags flags = 0) noexcept
|
||||||
|
: ScopeWrapper(ImGui::Begin(title, open, flags))
|
||||||
|
{}
|
||||||
|
static void dtor() noexcept { ImGui::End(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wrapper for ImGui::BeginChild ... EndChild, which will always call EndChild.
|
||||||
|
struct Child : public ScopeWrapper<Child, true>
|
||||||
|
{
|
||||||
|
Child(const char* title, const ImVec2& size = Zero, bool border = false,
|
||||||
|
ImGuiWindowFlags flags = 0) noexcept
|
||||||
|
: ScopeWrapper(ImGui::BeginChild(title, size, border, flags))
|
||||||
|
{}
|
||||||
|
Child(ImGuiID id, const ImVec2& size = Zero, bool border = false,
|
||||||
|
ImGuiWindowFlags flags = 0) noexcept
|
||||||
|
: ScopeWrapper(ImGui::BeginChild(id, size, border, flags))
|
||||||
|
{}
|
||||||
|
static void dtor() noexcept { ImGui::EndChild(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wrapper for ImGui::BeginChildFrame ... EndChildFrame, which will always call EndChildFrame.
|
||||||
|
struct ChildFrame : public ScopeWrapper<ChildFrame, true>
|
||||||
|
{
|
||||||
|
template<typename... Args>
|
||||||
|
ChildFrame(Args&&... args) noexcept
|
||||||
|
: ScopeWrapper(ImGui::BeginChildFrame(std::forward<Args>(args)...))
|
||||||
|
{}
|
||||||
|
static void dtor() noexcept { ImGui::EndChildFrame(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wrapper for ImGui::BeginGroup ... EndGroup which will always call EndGroup.
|
||||||
|
struct Group : public ScopeWrapper<Group, true>
|
||||||
|
{
|
||||||
|
Group() noexcept : ScopeWrapper(true) { ImGui::BeginGroup(); }
|
||||||
|
static void dtor() noexcept { ImGui::EndGroup(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wrapper for ImGui::Begin...EndCombo.
|
||||||
|
struct Combo : public ScopeWrapper<Combo>
|
||||||
|
{
|
||||||
|
Combo(const char* label, const char* preview, ImGuiComboFlags flags = 0) noexcept
|
||||||
|
: ScopeWrapper(ImGui::BeginCombo(label, preview, flags))
|
||||||
|
{}
|
||||||
|
static void dtor() noexcept { ImGui::EndCombo(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wrapper for ImGui::Begin...EndListBox.
|
||||||
|
struct ListBox : public ScopeWrapper<ListBox>
|
||||||
|
{
|
||||||
|
ListBox(const char* label, const ImVec2& size = Zero) noexcept
|
||||||
|
: ScopeWrapper(ImGui::BeginListBox(label, size))
|
||||||
|
{}
|
||||||
|
static void dtor() noexcept { ImGui::EndListBox(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ContextMenuWindow : public ScopeWrapper<ContextMenuWindow>
|
||||||
|
{
|
||||||
|
ContextMenuWindow(const char* str_id, ImGuiPopupFlags popup_flags = ImGuiPopupFlags_MouseButtonRight) noexcept
|
||||||
|
: ScopeWrapper(ImGui::BeginPopupContextWindow(str_id, popup_flags))
|
||||||
|
{
|
||||||
|
if (ok_)
|
||||||
|
{
|
||||||
|
auto spacing = ImGui::GetStyle().ItemSpacing;
|
||||||
|
spacing.y *= 2.f;
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, spacing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static void dtor() noexcept
|
||||||
|
{
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ContextMenu : public ScopeWrapper<ContextMenu>
|
||||||
|
{
|
||||||
|
ContextMenu(bool windowOrItem, const char* str_id, ImGuiPopupFlags popup_flags = ImGuiPopupFlags_MouseButtonRight) noexcept
|
||||||
|
: ScopeWrapper(windowOrItem ? ImGui::BeginPopupContextWindow(str_id, popup_flags) : ImGui::BeginPopupContextItem(str_id, popup_flags))
|
||||||
|
{
|
||||||
|
if (ok_)
|
||||||
|
{
|
||||||
|
auto spacing = ImGui::GetStyle().ItemSpacing;
|
||||||
|
spacing.y *= 2.f;
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, spacing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static void dtor() noexcept
|
||||||
|
{
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
ImGui::EndPopup();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wrapper for ImGui::Begin...EndMenuBar.
|
||||||
|
struct MenuBar : public ScopeWrapper<MenuBar>
|
||||||
|
{
|
||||||
|
MenuBar() noexcept : ScopeWrapper(ImGui::BeginMenuBar()) {}
|
||||||
|
static void dtor() noexcept { ImGui::EndMenuBar(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wrapper for ImGui::Begin...EndMainMenuBar.
|
||||||
|
struct MainMenuBar : public ScopeWrapper<MainMenuBar>
|
||||||
|
{
|
||||||
|
MainMenuBar() noexcept : ScopeWrapper(ImGui::BeginMainMenuBar()) {}
|
||||||
|
static void dtor() noexcept { ImGui::EndMainMenuBar(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wrapper for ImGui::BeginMenu...ImGui::EndMenu.
|
||||||
|
struct Menu : public ScopeWrapper<Menu>
|
||||||
|
{
|
||||||
|
Menu(const char* label, bool enabled = true) noexcept
|
||||||
|
: ScopeWrapper(ImGui::BeginMenu(label, enabled))
|
||||||
|
{}
|
||||||
|
static void dtor() noexcept { ImGui::EndMenu(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wrapper for ImGui::BeginTable...ImGui::EndTable.
|
||||||
|
// See also EditTableFlags.
|
||||||
|
struct Table : public ScopeWrapper<Table>
|
||||||
|
{
|
||||||
|
Table(const char* str_id, int column, ImGuiTableFlags flags = 0,
|
||||||
|
const ImVec2& outer_size = Zero, float inner_width = 0.0f) noexcept
|
||||||
|
: ScopeWrapper(ImGui::BeginTable(str_id, column, flags, outer_size, inner_width))
|
||||||
|
{}
|
||||||
|
static void dtor() noexcept { ImGui::EndTable(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wrapper for ImGui::Begin...EndToolTip.
|
||||||
|
struct Tooltip : public ScopeWrapper<Tooltip>
|
||||||
|
{
|
||||||
|
Tooltip() noexcept : ScopeWrapper(true) { ImGui::BeginTooltip(); }
|
||||||
|
static void dtor() noexcept { ImGui::EndTooltip(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wrapper around ImGui::CollapsingHeader to allow consistent code styling.
|
||||||
|
struct CollapsingHeader : public ScopeWrapper<CollapsingHeader>
|
||||||
|
{
|
||||||
|
CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags = 0) noexcept
|
||||||
|
: ScopeWrapper(ImGui::CollapsingHeader(label, flags))
|
||||||
|
{}
|
||||||
|
inline static void dtor() noexcept {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wrapper for ImGui::TreeNode...ImGui::TreePop.
|
||||||
|
// See also SeparatedTreeNode.
|
||||||
|
struct TreeNode : public ScopeWrapper<TreeNode>
|
||||||
|
{
|
||||||
|
TreeNode(const char* label) noexcept : ScopeWrapper(ImGui::TreeNode(label)) {}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
TreeNode(const char* str_id, const char* fmt, Args&&... args) noexcept : ScopeWrapper(ImGui::TreeNode(str_id, fmt, std::forward<Args>(args)...)) {}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
TreeNode(const void* ptr_id, const char* fmt, Args&&... args) noexcept : ScopeWrapper(ImGui::TreeNode(ptr_id, fmt, std::forward<Args>(args)...)) {}
|
||||||
|
|
||||||
|
TreeNode(const char* label, ImGuiTreeNodeFlags flags) noexcept : ScopeWrapper(ImGui::TreeNodeEx(label, flags)) {}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
TreeNode(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, Args&&... args) noexcept : ScopeWrapper(ImGui::TreeNodeEx(str_id, flags, fmt, std::forward<Args>(args)...)) {}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
TreeNode(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, Args&&... args) noexcept : ScopeWrapper(ImGui::TreeNodeEx(ptr_id, flags, fmt, std::forward<Args>(args)...)) {}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
TreeNode(const char* str_id, ImGuiTreeNodeFlags flags, const char* label) noexcept : ScopeWrapper(ImGui::TreeNode(str_id, flags, label)) {}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
TreeNode(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* label) noexcept : ScopeWrapper(ImGui::TreeNode(ptr_id, flags, label)) {}
|
||||||
|
|
||||||
|
static void dtor() noexcept { ImGui::TreePop(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wrapper around a TreeNode followed by a Separator (it's a fairly common sequence).
|
||||||
|
struct SeparatedTreeNode : public ScopeWrapper<SeparatedTreeNode>
|
||||||
|
{
|
||||||
|
template<typename... Args>
|
||||||
|
SeparatedTreeNode(Args&&... args) noexcept
|
||||||
|
: ScopeWrapper(ImGui::TreeNode(std::forward<Args>(args)...))
|
||||||
|
{}
|
||||||
|
static void dtor() noexcept
|
||||||
|
{
|
||||||
|
ImGui::TreePop();
|
||||||
|
ImGui::Separator();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Popup provides the stock wrapper around ImGui::BeginPopup...ImGui::EndPopup as well as two
|
||||||
|
// methods of instantiating a modal, for those who want modality to be a property fo Popup
|
||||||
|
// rather than a discrete type.
|
||||||
|
struct Popup : public ScopeWrapper<Popup>
|
||||||
|
{
|
||||||
|
// Non-modal Popup.
|
||||||
|
Popup(const char* str_id, ImGuiWindowFlags flags = 0) noexcept
|
||||||
|
: ScopeWrapper(ImGui::BeginPopup(str_id, flags))
|
||||||
|
{}
|
||||||
|
|
||||||
|
// Modal popups.
|
||||||
|
|
||||||
|
// imguiwrap provides 3 ways to construct a modal popup:
|
||||||
|
// - Use the PopupModal class,
|
||||||
|
// - Use Popup(modal{}, ...)
|
||||||
|
// - Use the static method Popup::Modal(...)
|
||||||
|
|
||||||
|
struct modal
|
||||||
|
{
|
||||||
|
};
|
||||||
|
Popup(modal, const char* name, bool* p_open = nullptr, ImGuiWindowFlags flags = 0) noexcept
|
||||||
|
: ScopeWrapper(ImGui::BeginPopupModal(name, p_open, flags))
|
||||||
|
{}
|
||||||
|
|
||||||
|
static Popup
|
||||||
|
Modal(const char* name, bool* p_open = nullptr, ImGuiWindowFlags flags = 0) noexcept
|
||||||
|
{
|
||||||
|
return Popup(modal{}, name, p_open, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dtor() noexcept { ImGui::EndPopup(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wrapper around ImGui's BeginPopupModal ... EndPopup sequence.
|
||||||
|
struct PopupModal : public ScopeWrapper<PopupModal>
|
||||||
|
{
|
||||||
|
PopupModal(const char* name, bool* p_open = nullptr, ImGuiWindowFlags flags = 0) noexcept
|
||||||
|
: ScopeWrapper(ImGui::BeginPopupModal(name, p_open, flags))
|
||||||
|
{}
|
||||||
|
static void dtor() noexcept { ImGui::EndPopup(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wrapper for ImGui::BeginTabBar ... EndTabBar
|
||||||
|
struct TabBar : public ScopeWrapper<TabBar>
|
||||||
|
{
|
||||||
|
TabBar(const char* name, ImGuiTabBarFlags flags = 0) noexcept
|
||||||
|
: ScopeWrapper(ImGui::BeginTabBar(name, flags))
|
||||||
|
{}
|
||||||
|
static void dtor() noexcept { ImGui::EndTabBar(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wrapper for ImGui::BeginTabItem ... EndTabItem
|
||||||
|
struct TabItem : public ScopeWrapper<TabItem>
|
||||||
|
{
|
||||||
|
TabItem(const char* name, bool* open = nullptr, ImGuiTabItemFlags flags = 0) noexcept
|
||||||
|
: ScopeWrapper(ImGui::BeginTabItem(name, open, flags))
|
||||||
|
{}
|
||||||
|
static void dtor() noexcept { ImGui::EndTabItem(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wrapper around pushing a style var onto ImGui's stack and popping it back off.
|
||||||
|
/// TODO: Support nesting so we can do a single pop operation.
|
||||||
|
struct WithStyleVar : public ScopeWrapper<WithStyleVar>
|
||||||
|
{
|
||||||
|
WithStyleVar(ImGuiStyleVar idx, const ImVec2& val) noexcept : ScopeWrapper(true)
|
||||||
|
{
|
||||||
|
ImGui::PushStyleVar(idx, val);
|
||||||
|
}
|
||||||
|
WithStyleVar(ImGuiStyleVar idx, float val = 0.0f) noexcept : ScopeWrapper(true)
|
||||||
|
{
|
||||||
|
ImGui::PushStyleVar(idx, val);
|
||||||
|
}
|
||||||
|
static void dtor() noexcept { ImGui::PopStyleVar(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// TODO: WithStyleColor
|
||||||
|
|
||||||
|
// Wrapper for BeginTooltip predicated on the previous item being hovered.
|
||||||
|
struct ItemTooltip : public ScopeWrapper<ItemTooltip>
|
||||||
|
{
|
||||||
|
ItemTooltip(ImGuiHoveredFlags flags = 0) noexcept
|
||||||
|
: ScopeWrapper(ImGui::IsItemHovered(flags))
|
||||||
|
{
|
||||||
|
if (ok_)
|
||||||
|
ImGui::BeginTooltip();
|
||||||
|
}
|
||||||
|
static void dtor() noexcept { ImGui::EndTooltip(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Wrapper for BeginTooltip predicated on the previous item being hovered.
|
||||||
|
struct ScopedItemWidth : public ScopeWrapper<ScopedItemWidth, true>
|
||||||
|
{
|
||||||
|
ScopedItemWidth(float item_width) noexcept
|
||||||
|
: ScopeWrapper(true)
|
||||||
|
{
|
||||||
|
ImGui::PushItemWidth(item_width);
|
||||||
|
}
|
||||||
|
static void dtor() noexcept { ImGui::PopItemWidth(); }
|
||||||
|
};
|
||||||
|
struct ScopedDisabled
|
||||||
|
{
|
||||||
|
ScopedDisabled(bool disabled)
|
||||||
|
: disabled(disabled)
|
||||||
|
{
|
||||||
|
if (disabled)
|
||||||
|
ImGui::BeginDisabled(disabled);
|
||||||
|
}
|
||||||
|
~ScopedDisabled()
|
||||||
|
{
|
||||||
|
if (disabled)
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
}
|
||||||
|
bool disabled;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//// Text helpers
|
||||||
|
|
||||||
|
// std::string helpers.
|
||||||
|
#ifndef DEAR_NO_STRING
|
||||||
|
static inline void Text(const tl::string& str) noexcept
|
||||||
|
{
|
||||||
|
ImGui::TextUnformatted(str.c_str(), str.c_str() + str.length());
|
||||||
|
}
|
||||||
|
inline void TextUnformatted(const tl::string& str) noexcept
|
||||||
|
{
|
||||||
|
ImGui::TextUnformatted(str.c_str(), str.c_str() + str.length());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
MenuItem(const char* text, bool selected = false, bool enabled = true) noexcept
|
||||||
|
{
|
||||||
|
return ImGui::MenuItem(text, nullptr, selected, enabled);
|
||||||
|
}
|
||||||
|
static inline bool MenuItem(const char* text, bool* selected, bool enabled = true) noexcept
|
||||||
|
{
|
||||||
|
return ImGui::MenuItem(text, nullptr, selected, enabled);
|
||||||
|
}
|
||||||
|
#ifndef DEAR_NO_STRING
|
||||||
|
static inline bool MenuItem(const tl::string& str, const char* shortcut = nullptr,
|
||||||
|
bool selected = false, bool enabled = true) noexcept
|
||||||
|
{
|
||||||
|
return ImGui::MenuItem(str.c_str(), shortcut, selected, enabled);
|
||||||
|
}
|
||||||
|
static inline bool
|
||||||
|
MenuItem(const tl::string& text, bool* selected, bool enabled = true) noexcept
|
||||||
|
{
|
||||||
|
return ImGui::MenuItem(text.c_str(), nullptr, selected, enabled);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// static inline bool Selectable(const char *text) noexcept { return imgui::Selectable(text); }
|
||||||
|
static inline bool
|
||||||
|
Selectable(const char* label, bool selected = false, ImGuiSelectableFlags flags = 0,
|
||||||
|
const ImVec2& size = Zero) noexcept
|
||||||
|
{
|
||||||
|
return ImGui::Selectable(label, selected, flags, size);
|
||||||
|
}
|
||||||
|
static inline bool
|
||||||
|
Selectable(const char* label, bool* p_selected, ImGuiSelectableFlags flags = 0,
|
||||||
|
const ImVec2& size = Zero) noexcept
|
||||||
|
{
|
||||||
|
return ImGui::Selectable(label, p_selected, flags, size);
|
||||||
|
}
|
||||||
|
#ifndef DEAR_NO_STRING
|
||||||
|
static inline bool
|
||||||
|
Selectable(const tl::string& label, bool selected = false, ImGuiSelectableFlags flags = 0,
|
||||||
|
const ImVec2& size = Zero) noexcept
|
||||||
|
{
|
||||||
|
return ImGui::Selectable(label.c_str(), selected, flags, size);
|
||||||
|
}
|
||||||
|
static inline bool
|
||||||
|
Selectable(const tl::string& label, bool* p_selected, ImGuiSelectableFlags flags = 0,
|
||||||
|
const ImVec2& size = Zero) noexcept
|
||||||
|
{
|
||||||
|
return ImGui::Selectable(label.c_str(), p_selected, flags, size);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace dear
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// "DEFER" macro "DEFER", for scheduling a function call for the end of the scope.
|
||||||
|
// E.g.
|
||||||
|
// DEFER(ImGui::End(););
|
||||||
|
// if (!ImGui::Begin("window"))
|
||||||
|
// return;
|
||||||
|
//
|
||||||
|
#define IMH_CONCAT_IMPL(s1, s2) s1##s2
|
||||||
|
#define IMH_CONCAT(s1, s2) IMH_CONCAT_IMPL(s1, s2)
|
||||||
|
#define DEFER(op) \
|
||||||
|
Deferral IMH_CONCAT(__deferral__, __LINE__) \
|
||||||
|
{ \
|
||||||
|
[]() { \
|
||||||
|
op \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
class Deferral
|
||||||
|
{
|
||||||
|
void (*mFn)();
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr Deferral(void (*fn)()) noexcept : mFn(fn) {}
|
||||||
|
~Deferral() noexcept { mFn(); }
|
||||||
|
};
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// define IMGUI_NOEXCEPT as 'noexcept' to enable noexcept behavior (requires compatible imgui.h)
|
||||||
|
#ifndef IMGUI_NOEXCEPT
|
||||||
|
# define IMGUI_NOEXCEPT
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "IUI/ImGui/imgui.h"
|
||||||
|
|
||||||
|
#include <tl/functional.h>
|
||||||
|
#include <tl/optional.h>
|
||||||
|
|
||||||
|
using ImGuiWrapperReturnType = tl::optional<int>;
|
||||||
|
using ImGuiWrapperFn = tl::function<ImGuiWrapperReturnType()>;
|
||||||
|
|
||||||
|
// ImGuiWrapConfig describes the parameters of the main window created by imgui_main.
|
||||||
|
struct ImGuiWrapConfig
|
||||||
|
{
|
||||||
|
// windowTitle_ sets the initial name given to the main window.
|
||||||
|
const char* windowTitle_{"Application"};
|
||||||
|
|
||||||
|
// width_ and height_ control the initial dimensions of the main window.
|
||||||
|
template<typename T>
|
||||||
|
static constexpr T DefaultWidth = 1280;
|
||||||
|
template<typename T>
|
||||||
|
static constexpr T DefaultHeight = 720;
|
||||||
|
|
||||||
|
int width_{DefaultWidth<int>}, height_{DefaultHeight<int>};
|
||||||
|
|
||||||
|
// clearColor_ determines the clear/background color for the window.
|
||||||
|
// NOLINTNEXTLINE(cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers)
|
||||||
|
ImVec4 clearColor_{0.45F, 0.55F, 0.60F, 1.00F};
|
||||||
|
|
||||||
|
// enableVsync_ enables vsync for the window.
|
||||||
|
bool enableVsync_;
|
||||||
|
|
||||||
|
// keyboardNav_ enables keyboard controls per ImGuiConfigFlags_NavEnableKeyboard;
|
||||||
|
bool keyboardNav_{true};
|
||||||
|
|
||||||
|
// startDark_ enables StyleColorsDark after creating the window.
|
||||||
|
bool startDark_{true};
|
||||||
|
};
|
||||||
|
|
||||||
|
// imgui_main implements a main-loop that constructs a GL window and calls the supplied
|
||||||
|
// mainFn every frame until the app is closed.
|
||||||
|
// See dear::SetHostWindowSize if your callback needs to change the GL window size.
|
||||||
|
extern int imgui_main(const ImGuiWrapConfig& config, const ImGuiWrapperFn& mainFn) noexcept;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "imguiwrap.h"
|
||||||
|
// DEPRECATED: I'm going to remove this in future.
|
||||||
|
#include "imguiwrap.defermacro.h"
|
||||||
|
|
||||||
|
// ImGui fails to provide these useful constants
|
||||||
|
constexpr int ImGuiTableFlags_SizingShift = 13;
|
||||||
|
constexpr int ImGuiTableFlags_SizingBits = 4;
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,250 @@
|
|||||||
|
// Crude implementation of JSON value object and parser.
|
||||||
|
//
|
||||||
|
// VERSION 0.1
|
||||||
|
//
|
||||||
|
// LICENSE
|
||||||
|
// This software is dual-licensed to the public domain and under the following
|
||||||
|
// license: you are granted a perpetual, irrevocable license to copy, modify,
|
||||||
|
// publish, and distribute this file as you see fit.
|
||||||
|
//
|
||||||
|
// CREDITS
|
||||||
|
// Written by Michal Cichon
|
||||||
|
# ifndef __CRUDE_JSON_H__
|
||||||
|
# define __CRUDE_JSON_H__
|
||||||
|
# pragma once
|
||||||
|
|
||||||
|
# include <type_traits>
|
||||||
|
# include <string>
|
||||||
|
# include <vector>
|
||||||
|
# include <map>
|
||||||
|
# include <cstddef>
|
||||||
|
# include <algorithm>
|
||||||
|
# include <sstream>
|
||||||
|
|
||||||
|
# ifndef CRUDE_ASSERT
|
||||||
|
# include <cassert>
|
||||||
|
# define CRUDE_ASSERT(expr) assert(expr)
|
||||||
|
# endif
|
||||||
|
|
||||||
|
# ifndef CRUDE_JSON_IO
|
||||||
|
# define CRUDE_JSON_IO 1
|
||||||
|
# endif
|
||||||
|
|
||||||
|
namespace crude_json {
|
||||||
|
|
||||||
|
struct value;
|
||||||
|
|
||||||
|
using string = std::string;
|
||||||
|
using object = std::map<string, value>;
|
||||||
|
using array = std::vector<value>;
|
||||||
|
using number = double;
|
||||||
|
using boolean = bool;
|
||||||
|
using null = std::nullptr_t;
|
||||||
|
|
||||||
|
enum class type_t
|
||||||
|
{
|
||||||
|
null,
|
||||||
|
object,
|
||||||
|
array,
|
||||||
|
string,
|
||||||
|
boolean,
|
||||||
|
number,
|
||||||
|
discarded
|
||||||
|
};
|
||||||
|
|
||||||
|
struct value
|
||||||
|
{
|
||||||
|
value(type_t type = type_t::null): m_Type(construct(m_Storage, type)) {}
|
||||||
|
value(value&& other);
|
||||||
|
value(const value& other);
|
||||||
|
|
||||||
|
value( null) : m_Type(construct(m_Storage, null())) {}
|
||||||
|
value( object&& v): m_Type(construct(m_Storage, std::move(v))) {}
|
||||||
|
value(const object& v): m_Type(construct(m_Storage, v)) {}
|
||||||
|
value( array&& v): m_Type(construct(m_Storage, std::move(v))) {}
|
||||||
|
value(const array& v): m_Type(construct(m_Storage, v)) {}
|
||||||
|
value( string&& v): m_Type(construct(m_Storage, std::move(v))) {}
|
||||||
|
value(const string& v): m_Type(construct(m_Storage, v)) {}
|
||||||
|
value(const char* v): m_Type(construct(m_Storage, v)) {}
|
||||||
|
value( boolean v): m_Type(construct(m_Storage, v)) {}
|
||||||
|
value( number v): m_Type(construct(m_Storage, v)) {}
|
||||||
|
~value() { destruct(m_Storage, m_Type); }
|
||||||
|
|
||||||
|
value& operator=(value&& other) { if (this != &other) { value(std::move(other)).swap(*this); } return *this; }
|
||||||
|
value& operator=(const value& other) { if (this != &other) { value( other).swap(*this); } return *this; }
|
||||||
|
|
||||||
|
value& operator=( null) { auto other = value( ); swap(other); return *this; }
|
||||||
|
value& operator=( object&& v) { auto other = value(std::move(v)); swap(other); return *this; }
|
||||||
|
value& operator=(const object& v) { auto other = value( v); swap(other); return *this; }
|
||||||
|
value& operator=( array&& v) { auto other = value(std::move(v)); swap(other); return *this; }
|
||||||
|
value& operator=(const array& v) { auto other = value( v); swap(other); return *this; }
|
||||||
|
value& operator=( string&& v) { auto other = value(std::move(v)); swap(other); return *this; }
|
||||||
|
value& operator=(const string& v) { auto other = value( v); swap(other); return *this; }
|
||||||
|
value& operator=(const char* v) { auto other = value( v); swap(other); return *this; }
|
||||||
|
value& operator=( boolean v) { auto other = value( v); swap(other); return *this; }
|
||||||
|
value& operator=( number v) { auto other = value( v); swap(other); return *this; }
|
||||||
|
|
||||||
|
type_t type() const { return m_Type; }
|
||||||
|
|
||||||
|
operator type_t() const { return m_Type; }
|
||||||
|
|
||||||
|
value& operator[](size_t index);
|
||||||
|
const value& operator[](size_t index) const;
|
||||||
|
value& operator[](const string& key);
|
||||||
|
const value& operator[](const string& key) const;
|
||||||
|
|
||||||
|
bool contains(const string& key) const;
|
||||||
|
|
||||||
|
void push_back(const value& value);
|
||||||
|
void push_back(value&& value);
|
||||||
|
|
||||||
|
size_t erase(const string& key);
|
||||||
|
|
||||||
|
bool is_primitive() const { return is_string() || is_number() || is_boolean() || is_null(); }
|
||||||
|
bool is_structured() const { return is_object() || is_array(); }
|
||||||
|
bool is_null() const { return m_Type == type_t::null; }
|
||||||
|
bool is_object() const { return m_Type == type_t::object; }
|
||||||
|
bool is_array() const { return m_Type == type_t::array; }
|
||||||
|
bool is_string() const { return m_Type == type_t::string; }
|
||||||
|
bool is_boolean() const { return m_Type == type_t::boolean; }
|
||||||
|
bool is_number() const { return m_Type == type_t::number; }
|
||||||
|
bool is_discarded() const { return m_Type == type_t::discarded; }
|
||||||
|
|
||||||
|
template <typename T> const T& get() const;
|
||||||
|
template <typename T> T& get();
|
||||||
|
|
||||||
|
template <typename T> const T* get_ptr() const;
|
||||||
|
template <typename T> T* get_ptr();
|
||||||
|
|
||||||
|
string dump(const int indent = -1, const char indent_char = ' ') const;
|
||||||
|
|
||||||
|
void swap(value& other);
|
||||||
|
|
||||||
|
inline friend void swap(value& lhs, value& rhs) { lhs.swap(rhs); }
|
||||||
|
|
||||||
|
// Returns discarded value for invalid inputs.
|
||||||
|
static value parse(const string& data);
|
||||||
|
|
||||||
|
# if CRUDE_JSON_IO
|
||||||
|
static std::pair<value, bool> load(const string& path);
|
||||||
|
bool save(const string& path, const int indent = -1, const char indent_char = ' ') const;
|
||||||
|
# endif
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct parser;
|
||||||
|
|
||||||
|
// VS2015: std::max() is not constexpr yet.
|
||||||
|
# define CRUDE_MAX2(a, b) ((a) < (b) ? (b) : (a))
|
||||||
|
# define CRUDE_MAX3(a, b, c) CRUDE_MAX2(CRUDE_MAX2(a, b), c)
|
||||||
|
# define CRUDE_MAX4(a, b, c, d) CRUDE_MAX2(CRUDE_MAX3(a, b, c), d)
|
||||||
|
# define CRUDE_MAX5(a, b, c, d, e) CRUDE_MAX2(CRUDE_MAX4(a, b, c, d), e)
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
max_size = CRUDE_MAX5( sizeof(string), sizeof(object), sizeof(array), sizeof(number), sizeof(boolean)),
|
||||||
|
max_align = CRUDE_MAX5(alignof(string), alignof(object), alignof(array), alignof(number), alignof(boolean))
|
||||||
|
};
|
||||||
|
# undef CRUDE_MAX5
|
||||||
|
# undef CRUDE_MAX4
|
||||||
|
# undef CRUDE_MAX3
|
||||||
|
# undef CRUDE_MAX2
|
||||||
|
using storage_t = std::aligned_storage<max_size, max_align>::type;
|
||||||
|
|
||||||
|
static object* object_ptr( storage_t& storage) { return reinterpret_cast< object*>(&storage); }
|
||||||
|
static const object* object_ptr(const storage_t& storage) { return reinterpret_cast<const object*>(&storage); }
|
||||||
|
static array* array_ptr( storage_t& storage) { return reinterpret_cast< array*>(&storage); }
|
||||||
|
static const array* array_ptr(const storage_t& storage) { return reinterpret_cast<const array*>(&storage); }
|
||||||
|
static string* string_ptr( storage_t& storage) { return reinterpret_cast< string*>(&storage); }
|
||||||
|
static const string* string_ptr(const storage_t& storage) { return reinterpret_cast<const string*>(&storage); }
|
||||||
|
static boolean* boolean_ptr( storage_t& storage) { return reinterpret_cast< boolean*>(&storage); }
|
||||||
|
static const boolean* boolean_ptr(const storage_t& storage) { return reinterpret_cast<const boolean*>(&storage); }
|
||||||
|
static number* number_ptr( storage_t& storage) { return reinterpret_cast< number*>(&storage); }
|
||||||
|
static const number* number_ptr(const storage_t& storage) { return reinterpret_cast<const number*>(&storage); }
|
||||||
|
|
||||||
|
static type_t construct(storage_t& storage, type_t type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case type_t::object: new (&storage) object(); break;
|
||||||
|
case type_t::array: new (&storage) array(); break;
|
||||||
|
case type_t::string: new (&storage) string(); break;
|
||||||
|
case type_t::boolean: new (&storage) boolean(); break;
|
||||||
|
case type_t::number: new (&storage) number(); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
static type_t construct(storage_t& storage, null) { (void)storage; return type_t::null; }
|
||||||
|
static type_t construct(storage_t& storage, object&& value) { new (&storage) object(std::forward<object>(value)); return type_t::object; }
|
||||||
|
static type_t construct(storage_t& storage, const object& value) { new (&storage) object(value); return type_t::object; }
|
||||||
|
static type_t construct(storage_t& storage, array&& value) { new (&storage) array(std::forward<array>(value)); return type_t::array; }
|
||||||
|
static type_t construct(storage_t& storage, const array& value) { new (&storage) array(value); return type_t::array; }
|
||||||
|
static type_t construct(storage_t& storage, string&& value) { new (&storage) string(std::forward<string>(value)); return type_t::string; }
|
||||||
|
static type_t construct(storage_t& storage, const string& value) { new (&storage) string(value); return type_t::string; }
|
||||||
|
static type_t construct(storage_t& storage, const char* value) { new (&storage) string(value); return type_t::string; }
|
||||||
|
static type_t construct(storage_t& storage, boolean value) { new (&storage) boolean(value); return type_t::boolean; }
|
||||||
|
static type_t construct(storage_t& storage, number value) { new (&storage) number(value); return type_t::number; }
|
||||||
|
|
||||||
|
static void destruct(storage_t& storage, type_t type)
|
||||||
|
{
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case type_t::object: object_ptr(storage)->~object(); break;
|
||||||
|
case type_t::array: array_ptr(storage)->~array(); break;
|
||||||
|
case type_t::string: string_ptr(storage)->~string(); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dump_context_t
|
||||||
|
{
|
||||||
|
std::ostringstream out;
|
||||||
|
const int indent = -1;
|
||||||
|
const char indent_char = ' ';
|
||||||
|
|
||||||
|
// VS2015: Aggregate initialization isn't a thing yet.
|
||||||
|
dump_context_t(const int indent, const char indent_char)
|
||||||
|
: indent(indent)
|
||||||
|
, indent_char(indent_char)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_indent(int level);
|
||||||
|
void write_separator();
|
||||||
|
void write_newline();
|
||||||
|
};
|
||||||
|
|
||||||
|
void dump(dump_context_t& context, int level) const;
|
||||||
|
|
||||||
|
storage_t m_Storage;
|
||||||
|
type_t m_Type;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <> inline const object& value::get<object>() const { CRUDE_ASSERT(m_Type == type_t::object); return *object_ptr(m_Storage); }
|
||||||
|
template <> inline const array& value::get<array>() const { CRUDE_ASSERT(m_Type == type_t::array); return *array_ptr(m_Storage); }
|
||||||
|
template <> inline const string& value::get<string>() const { CRUDE_ASSERT(m_Type == type_t::string); return *string_ptr(m_Storage); }
|
||||||
|
template <> inline const boolean& value::get<boolean>() const { CRUDE_ASSERT(m_Type == type_t::boolean); return *boolean_ptr(m_Storage); }
|
||||||
|
template <> inline const number& value::get<number>() const { CRUDE_ASSERT(m_Type == type_t::number); return *number_ptr(m_Storage); }
|
||||||
|
|
||||||
|
template <> inline object& value::get<object>() { CRUDE_ASSERT(m_Type == type_t::object); return *object_ptr(m_Storage); }
|
||||||
|
template <> inline array& value::get<array>() { CRUDE_ASSERT(m_Type == type_t::array); return *array_ptr(m_Storage); }
|
||||||
|
template <> inline string& value::get<string>() { CRUDE_ASSERT(m_Type == type_t::string); return *string_ptr(m_Storage); }
|
||||||
|
template <> inline boolean& value::get<boolean>() { CRUDE_ASSERT(m_Type == type_t::boolean); return *boolean_ptr(m_Storage); }
|
||||||
|
template <> inline number& value::get<number>() { CRUDE_ASSERT(m_Type == type_t::number); return *number_ptr(m_Storage); }
|
||||||
|
|
||||||
|
template <> inline const object* value::get_ptr<object>() const { if (m_Type == type_t::object) return object_ptr(m_Storage); else return nullptr; }
|
||||||
|
template <> inline const array* value::get_ptr<array>() const { if (m_Type == type_t::array) return array_ptr(m_Storage); else return nullptr; }
|
||||||
|
template <> inline const string* value::get_ptr<string>() const { if (m_Type == type_t::string) return string_ptr(m_Storage); else return nullptr; }
|
||||||
|
template <> inline const boolean* value::get_ptr<boolean>() const { if (m_Type == type_t::boolean) return boolean_ptr(m_Storage); else return nullptr; }
|
||||||
|
template <> inline const number* value::get_ptr<number>() const { if (m_Type == type_t::number) return number_ptr(m_Storage); else return nullptr; }
|
||||||
|
|
||||||
|
template <> inline object* value::get_ptr<object>() { if (m_Type == type_t::object) return object_ptr(m_Storage); else return nullptr; }
|
||||||
|
template <> inline array* value::get_ptr<array>() { if (m_Type == type_t::array) return array_ptr(m_Storage); else return nullptr; }
|
||||||
|
template <> inline string* value::get_ptr<string>() { if (m_Type == type_t::string) return string_ptr(m_Storage); else return nullptr; }
|
||||||
|
template <> inline boolean* value::get_ptr<boolean>() { if (m_Type == type_t::boolean) return boolean_ptr(m_Storage); else return nullptr; }
|
||||||
|
template <> inline number* value::get_ptr<number>() { if (m_Type == type_t::number) return number_ptr(m_Storage); else return nullptr; }
|
||||||
|
|
||||||
|
} // namespace crude_json
|
||||||
|
|
||||||
|
# endif // __CRUDE_JSON_H__
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// VERSION 0.1
|
||||||
|
//
|
||||||
|
// LICENSE
|
||||||
|
// This software is dual-licensed to the public domain and under the following
|
||||||
|
// license: you are granted a perpetual, irrevocable license to copy, modify,
|
||||||
|
// publish, and distribute this file as you see fit.
|
||||||
|
//
|
||||||
|
// CREDITS
|
||||||
|
// Written by Michal Cichon
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
# ifndef __IMGUI_BEZIER_MATH_H__
|
||||||
|
# define __IMGUI_BEZIER_MATH_H__
|
||||||
|
# pragma once
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
# include "imgui_extra_math.h"
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
template <typename T>
|
||||||
|
struct ImCubicBezierPointsT
|
||||||
|
{
|
||||||
|
T P0;
|
||||||
|
T P1;
|
||||||
|
T P2;
|
||||||
|
T P3;
|
||||||
|
};
|
||||||
|
using ImCubicBezierPoints = ImCubicBezierPointsT<ImVec2>;
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// Low-level Bezier curve sampling.
|
||||||
|
template <typename T> inline T ImLinearBezier(const T& p0, const T& p1, float t);
|
||||||
|
template <typename T> inline T ImLinearBezierDt(const T& p0, const T& p1, float t);
|
||||||
|
template <typename T> inline T ImQuadraticBezier(const T& p0, const T& p1, const T& p2, float t);
|
||||||
|
template <typename T> inline T ImQuadraticBezierDt(const T& p0, const T& p1, const T& p2, float t);
|
||||||
|
template <typename T> inline T ImCubicBezier(const T& p0, const T& p1, const T& p2, const T& p3, float t);
|
||||||
|
template <typename T> inline T ImCubicBezierDt(const T& p0, const T& p1, const T& p2, const T& p3, float t);
|
||||||
|
|
||||||
|
|
||||||
|
// High-level Bezier sampling, automatically collapse to lower level Bezier curves if control points overlap.
|
||||||
|
template <typename T> inline T ImCubicBezierSample(const T& p0, const T& p1, const T& p2, const T& p3, float t);
|
||||||
|
template <typename T> inline T ImCubicBezierSample(const ImCubicBezierPointsT<T>& curve, float t);
|
||||||
|
template <typename T> inline T ImCubicBezierTangent(const T& p0, const T& p1, const T& p2, const T& p3, float t);
|
||||||
|
template <typename T> inline T ImCubicBezierTangent(const ImCubicBezierPointsT<T>& curve, float t);
|
||||||
|
|
||||||
|
|
||||||
|
// Calculate approximate length of Cubic Bezier curve.
|
||||||
|
template <typename T> inline float ImCubicBezierLength(const T& p0, const T& p1, const T& p2, const T& p3);
|
||||||
|
template <typename T> inline float ImCubicBezierLength(const ImCubicBezierPointsT<T>& curve);
|
||||||
|
|
||||||
|
|
||||||
|
// Splits Cubic Bezier curve into two curves.
|
||||||
|
template <typename T>
|
||||||
|
struct ImCubicBezierSplitResultT
|
||||||
|
{
|
||||||
|
ImCubicBezierPointsT<T> Left;
|
||||||
|
ImCubicBezierPointsT<T> Right;
|
||||||
|
};
|
||||||
|
using ImCubicBezierSplitResult = ImCubicBezierSplitResultT<ImVec2>;
|
||||||
|
|
||||||
|
template <typename T> inline ImCubicBezierSplitResultT<T> ImCubicBezierSplit(const T& p0, const T& p1, const T& p2, const T& p3, float t);
|
||||||
|
template <typename T> inline ImCubicBezierSplitResultT<T> ImCubicBezierSplit(const ImCubicBezierPointsT<T>& curve, float t);
|
||||||
|
|
||||||
|
|
||||||
|
// Returns bounding rectangle of Cubic Bezier curve.
|
||||||
|
inline ImRect ImCubicBezierBoundingRect(const ImVec2& p0, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3);
|
||||||
|
inline ImRect ImCubicBezierBoundingRect(const ImCubicBezierPoints& curve);
|
||||||
|
|
||||||
|
|
||||||
|
// Project point on Cubic Bezier curve.
|
||||||
|
struct ImProjectResult
|
||||||
|
{
|
||||||
|
ImVec2 Point; // Point on curve
|
||||||
|
float Time; // [0 - 1]
|
||||||
|
float Distance; // Distance to curve
|
||||||
|
};
|
||||||
|
|
||||||
|
inline ImProjectResult ImProjectOnCubicBezier(const ImVec2& p, const ImVec2& p0, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const int subdivisions = 100);
|
||||||
|
inline ImProjectResult ImProjectOnCubicBezier(const ImVec2& p, const ImCubicBezierPoints& curve, const int subdivisions = 100);
|
||||||
|
|
||||||
|
|
||||||
|
// Calculate intersection between line and a Cubic Bezier curve.
|
||||||
|
struct ImCubicBezierIntersectResult
|
||||||
|
{
|
||||||
|
int Count;
|
||||||
|
ImVec2 Points[3];
|
||||||
|
};
|
||||||
|
|
||||||
|
inline ImCubicBezierIntersectResult ImCubicBezierLineIntersect(const ImVec2& p0, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& a0, const ImVec2& a1);
|
||||||
|
inline ImCubicBezierIntersectResult ImCubicBezierLineIntersect(const ImCubicBezierPoints& curve, const ImLine& line);
|
||||||
|
|
||||||
|
|
||||||
|
// Adaptive Cubic Bezier subdivision.
|
||||||
|
enum ImCubicBezierSubdivideFlags
|
||||||
|
{
|
||||||
|
ImCubicBezierSubdivide_None = 0,
|
||||||
|
ImCubicBezierSubdivide_SkipFirst = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ImCubicBezierSubdivideSample
|
||||||
|
{
|
||||||
|
ImVec2 Point;
|
||||||
|
ImVec2 Tangent;
|
||||||
|
};
|
||||||
|
|
||||||
|
using ImCubicBezierSubdivideCallback = void (*)(const ImCubicBezierSubdivideSample& p, void* user_pointer);
|
||||||
|
|
||||||
|
inline void ImCubicBezierSubdivide(ImCubicBezierSubdivideCallback callback, void* user_pointer, const ImVec2& p0, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float tess_tol = -1.0f, ImCubicBezierSubdivideFlags flags = ImCubicBezierSubdivide_None);
|
||||||
|
inline void ImCubicBezierSubdivide(ImCubicBezierSubdivideCallback callback, void* user_pointer, const ImCubicBezierPoints& curve, float tess_tol = -1.0f, ImCubicBezierSubdivideFlags flags = ImCubicBezierSubdivide_None);
|
||||||
|
|
||||||
|
|
||||||
|
// F has signature void(const ImCubicBezierSubdivideSample& p)
|
||||||
|
template <typename F> inline void ImCubicBezierSubdivide(F& callback, const ImVec2& p0, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float tess_tol = -1.0f, ImCubicBezierSubdivideFlags flags = ImCubicBezierSubdivide_None);
|
||||||
|
template <typename F> inline void ImCubicBezierSubdivide(F& callback, const ImCubicBezierPoints& curve, float tess_tol = -1.0f, ImCubicBezierSubdivideFlags flags = ImCubicBezierSubdivide_None);
|
||||||
|
|
||||||
|
// Fixed step Cubic Bezier subdivision.
|
||||||
|
struct ImCubicBezierFixedStepSample
|
||||||
|
{
|
||||||
|
float T;
|
||||||
|
float Length;
|
||||||
|
ImVec2 Point;
|
||||||
|
bool BreakSearch;
|
||||||
|
};
|
||||||
|
|
||||||
|
using ImCubicBezierFixedStepCallback = void (*)(ImCubicBezierFixedStepSample& sample, void* user_pointer);
|
||||||
|
|
||||||
|
inline void ImCubicBezierFixedStep(ImCubicBezierFixedStepCallback callback, void* user_pointer, const ImVec2& p0, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float step, bool overshoot = false, float max_value_error = 1e-3f, float max_t_error = 1e-5f);
|
||||||
|
inline void ImCubicBezierFixedStep(ImCubicBezierFixedStepCallback callback, void* user_pointer, const ImCubicBezierPoints& curve, float step, bool overshoot = false, float max_value_error = 1e-3f, float max_t_error = 1e-5f);
|
||||||
|
|
||||||
|
|
||||||
|
// F has signature void(const ImCubicBezierFixedStepSample& p)
|
||||||
|
template <typename F> inline void ImCubicBezierFixedStep(F& callback, const ImVec2& p0, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float step, bool overshoot = false, float max_value_error = 1e-3f, float max_t_error = 1e-5f);
|
||||||
|
template <typename F> inline void ImCubicBezierFixedStep(F& callback, const ImCubicBezierPoints& curve, float step, bool overshoot = false, float max_value_error = 1e-3f, float max_t_error = 1e-5f);
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
# include "imgui_bezier_math.inl"
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
# endif // __IMGUI_BEZIER_MATH_H__
|
||||||
@@ -0,0 +1,677 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// VERSION 0.1
|
||||||
|
//
|
||||||
|
// LICENSE
|
||||||
|
// This software is dual-licensed to the public domain and under the following
|
||||||
|
// license: you are granted a perpetual, irrevocable license to copy, modify,
|
||||||
|
// publish, and distribute this file as you see fit.
|
||||||
|
//
|
||||||
|
// CREDITS
|
||||||
|
// Written by Michal Cichon
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
# ifndef __IMGUI_BEZIER_MATH_INL__
|
||||||
|
# define __IMGUI_BEZIER_MATH_INL__
|
||||||
|
# pragma once
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
# include "imgui_bezier_math.h"
|
||||||
|
# include <map> // used in ImCubicBezierFixedStep
|
||||||
|
|
||||||
|
#include "tl/narrow_cast.h"
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
template <typename T>
|
||||||
|
inline T ImLinearBezier(const T& p0, const T& p1, float t)
|
||||||
|
{
|
||||||
|
return p0 + t * (p1 - p0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline T ImLinearBezierDt(const T& p0, const T& p1, float t)
|
||||||
|
{
|
||||||
|
IM_UNUSED(t);
|
||||||
|
|
||||||
|
return p1 - p0;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline T ImQuadraticBezier(const T& p0, const T& p1, const T& p2, float t)
|
||||||
|
{
|
||||||
|
const auto a = 1 - t;
|
||||||
|
|
||||||
|
return a * a * p0 + 2 * t * a * p1 + t * t * p2;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline T ImQuadraticBezierDt(const T& p0, const T& p1, const T& p2, float t)
|
||||||
|
{
|
||||||
|
return 2 * (1 - t) * (p1 - p0) + 2 * t * (p2 - p1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline T ImCubicBezier(const T& p0, const T& p1, const T& p2, const T& p3, float t)
|
||||||
|
{
|
||||||
|
const auto a = 1 - t;
|
||||||
|
const auto b = a * a * a;
|
||||||
|
const auto c = t * t * t;
|
||||||
|
|
||||||
|
return b * p0 + 3 * t * a * a * p1 + 3 * t * t * a * p2 + c * p3;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline T ImCubicBezierDt(const T& p0, const T& p1, const T& p2, const T& p3, float t)
|
||||||
|
{
|
||||||
|
const auto a = 1 - t;
|
||||||
|
const auto b = a * a;
|
||||||
|
const auto c = t * t;
|
||||||
|
const auto d = 2 * t * a;
|
||||||
|
|
||||||
|
return -3 * p0 * b + 3 * p1 * (b - d) + 3 * p2 * (d - c) + 3 * p3 * c;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline T ImCubicBezierSample(const T& p0, const T& p1, const T& p2, const T& p3, float t)
|
||||||
|
{
|
||||||
|
const auto cp0_zero = ImLengthSqr(p1 - p0) < 1e-5f;
|
||||||
|
const auto cp1_zero = ImLengthSqr(p3 - p2) < 1e-5f;
|
||||||
|
|
||||||
|
if (cp0_zero && cp1_zero)
|
||||||
|
return ImLinearBezier(p0, p3, t);
|
||||||
|
else if (cp0_zero)
|
||||||
|
return ImQuadraticBezier(p0, p2, p3, t);
|
||||||
|
else if (cp1_zero)
|
||||||
|
return ImQuadraticBezier(p0, p1, p3, t);
|
||||||
|
else
|
||||||
|
return ImCubicBezier(p0, p1, p2, p3, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline T ImCubicBezierSample(const ImCubicBezierPointsT<T>& curve, float t)
|
||||||
|
{
|
||||||
|
return ImCubicBezierSample(curve.P0, curve.P1, curve.P2, curve.P3, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline T ImCubicBezierTangent(const T& p0, const T& p1, const T& p2, const T& p3, float t)
|
||||||
|
{
|
||||||
|
const auto cp0_zero = ImLengthSqr(p1 - p0) < 1e-5f;
|
||||||
|
const auto cp1_zero = ImLengthSqr(p3 - p2) < 1e-5f;
|
||||||
|
|
||||||
|
if (cp0_zero && cp1_zero)
|
||||||
|
return ImLinearBezierDt(p0, p3, t);
|
||||||
|
else if (cp0_zero)
|
||||||
|
return ImQuadraticBezierDt(p0, p2, p3, t);
|
||||||
|
else if (cp1_zero)
|
||||||
|
return ImQuadraticBezierDt(p0, p1, p3, t);
|
||||||
|
else
|
||||||
|
return ImCubicBezierDt(p0, p1, p2, p3, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline T ImCubicBezierTangent(const ImCubicBezierPointsT<T>& curve, float t)
|
||||||
|
{
|
||||||
|
return ImCubicBezierTangent(curve.P0, curve.P1, curve.P2, curve.P3, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline float ImCubicBezierLength(const T& p0, const T& p1, const T& p2, const T& p3)
|
||||||
|
{
|
||||||
|
// Legendre-Gauss abscissae with n=24 (x_i values, defined at i=n as the roots of the nth order Legendre polynomial Pn(x))
|
||||||
|
static const float t_values[] =
|
||||||
|
{
|
||||||
|
-0.0640568928626056260850430826247450385909f,
|
||||||
|
0.0640568928626056260850430826247450385909f,
|
||||||
|
-0.1911188674736163091586398207570696318404f,
|
||||||
|
0.1911188674736163091586398207570696318404f,
|
||||||
|
-0.3150426796961633743867932913198102407864f,
|
||||||
|
0.3150426796961633743867932913198102407864f,
|
||||||
|
-0.4337935076260451384870842319133497124524f,
|
||||||
|
0.4337935076260451384870842319133497124524f,
|
||||||
|
-0.5454214713888395356583756172183723700107f,
|
||||||
|
0.5454214713888395356583756172183723700107f,
|
||||||
|
-0.6480936519369755692524957869107476266696f,
|
||||||
|
0.6480936519369755692524957869107476266696f,
|
||||||
|
-0.7401241915785543642438281030999784255232f,
|
||||||
|
0.7401241915785543642438281030999784255232f,
|
||||||
|
-0.8200019859739029219539498726697452080761f,
|
||||||
|
0.8200019859739029219539498726697452080761f,
|
||||||
|
-0.8864155270044010342131543419821967550873f,
|
||||||
|
0.8864155270044010342131543419821967550873f,
|
||||||
|
-0.9382745520027327585236490017087214496548f,
|
||||||
|
0.9382745520027327585236490017087214496548f,
|
||||||
|
-0.9747285559713094981983919930081690617411f,
|
||||||
|
0.9747285559713094981983919930081690617411f,
|
||||||
|
-0.9951872199970213601799974097007368118745f,
|
||||||
|
0.9951872199970213601799974097007368118745f
|
||||||
|
};
|
||||||
|
|
||||||
|
// Legendre-Gauss weights with n=24 (w_i values, defined by a function linked to in the Bezier primer article)
|
||||||
|
static const float c_values[] =
|
||||||
|
{
|
||||||
|
0.1279381953467521569740561652246953718517f,
|
||||||
|
0.1279381953467521569740561652246953718517f,
|
||||||
|
0.1258374563468282961213753825111836887264f,
|
||||||
|
0.1258374563468282961213753825111836887264f,
|
||||||
|
0.1216704729278033912044631534762624256070f,
|
||||||
|
0.1216704729278033912044631534762624256070f,
|
||||||
|
0.1155056680537256013533444839067835598622f,
|
||||||
|
0.1155056680537256013533444839067835598622f,
|
||||||
|
0.1074442701159656347825773424466062227946f,
|
||||||
|
0.1074442701159656347825773424466062227946f,
|
||||||
|
0.0976186521041138882698806644642471544279f,
|
||||||
|
0.0976186521041138882698806644642471544279f,
|
||||||
|
0.0861901615319532759171852029837426671850f,
|
||||||
|
0.0861901615319532759171852029837426671850f,
|
||||||
|
0.0733464814110803057340336152531165181193f,
|
||||||
|
0.0733464814110803057340336152531165181193f,
|
||||||
|
0.0592985849154367807463677585001085845412f,
|
||||||
|
0.0592985849154367807463677585001085845412f,
|
||||||
|
0.0442774388174198061686027482113382288593f,
|
||||||
|
0.0442774388174198061686027482113382288593f,
|
||||||
|
0.0285313886289336631813078159518782864491f,
|
||||||
|
0.0285313886289336631813078159518782864491f,
|
||||||
|
0.0123412297999871995468056670700372915759f,
|
||||||
|
0.0123412297999871995468056670700372915759f
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(t_values) / sizeof(*t_values) == sizeof(c_values) / sizeof(*c_values), "");
|
||||||
|
|
||||||
|
auto arc = [p0, p1, p2, p3](float t)
|
||||||
|
{
|
||||||
|
const auto p = ImCubicBezierDt(p0, p1, p2, p3, t);
|
||||||
|
const auto l = ImLength(p);
|
||||||
|
return l;
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto z = 0.5f;
|
||||||
|
const auto n = sizeof(t_values) / sizeof(*t_values);
|
||||||
|
|
||||||
|
auto accumulator = 0.0f;
|
||||||
|
for (size_t i = 0; i < n; ++i)
|
||||||
|
{
|
||||||
|
const auto t = z * t_values[i] + z;
|
||||||
|
accumulator += c_values[i] * arc(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
return z * accumulator;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline float ImCubicBezierLength(const ImCubicBezierPointsT<T>& curve)
|
||||||
|
{
|
||||||
|
return ImCubicBezierLength(curve.P0, curve.P1, curve.P2, curve.P3);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline ImCubicBezierSplitResultT<T> ImCubicBezierSplit(const T& p0, const T& p1, const T& p2, const T& p3, float t)
|
||||||
|
{
|
||||||
|
const auto z1 = t;
|
||||||
|
const auto z2 = z1 * z1;
|
||||||
|
const auto z3 = z1 * z1 * z1;
|
||||||
|
const auto s1 = z1 - 1;
|
||||||
|
const auto s2 = s1 * s1;
|
||||||
|
const auto s3 = s1 * s1 * s1;
|
||||||
|
|
||||||
|
return ImCubicBezierSplitResultT<T>
|
||||||
|
{
|
||||||
|
ImCubicBezierPointsT<T>
|
||||||
|
{
|
||||||
|
p0,
|
||||||
|
z1 * p1 - s1 * p0,
|
||||||
|
z2 * p2 - 2 * z1 * s1 * p1 + s2 * p0,
|
||||||
|
z3 * p3 - 3 * z2 * s1 * p2 + 3 * z1 * s2 * p1 - s3 * p0
|
||||||
|
},
|
||||||
|
ImCubicBezierPointsT<T>
|
||||||
|
{
|
||||||
|
z3 * p0 - 3 * z2 * s1 * p1 + 3 * z1 * s2 * p2 - s3 * p3,
|
||||||
|
z2 * p1 - 2 * z1 * s1 * p2 + s2 * p3,
|
||||||
|
z1 * p2 - s1 * p3,
|
||||||
|
p3,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline ImCubicBezierSplitResultT<T> ImCubicBezierSplit(const ImCubicBezierPointsT<T>& curve, float t)
|
||||||
|
{
|
||||||
|
return ImCubicBezierSplit(curve.P0, curve.P1, curve.P2, curve.P3, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ImRect ImCubicBezierBoundingRect(const ImVec2& p0, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3)
|
||||||
|
{
|
||||||
|
auto a = 3 * p3 - 9 * p2 + 9 * p1 - 3 * p0;
|
||||||
|
auto b = 6 * p0 - 12 * p1 + 6 * p2;
|
||||||
|
auto c = 3 * p1 - 3 * p0;
|
||||||
|
auto delta_squared = ImMul(b, b) - 4 * ImMul(a, c);
|
||||||
|
|
||||||
|
auto tl = ImMin(p0, p3);
|
||||||
|
auto rb = ImMax(p0, p3);
|
||||||
|
|
||||||
|
# define IM_VEC2_INDEX(v, i) *(&v.x + i)
|
||||||
|
|
||||||
|
for (int i = 0; i < 2; ++i)
|
||||||
|
{
|
||||||
|
if (IM_VEC2_INDEX(a, i) == 0.0f)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (IM_VEC2_INDEX(delta_squared, i) >= 0)
|
||||||
|
{
|
||||||
|
auto delta = ImSqrt(IM_VEC2_INDEX(delta_squared, i));
|
||||||
|
|
||||||
|
auto t0 = (-IM_VEC2_INDEX(b, i) + delta) / (2 * IM_VEC2_INDEX(a, i));
|
||||||
|
if (t0 > 0 && t0 < 1)
|
||||||
|
{
|
||||||
|
auto p = ImCubicBezier(IM_VEC2_INDEX(p0, i), IM_VEC2_INDEX(p1, i), IM_VEC2_INDEX(p2, i), IM_VEC2_INDEX(p3, i), t0);
|
||||||
|
IM_VEC2_INDEX(tl, i) = ImMin(IM_VEC2_INDEX(tl, i), p);
|
||||||
|
IM_VEC2_INDEX(rb, i) = ImMax(IM_VEC2_INDEX(rb, i), p);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto t1 = (-IM_VEC2_INDEX(b, i) - delta) / (2 * IM_VEC2_INDEX(a, i));
|
||||||
|
if (t1 > 0 && t1 < 1)
|
||||||
|
{
|
||||||
|
auto p = ImCubicBezier(IM_VEC2_INDEX(p0, i), IM_VEC2_INDEX(p1, i), IM_VEC2_INDEX(p2, i), IM_VEC2_INDEX(p3, i), t1);
|
||||||
|
IM_VEC2_INDEX(tl, i) = ImMin(IM_VEC2_INDEX(tl, i), p);
|
||||||
|
IM_VEC2_INDEX(rb, i) = ImMax(IM_VEC2_INDEX(rb, i), p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# undef IM_VEC2_INDEX
|
||||||
|
|
||||||
|
return ImRect(tl, rb);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ImRect ImCubicBezierBoundingRect(const ImCubicBezierPoints& curve)
|
||||||
|
{
|
||||||
|
return ImCubicBezierBoundingRect(curve.P0, curve.P1, curve.P2, curve.P3);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ImProjectResult ImProjectOnCubicBezier(const ImVec2& point, const ImVec2& p0, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const int subdivisions)
|
||||||
|
{
|
||||||
|
// http://pomax.github.io/bezierinfo/#projections
|
||||||
|
|
||||||
|
const float epsilon = 1e-5f;
|
||||||
|
const float fixed_step = 1.0f / static_cast<float>(subdivisions - 1);
|
||||||
|
|
||||||
|
ImProjectResult result;
|
||||||
|
result.Point = point;
|
||||||
|
result.Time = 0.0f;
|
||||||
|
result.Distance = FLT_MAX;
|
||||||
|
|
||||||
|
// Step 1: Coarse check
|
||||||
|
for (int i = 0; i < subdivisions; ++i)
|
||||||
|
{
|
||||||
|
auto t = i * fixed_step;
|
||||||
|
auto p = ImCubicBezier(p0, p1, p2, p3, t);
|
||||||
|
auto s = point - p;
|
||||||
|
auto d = ImDot(s, s);
|
||||||
|
|
||||||
|
if (d < result.Distance)
|
||||||
|
{
|
||||||
|
result.Point = p;
|
||||||
|
result.Time = t;
|
||||||
|
result.Distance = d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.Time == 0.0f || ImFabs(result.Time - 1.0f) <= epsilon)
|
||||||
|
{
|
||||||
|
result.Distance = ImSqrt(result.Distance);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: Fine check
|
||||||
|
auto left = result.Time - fixed_step;
|
||||||
|
auto right = result.Time + fixed_step;
|
||||||
|
auto step = fixed_step * 0.1f;
|
||||||
|
|
||||||
|
for (auto t = left; t < right + step; t += step)
|
||||||
|
{
|
||||||
|
auto p = ImCubicBezier(p0, p1, p2, p3, t);
|
||||||
|
auto s = point - p;
|
||||||
|
auto d = ImDot(s, s);
|
||||||
|
|
||||||
|
if (d < result.Distance)
|
||||||
|
{
|
||||||
|
result.Point = p;
|
||||||
|
result.Time = t;
|
||||||
|
result.Distance = d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Distance = ImSqrt(result.Distance);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ImProjectResult ImProjectOnCubicBezier(const ImVec2& p, const ImCubicBezierPoints& curve, const int subdivisions)
|
||||||
|
{
|
||||||
|
return ImProjectOnCubicBezier(p, curve.P0, curve.P1, curve.P2, curve.P3, subdivisions);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ImCubicBezierIntersectResult ImCubicBezierLineIntersect(const ImVec2& p0, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, const ImVec2& a0, const ImVec2& a1)
|
||||||
|
{
|
||||||
|
auto cubic_roots = [](float a, float b, float c, float d, float* roots) -> int
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
auto sign = [](float x) -> float { return x < 0 ? -1.0f : 1.0f; };
|
||||||
|
|
||||||
|
auto A = b / a;
|
||||||
|
auto B = c / a;
|
||||||
|
auto C = d / a;
|
||||||
|
|
||||||
|
auto Q = (3 * B - ImPow(A, 2)) / 9;
|
||||||
|
auto R = (9 * A * B - 27 * C - 2 * ImPow(A, 3)) / 54;
|
||||||
|
auto D = ImPow(Q, 3) + ImPow(R, 2); // polynomial discriminant
|
||||||
|
|
||||||
|
if (D >= 0) // complex or duplicate roots
|
||||||
|
{
|
||||||
|
auto S = sign(R + ImSqrt(D)) * ImPow(ImFabs(R + ImSqrt(D)), (1.0f / 3.0f));
|
||||||
|
auto T = sign(R - ImSqrt(D)) * ImPow(ImFabs(R - ImSqrt(D)), (1.0f / 3.0f));
|
||||||
|
|
||||||
|
roots[0] = -A / 3 + (S + T); // real root
|
||||||
|
roots[1] = -A / 3 - (S + T) / 2; // real part of complex root
|
||||||
|
roots[2] = -A / 3 - (S + T) / 2; // real part of complex root
|
||||||
|
auto Im = ImFabs(ImSqrt(3) * (S - T) / 2); // complex part of root pair
|
||||||
|
|
||||||
|
// discard complex roots
|
||||||
|
if (Im != 0)
|
||||||
|
count = 1;
|
||||||
|
else
|
||||||
|
count = 3;
|
||||||
|
}
|
||||||
|
else // distinct real roots
|
||||||
|
{
|
||||||
|
auto th = ImAcos(R / ImSqrt(-ImPow(Q, 3)));
|
||||||
|
|
||||||
|
roots[0] = 2 * ImSqrt(-Q) * ImCos(th / 3) - A / 3;
|
||||||
|
roots[1] = 2 * ImSqrt(-Q) * ImCos((th + 2 * IM_PI) / 3) - A / 3;
|
||||||
|
roots[2] = 2 * ImSqrt(-Q) * ImCos((th + 4 * IM_PI) / 3) - A / 3;
|
||||||
|
|
||||||
|
count = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
};
|
||||||
|
|
||||||
|
// https://github.com/kaishiqi/Geometric-Bezier/blob/master/GeometricBezier/src/kaishiqi/geometric/intersection/Intersection.as
|
||||||
|
//
|
||||||
|
// Start with Bezier using Bernstein polynomials for weighting functions:
|
||||||
|
// (1-t^3)P0 + 3t(1-t)^2P1 + 3t^2(1-t)P2 + t^3P3
|
||||||
|
//
|
||||||
|
// Expand and collect terms to form linear combinations of original Bezier
|
||||||
|
// controls. This ends up with a vector cubic in t:
|
||||||
|
// (-P0+3P1-3P2+P3)t^3 + (3P0-6P1+3P2)t^2 + (-3P0+3P1)t + P0
|
||||||
|
// /\ /\ /\ /\
|
||||||
|
// || || || ||
|
||||||
|
// c3 c2 c1 c0
|
||||||
|
|
||||||
|
// Calculate the coefficients
|
||||||
|
auto c3 = -p0 + 3 * p1 - 3 * p2 + p3;
|
||||||
|
auto c2 = 3 * p0 - 6 * p1 + 3 * p2;
|
||||||
|
auto c1 = -3 * p0 + 3 * p1;
|
||||||
|
auto c0 = p0;
|
||||||
|
|
||||||
|
// Convert line to normal form: ax + by + c = 0
|
||||||
|
auto a = a1.y - a0.y;
|
||||||
|
auto b = a0.x - a1.x;
|
||||||
|
auto c = a0.x * (a0.y - a1.y) + a0.y * (a1.x - a0.x);
|
||||||
|
|
||||||
|
// Rotate each cubic coefficient using line for new coordinate system?
|
||||||
|
// Find roots of rotated cubic
|
||||||
|
float roots[3];
|
||||||
|
auto rootCount = cubic_roots(
|
||||||
|
a * c3.x + b * c3.y,
|
||||||
|
a * c2.x + b * c2.y,
|
||||||
|
a * c1.x + b * c1.y,
|
||||||
|
a * c0.x + b * c0.y + c,
|
||||||
|
roots);
|
||||||
|
|
||||||
|
// Any roots in closed interval [0,1] are intersections on Bezier, but
|
||||||
|
// might not be on the line segment.
|
||||||
|
// Find intersections and calculate point coordinates
|
||||||
|
|
||||||
|
auto min = ImMin(a0, a1);
|
||||||
|
auto max = ImMax(a0, a1);
|
||||||
|
|
||||||
|
ImCubicBezierIntersectResult result;
|
||||||
|
auto points = result.Points;
|
||||||
|
|
||||||
|
for (int i = 0; i < rootCount; ++i)
|
||||||
|
{
|
||||||
|
auto root = roots[i];
|
||||||
|
|
||||||
|
if (0 <= root && root <= 1)
|
||||||
|
{
|
||||||
|
// We're within the Bezier curve
|
||||||
|
// Find point on Bezier
|
||||||
|
auto p = ImCubicBezier(p0, p1, p2, p3, root);
|
||||||
|
|
||||||
|
// See if point is on line segment
|
||||||
|
// Had to make special cases for vertical and horizontal lines due
|
||||||
|
// to slight errors in calculation of p00
|
||||||
|
if (a0.x == a1.x)
|
||||||
|
{
|
||||||
|
if (min.y <= p.y && p.y <= max.y)
|
||||||
|
*points++ = p;
|
||||||
|
}
|
||||||
|
else if (a0.y == a1.y)
|
||||||
|
{
|
||||||
|
if (min.x <= p.x && p.x <= max.x)
|
||||||
|
*points++ = p;
|
||||||
|
}
|
||||||
|
else if (p.x >= min.x && p.y >= min.y && p.x <= max.x && p.y <= max.y)
|
||||||
|
{
|
||||||
|
*points++ = p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.Count = tl::narrow<int>(points - result.Points);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ImCubicBezierIntersectResult ImCubicBezierLineIntersect(const ImCubicBezierPoints& curve, const ImLine& line)
|
||||||
|
{
|
||||||
|
return ImCubicBezierLineIntersect(curve.P0, curve.P1, curve.P2, curve.P3, line.A, line.B);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void ImCubicBezierSubdivide(ImCubicBezierSubdivideCallback callback, void* user_pointer, const ImVec2& p0, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float tess_tol, ImCubicBezierSubdivideFlags flags)
|
||||||
|
{
|
||||||
|
return ImCubicBezierSubdivide(callback, user_pointer, ImCubicBezierPoints{ p0, p1, p2, p3 }, tess_tol, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void ImCubicBezierSubdivide(ImCubicBezierSubdivideCallback callback, void* user_pointer, const ImCubicBezierPoints& curve, float tess_tol, ImCubicBezierSubdivideFlags flags)
|
||||||
|
{
|
||||||
|
struct Tesselator
|
||||||
|
{
|
||||||
|
ImCubicBezierSubdivideCallback Callback;
|
||||||
|
void* UserPointer;
|
||||||
|
float TesselationTollerance;
|
||||||
|
ImCubicBezierSubdivideFlags Flags;
|
||||||
|
|
||||||
|
void Commit(const ImVec2& p, const ImVec2& t)
|
||||||
|
{
|
||||||
|
ImCubicBezierSubdivideSample sample;
|
||||||
|
sample.Point = p;
|
||||||
|
sample.Tangent = t;
|
||||||
|
Callback(sample, UserPointer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Subdivide(const ImCubicBezierPoints& curve, int level = 0)
|
||||||
|
{
|
||||||
|
float dx = curve.P3.x - curve.P0.x;
|
||||||
|
float dy = curve.P3.y - curve.P0.y;
|
||||||
|
float d2 = ((curve.P1.x - curve.P3.x) * dy - (curve.P1.y - curve.P3.y) * dx);
|
||||||
|
float d3 = ((curve.P2.x - curve.P3.x) * dy - (curve.P2.y - curve.P3.y) * dx);
|
||||||
|
d2 = (d2 >= 0) ? d2 : -d2;
|
||||||
|
d3 = (d3 >= 0) ? d3 : -d3;
|
||||||
|
if ((d2 + d3) * (d2 + d3) < TesselationTollerance * (dx * dx + dy * dy))
|
||||||
|
{
|
||||||
|
Commit(curve.P3, ImCubicBezierTangent(curve, 1.0f));
|
||||||
|
}
|
||||||
|
else if (level < 10)
|
||||||
|
{
|
||||||
|
const auto p12 = (curve.P0 + curve.P1) * 0.5f;
|
||||||
|
const auto p23 = (curve.P1 + curve.P2) * 0.5f;
|
||||||
|
const auto p34 = (curve.P2 + curve.P3) * 0.5f;
|
||||||
|
const auto p123 = (p12 + p23) * 0.5f;
|
||||||
|
const auto p234 = (p23 + p34) * 0.5f;
|
||||||
|
const auto p1234 = (p123 + p234) * 0.5f;
|
||||||
|
|
||||||
|
Subdivide(ImCubicBezierPoints { curve.P0, p12, p123, p1234 }, level + 1);
|
||||||
|
Subdivide(ImCubicBezierPoints { p1234, p234, p34, curve.P3 }, level + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (tess_tol < 0)
|
||||||
|
tess_tol = 1.118f; // sqrtf(1.25f)
|
||||||
|
|
||||||
|
Tesselator tesselator;
|
||||||
|
tesselator.Callback = callback;
|
||||||
|
tesselator.UserPointer = user_pointer;
|
||||||
|
tesselator.TesselationTollerance = tess_tol * tess_tol;
|
||||||
|
tesselator.Flags = flags;
|
||||||
|
|
||||||
|
if (!(tesselator.Flags & ImCubicBezierSubdivide_SkipFirst))
|
||||||
|
tesselator.Commit(curve.P0, ImCubicBezierTangent(curve, 0.0f));
|
||||||
|
|
||||||
|
tesselator.Subdivide(curve, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F> inline void ImCubicBezierSubdivide(F& callback, const ImVec2& p0, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float tess_tol, ImCubicBezierSubdivideFlags flags)
|
||||||
|
{
|
||||||
|
auto handler = [](const ImCubicBezierSubdivideSample& p, void* user_pointer)
|
||||||
|
{
|
||||||
|
auto& callback = *reinterpret_cast<F*>(user_pointer);
|
||||||
|
callback(p);
|
||||||
|
};
|
||||||
|
|
||||||
|
ImCubicBezierSubdivide(handler, &callback, ImCubicBezierPoints{ p0, p1, p2, p3 }, tess_tol, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F> inline void ImCubicBezierSubdivide(F& callback, const ImCubicBezierPoints& curve, float tess_tol, ImCubicBezierSubdivideFlags flags)
|
||||||
|
{
|
||||||
|
auto handler = [](const ImCubicBezierSubdivideSample& p, void* user_pointer)
|
||||||
|
{
|
||||||
|
auto& callback = *reinterpret_cast<F*>(user_pointer);
|
||||||
|
callback(p);
|
||||||
|
};
|
||||||
|
|
||||||
|
ImCubicBezierSubdivide(handler, &callback, curve, tess_tol, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void ImCubicBezierFixedStep(ImCubicBezierFixedStepCallback callback, void* user_pointer, const ImVec2& p0, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float step, bool overshoot, float max_value_error, float max_t_error)
|
||||||
|
{
|
||||||
|
if (step <= 0.0f || !callback || max_value_error <= 0 || max_t_error <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ImCubicBezierFixedStepSample sample;
|
||||||
|
sample.T = 0.0f;
|
||||||
|
sample.Length = 0.0f;
|
||||||
|
sample.Point = p0;
|
||||||
|
sample.BreakSearch = false;
|
||||||
|
|
||||||
|
callback(sample, user_pointer);
|
||||||
|
if (sample.BreakSearch)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto total_length = ImCubicBezierLength(p0, p1, p2, p3);
|
||||||
|
const auto point_count = tl::narrow<int>(total_length / step) + (overshoot ? 2 : 1);
|
||||||
|
const auto t_min = 0.0f;
|
||||||
|
const auto t_max = step * point_count / total_length;
|
||||||
|
const auto t_0 = (t_min + t_max) * 0.5f;
|
||||||
|
|
||||||
|
// #todo: replace map with ImVector + binary search
|
||||||
|
std::map<float, float> cache;
|
||||||
|
for (int point_index = 1; point_index < point_count; ++point_index)
|
||||||
|
{
|
||||||
|
const auto targetLength = point_index * step;
|
||||||
|
|
||||||
|
float t_start = t_min;
|
||||||
|
float t_end = t_max;
|
||||||
|
float t = t_0;
|
||||||
|
|
||||||
|
float t_best = t;
|
||||||
|
float error_best = total_length;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
auto cacheIt = cache.find(t);
|
||||||
|
if (cacheIt == cache.end())
|
||||||
|
{
|
||||||
|
const auto front = ImCubicBezierSplit(p0, p1, p2, p3, t).Left;
|
||||||
|
const auto split_length = ImCubicBezierLength(front);
|
||||||
|
|
||||||
|
cacheIt = cache.emplace(t, split_length).first;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto length = cacheIt->second;
|
||||||
|
const auto error = targetLength - length;
|
||||||
|
|
||||||
|
if (error < error_best)
|
||||||
|
{
|
||||||
|
error_best = error;
|
||||||
|
t_best = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImFabs(error) <= max_value_error || ImFabs(t_start - t_end) <= max_t_error)
|
||||||
|
{
|
||||||
|
sample.T = t;
|
||||||
|
sample.Length = length;
|
||||||
|
sample.Point = ImCubicBezier(p0, p1, p2, p3, t);
|
||||||
|
|
||||||
|
callback(sample, user_pointer);
|
||||||
|
if (sample.BreakSearch)
|
||||||
|
return;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (error < 0.0f)
|
||||||
|
t_end = t;
|
||||||
|
else // if (error > 0.0f)
|
||||||
|
t_start = t;
|
||||||
|
|
||||||
|
t = (t_start + t_end) * 0.5f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void ImCubicBezierFixedStep(ImCubicBezierFixedStepCallback callback, void* user_pointer, const ImCubicBezierPoints& curve, float step, bool overshoot, float max_value_error, float max_t_error)
|
||||||
|
{
|
||||||
|
ImCubicBezierFixedStep(callback, user_pointer, curve.P0, curve.P1, curve.P2, curve.P3, step, overshoot, max_value_error, max_t_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
// F has signature void(const ImCubicBezierFixedStepSample& p)
|
||||||
|
template <typename F>
|
||||||
|
inline void ImCubicBezierFixedStep(F& callback, const ImVec2& p0, const ImVec2& p1, const ImVec2& p2, const ImVec2& p3, float step, bool overshoot, float max_value_error, float max_t_error)
|
||||||
|
{
|
||||||
|
auto handler = [](ImCubicBezierFixedStepSample& sample, void* user_pointer)
|
||||||
|
{
|
||||||
|
auto& callback = *reinterpret_cast<F*>(user_pointer);
|
||||||
|
callback(sample);
|
||||||
|
};
|
||||||
|
|
||||||
|
ImCubicBezierFixedStep(handler, &callback, p0, p1, p2, p3, step, overshoot, max_value_error, max_t_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
inline void ImCubicBezierFixedStep(F& callback, const ImCubicBezierPoints& curve, float step, bool overshoot, float max_value_error, float max_t_error)
|
||||||
|
{
|
||||||
|
auto handler = [](ImCubicBezierFixedStepSample& sample, void* user_pointer)
|
||||||
|
{
|
||||||
|
auto& callback = *reinterpret_cast<F*>(user_pointer);
|
||||||
|
callback(sample);
|
||||||
|
};
|
||||||
|
|
||||||
|
ImCubicBezierFixedStep(handler, &callback, curve.P0, curve.P1, curve.P2, curve.P3, step, overshoot, max_value_error, max_t_error);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
# endif // __IMGUI_BEZIER_MATH_INL__
|
||||||
@@ -0,0 +1,268 @@
|
|||||||
|
// Canvas widget - view over infinite virtual space.
|
||||||
|
//
|
||||||
|
// Canvas allows you to draw your widgets anywhere over infinite space and provide
|
||||||
|
// view over it with support for panning and scaling.
|
||||||
|
//
|
||||||
|
// When you enter a canvas ImGui is moved to virtual space which mean:
|
||||||
|
// - ImGui::GetCursorScreenPos() return (0, 0) and which correspond to top left corner
|
||||||
|
// of the canvas on the screen (this can be changed using CanvasView()).
|
||||||
|
// - Mouse input is brought to canvas space, so widgets works as usual.
|
||||||
|
// - Everything you draw with ImDrawList will be in virtual space.
|
||||||
|
//
|
||||||
|
// By default origin point is on top left corner of canvas widget. It can be
|
||||||
|
// changed with call to CanvasView() where you can specify what part of space
|
||||||
|
// should be viewed by setting viewport origin point and scale. Current state
|
||||||
|
// can be queried with CanvasViewOrigin() and CanvasViewScale().
|
||||||
|
//
|
||||||
|
// Viewport size is controlled by 'size' parameter in BeginCanvas(). You can query
|
||||||
|
// it using CanvasContentMin/Max/Size functions. They are useful if you to not specify
|
||||||
|
// canvas size in which case all free space is used.
|
||||||
|
//
|
||||||
|
// Bounds of visible region of infinite space can be queried using CanvasViewMin/Max/Size
|
||||||
|
// functions. Everything that is drawn outside of this region will be clipped
|
||||||
|
// as usual in ImGui.
|
||||||
|
//
|
||||||
|
// While drawing inside canvas you can translate position from world (usual ImGui space)
|
||||||
|
// to virtual space and back using CanvasFromWorld()/CanvasToWorld().
|
||||||
|
//
|
||||||
|
// Canvas can be nested in each other (they are regular widgets after all). There
|
||||||
|
// is a way to transform position between current and parent canvas with
|
||||||
|
// CanvasFromParent()/CanvasToParent().
|
||||||
|
//
|
||||||
|
// Sometimes in more elaborate scenarios you want to move out canvas virtual space,
|
||||||
|
// do something and came back. You can do that with SuspendCanvas() and ResumeCanvas().
|
||||||
|
//
|
||||||
|
// Note:
|
||||||
|
// It is not valid to call canvas API outside of BeginCanvas() / EndCanvas() scope.
|
||||||
|
//
|
||||||
|
// VERSION 0.1
|
||||||
|
//
|
||||||
|
// LICENSE
|
||||||
|
// This software is dual-licensed to the public domain and under the following
|
||||||
|
// license: you are granted a perpetual, irrevocable license to copy, modify,
|
||||||
|
// publish, and distribute this file as you see fit.
|
||||||
|
//
|
||||||
|
// CREDITS
|
||||||
|
// Written by Michal Cichon
|
||||||
|
# ifndef __IMGUI_EX_CANVAS_H__
|
||||||
|
# define __IMGUI_EX_CANVAS_H__
|
||||||
|
# pragma once
|
||||||
|
|
||||||
|
# include <IUI/ImGui/imgui.h>
|
||||||
|
# include <IUI/ImGui/imgui_internal.h> // ImRect, ImFloor
|
||||||
|
|
||||||
|
namespace ImGuiEx {
|
||||||
|
|
||||||
|
struct CanvasView
|
||||||
|
{
|
||||||
|
ImVec2 Origin;
|
||||||
|
float Scale = 1.0f;
|
||||||
|
float InvScale = 1.0f;
|
||||||
|
|
||||||
|
CanvasView() = default;
|
||||||
|
CanvasView(const ImVec2& origin, float scale)
|
||||||
|
: Origin(origin)
|
||||||
|
, Scale(scale)
|
||||||
|
, InvScale(scale ? 1.0f / scale : 0.0f)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Set(const ImVec2& origin, float scale)
|
||||||
|
{
|
||||||
|
*this = CanvasView(origin, scale);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Canvas widget represent view over infinite plane.
|
||||||
|
//
|
||||||
|
// It acts like a child window without scroll bars with
|
||||||
|
// ability to zoom to specific part of canvas plane.
|
||||||
|
//
|
||||||
|
// Widgets are clipped according to current view exactly
|
||||||
|
// same way ImGui do. To avoid `missing widgets` artifacts first
|
||||||
|
// setup visible region with SetView() then draw content.
|
||||||
|
//
|
||||||
|
// Everything drawn with ImDrawList betwen calls to Begin()/End()
|
||||||
|
// will be drawn on canvas plane. This behavior can be suspended
|
||||||
|
// by calling Suspend() and resumed by calling Resume().
|
||||||
|
//
|
||||||
|
// Warning:
|
||||||
|
// Please do not interleave canvas with use of channel splitter.
|
||||||
|
// Keep channel splitter contained inside canvas or always
|
||||||
|
// call canvas functions from same channel.
|
||||||
|
struct Canvas
|
||||||
|
{
|
||||||
|
// Begins drawing content of canvas plane.
|
||||||
|
//
|
||||||
|
// When false is returned that mean canvas is not visible to the
|
||||||
|
// user can drawing should be skipped and End() not called.
|
||||||
|
// When true is returned drawing must be ended with call to End().
|
||||||
|
//
|
||||||
|
// If any size component is equal to zero or less canvas will
|
||||||
|
// automatically expand to all available area on that axis.
|
||||||
|
// So (0, 300) will take horizontal space and have height
|
||||||
|
// of 300 points. (0, 0) will take all remaining space of
|
||||||
|
// the window.
|
||||||
|
//
|
||||||
|
// You can query size of the canvas while it is being drawn
|
||||||
|
// by calling Rect().
|
||||||
|
bool Begin(const char* id, const ImVec2& size);
|
||||||
|
bool Begin(ImGuiID id, const ImVec2& size);
|
||||||
|
|
||||||
|
// Ends interaction with canvas plane.
|
||||||
|
//
|
||||||
|
// Must be called only when Begin() retuned true.
|
||||||
|
void End();
|
||||||
|
|
||||||
|
// Sets visible region of canvas plane.
|
||||||
|
//
|
||||||
|
// Origin is an offset of infinite plane origin from top left
|
||||||
|
// corner of the canvas.
|
||||||
|
//
|
||||||
|
// Scale greater than 1 make canvas content be bigger, less than 1 smaller.
|
||||||
|
void SetView(const ImVec2& origin, float scale);
|
||||||
|
void SetView(const CanvasView& view);
|
||||||
|
|
||||||
|
// Centers view over specific point on canvas plane.
|
||||||
|
//
|
||||||
|
// View will be centered on specific point by changing origin
|
||||||
|
// but not scale.
|
||||||
|
void CenterView(const ImVec2& canvasPoint);
|
||||||
|
|
||||||
|
// Calculates view over specific point on canvas plane.
|
||||||
|
CanvasView CalcCenterView(const ImVec2& canvasPoint) const;
|
||||||
|
|
||||||
|
// Centers view over specific rectangle on canvas plane.
|
||||||
|
//
|
||||||
|
// Whole rectangle will fit in canvas view. This will affect both
|
||||||
|
// origin and scale.
|
||||||
|
void CenterView(const ImRect& canvasRect);
|
||||||
|
|
||||||
|
// Calculates view over specific rectangle on canvas plane.
|
||||||
|
CanvasView CalcCenterView(const ImRect& canvasRect) const;
|
||||||
|
|
||||||
|
// Suspends canvas by returning to normal ImGui transformation space.
|
||||||
|
// While suspended UI will not be drawn on canvas plane.
|
||||||
|
//
|
||||||
|
// Calls to Suspend()/Resume() are symetrical. Each call to Suspend()
|
||||||
|
// must be matched with call to Resume().
|
||||||
|
void Suspend();
|
||||||
|
void Resume();
|
||||||
|
|
||||||
|
// Transforms point from canvas plane to ImGui.
|
||||||
|
ImVec2 FromLocal(const ImVec2& point) const;
|
||||||
|
ImVec2 FromLocal(const ImVec2& point, const CanvasView& view) const;
|
||||||
|
|
||||||
|
// Transforms vector from canvas plant to ImGui.
|
||||||
|
ImVec2 FromLocalV(const ImVec2& vector) const;
|
||||||
|
ImVec2 FromLocalV(const ImVec2& vector, const CanvasView& view) const;
|
||||||
|
|
||||||
|
// Transforms point from ImGui to canvas plane.
|
||||||
|
ImVec2 ToLocal(const ImVec2& point) const;
|
||||||
|
ImVec2 ToLocal(const ImVec2& point, const CanvasView& view) const;
|
||||||
|
|
||||||
|
// Transforms vector from ImGui to canvas plane.
|
||||||
|
ImVec2 ToLocalV(const ImVec2& vector) const;
|
||||||
|
ImVec2 ToLocalV(const ImVec2& vector, const CanvasView& view) const;
|
||||||
|
|
||||||
|
// Returns widget bounds.
|
||||||
|
//
|
||||||
|
// Note:
|
||||||
|
// Rect is valid after call to Begin().
|
||||||
|
const ImRect& Rect() const { return m_WidgetRect; }
|
||||||
|
|
||||||
|
// Returns visible region on canvas plane (in canvas plane coordinates).
|
||||||
|
const ImRect& ViewRect() const { return m_ViewRect; }
|
||||||
|
|
||||||
|
// Calculates visible region for view.
|
||||||
|
ImRect CalcViewRect(const CanvasView& view) const;
|
||||||
|
|
||||||
|
// Returns current view.
|
||||||
|
const CanvasView& View() const { return m_View; }
|
||||||
|
|
||||||
|
// Returns origin of the view.
|
||||||
|
//
|
||||||
|
// Origin is an offset of infinite plane origin from top left
|
||||||
|
// corner of the canvas.
|
||||||
|
const ImVec2& ViewOrigin() const { return m_View.Origin; }
|
||||||
|
|
||||||
|
// Returns scale of the view.
|
||||||
|
float ViewScale() const { return m_View.Scale; }
|
||||||
|
|
||||||
|
// Returns true if canvas is suspended.
|
||||||
|
//
|
||||||
|
// See: Suspend()/Resume()
|
||||||
|
bool IsSuspended() const { return m_SuspendCounter > 0; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
# define IMGUI_EX_CANVAS_DEFERED() 0
|
||||||
|
|
||||||
|
# if IMGUI_EX_CANVAS_DEFERED()
|
||||||
|
struct Range
|
||||||
|
{
|
||||||
|
int BeginVertexIndex = 0;
|
||||||
|
int EndVertexIndex = 0;
|
||||||
|
int BeginComandIndex = 0;
|
||||||
|
int EndCommandIndex = 0;
|
||||||
|
};
|
||||||
|
# endif
|
||||||
|
|
||||||
|
void UpdateViewTransformPosition();
|
||||||
|
|
||||||
|
void SaveInputState();
|
||||||
|
void RestoreInputState();
|
||||||
|
|
||||||
|
void SaveViewportState();
|
||||||
|
void RestoreViewportState();
|
||||||
|
|
||||||
|
void EnterLocalSpace();
|
||||||
|
void LeaveLocalSpace();
|
||||||
|
|
||||||
|
bool m_InBeginEnd = false;
|
||||||
|
|
||||||
|
ImVec2 m_WidgetPosition;
|
||||||
|
ImVec2 m_WidgetSize;
|
||||||
|
ImRect m_WidgetRect;
|
||||||
|
|
||||||
|
ImDrawList* m_DrawList = nullptr;
|
||||||
|
int m_ExpectedChannel = 0;
|
||||||
|
|
||||||
|
# if IMGUI_EX_CANVAS_DEFERED()
|
||||||
|
ImVector<Range> m_Ranges;
|
||||||
|
Range* m_CurrentRange = nullptr;
|
||||||
|
# endif
|
||||||
|
|
||||||
|
int m_DrawListCommadBufferSize = 0;
|
||||||
|
int m_DrawListStartVertexIndex = 0;
|
||||||
|
|
||||||
|
CanvasView m_View;
|
||||||
|
ImRect m_ViewRect;
|
||||||
|
|
||||||
|
ImVec2 m_ViewTransformPosition;
|
||||||
|
|
||||||
|
int m_SuspendCounter = 0;
|
||||||
|
|
||||||
|
float m_LastFringeScale = 1.0f;
|
||||||
|
|
||||||
|
ImVec2 m_MousePosBackup;
|
||||||
|
ImVec2 m_MousePosPrevBackup;
|
||||||
|
ImVec2 m_MouseClickedPosBackup[IM_ARRAYSIZE(ImGuiIO::MouseClickedPos)];
|
||||||
|
ImVec2 m_WindowCursorMaxBackup;
|
||||||
|
|
||||||
|
# if defined(IMGUI_HAS_VIEWPORT)
|
||||||
|
ImVec2 m_WindowPosBackup;
|
||||||
|
ImVec2 m_ViewportPosBackup;
|
||||||
|
ImVec2 m_ViewportSizeBackup;
|
||||||
|
# if IMGUI_VERSION_NUM > 18002
|
||||||
|
ImVec2 m_ViewportWorkPosBackup;
|
||||||
|
ImVec2 m_ViewportWorkSizeBackup;
|
||||||
|
# else
|
||||||
|
ImVec2 m_ViewportWorkOffsetMinBackup;
|
||||||
|
ImVec2 m_ViewportWorkOffsetMaxBackup;
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ImGuiEx
|
||||||
|
|
||||||
|
# endif // __IMGUI_EX_CANVAS_H__
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// VERSION 0.9.1
|
||||||
|
//
|
||||||
|
// LICENSE
|
||||||
|
// This software is dual-licensed to the public domain and under the following
|
||||||
|
// license: you are granted a perpetual, irrevocable license to copy, modify,
|
||||||
|
// publish, and distribute this file as you see fit.
|
||||||
|
//
|
||||||
|
// CREDITS
|
||||||
|
// Written by Michal Cichon
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
# ifndef __IMGUI_EXTRA_MATH_H__
|
||||||
|
# define __IMGUI_EXTRA_MATH_H__
|
||||||
|
# pragma once
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
# include <IUI/ImGui/imgui.h>
|
||||||
|
# ifndef IMGUI_DEFINE_MATH_OPERATORS
|
||||||
|
# define IMGUI_DEFINE_MATH_OPERATORS
|
||||||
|
# endif
|
||||||
|
# include <IUI/ImGui/imgui_internal.h>
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
struct ImLine
|
||||||
|
{
|
||||||
|
ImVec2 A, B;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
inline bool operator==(const ImVec2& lhs, const ImVec2& rhs);
|
||||||
|
inline bool operator!=(const ImVec2& lhs, const ImVec2& rhs);
|
||||||
|
inline ImVec2 operator*(const float lhs, const ImVec2& rhs);
|
||||||
|
inline ImVec2 operator-(const ImVec2& lhs);
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
inline float ImLength(float v);
|
||||||
|
inline float ImLength(const ImVec2& v);
|
||||||
|
inline float ImLengthSqr(float v);
|
||||||
|
inline ImVec2 ImNormalized(const ImVec2& v);
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
inline bool ImRect_IsEmpty(const ImRect& rect);
|
||||||
|
inline ImVec2 ImRect_ClosestPoint(const ImRect& rect, const ImVec2& p, bool snap_to_edge);
|
||||||
|
inline ImVec2 ImRect_ClosestPoint(const ImRect& rect, const ImVec2& p, bool snap_to_edge, float radius);
|
||||||
|
inline ImVec2 ImRect_ClosestPoint(const ImRect& rect, const ImRect& b);
|
||||||
|
inline ImLine ImRect_ClosestLine(const ImRect& rect_a, const ImRect& rect_b);
|
||||||
|
inline ImLine ImRect_ClosestLine(const ImRect& rect_a, const ImRect& rect_b, float radius_a, float radius_b);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
namespace ImEasing {
|
||||||
|
|
||||||
|
template <typename V, typename T>
|
||||||
|
inline V EaseOutQuad(V b, V c, T t)
|
||||||
|
{
|
||||||
|
return b - c * (t * (t - 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace ImEasing
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
# include "imgui_extra_math.inl"
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
# endif // __IMGUI_EXTRA_MATH_H__
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// VERSION 0.9.1
|
||||||
|
//
|
||||||
|
// LICENSE
|
||||||
|
// This software is dual-licensed to the public domain and under the following
|
||||||
|
// license: you are granted a perpetual, irrevocable license to copy, modify,
|
||||||
|
// publish, and distribute this file as you see fit.
|
||||||
|
//
|
||||||
|
// CREDITS
|
||||||
|
// Written by Michal Cichon
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
# ifndef __IMGUI_EXTRA_MATH_INL__
|
||||||
|
# define __IMGUI_EXTRA_MATH_INL__
|
||||||
|
# pragma once
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
# include "imgui_extra_math.h"
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
//inline bool operator==(const ImVec2& lhs, const ImVec2& rhs)
|
||||||
|
//{
|
||||||
|
// return lhs.x == rhs.x && lhs.y == rhs.y;
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//inline bool operator!=(const ImVec2& lhs, const ImVec2& rhs)
|
||||||
|
//{
|
||||||
|
// return lhs.x != rhs.x || lhs.y != rhs.y;
|
||||||
|
//}
|
||||||
|
|
||||||
|
inline ImVec2 operator*(const float lhs, const ImVec2& rhs)
|
||||||
|
{
|
||||||
|
return ImVec2(lhs * rhs.x, lhs * rhs.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
//inline ImVec2 operator-(const ImVec2& lhs)
|
||||||
|
//{
|
||||||
|
// return ImVec2(-lhs.x, -lhs.y);
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
inline float ImLength(float v)
|
||||||
|
{
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float ImLength(const ImVec2& v)
|
||||||
|
{
|
||||||
|
return ImSqrt(ImLengthSqr(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline float ImLengthSqr(float v)
|
||||||
|
{
|
||||||
|
return v * v;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ImVec2 ImNormalized(const ImVec2& v)
|
||||||
|
{
|
||||||
|
return v * ImInvLength(v, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
inline bool ImRect_IsEmpty(const ImRect& rect)
|
||||||
|
{
|
||||||
|
return rect.Min.x >= rect.Max.x
|
||||||
|
|| rect.Min.y >= rect.Max.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ImVec2 ImRect_ClosestPoint(const ImRect& rect, const ImVec2& p, bool snap_to_edge)
|
||||||
|
{
|
||||||
|
if (!snap_to_edge && rect.Contains(p))
|
||||||
|
return p;
|
||||||
|
|
||||||
|
return ImVec2(
|
||||||
|
(p.x > rect.Max.x) ? rect.Max.x : (p.x < rect.Min.x ? rect.Min.x : p.x),
|
||||||
|
(p.y > rect.Max.y) ? rect.Max.y : (p.y < rect.Min.y ? rect.Min.y : p.y)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ImVec2 ImRect_ClosestPoint(const ImRect& rect, const ImVec2& p, bool snap_to_edge, float radius)
|
||||||
|
{
|
||||||
|
auto point = ImRect_ClosestPoint(rect, p, snap_to_edge);
|
||||||
|
|
||||||
|
const auto offset = p - point;
|
||||||
|
const auto distance_sq = offset.x * offset.x + offset.y * offset.y;
|
||||||
|
if (distance_sq <= 0)
|
||||||
|
return point;
|
||||||
|
|
||||||
|
const auto distance = ImSqrt(distance_sq);
|
||||||
|
|
||||||
|
return point + offset * (ImMin(distance, radius) * (1.0f / distance));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ImVec2 ImRect_ClosestPoint(const ImRect& rect, const ImRect& other)
|
||||||
|
{
|
||||||
|
ImVec2 result;
|
||||||
|
if (other.Min.x >= rect.Max.x)
|
||||||
|
result.x = rect.Max.x;
|
||||||
|
else if (other.Max.x <= rect.Min.x)
|
||||||
|
result.x = rect.Min.x;
|
||||||
|
else
|
||||||
|
result.x = (ImMax(rect.Min.x, other.Min.x) + ImMin(rect.Max.x, other.Max.x)) / 2;
|
||||||
|
|
||||||
|
if (other.Min.y >= rect.Max.y)
|
||||||
|
result.y = rect.Max.y;
|
||||||
|
else if (other.Max.y <= rect.Min.y)
|
||||||
|
result.y = rect.Min.y;
|
||||||
|
else
|
||||||
|
result.y = (ImMax(rect.Min.y, other.Min.y) + ImMin(rect.Max.y, other.Max.y)) / 2;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ImLine ImRect_ClosestLine(const ImRect& rect_a, const ImRect& rect_b)
|
||||||
|
{
|
||||||
|
ImLine result;
|
||||||
|
result.A = ImRect_ClosestPoint(rect_a, rect_b);
|
||||||
|
result.B = ImRect_ClosestPoint(rect_b, rect_a);
|
||||||
|
|
||||||
|
auto distribute = [](float& a, float& b, float a0, float a1, float b0, float b1)
|
||||||
|
{
|
||||||
|
if (a0 >= b1 || a1 <= b0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto aw = a1 - a0;
|
||||||
|
const auto bw = b1 - b0;
|
||||||
|
|
||||||
|
if (aw > bw)
|
||||||
|
{
|
||||||
|
b = b0 + bw - bw * (a - a0) / aw;
|
||||||
|
a = b;
|
||||||
|
}
|
||||||
|
else if (aw < bw)
|
||||||
|
{
|
||||||
|
a = a0 + aw - aw * (b - b0) / bw;
|
||||||
|
b = a;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
distribute(result.A.x, result.B.x, rect_a.Min.x, rect_a.Max.x, rect_b.Min.x, rect_b.Max.x);
|
||||||
|
distribute(result.A.y, result.B.y, rect_a.Min.y, rect_a.Max.y, rect_b.Min.y, rect_b.Max.y);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ImLine ImRect_ClosestLine(const ImRect& rect_a, const ImRect& rect_b, float radius_a, float radius_b)
|
||||||
|
{
|
||||||
|
auto line = ImRect_ClosestLine(rect_a, rect_b);
|
||||||
|
if (radius_a < 0)
|
||||||
|
radius_a = 0;
|
||||||
|
if (radius_b < 0)
|
||||||
|
radius_b = 0;
|
||||||
|
|
||||||
|
if (radius_a == 0 && radius_b == 0)
|
||||||
|
return line;
|
||||||
|
|
||||||
|
const auto offset = line.B - line.A;
|
||||||
|
const auto length_sq = offset.x * offset.x + offset.y * offset.y;
|
||||||
|
const auto radius_a_sq = radius_a * radius_a;
|
||||||
|
const auto radius_b_sq = radius_b * radius_b;
|
||||||
|
|
||||||
|
if (length_sq <= 0)
|
||||||
|
return line;
|
||||||
|
|
||||||
|
const auto length = ImSqrt(length_sq);
|
||||||
|
const auto direction = ImVec2(offset.x / length, offset.y / length);
|
||||||
|
|
||||||
|
const auto total_radius_sq = radius_a_sq + radius_b_sq;
|
||||||
|
if (total_radius_sq > length_sq)
|
||||||
|
{
|
||||||
|
const auto scale = length / (radius_a + radius_b);
|
||||||
|
radius_a *= scale;
|
||||||
|
radius_b *= scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
line.A = line.A + (direction * radius_a);
|
||||||
|
line.B = line.B - (direction * radius_b);
|
||||||
|
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
# endif // __IMGUI_EXTRA_MATH_INL__
|
||||||
@@ -0,0 +1,510 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// VERSION 0.9.1
|
||||||
|
//
|
||||||
|
// LICENSE
|
||||||
|
// This software is dual-licensed to the public domain and under the following
|
||||||
|
// license: you are granted a perpetual, irrevocable license to copy, modify,
|
||||||
|
// publish, and distribute this file as you see fit.
|
||||||
|
//
|
||||||
|
// CREDITS
|
||||||
|
// Written by Michal Cichon
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
# ifndef __IMGUI_NODE_EDITOR_H__
|
||||||
|
# define __IMGUI_NODE_EDITOR_H__
|
||||||
|
# pragma once
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
# include <IUI/ImGui/imgui.h>
|
||||||
|
# include <cstdint> // std::uintXX_t
|
||||||
|
# include <utility> // std::move
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
# define IMGUI_NODE_EDITOR_VERSION "0.9.2"
|
||||||
|
# define IMGUI_NODE_EDITOR_VERSION_NUM 000902
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
namespace ax {
|
||||||
|
namespace NodeEditor {
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
struct NodeId;
|
||||||
|
struct LinkId;
|
||||||
|
struct PinId;
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
enum class PinKind
|
||||||
|
{
|
||||||
|
Input,
|
||||||
|
Output
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class FlowDirection
|
||||||
|
{
|
||||||
|
Forward,
|
||||||
|
Backward
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class CanvasSizeMode
|
||||||
|
{
|
||||||
|
FitVerticalView, // Previous view will be scaled to fit new view on Y axis
|
||||||
|
FitHorizontalView, // Previous view will be scaled to fit new view on X axis
|
||||||
|
CenterOnly, // Previous view will be centered on new view
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
enum class SaveReasonFlags: uint32_t
|
||||||
|
{
|
||||||
|
None = 0x00000000,
|
||||||
|
Navigation = 0x00000001,
|
||||||
|
Position = 0x00000002,
|
||||||
|
Size = 0x00000004,
|
||||||
|
Selection = 0x00000008,
|
||||||
|
AddNode = 0x00000010,
|
||||||
|
RemoveNode = 0x00000020,
|
||||||
|
User = 0x00000040
|
||||||
|
};
|
||||||
|
|
||||||
|
inline SaveReasonFlags operator |(SaveReasonFlags lhs, SaveReasonFlags rhs) { return static_cast<SaveReasonFlags>(static_cast<uint32_t>(lhs) | static_cast<uint32_t>(rhs)); }
|
||||||
|
inline SaveReasonFlags operator &(SaveReasonFlags lhs, SaveReasonFlags rhs) { return static_cast<SaveReasonFlags>(static_cast<uint32_t>(lhs) & static_cast<uint32_t>(rhs)); }
|
||||||
|
|
||||||
|
using ConfigSaveSettings = bool (*)(const char* data, size_t size, SaveReasonFlags reason, void* userPointer);
|
||||||
|
using ConfigLoadSettings = size_t (*)(char* data, void* userPointer);
|
||||||
|
|
||||||
|
using ConfigSaveNodeSettings = bool (*)(NodeId nodeId, const char* data, size_t size, SaveReasonFlags reason, void* userPointer);
|
||||||
|
using ConfigLoadNodeSettings = size_t (*)(NodeId nodeId, char* data, void* userPointer);
|
||||||
|
|
||||||
|
using ConfigSession = void (*)(void* userPointer);
|
||||||
|
|
||||||
|
struct Config
|
||||||
|
{
|
||||||
|
using CanvasSizeModeAlias = ax::NodeEditor::CanvasSizeMode;
|
||||||
|
|
||||||
|
const char* SettingsFile;
|
||||||
|
ConfigSession BeginSaveSession;
|
||||||
|
ConfigSession EndSaveSession;
|
||||||
|
ConfigSaveSettings SaveSettings;
|
||||||
|
ConfigLoadSettings LoadSettings;
|
||||||
|
ConfigSaveNodeSettings SaveNodeSettings;
|
||||||
|
ConfigLoadNodeSettings LoadNodeSettings;
|
||||||
|
void* UserPointer;
|
||||||
|
ImVector<float> CustomZoomLevels;
|
||||||
|
CanvasSizeModeAlias CanvasSizeMode;
|
||||||
|
int DragButtonIndex; // Mouse button index drag action will react to (0-left, 1-right, 2-middle)
|
||||||
|
int SelectButtonIndex; // Mouse button index select action will react to (0-left, 1-right, 2-middle)
|
||||||
|
int NavigateButtonIndex; // Mouse button index navigate action will react to (0-left, 1-right, 2-middle)
|
||||||
|
int ContextMenuButtonIndex; // Mouse button index context menu action will react to (0-left, 1-right, 2-middle)
|
||||||
|
|
||||||
|
Config()
|
||||||
|
: SettingsFile("NodeEditor.json")
|
||||||
|
, BeginSaveSession(nullptr)
|
||||||
|
, EndSaveSession(nullptr)
|
||||||
|
, SaveSettings(nullptr)
|
||||||
|
, LoadSettings(nullptr)
|
||||||
|
, SaveNodeSettings(nullptr)
|
||||||
|
, LoadNodeSettings(nullptr)
|
||||||
|
, UserPointer(nullptr)
|
||||||
|
, CustomZoomLevels()
|
||||||
|
, CanvasSizeMode(CanvasSizeModeAlias::FitVerticalView)
|
||||||
|
, DragButtonIndex(0)
|
||||||
|
, SelectButtonIndex(0)
|
||||||
|
, NavigateButtonIndex(1)
|
||||||
|
, ContextMenuButtonIndex(1)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
enum StyleColor
|
||||||
|
{
|
||||||
|
StyleColor_Bg,
|
||||||
|
StyleColor_Grid,
|
||||||
|
StyleColor_NodeBg,
|
||||||
|
StyleColor_NodeBorder,
|
||||||
|
StyleColor_HovNodeBorder,
|
||||||
|
StyleColor_SelNodeBorder,
|
||||||
|
StyleColor_NodeSelRect,
|
||||||
|
StyleColor_NodeSelRectBorder,
|
||||||
|
StyleColor_HovLinkBorder,
|
||||||
|
StyleColor_SelLinkBorder,
|
||||||
|
StyleColor_HighlightLinkBorder,
|
||||||
|
StyleColor_LinkSelRect,
|
||||||
|
StyleColor_LinkSelRectBorder,
|
||||||
|
StyleColor_PinRect,
|
||||||
|
StyleColor_PinRectBorder,
|
||||||
|
StyleColor_Flow,
|
||||||
|
StyleColor_FlowMarker,
|
||||||
|
StyleColor_GroupBg,
|
||||||
|
StyleColor_GroupBorder,
|
||||||
|
|
||||||
|
StyleColor_Count
|
||||||
|
};
|
||||||
|
|
||||||
|
enum StyleVar
|
||||||
|
{
|
||||||
|
StyleVar_NodePadding,
|
||||||
|
StyleVar_NodeRounding,
|
||||||
|
StyleVar_NodeBorderWidth,
|
||||||
|
StyleVar_HoveredNodeBorderWidth,
|
||||||
|
StyleVar_SelectedNodeBorderWidth,
|
||||||
|
StyleVar_PinRounding,
|
||||||
|
StyleVar_PinBorderWidth,
|
||||||
|
StyleVar_LinkStrength,
|
||||||
|
StyleVar_SourceDirection,
|
||||||
|
StyleVar_TargetDirection,
|
||||||
|
StyleVar_ScrollDuration,
|
||||||
|
StyleVar_FlowMarkerDistance,
|
||||||
|
StyleVar_FlowSpeed,
|
||||||
|
StyleVar_FlowDuration,
|
||||||
|
StyleVar_PivotAlignment,
|
||||||
|
StyleVar_PivotSize,
|
||||||
|
StyleVar_PivotScale,
|
||||||
|
StyleVar_PinCorners,
|
||||||
|
StyleVar_PinRadius,
|
||||||
|
StyleVar_PinArrowSize,
|
||||||
|
StyleVar_PinArrowWidth,
|
||||||
|
StyleVar_GroupRounding,
|
||||||
|
StyleVar_GroupBorderWidth,
|
||||||
|
StyleVar_HighlightConnectedLinks,
|
||||||
|
StyleVar_SnapLinkToPinDir,
|
||||||
|
|
||||||
|
StyleVar_Count
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Style
|
||||||
|
{
|
||||||
|
ImVec4 NodePadding;
|
||||||
|
float NodeRounding;
|
||||||
|
float NodeBorderWidth;
|
||||||
|
float HoveredNodeBorderWidth;
|
||||||
|
float SelectedNodeBorderWidth;
|
||||||
|
float PinRounding;
|
||||||
|
float PinBorderWidth;
|
||||||
|
float LinkStrength;
|
||||||
|
ImVec2 SourceDirection;
|
||||||
|
ImVec2 TargetDirection;
|
||||||
|
float ScrollDuration;
|
||||||
|
float FlowMarkerDistance;
|
||||||
|
float FlowSpeed;
|
||||||
|
float FlowDuration;
|
||||||
|
ImVec2 PivotAlignment;
|
||||||
|
ImVec2 PivotSize;
|
||||||
|
ImVec2 PivotScale;
|
||||||
|
float PinCorners;
|
||||||
|
float PinRadius;
|
||||||
|
float PinArrowSize;
|
||||||
|
float PinArrowWidth;
|
||||||
|
float GroupRounding;
|
||||||
|
float GroupBorderWidth;
|
||||||
|
float HighlightConnectedLinks;
|
||||||
|
float SnapLinkToPinDir; // when true link will start on the line defined by pin direction
|
||||||
|
ImVec4 Colors[StyleColor_Count];
|
||||||
|
|
||||||
|
Style()
|
||||||
|
{
|
||||||
|
NodePadding = ImVec4(8, 8, 8, 8);
|
||||||
|
NodeRounding = 12.0f;
|
||||||
|
NodeBorderWidth = 1.5f;
|
||||||
|
HoveredNodeBorderWidth = 3.5f;
|
||||||
|
SelectedNodeBorderWidth = 3.5f;
|
||||||
|
PinRounding = 4.0f;
|
||||||
|
PinBorderWidth = 0.0f;
|
||||||
|
LinkStrength = 100.0f;
|
||||||
|
SourceDirection = ImVec2(1.0f, 0.0f);
|
||||||
|
TargetDirection = ImVec2(-1.0f, 0.0f);
|
||||||
|
ScrollDuration = 0.35f;
|
||||||
|
FlowMarkerDistance = 30.0f;
|
||||||
|
FlowSpeed = 150.0f;
|
||||||
|
FlowDuration = 2.0f;
|
||||||
|
PivotAlignment = ImVec2(0.5f, 0.5f);
|
||||||
|
PivotSize = ImVec2(0.0f, 0.0f);
|
||||||
|
PivotScale = ImVec2(1, 1);
|
||||||
|
#if IMGUI_VERSION_NUM > 18101
|
||||||
|
PinCorners = ImDrawFlags_RoundCornersAll;
|
||||||
|
#else
|
||||||
|
PinCorners = ImDrawCornerFlags_All;
|
||||||
|
#endif
|
||||||
|
PinRadius = 0.0f;
|
||||||
|
PinArrowSize = 0.0f;
|
||||||
|
PinArrowWidth = 0.0f;
|
||||||
|
GroupRounding = 6.0f;
|
||||||
|
GroupBorderWidth = 1.0f;
|
||||||
|
HighlightConnectedLinks = 0.0f;
|
||||||
|
SnapLinkToPinDir = 0.0f;
|
||||||
|
|
||||||
|
Colors[StyleColor_Bg] = ImColor( 60, 60, 70, 200);
|
||||||
|
Colors[StyleColor_Grid] = ImColor(120, 120, 120, 40);
|
||||||
|
Colors[StyleColor_NodeBg] = ImColor( 32, 32, 32, 200);
|
||||||
|
Colors[StyleColor_NodeBorder] = ImColor(255, 255, 255, 96);
|
||||||
|
Colors[StyleColor_HovNodeBorder] = ImColor( 50, 176, 255, 255);
|
||||||
|
Colors[StyleColor_SelNodeBorder] = ImColor(255, 176, 50, 255);
|
||||||
|
Colors[StyleColor_NodeSelRect] = ImColor( 5, 130, 255, 64);
|
||||||
|
Colors[StyleColor_NodeSelRectBorder] = ImColor( 5, 130, 255, 128);
|
||||||
|
Colors[StyleColor_HovLinkBorder] = ImColor( 50, 176, 255, 255);
|
||||||
|
Colors[StyleColor_SelLinkBorder] = ImColor(255, 176, 50, 255);
|
||||||
|
Colors[StyleColor_HighlightLinkBorder]= ImColor(204, 105, 0, 255);
|
||||||
|
Colors[StyleColor_LinkSelRect] = ImColor( 5, 130, 255, 64);
|
||||||
|
Colors[StyleColor_LinkSelRectBorder] = ImColor( 5, 130, 255, 128);
|
||||||
|
Colors[StyleColor_PinRect] = ImColor( 60, 180, 255, 100);
|
||||||
|
Colors[StyleColor_PinRectBorder] = ImColor( 60, 180, 255, 128);
|
||||||
|
Colors[StyleColor_Flow] = ImColor(255, 128, 64, 255);
|
||||||
|
Colors[StyleColor_FlowMarker] = ImColor(255, 128, 64, 255);
|
||||||
|
Colors[StyleColor_GroupBg] = ImColor( 0, 0, 0, 160);
|
||||||
|
Colors[StyleColor_GroupBorder] = ImColor(255, 255, 255, 32);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
struct EditorContext;
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
void SetCurrentEditor(EditorContext* ctx);
|
||||||
|
EditorContext* GetCurrentEditor();
|
||||||
|
EditorContext* CreateEditor(const Config* config = nullptr);
|
||||||
|
void DestroyEditor(EditorContext* ctx);
|
||||||
|
const Config& GetConfig(EditorContext* ctx = nullptr);
|
||||||
|
|
||||||
|
Style& GetStyle();
|
||||||
|
const char* GetStyleColorName(StyleColor colorIndex);
|
||||||
|
|
||||||
|
void PushStyleColor(StyleColor colorIndex, const ImVec4& color);
|
||||||
|
void PopStyleColor(int count = 1);
|
||||||
|
|
||||||
|
void PushStyleVar(StyleVar varIndex, float value);
|
||||||
|
void PushStyleVar(StyleVar varIndex, const ImVec2& value);
|
||||||
|
void PushStyleVar(StyleVar varIndex, const ImVec4& value);
|
||||||
|
void PopStyleVar(int count = 1);
|
||||||
|
|
||||||
|
void Begin(const char* id, const ImVec2& size = ImVec2(0, 0));
|
||||||
|
void End();
|
||||||
|
|
||||||
|
void BeginNode(NodeId id);
|
||||||
|
void BeginPin(PinId id, PinKind kind);
|
||||||
|
void PinRect(const ImVec2& a, const ImVec2& b);
|
||||||
|
void PinPivotRect(const ImVec2& a, const ImVec2& b);
|
||||||
|
void PinPivotSize(const ImVec2& size);
|
||||||
|
void PinPivotScale(const ImVec2& scale);
|
||||||
|
void PinPivotAlignment(const ImVec2& alignment);
|
||||||
|
void EndPin();
|
||||||
|
void Group(const ImVec2& size);
|
||||||
|
void EndNode();
|
||||||
|
|
||||||
|
bool BeginGroupHint(NodeId nodeId);
|
||||||
|
ImVec2 GetGroupMin();
|
||||||
|
ImVec2 GetGroupMax();
|
||||||
|
ImDrawList* GetHintForegroundDrawList();
|
||||||
|
ImDrawList* GetHintBackgroundDrawList();
|
||||||
|
void EndGroupHint();
|
||||||
|
|
||||||
|
// TODO: Add a way to manage node background channels
|
||||||
|
ImDrawList* GetNodeBackgroundDrawList(NodeId nodeId);
|
||||||
|
|
||||||
|
bool Link(LinkId id, PinId startPinId, PinId endPinId, const ImVec4& color = ImVec4(1, 1, 1, 1), float thickness = 1.0f);
|
||||||
|
|
||||||
|
void Flow(LinkId linkId, FlowDirection direction = FlowDirection::Forward);
|
||||||
|
|
||||||
|
bool BeginCreate(const ImVec4& color = ImVec4(1, 1, 1, 1), float thickness = 1.0f);
|
||||||
|
bool QueryNewLink(PinId* startId, PinId* endId);
|
||||||
|
bool QueryNewLink(PinId* startId, PinId* endId, const ImVec4& color, float thickness = 1.0f);
|
||||||
|
bool QueryNewNode(PinId* pinId);
|
||||||
|
bool QueryNewNode(PinId* pinId, const ImVec4& color, float thickness = 1.0f);
|
||||||
|
bool AcceptNewItem();
|
||||||
|
bool AcceptNewItem(const ImVec4& color, float thickness = 1.0f);
|
||||||
|
void RejectNewItem();
|
||||||
|
void RejectNewItem(const ImVec4& color, float thickness = 1.0f);
|
||||||
|
void EndCreate();
|
||||||
|
|
||||||
|
bool BeginDelete();
|
||||||
|
bool QueryDeletedLink(LinkId* linkId, PinId* startId = nullptr, PinId* endId = nullptr);
|
||||||
|
bool QueryDeletedNode(NodeId* nodeId);
|
||||||
|
bool AcceptDeletedItem(bool deleteDependencies = true);
|
||||||
|
void RejectDeletedItem();
|
||||||
|
void EndDelete();
|
||||||
|
|
||||||
|
void SetNodePosition(NodeId nodeId, const ImVec2& editorPosition);
|
||||||
|
void SetGroupSize(NodeId nodeId, const ImVec2& size);
|
||||||
|
ImVec2 GetNodePosition(NodeId nodeId);
|
||||||
|
ImVec2 GetNodeSize(NodeId nodeId);
|
||||||
|
void CenterNodeOnScreen(NodeId nodeId);
|
||||||
|
void SetNodeZPosition(NodeId nodeId, float z); // Sets node z position, nodes with higher value are drawn over nodes with lower value
|
||||||
|
float GetNodeZPosition(NodeId nodeId); // Returns node z position, defaults is 0.0f
|
||||||
|
|
||||||
|
void RestoreNodeState(NodeId nodeId);
|
||||||
|
|
||||||
|
void Suspend();
|
||||||
|
void Resume();
|
||||||
|
bool IsSuspended();
|
||||||
|
|
||||||
|
bool IsActive();
|
||||||
|
|
||||||
|
bool HasSelectionChanged();
|
||||||
|
int GetSelectedObjectCount();
|
||||||
|
int GetSelectedNodes(NodeId* nodes, int size);
|
||||||
|
int GetSelectedLinks(LinkId* links, int size);
|
||||||
|
bool IsNodeSelected(NodeId nodeId);
|
||||||
|
bool IsLinkSelected(LinkId linkId);
|
||||||
|
void ClearSelection();
|
||||||
|
void SelectNode(NodeId nodeId, bool append = false);
|
||||||
|
void SelectLink(LinkId linkId, bool append = false);
|
||||||
|
void DeselectNode(NodeId nodeId);
|
||||||
|
void DeselectLink(LinkId linkId);
|
||||||
|
|
||||||
|
bool DeleteNode(NodeId nodeId);
|
||||||
|
bool DeleteLink(LinkId linkId);
|
||||||
|
|
||||||
|
bool HasAnyLinks(NodeId nodeId); // Returns true if node has any link connected
|
||||||
|
bool HasAnyLinks(PinId pinId); // Return true if pin has any link connected
|
||||||
|
int BreakLinks(NodeId nodeId); // Break all links connected to this node
|
||||||
|
int BreakLinks(PinId pinId); // Break all links connected to this pin
|
||||||
|
|
||||||
|
void NavigateToContent(float duration = -1);
|
||||||
|
void NavigateToSelection(bool zoomIn = false, float duration = -1);
|
||||||
|
|
||||||
|
bool ShowNodeContextMenu(NodeId* nodeId);
|
||||||
|
bool ShowPinContextMenu(PinId* pinId);
|
||||||
|
bool ShowLinkContextMenu(LinkId* linkId);
|
||||||
|
bool ShowBackgroundContextMenu();
|
||||||
|
|
||||||
|
void EnableShortcuts(bool enable);
|
||||||
|
bool AreShortcutsEnabled();
|
||||||
|
|
||||||
|
bool BeginShortcut();
|
||||||
|
bool AcceptCut();
|
||||||
|
bool AcceptCopy();
|
||||||
|
bool AcceptPaste();
|
||||||
|
bool AcceptDuplicate();
|
||||||
|
bool AcceptCreateNode();
|
||||||
|
int GetActionContextSize();
|
||||||
|
int GetActionContextNodes(NodeId* nodes, int size);
|
||||||
|
int GetActionContextLinks(LinkId* links, int size);
|
||||||
|
void EndShortcut();
|
||||||
|
|
||||||
|
float GetCurrentZoom();
|
||||||
|
|
||||||
|
NodeId GetHoveredNode();
|
||||||
|
PinId GetHoveredPin();
|
||||||
|
LinkId GetHoveredLink();
|
||||||
|
NodeId GetDoubleClickedNode();
|
||||||
|
PinId GetDoubleClickedPin();
|
||||||
|
LinkId GetDoubleClickedLink();
|
||||||
|
bool IsBackgroundClicked();
|
||||||
|
bool IsBackgroundDoubleClicked();
|
||||||
|
ImGuiMouseButton GetBackgroundClickButtonIndex(); // -1 if none
|
||||||
|
ImGuiMouseButton GetBackgroundDoubleClickButtonIndex(); // -1 if none
|
||||||
|
|
||||||
|
bool GetLinkPins(LinkId linkId, PinId* startPinId, PinId* endPinId); // pass nullptr if particular pin do not interest you
|
||||||
|
|
||||||
|
bool PinHadAnyLinks(PinId pinId);
|
||||||
|
|
||||||
|
ImVec2 GetScreenSize();
|
||||||
|
ImVec2 ScreenToCanvas(const ImVec2& pos);
|
||||||
|
ImVec2 CanvasToScreen(const ImVec2& pos);
|
||||||
|
|
||||||
|
int GetNodeCount(); // Returns number of submitted nodes since Begin() call
|
||||||
|
int GetOrderedNodeIds(NodeId* nodes, int size); // Fills an array with node id's in order they're drawn; up to 'size` elements are set. Returns actual size of filled id's.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
namespace Details {
|
||||||
|
|
||||||
|
template <typename T, typename Tag>
|
||||||
|
struct SafeType
|
||||||
|
{
|
||||||
|
SafeType(T t)
|
||||||
|
: m_Value(std::move(t))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SafeType(const SafeType&) = default;
|
||||||
|
|
||||||
|
template <typename T2, typename Tag2>
|
||||||
|
SafeType(
|
||||||
|
const SafeType
|
||||||
|
<
|
||||||
|
typename std::enable_if<!std::is_same<T, T2>::value, T2>::type,
|
||||||
|
typename std::enable_if<!std::is_same<Tag, Tag2>::value, Tag2>::type
|
||||||
|
>&) = delete;
|
||||||
|
|
||||||
|
SafeType& operator=(const SafeType&) = default;
|
||||||
|
|
||||||
|
explicit operator T() const { return Get(); }
|
||||||
|
|
||||||
|
T Get() const { return m_Value; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
T m_Value;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Tag>
|
||||||
|
struct SafePointerType
|
||||||
|
: SafeType<uintptr_t, Tag>
|
||||||
|
{
|
||||||
|
static const Tag Invalid;
|
||||||
|
|
||||||
|
using SafeType<uintptr_t, Tag>::SafeType;
|
||||||
|
|
||||||
|
SafePointerType()
|
||||||
|
: SafePointerType(Invalid)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T = void> explicit SafePointerType(T* ptr): SafePointerType(reinterpret_cast<uintptr_t>(ptr)) {}
|
||||||
|
template <typename T = void> T* AsPointer() const { return reinterpret_cast<T*>(this->Get()); }
|
||||||
|
|
||||||
|
explicit operator bool() const { return *this != Invalid; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Tag>
|
||||||
|
const Tag SafePointerType<Tag>::Invalid = { 0 };
|
||||||
|
|
||||||
|
template <typename Tag>
|
||||||
|
inline bool operator==(const SafePointerType<Tag>& lhs, const SafePointerType<Tag>& rhs)
|
||||||
|
{
|
||||||
|
return lhs.Get() == rhs.Get();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Tag>
|
||||||
|
inline bool operator!=(const SafePointerType<Tag>& lhs, const SafePointerType<Tag>& rhs)
|
||||||
|
{
|
||||||
|
return lhs.Get() != rhs.Get();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Details
|
||||||
|
|
||||||
|
struct NodeId final: Details::SafePointerType<NodeId>
|
||||||
|
{
|
||||||
|
using SafePointerType::SafePointerType;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LinkId final: Details::SafePointerType<LinkId>
|
||||||
|
{
|
||||||
|
using SafePointerType::SafePointerType;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PinId final: Details::SafePointerType<PinId>
|
||||||
|
{
|
||||||
|
using SafePointerType::SafePointerType;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
} // namespace Editor
|
||||||
|
} // namespace ax
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
# endif // __IMGUI_NODE_EDITOR_H__
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,65 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// VERSION 0.9.1
|
||||||
|
//
|
||||||
|
// LICENSE
|
||||||
|
// This software is dual-licensed to the public domain and under the following
|
||||||
|
// license: you are granted a perpetual, irrevocable license to copy, modify,
|
||||||
|
// publish, and distribute this file as you see fit.
|
||||||
|
//
|
||||||
|
// CREDITS
|
||||||
|
// Written by Michal Cichon
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
# ifndef __IMGUI_NODE_EDITOR_INTERNAL_INL__
|
||||||
|
# define __IMGUI_NODE_EDITOR_INTERNAL_INL__
|
||||||
|
# pragma once
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
# include "imgui_node_editor_internal.h"
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
namespace ax {
|
||||||
|
namespace NodeEditor {
|
||||||
|
namespace Detail {
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
//inline ImRect ToRect(const ax::rectf& rect)
|
||||||
|
//{
|
||||||
|
// return ImRect(
|
||||||
|
// to_imvec(rect.top_left()),
|
||||||
|
// to_imvec(rect.bottom_right())
|
||||||
|
// );
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//inline ImRect ToRect(const ax::rect& rect)
|
||||||
|
//{
|
||||||
|
// return ImRect(
|
||||||
|
// to_imvec(rect.top_left()),
|
||||||
|
// to_imvec(rect.bottom_right())
|
||||||
|
// );
|
||||||
|
//}
|
||||||
|
|
||||||
|
inline ImRect ImGui_GetItemRect()
|
||||||
|
{
|
||||||
|
return ImRect(ImGui::GetItemRectMin(), ImGui::GetItemRectMax());
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ImVec2 ImGui_GetMouseClickPos(ImGuiMouseButton buttonIndex)
|
||||||
|
{
|
||||||
|
if (ImGui::IsMouseDown(buttonIndex))
|
||||||
|
return ImGui::GetIO().MouseClickedPos[buttonIndex];
|
||||||
|
else
|
||||||
|
return ImGui::GetMousePos();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
} // namespace Detail
|
||||||
|
} // namespace Editor
|
||||||
|
} // namespace ax
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
# endif // __IMGUI_NODE_EDITOR_INTERNAL_INL__
|
||||||
+212
@@ -0,0 +1,212 @@
|
|||||||
|
#include "IUI/Icons.h"
|
||||||
|
|
||||||
|
#include "Kit/EngineManager.h"
|
||||||
|
#include "IUI/ImGui/imgui.h"
|
||||||
|
#include "IUI/ImGui/imgui_internal.h"
|
||||||
|
#include "fs/ReadStream.h"
|
||||||
|
|
||||||
|
#include "Kit/Systems/RenderSystem/TextureCPU.h"
|
||||||
|
#include "Kit/util/Atlaser.h"
|
||||||
|
|
||||||
|
#include "Kit/util/stb/stb_image.h"
|
||||||
|
|
||||||
|
#include <filament/Engine.h>
|
||||||
|
#include <filament/Texture.h>
|
||||||
|
#include "tl/chrono.h"
|
||||||
|
#include "Logger.h"
|
||||||
|
#include "Kit/util/Image.h"
|
||||||
|
#include "tl/algorithm/string.h"
|
||||||
|
|
||||||
|
#include "fs/NativeFilesystem.h"
|
||||||
|
#include "tl/narrow_cast.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace kit
|
||||||
|
{
|
||||||
|
namespace icons
|
||||||
|
{
|
||||||
|
constexpr uint32_t k_atlasSize = 1024;
|
||||||
|
|
||||||
|
tl::lent_ptr<kit::EngineManager> s_engineManager;
|
||||||
|
tl::lent_ptr<fs::IFilesystem> s_filesystem;
|
||||||
|
fs::AbsPath s_folderPath;
|
||||||
|
|
||||||
|
using Image = kit::Image<kit::pf::R8G8B8A8>;
|
||||||
|
|
||||||
|
struct TextureData
|
||||||
|
{
|
||||||
|
tl::unique_ref<filament::Texture> texture;
|
||||||
|
Image image;
|
||||||
|
kit::Atlaser atlaser;
|
||||||
|
};
|
||||||
|
|
||||||
|
tl::vector<TextureData> s_textures;
|
||||||
|
tl::unordered_map<tl::string, Icon> s_icons;
|
||||||
|
|
||||||
|
void init(tl::lent_ref<kit::EngineManager> engineManager, tl::lent_ref<fs::IFilesystem> filesystem, fs::AbsPath folderPath)
|
||||||
|
{
|
||||||
|
s_engineManager = std::move(engineManager);
|
||||||
|
s_filesystem = std::move(filesystem);
|
||||||
|
s_folderPath = std::move(folderPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
void shutdown()
|
||||||
|
{
|
||||||
|
s_icons.clear();
|
||||||
|
s_textures.clear();
|
||||||
|
s_engineManager = nullptr;
|
||||||
|
s_filesystem = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Icon loadIcon(const tl::string& name)
|
||||||
|
{
|
||||||
|
TL_ASSERT(s_engineManager);
|
||||||
|
fs::AbsPath filePath = s_folderPath + (name + ".png");
|
||||||
|
|
||||||
|
tl::memory_buffer rawData;
|
||||||
|
rawData.reserve(32768);
|
||||||
|
auto readResult = fs::readFile(rawData, *s_filesystem, filePath);
|
||||||
|
if (readResult.has_error())
|
||||||
|
{
|
||||||
|
LOGE("{}", readResult.error());
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
int w = 0, h = 0, comp = 0;
|
||||||
|
|
||||||
|
//expand anything to RGBA
|
||||||
|
constexpr int reqComp = 4;
|
||||||
|
tl::shared_ptr<uint8_t> imageData(kit_stbi_load_from_memory(rawData.data(), (int)rawData.size(), &w, &h, &comp, reqComp), tl::aligned_free16);
|
||||||
|
if (!imageData)
|
||||||
|
{
|
||||||
|
LOGE("Unknown image format for icon '{}'", filePath);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if ((w < 1) || (h < 1))
|
||||||
|
{
|
||||||
|
LOGE("Invalid size for icon '{}': {}x{}", filePath, w, h);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
if (comp != 4)
|
||||||
|
{
|
||||||
|
LOGE("Invalid number of components for icon '{}': {}", filePath, comp);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
tl::lent_ref<filament::Engine> engine = s_engineManager->getEngine();
|
||||||
|
|
||||||
|
const math::vec2u size(tl::narrow<uint32_t>(w), tl::narrow<uint32_t>(h));
|
||||||
|
math::vec2u offset;
|
||||||
|
|
||||||
|
TextureData* textureData = nullptr;
|
||||||
|
|
||||||
|
for (TextureData& td : s_textures)
|
||||||
|
{
|
||||||
|
kit::Atlaser& atlaser = td.atlaser;
|
||||||
|
const tl::optional<math::vec2u> region = atlaser.reserveRegion(size);
|
||||||
|
if (!region)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
textureData = &td;
|
||||||
|
offset = region.value();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!textureData)
|
||||||
|
{
|
||||||
|
constexpr bool isSRGB = false; //we're using icons for imgui, which doesn't use a linear FB
|
||||||
|
constexpr auto format = isSRGB ? filament::Texture::InternalFormat::SRGB8_A8 : filament::Texture::InternalFormat::RGBA8;
|
||||||
|
constexpr filament::Texture::Sampler sampler = filament::Texture::Sampler::SAMPLER_2D;
|
||||||
|
|
||||||
|
const uint8_t targetMipmapCount = 1 + (uint8_t)std::log2(k_atlasSize);
|
||||||
|
|
||||||
|
const auto texture = filament::Texture::Builder()
|
||||||
|
.width(k_atlasSize)
|
||||||
|
.height(k_atlasSize)
|
||||||
|
.depth(1)
|
||||||
|
.levels(targetMipmapCount)
|
||||||
|
.sampler(sampler)
|
||||||
|
.format(format)
|
||||||
|
.build(*engine);
|
||||||
|
|
||||||
|
TextureData td
|
||||||
|
{
|
||||||
|
.texture = s_engineManager->adopt(texture),
|
||||||
|
.image = Image(math::vec2u(k_atlasSize)),
|
||||||
|
.atlaser = kit::Atlaser(math::vec2u(k_atlasSize), 4)
|
||||||
|
};
|
||||||
|
const tl::optional<math::vec2u> region = td.atlaser.reserveRegion(size);
|
||||||
|
TL_ASSERT(region);
|
||||||
|
offset = region.value();
|
||||||
|
|
||||||
|
s_textures.push_back(std::move(td));
|
||||||
|
|
||||||
|
textureData = &s_textures.back();
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr auto pixelDataFormat = filament::Texture::PixelBufferDescriptor::PixelDataFormat::RGBA;
|
||||||
|
constexpr auto pixelDataType = filament::Texture::PixelBufferDescriptor::PixelDataType::UBYTE;
|
||||||
|
|
||||||
|
{
|
||||||
|
Image img;
|
||||||
|
img.allocateNonOwning(math::vec2u(w, h), (kit::pf::R8G8B8A8*)imageData.get());
|
||||||
|
textureData->image.copyFrom(img, {0, 0}, img.getSize(), offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
//for debugging purposes
|
||||||
|
//textureData->image.saveAsTga(*fs::native.openSink(fs::AbsPath::from_string("icons.tga", fs::native.getCurrentFolder()), fs::Mode::CreateOrOpenAndClear).value());
|
||||||
|
|
||||||
|
textureData->texture->setImage(*engine,
|
||||||
|
0,
|
||||||
|
filament::Texture::PixelBufferDescriptor(textureData->image.getData(), k_atlasSize * k_atlasSize * 4, pixelDataFormat, pixelDataType));
|
||||||
|
|
||||||
|
textureData->texture->generateMipmaps(*engine);
|
||||||
|
|
||||||
|
Icon icon
|
||||||
|
{
|
||||||
|
.texture = textureData->texture,
|
||||||
|
.uv0 = math::to_vec2<float>(offset) / float(k_atlasSize),
|
||||||
|
.uv1 = math::to_vec2<float>(offset + size) / float(k_atlasSize)
|
||||||
|
};
|
||||||
|
|
||||||
|
return icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Icon& getIcon(const tl::string& name)
|
||||||
|
{
|
||||||
|
auto it = s_icons.find(name);
|
||||||
|
if (it == s_icons.end())
|
||||||
|
{
|
||||||
|
Icon data = loadIcon(name);
|
||||||
|
it = s_icons.emplace(name, std::move(data)).first;
|
||||||
|
}
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const Icon k_empty;
|
||||||
|
|
||||||
|
struct c_str_hasher
|
||||||
|
{
|
||||||
|
using hash_code_t = tl::unordered_map<tl::string, Icon>::hash_code_t;
|
||||||
|
|
||||||
|
hash_code_t operator()(const char* c_str) const noexcept
|
||||||
|
{
|
||||||
|
return tl::string::raw_hash(c_str);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const Icon& getIcon(const char* name)
|
||||||
|
{
|
||||||
|
if (!name)
|
||||||
|
return k_empty;
|
||||||
|
|
||||||
|
auto it = s_icons.find_as(name, c_str_hasher(), tl::equal_to_2<tl::string, const char*>());
|
||||||
|
if (it == s_icons.end())
|
||||||
|
{
|
||||||
|
Icon data = loadIcon(name);
|
||||||
|
it = s_icons.emplace(name, std::move(data)).first;
|
||||||
|
}
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+21949
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
imgui_stdlib.h + imgui_stdlib.cpp
|
||||||
|
InputText() wrappers for C++ standard library (STL) type: std::string.
|
||||||
|
This is also an example of how you may wrap your own similar types.
|
||||||
|
|
||||||
|
imgui_scoped.h
|
||||||
|
[Experimental, not currently in main repository]
|
||||||
|
Additional header file with some RAII-style wrappers for common Dear ImGui functions.
|
||||||
|
Try by merging: https://github.com/ocornut/imgui/pull/2197
|
||||||
|
Discuss at: https://github.com/ocornut/imgui/issues/2096
|
||||||
|
|
||||||
|
See more C++ related extension (fmt, RAII, syntaxis sugar) on Wiki:
|
||||||
|
https://github.com/ocornut/imgui/wiki/Useful-Extensions#cness
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
// dear imgui: wrappers for C++ standard library (STL) types (std::string, etc.)
|
||||||
|
// This is also an example of how you may wrap your own similar types.
|
||||||
|
|
||||||
|
// Changelog:
|
||||||
|
// - v0.10: Initial version. Added InputText() / InputTextMultiline() calls with std::string
|
||||||
|
|
||||||
|
// See more C++ related extension (fmt, RAII, syntaxis sugar) on Wiki:
|
||||||
|
// https://github.com/ocornut/imgui/wiki/Useful-Extensions#cness
|
||||||
|
#include "IUI/ImGui/imgui.h"
|
||||||
|
#include "IUI/ImGui/misc/cpp/imgui_stdlib.h"
|
||||||
|
|
||||||
|
// Clang warnings with -Weverything
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct InputTextCallback_UserData
|
||||||
|
{
|
||||||
|
eastl::string* Str;
|
||||||
|
ImGuiInputTextCallback ChainCallback;
|
||||||
|
void* ChainCallbackUserData;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int InputTextCallback(ImGuiInputTextCallbackData* data)
|
||||||
|
{
|
||||||
|
const InputTextCallback_UserData* user_data = (InputTextCallback_UserData*)data->UserData;
|
||||||
|
if (data->EventFlag == ImGuiInputTextFlags_CallbackResize)
|
||||||
|
{
|
||||||
|
// Resize string callback
|
||||||
|
// If for some reason we refuse the new length (BufTextLen) and/or capacity (BufSize) we need to set them back to what we want.
|
||||||
|
eastl::string* str = user_data->Str;
|
||||||
|
IM_ASSERT(data->Buf == str->c_str());
|
||||||
|
str->resize(data->BufTextLen);
|
||||||
|
data->Buf = (char*)str->c_str();
|
||||||
|
}
|
||||||
|
else if (user_data->ChainCallback)
|
||||||
|
{
|
||||||
|
// Forward to user callback, if any
|
||||||
|
data->UserData = user_data->ChainCallbackUserData;
|
||||||
|
return user_data->ChainCallback(data);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui::InputText(const char* label, eastl::string* str, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)
|
||||||
|
{
|
||||||
|
IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);
|
||||||
|
flags |= ImGuiInputTextFlags_CallbackResize;
|
||||||
|
|
||||||
|
InputTextCallback_UserData cb_user_data;
|
||||||
|
cb_user_data.Str = str;
|
||||||
|
cb_user_data.ChainCallback = callback;
|
||||||
|
cb_user_data.ChainCallbackUserData = user_data;
|
||||||
|
return InputText(label, (char*)str->c_str(), str->capacity() + 1, flags, InputTextCallback, &cb_user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui::InputTextMultiline(const char* label, eastl::string* str, const ImVec2& size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)
|
||||||
|
{
|
||||||
|
IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);
|
||||||
|
flags |= ImGuiInputTextFlags_CallbackResize;
|
||||||
|
|
||||||
|
InputTextCallback_UserData cb_user_data;
|
||||||
|
cb_user_data.Str = str;
|
||||||
|
cb_user_data.ChainCallback = callback;
|
||||||
|
cb_user_data.ChainCallbackUserData = user_data;
|
||||||
|
return InputTextMultiline(label, (char*)str->c_str(), str->capacity() + 1, size, flags, InputTextCallback, &cb_user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGui::InputTextWithHint(const char* label, const char* hint, eastl::string* str, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data)
|
||||||
|
{
|
||||||
|
IM_ASSERT((flags & ImGuiInputTextFlags_CallbackResize) == 0);
|
||||||
|
flags |= ImGuiInputTextFlags_CallbackResize;
|
||||||
|
|
||||||
|
InputTextCallback_UserData cb_user_data;
|
||||||
|
cb_user_data.Str = str;
|
||||||
|
cb_user_data.ChainCallback = callback;
|
||||||
|
cb_user_data.ChainCallbackUserData = user_data;
|
||||||
|
return InputTextWithHint(label, hint, (char*)str->c_str(), str->capacity() + 1, flags, InputTextCallback, &cb_user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__clang__)
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
# imgui_freetype
|
||||||
|
|
||||||
|
Build font atlases using FreeType instead of stb_truetype (which is the default font rasterizer).
|
||||||
|
<br>by @vuhdo, @mikesart, @ocornut.
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
1. Get latest FreeType binaries or build yourself (under Windows you may use vcpkg with `vcpkg install freetype --triplet=x64-windows`, `vcpkg integrate install`).
|
||||||
|
2. Add imgui_freetype.h/cpp alongside your project files.
|
||||||
|
3. Add `#define IMGUI_ENABLE_FREETYPE` in your [imconfig.h](https://github.com/ocornut/imgui/blob/master/imconfig.h) file
|
||||||
|
|
||||||
|
### About Gamma Correct Blending
|
||||||
|
|
||||||
|
FreeType assumes blending in linear space rather than gamma space.
|
||||||
|
See FreeType note for [FT_Render_Glyph](https://freetype.org/freetype2/docs/reference/ft2-glyph_retrieval.html#ft_render_glyph).
|
||||||
|
For correct results you need to be using sRGB and convert to linear space in the pixel shader output.
|
||||||
|
The default Dear ImGui styles will be impacted by this change (alpha values will need tweaking).
|
||||||
|
|
||||||
|
### Testbed for toying with settings (for developers)
|
||||||
|
|
||||||
|
See https://gist.github.com/ocornut/b3a9ecf13502fd818799a452969649ad
|
||||||
|
|
||||||
|
### Known issues
|
||||||
|
|
||||||
|
- Oversampling settings are ignored but also not so much necessary with the higher quality rendering.
|
||||||
|
|
||||||
|
### Comparison
|
||||||
|
|
||||||
|
Small, thin anti-aliased fonts typically benefit a lot from FreeType's hinting:
|
||||||
|

|
||||||
|
|
||||||
|
### Colorful glyphs/emojis
|
||||||
|
|
||||||
|
You can use the `ImGuiFreeTypeBuilderFlags_LoadColor` flag to load certain colorful glyphs. See the
|
||||||
|
["Using Colorful Glyphs/Emojis"](https://github.com/ocornut/imgui/blob/master/docs/FONTS.md#using-colorful-glyphsemojis) section of FONTS.md.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Using OpenType SVG fonts (SVGinOT)
|
||||||
|
- *SVG in Open Type* is a standard by Adobe and Mozilla for color OpenType and Open Font Format fonts. It allows font creators to embed complete SVG files within a font enabling full color and even animations.
|
||||||
|
- Popular fonts such as [twemoji](https://github.com/13rac1/twemoji-color-font) and fonts made with [scfbuild](https://github.com/13rac1/scfbuild) is SVGinOT
|
||||||
|
- Requires: [lunasvg](https://github.com/sammycage/lunasvg) v2.3.2 and above
|
||||||
|
1. Add `#define IMGUI_ENABLE_FREETYPE_LUNASVG` in your `imconfig.h`.
|
||||||
|
2. Get latest lunasvg binaries or build yourself. Under Windows you may use vcpkg with: `vcpkg install lunasvg --triplet=x64-windows`.
|
||||||
@@ -0,0 +1,947 @@
|
|||||||
|
// dear imgui: FreeType font builder (used as a replacement for the stb_truetype builder)
|
||||||
|
// (code)
|
||||||
|
|
||||||
|
// Get the latest version at https://github.com/ocornut/imgui/tree/master/misc/freetype
|
||||||
|
// Original code by @vuhdo (Aleksei Skriabin). Improvements by @mikesart. Maintained since 2019 by @ocornut.
|
||||||
|
|
||||||
|
// CHANGELOG
|
||||||
|
// (minor and older changes stripped away, please see git history for details)
|
||||||
|
// 2023/11/13: added support for ImFontConfig::RasterizationDensity field for scaling render density without scaling metrics.
|
||||||
|
// 2023/08/01: added support for SVG fonts, enable by using '#define IMGUI_ENABLE_FREETYPE_LUNASVG' (#6591)
|
||||||
|
// 2023/01/04: fixed a packing issue which in some occurrences would prevent large amount of glyphs from being packed correctly.
|
||||||
|
// 2021/08/23: fixed crash when FT_Render_Glyph() fails to render a glyph and returns NULL.
|
||||||
|
// 2021/03/05: added ImGuiFreeTypeBuilderFlags_Bitmap to load bitmap glyphs.
|
||||||
|
// 2021/03/02: set 'atlas->TexPixelsUseColors = true' to help some backends with deciding of a preferred texture format.
|
||||||
|
// 2021/01/28: added support for color-layered glyphs via ImGuiFreeTypeBuilderFlags_LoadColor (require Freetype 2.10+).
|
||||||
|
// 2021/01/26: simplified integration by using '#define IMGUI_ENABLE_FREETYPE'. renamed ImGuiFreeType::XXX flags to ImGuiFreeTypeBuilderFlags_XXX for consistency with other API. removed ImGuiFreeType::BuildFontAtlas().
|
||||||
|
// 2020/06/04: fix for rare case where FT_Get_Char_Index() succeed but FT_Load_Glyph() fails.
|
||||||
|
// 2019/02/09: added RasterizerFlags::Monochrome flag to disable font anti-aliasing (combine with ::MonoHinting for best results!)
|
||||||
|
// 2019/01/15: added support for imgui allocators + added FreeType only override function SetAllocatorFunctions().
|
||||||
|
// 2019/01/10: re-factored to match big update in STB builder. fixed texture height waste. fixed redundant glyphs when merging. support for glyph padding.
|
||||||
|
// 2018/06/08: added support for ImFontConfig::GlyphMinAdvanceX, GlyphMaxAdvanceX.
|
||||||
|
// 2018/02/04: moved to main imgui repository (away from http://www.github.com/ocornut/imgui_club)
|
||||||
|
// 2018/01/22: fix for addition of ImFontAtlas::TexUvscale member.
|
||||||
|
// 2017/10/22: minor inconsequential change to match change in master (removed an unnecessary statement).
|
||||||
|
// 2017/09/26: fixes for imgui internal changes.
|
||||||
|
// 2017/08/26: cleanup, optimizations, support for ImFontConfig::RasterizerFlags, ImFontConfig::RasterizerMultiply.
|
||||||
|
// 2017/08/16: imported from https://github.com/Vuhdo/imgui_freetype into http://www.github.com/ocornut/imgui_club, updated for latest changes in ImFontAtlas, minor tweaks.
|
||||||
|
|
||||||
|
// About Gamma Correct Blending:
|
||||||
|
// - FreeType assumes blending in linear space rather than gamma space.
|
||||||
|
// - See https://www.freetype.org/freetype2/docs/reference/ft2-base_interface.html#FT_Render_Glyph
|
||||||
|
// - For correct results you need to be using sRGB and convert to linear space in the pixel shader output.
|
||||||
|
// - The default dear imgui styles will be impacted by this change (alpha values will need tweaking).
|
||||||
|
|
||||||
|
// FIXME: cfg.OversampleH, OversampleV are not supported (but perhaps not so necessary with this rasterizer).
|
||||||
|
|
||||||
|
#include "IUI/ImGui/misc/freetype/imgui_freetype.h"
|
||||||
|
|
||||||
|
#include "IUI/ImGui/imgui_internal.h" // ImMin,ImMax,ImFontAtlasBuild*,
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <ft2build.h>
|
||||||
|
#include FT_FREETYPE_H // <freetype/freetype.h>
|
||||||
|
#include FT_MODULE_H // <freetype/ftmodapi.h>
|
||||||
|
#include FT_GLYPH_H // <freetype/ftglyph.h>
|
||||||
|
#include FT_SYNTHESIS_H // <freetype/ftsynth.h>
|
||||||
|
|
||||||
|
#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||||
|
#include FT_OTSVG_H // <freetype/otsvg.h>
|
||||||
|
#include FT_BBOX_H // <freetype/ftbbox.h>
|
||||||
|
#include <lunasvg.h>
|
||||||
|
#if !((FREETYPE_MAJOR >= 2) && (FREETYPE_MINOR >= 12))
|
||||||
|
#error IMGUI_ENABLE_FREETYPE_LUNASVG requires FreeType version >= 2.12
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning (push)
|
||||||
|
#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
|
||||||
|
#pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3).
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
|
||||||
|
#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
|
||||||
|
#ifndef __clang__
|
||||||
|
#pragma GCC diagnostic ignored "-Wsubobject-linkage" // warning: 'xxxx' has a field 'xxxx' whose type uses the anonymous namespace
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// Data
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
// Default memory allocators
|
||||||
|
static void* ImGuiFreeTypeDefaultAllocFunc(size_t size, void* user_data) { IM_UNUSED(user_data); return IM_ALLOC(size); }
|
||||||
|
static void ImGuiFreeTypeDefaultFreeFunc(void* ptr, void* user_data) { IM_UNUSED(user_data); IM_FREE(ptr); }
|
||||||
|
|
||||||
|
// Current memory allocators
|
||||||
|
static void* (*GImGuiFreeTypeAllocFunc)(size_t size, void* user_data) = ImGuiFreeTypeDefaultAllocFunc;
|
||||||
|
static void (*GImGuiFreeTypeFreeFunc)(void* ptr, void* user_data) = ImGuiFreeTypeDefaultFreeFunc;
|
||||||
|
static void* GImGuiFreeTypeAllocatorUserData = nullptr;
|
||||||
|
|
||||||
|
// Lunasvg support
|
||||||
|
#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||||
|
static FT_Error ImGuiLunasvgPortInit(FT_Pointer* state);
|
||||||
|
static void ImGuiLunasvgPortFree(FT_Pointer* state);
|
||||||
|
static FT_Error ImGuiLunasvgPortRender(FT_GlyphSlot slot, FT_Pointer* _state);
|
||||||
|
static FT_Error ImGuiLunasvgPortPresetSlot(FT_GlyphSlot slot, FT_Bool cache, FT_Pointer* _state);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
// Code
|
||||||
|
//-------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
// Glyph metrics:
|
||||||
|
// --------------
|
||||||
|
//
|
||||||
|
// xmin xmax
|
||||||
|
// | |
|
||||||
|
// |<-------- width -------->|
|
||||||
|
// | |
|
||||||
|
// | +-------------------------+----------------- ymax
|
||||||
|
// | | ggggggggg ggggg | ^ ^
|
||||||
|
// | | g:::::::::ggg::::g | | |
|
||||||
|
// | | g:::::::::::::::::g | | |
|
||||||
|
// | | g::::::ggggg::::::gg | | |
|
||||||
|
// | | g:::::g g:::::g | | |
|
||||||
|
// offsetX -|-------->| g:::::g g:::::g | offsetY |
|
||||||
|
// | | g:::::g g:::::g | | |
|
||||||
|
// | | g::::::g g:::::g | | |
|
||||||
|
// | | g:::::::ggggg:::::g | | |
|
||||||
|
// | | g::::::::::::::::g | | height
|
||||||
|
// | | gg::::::::::::::g | | |
|
||||||
|
// baseline ---*---------|---- gggggggg::::::g-----*-------- |
|
||||||
|
// / | | g:::::g | |
|
||||||
|
// origin | | gggggg g:::::g | |
|
||||||
|
// | | g:::::gg gg:::::g | |
|
||||||
|
// | | g::::::ggg:::::::g | |
|
||||||
|
// | | gg:::::::::::::g | |
|
||||||
|
// | | ggg::::::ggg | |
|
||||||
|
// | | gggggg | v
|
||||||
|
// | +-------------------------+----------------- ymin
|
||||||
|
// | |
|
||||||
|
// |------------- advanceX ----------->|
|
||||||
|
|
||||||
|
// A structure that describe a glyph.
|
||||||
|
struct GlyphInfo
|
||||||
|
{
|
||||||
|
int Width; // Glyph's width in pixels.
|
||||||
|
int Height; // Glyph's height in pixels.
|
||||||
|
FT_Int OffsetX; // The distance from the origin ("pen position") to the left of the glyph.
|
||||||
|
FT_Int OffsetY; // The distance from the origin to the top of the glyph. This is usually a value < 0.
|
||||||
|
float AdvanceX; // The distance from the origin to the origin of the next glyph. This is usually a value > 0.
|
||||||
|
bool IsColored; // The glyph is colored
|
||||||
|
};
|
||||||
|
|
||||||
|
// Font parameters and metrics.
|
||||||
|
struct FontInfo
|
||||||
|
{
|
||||||
|
uint32_t PixelHeight; // Size this font was generated with.
|
||||||
|
float Ascender; // The pixel extents above the baseline in pixels (typically positive).
|
||||||
|
float Descender; // The extents below the baseline in pixels (typically negative).
|
||||||
|
float LineSpacing; // The baseline-to-baseline distance. Note that it usually is larger than the sum of the ascender and descender taken as absolute values. There is also no guarantee that no glyphs extend above or below subsequent baselines when using this distance. Think of it as a value the designer of the font finds appropriate.
|
||||||
|
float LineGap; // The spacing in pixels between one row's descent and the next row's ascent.
|
||||||
|
float MaxAdvanceWidth; // This field gives the maximum horizontal cursor advance for all glyphs in the font.
|
||||||
|
};
|
||||||
|
|
||||||
|
// FreeType glyph rasterizer.
|
||||||
|
// NB: No ctor/dtor, explicitly call Init()/Shutdown()
|
||||||
|
struct FreeTypeFont
|
||||||
|
{
|
||||||
|
bool InitFont(FT_Library ft_library, const ImFontConfig& cfg, unsigned int extra_user_flags); // Initialize from an external data buffer. Doesn't copy data, and you must ensure it stays valid up to this object lifetime.
|
||||||
|
void CloseFont();
|
||||||
|
void SetPixelHeight(int pixel_height); // Change font pixel size. All following calls to RasterizeGlyph() will use this size
|
||||||
|
const FT_Glyph_Metrics* LoadGlyph(uint32_t in_codepoint);
|
||||||
|
const FT_Bitmap* RenderGlyphAndGetInfo(GlyphInfo* out_glyph_info);
|
||||||
|
void BlitGlyph(const FT_Bitmap* ft_bitmap, uint32_t* dst, uint32_t dst_pitch, unsigned char* multiply_table = nullptr);
|
||||||
|
~FreeTypeFont() { CloseFont(); }
|
||||||
|
|
||||||
|
// [Internals]
|
||||||
|
FontInfo Info; // Font descriptor of the current font.
|
||||||
|
FT_Face Face;
|
||||||
|
unsigned int UserFlags; // = ImFontConfig::RasterizerFlags
|
||||||
|
FT_Int32 LoadFlags;
|
||||||
|
FT_Render_Mode RenderMode;
|
||||||
|
float RasterizationDensity;
|
||||||
|
float InvRasterizationDensity;
|
||||||
|
};
|
||||||
|
|
||||||
|
// From SDL_ttf: Handy routines for converting from fixed point
|
||||||
|
#define FT_CEIL(X) (((X + 63) & -64) / 64)
|
||||||
|
|
||||||
|
bool FreeTypeFont::InitFont(FT_Library ft_library, const ImFontConfig& cfg, unsigned int extra_font_builder_flags)
|
||||||
|
{
|
||||||
|
FT_Error error = FT_New_Memory_Face(ft_library, (uint8_t*)cfg.FontData, (uint32_t)cfg.FontDataSize, (uint32_t)cfg.FontNo, &Face);
|
||||||
|
if (error != 0)
|
||||||
|
return false;
|
||||||
|
error = FT_Select_Charmap(Face, FT_ENCODING_UNICODE);
|
||||||
|
if (error != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Convert to FreeType flags (NB: Bold and Oblique are processed separately)
|
||||||
|
UserFlags = cfg.FontBuilderFlags | extra_font_builder_flags;
|
||||||
|
|
||||||
|
LoadFlags = 0;
|
||||||
|
if ((UserFlags & ImGuiFreeTypeBuilderFlags_Bitmap) == 0)
|
||||||
|
LoadFlags |= FT_LOAD_NO_BITMAP;
|
||||||
|
|
||||||
|
if (UserFlags & ImGuiFreeTypeBuilderFlags_NoHinting)
|
||||||
|
LoadFlags |= FT_LOAD_NO_HINTING;
|
||||||
|
if (UserFlags & ImGuiFreeTypeBuilderFlags_NoAutoHint)
|
||||||
|
LoadFlags |= FT_LOAD_NO_AUTOHINT;
|
||||||
|
if (UserFlags & ImGuiFreeTypeBuilderFlags_ForceAutoHint)
|
||||||
|
LoadFlags |= FT_LOAD_FORCE_AUTOHINT;
|
||||||
|
if (UserFlags & ImGuiFreeTypeBuilderFlags_LightHinting)
|
||||||
|
LoadFlags |= FT_LOAD_TARGET_LIGHT;
|
||||||
|
else if (UserFlags & ImGuiFreeTypeBuilderFlags_MonoHinting)
|
||||||
|
LoadFlags |= FT_LOAD_TARGET_MONO;
|
||||||
|
else
|
||||||
|
LoadFlags |= FT_LOAD_TARGET_NORMAL;
|
||||||
|
|
||||||
|
if (UserFlags & ImGuiFreeTypeBuilderFlags_Monochrome)
|
||||||
|
RenderMode = FT_RENDER_MODE_MONO;
|
||||||
|
else
|
||||||
|
RenderMode = FT_RENDER_MODE_NORMAL;
|
||||||
|
|
||||||
|
if (UserFlags & ImGuiFreeTypeBuilderFlags_LoadColor)
|
||||||
|
LoadFlags |= FT_LOAD_COLOR;
|
||||||
|
|
||||||
|
RasterizationDensity = cfg.RasterizerDensity;
|
||||||
|
InvRasterizationDensity = 1.0f / RasterizationDensity;
|
||||||
|
|
||||||
|
memset(&Info, 0, sizeof(Info));
|
||||||
|
SetPixelHeight((uint32_t)cfg.SizePixels);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeTypeFont::CloseFont()
|
||||||
|
{
|
||||||
|
if (Face)
|
||||||
|
{
|
||||||
|
FT_Done_Face(Face);
|
||||||
|
Face = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeTypeFont::SetPixelHeight(int pixel_height)
|
||||||
|
{
|
||||||
|
// Vuhdo: I'm not sure how to deal with font sizes properly. As far as I understand, currently ImGui assumes that the 'pixel_height'
|
||||||
|
// is a maximum height of an any given glyph, i.e. it's the sum of font's ascender and descender. Seems strange to me.
|
||||||
|
// NB: FT_Set_Pixel_Sizes() doesn't seem to get us the same result.
|
||||||
|
FT_Size_RequestRec req;
|
||||||
|
req.type = (UserFlags & ImGuiFreeTypeBuilderFlags_Bitmap) ? FT_SIZE_REQUEST_TYPE_NOMINAL : FT_SIZE_REQUEST_TYPE_REAL_DIM;
|
||||||
|
req.width = 0;
|
||||||
|
req.height = (uint32_t)(pixel_height * 64 * RasterizationDensity);
|
||||||
|
req.horiResolution = 0;
|
||||||
|
req.vertResolution = 0;
|
||||||
|
FT_Request_Size(Face, &req);
|
||||||
|
|
||||||
|
// Update font info
|
||||||
|
FT_Size_Metrics metrics = Face->size->metrics;
|
||||||
|
Info.PixelHeight = (uint32_t)(pixel_height * InvRasterizationDensity);
|
||||||
|
Info.Ascender = (float)FT_CEIL(metrics.ascender) * InvRasterizationDensity;
|
||||||
|
Info.Descender = (float)FT_CEIL(metrics.descender) * InvRasterizationDensity;
|
||||||
|
Info.LineSpacing = (float)FT_CEIL(metrics.height) * InvRasterizationDensity;
|
||||||
|
Info.LineGap = (float)FT_CEIL(metrics.height - metrics.ascender + metrics.descender) * InvRasterizationDensity;
|
||||||
|
Info.MaxAdvanceWidth = (float)FT_CEIL(metrics.max_advance) * InvRasterizationDensity;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FT_Glyph_Metrics* FreeTypeFont::LoadGlyph(uint32_t codepoint)
|
||||||
|
{
|
||||||
|
uint32_t glyph_index = FT_Get_Char_Index(Face, codepoint);
|
||||||
|
if (glyph_index == 0)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// If this crash for you: FreeType 2.11.0 has a crash bug on some bitmap/colored fonts.
|
||||||
|
// - https://gitlab.freedesktop.org/freetype/freetype/-/issues/1076
|
||||||
|
// - https://github.com/ocornut/imgui/issues/4567
|
||||||
|
// - https://github.com/ocornut/imgui/issues/4566
|
||||||
|
// You can use FreeType 2.10, or the patched version of 2.11.0 in VcPkg, or probably any upcoming FreeType version.
|
||||||
|
FT_Error error = FT_Load_Glyph(Face, glyph_index, LoadFlags);
|
||||||
|
if (error)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
// Need an outline for this to work
|
||||||
|
FT_GlyphSlot slot = Face->glyph;
|
||||||
|
#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||||
|
IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE || slot->format == FT_GLYPH_FORMAT_BITMAP || slot->format == FT_GLYPH_FORMAT_SVG);
|
||||||
|
#else
|
||||||
|
#if ((FREETYPE_MAJOR >= 2) && (FREETYPE_MINOR >= 12))
|
||||||
|
IM_ASSERT(slot->format != FT_GLYPH_FORMAT_SVG && "The font contains SVG glyphs, you'll need to enable IMGUI_ENABLE_FREETYPE_LUNASVG in imconfig.h and install required libraries in order to use this font");
|
||||||
|
#endif
|
||||||
|
IM_ASSERT(slot->format == FT_GLYPH_FORMAT_OUTLINE || slot->format == FT_GLYPH_FORMAT_BITMAP);
|
||||||
|
#endif // IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||||
|
|
||||||
|
// Apply convenience transform (this is not picking from real "Bold"/"Italic" fonts! Merely applying FreeType helper transform. Oblique == Slanting)
|
||||||
|
if (UserFlags & ImGuiFreeTypeBuilderFlags_Bold)
|
||||||
|
FT_GlyphSlot_Embolden(slot);
|
||||||
|
if (UserFlags & ImGuiFreeTypeBuilderFlags_Oblique)
|
||||||
|
{
|
||||||
|
FT_GlyphSlot_Oblique(slot);
|
||||||
|
//FT_BBox bbox;
|
||||||
|
//FT_Outline_Get_BBox(&slot->outline, &bbox);
|
||||||
|
//slot->metrics.width = bbox.xMax - bbox.xMin;
|
||||||
|
//slot->metrics.height = bbox.yMax - bbox.yMin;
|
||||||
|
}
|
||||||
|
|
||||||
|
return &slot->metrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FT_Bitmap* FreeTypeFont::RenderGlyphAndGetInfo(GlyphInfo* out_glyph_info)
|
||||||
|
{
|
||||||
|
FT_GlyphSlot slot = Face->glyph;
|
||||||
|
FT_Error error = FT_Render_Glyph(slot, RenderMode);
|
||||||
|
if (error != 0)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
FT_Bitmap* ft_bitmap = &Face->glyph->bitmap;
|
||||||
|
out_glyph_info->Width = (int)ft_bitmap->width;
|
||||||
|
out_glyph_info->Height = (int)ft_bitmap->rows;
|
||||||
|
out_glyph_info->OffsetX = Face->glyph->bitmap_left;
|
||||||
|
out_glyph_info->OffsetY = -Face->glyph->bitmap_top;
|
||||||
|
out_glyph_info->AdvanceX = (float)FT_CEIL(slot->advance.x);
|
||||||
|
out_glyph_info->IsColored = (ft_bitmap->pixel_mode == FT_PIXEL_MODE_BGRA);
|
||||||
|
|
||||||
|
return ft_bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FreeTypeFont::BlitGlyph(const FT_Bitmap* ft_bitmap, uint32_t* dst, uint32_t dst_pitch, unsigned char* multiply_table)
|
||||||
|
{
|
||||||
|
IM_ASSERT(ft_bitmap != nullptr);
|
||||||
|
const uint32_t w = ft_bitmap->width;
|
||||||
|
const uint32_t h = ft_bitmap->rows;
|
||||||
|
const uint8_t* src = ft_bitmap->buffer;
|
||||||
|
const uint32_t src_pitch = ft_bitmap->pitch;
|
||||||
|
|
||||||
|
switch (ft_bitmap->pixel_mode)
|
||||||
|
{
|
||||||
|
case FT_PIXEL_MODE_GRAY: // Grayscale image, 1 byte per pixel.
|
||||||
|
{
|
||||||
|
if (multiply_table == nullptr)
|
||||||
|
{
|
||||||
|
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
|
||||||
|
for (uint32_t x = 0; x < w; x++)
|
||||||
|
dst[x] = IM_COL32(255, 255, 255, src[x]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
|
||||||
|
for (uint32_t x = 0; x < w; x++)
|
||||||
|
dst[x] = IM_COL32(255, 255, 255, multiply_table[src[x]]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FT_PIXEL_MODE_MONO: // Monochrome image, 1 bit per pixel. The bits in each byte are ordered from MSB to LSB.
|
||||||
|
{
|
||||||
|
uint8_t color0 = multiply_table ? multiply_table[0] : 0;
|
||||||
|
uint8_t color1 = multiply_table ? multiply_table[255] : 255;
|
||||||
|
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
|
||||||
|
{
|
||||||
|
uint8_t bits = 0;
|
||||||
|
const uint8_t* bits_ptr = src;
|
||||||
|
for (uint32_t x = 0; x < w; x++, bits <<= 1)
|
||||||
|
{
|
||||||
|
if ((x & 7) == 0)
|
||||||
|
bits = *bits_ptr++;
|
||||||
|
dst[x] = IM_COL32(255, 255, 255, (bits & 0x80) ? color1 : color0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FT_PIXEL_MODE_BGRA:
|
||||||
|
{
|
||||||
|
// FIXME: Converting pre-multiplied alpha to straight. Doesn't smell good.
|
||||||
|
#define DE_MULTIPLY(color, alpha) (ImU32)(255.0f * (float)color / (float)alpha + 0.5f)
|
||||||
|
if (multiply_table == nullptr)
|
||||||
|
{
|
||||||
|
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
|
||||||
|
for (uint32_t x = 0; x < w; x++)
|
||||||
|
{
|
||||||
|
uint8_t r = src[x * 4 + 2], g = src[x * 4 + 1], b = src[x * 4], a = src[x * 4 + 3];
|
||||||
|
dst[x] = IM_COL32(DE_MULTIPLY(r, a), DE_MULTIPLY(g, a), DE_MULTIPLY(b, a), a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (uint32_t y = 0; y < h; y++, src += src_pitch, dst += dst_pitch)
|
||||||
|
{
|
||||||
|
for (uint32_t x = 0; x < w; x++)
|
||||||
|
{
|
||||||
|
uint8_t r = src[x * 4 + 2], g = src[x * 4 + 1], b = src[x * 4], a = src[x * 4 + 3];
|
||||||
|
dst[x] = IM_COL32(multiply_table[DE_MULTIPLY(r, a)], multiply_table[DE_MULTIPLY(g, a)], multiply_table[DE_MULTIPLY(b, a)], multiply_table[a]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#undef DE_MULTIPLY
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
IM_ASSERT(0 && "FreeTypeFont::BlitGlyph(): Unknown bitmap pixel mode!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
#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_ASSERT(x) do { IM_ASSERT(x); } while (0)
|
||||||
|
#define STBRP_STATIC
|
||||||
|
#define STB_RECT_PACK_IMPLEMENTATION
|
||||||
|
#endif
|
||||||
|
#ifdef IMGUI_STB_RECT_PACK_FILENAME
|
||||||
|
#include IMGUI_STB_RECT_PACK_FILENAME
|
||||||
|
#else
|
||||||
|
#include "IUI/ImGui/imstb_rectpack.h"
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct ImFontBuildSrcGlyphFT
|
||||||
|
{
|
||||||
|
GlyphInfo Info;
|
||||||
|
uint32_t Codepoint;
|
||||||
|
unsigned int* BitmapData; // Point within one of the dst_tmp_bitmap_buffers[] array
|
||||||
|
|
||||||
|
ImFontBuildSrcGlyphFT() { memset((void*)this, 0, sizeof(*this)); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ImFontBuildSrcDataFT
|
||||||
|
{
|
||||||
|
FreeTypeFont Font;
|
||||||
|
stbrp_rect* Rects; // Rectangle to pack. We first fill in their size and the packer will give us their position.
|
||||||
|
const ImWchar* SrcRanges; // Ranges as requested by user (user is allowed to request too much, e.g. 0x0020..0xFFFF)
|
||||||
|
int DstIndex; // Index into atlas->Fonts[] and dst_tmp_array[]
|
||||||
|
int GlyphsHighest; // Highest requested codepoint
|
||||||
|
int GlyphsCount; // Glyph count (excluding missing glyphs and glyphs already set by an earlier source font)
|
||||||
|
ImBitVector GlyphsSet; // Glyph bit map (random access, 1-bit per codepoint. This will be a maximum of 8KB)
|
||||||
|
ImVector<ImFontBuildSrcGlyphFT> GlyphsList;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Temporary data for one destination ImFont* (multiple source fonts can be merged into one destination ImFont)
|
||||||
|
struct ImFontBuildDstDataFT
|
||||||
|
{
|
||||||
|
int SrcCount; // Number of source fonts targeting this destination font.
|
||||||
|
int GlyphsHighest;
|
||||||
|
int GlyphsCount;
|
||||||
|
ImBitVector GlyphsSet; // This is used to resolve collision when multiple sources are merged into a same destination font.
|
||||||
|
};
|
||||||
|
|
||||||
|
bool ImFontAtlasBuildWithFreeTypeEx(FT_Library ft_library, ImFontAtlas* atlas, unsigned int extra_flags)
|
||||||
|
{
|
||||||
|
IM_ASSERT(atlas->ConfigData.Size > 0);
|
||||||
|
|
||||||
|
ImFontAtlasBuildInit(atlas);
|
||||||
|
|
||||||
|
// Clear atlas
|
||||||
|
atlas->TexID = 0;
|
||||||
|
atlas->TexWidth = atlas->TexHeight = 0;
|
||||||
|
atlas->TexUvScale = ImVec2(0.0f, 0.0f);
|
||||||
|
atlas->TexUvWhitePixel = ImVec2(0.0f, 0.0f);
|
||||||
|
atlas->ClearTexData();
|
||||||
|
|
||||||
|
// Temporary storage for building
|
||||||
|
bool src_load_color = false;
|
||||||
|
ImVector<ImFontBuildSrcDataFT> src_tmp_array;
|
||||||
|
ImVector<ImFontBuildDstDataFT> dst_tmp_array;
|
||||||
|
src_tmp_array.resize(atlas->ConfigData.Size);
|
||||||
|
dst_tmp_array.resize(atlas->Fonts.Size);
|
||||||
|
memset((void*)src_tmp_array.Data, 0, (size_t)src_tmp_array.size_in_bytes());
|
||||||
|
memset((void*)dst_tmp_array.Data, 0, (size_t)dst_tmp_array.size_in_bytes());
|
||||||
|
|
||||||
|
// 1. Initialize font loading structure, check font data validity
|
||||||
|
for (int src_i = 0; src_i < atlas->ConfigData.Size; src_i++)
|
||||||
|
{
|
||||||
|
ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
|
||||||
|
ImFontConfig& cfg = atlas->ConfigData[src_i];
|
||||||
|
FreeTypeFont& font_face = src_tmp.Font;
|
||||||
|
IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == atlas));
|
||||||
|
|
||||||
|
// Find index from cfg.DstFont (we allow the user to set cfg.DstFont. Also it makes casual debugging nicer than when storing indices)
|
||||||
|
src_tmp.DstIndex = -1;
|
||||||
|
for (int output_i = 0; output_i < atlas->Fonts.Size && src_tmp.DstIndex == -1; output_i++)
|
||||||
|
if (cfg.DstFont == atlas->Fonts[output_i])
|
||||||
|
src_tmp.DstIndex = output_i;
|
||||||
|
IM_ASSERT(src_tmp.DstIndex != -1); // cfg.DstFont not pointing within atlas->Fonts[] array?
|
||||||
|
if (src_tmp.DstIndex == -1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Load font
|
||||||
|
if (!font_face.InitFont(ft_library, cfg, extra_flags))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Measure highest codepoints
|
||||||
|
src_load_color |= (cfg.FontBuilderFlags & ImGuiFreeTypeBuilderFlags_LoadColor) != 0;
|
||||||
|
ImFontBuildDstDataFT& dst_tmp = dst_tmp_array[src_tmp.DstIndex];
|
||||||
|
src_tmp.SrcRanges = cfg.GlyphRanges ? cfg.GlyphRanges : atlas->GetGlyphRangesDefault();
|
||||||
|
for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2)
|
||||||
|
{
|
||||||
|
// Check for valid range. This may also help detect *some* dangling pointers, because a common
|
||||||
|
// user error is to setup ImFontConfig::GlyphRanges with a pointer to data that isn't persistent.
|
||||||
|
IM_ASSERT(src_range[0] <= src_range[1]);
|
||||||
|
src_tmp.GlyphsHighest = ImMax(src_tmp.GlyphsHighest, (int)src_range[1]);
|
||||||
|
}
|
||||||
|
dst_tmp.SrcCount++;
|
||||||
|
dst_tmp.GlyphsHighest = ImMax(dst_tmp.GlyphsHighest, src_tmp.GlyphsHighest);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. For every requested codepoint, check for their presence in the font data, and handle redundancy or overlaps between source fonts to avoid unused glyphs.
|
||||||
|
int total_glyphs_count = 0;
|
||||||
|
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
|
||||||
|
{
|
||||||
|
ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
|
||||||
|
ImFontBuildDstDataFT& dst_tmp = dst_tmp_array[src_tmp.DstIndex];
|
||||||
|
src_tmp.GlyphsSet.Create(src_tmp.GlyphsHighest + 1);
|
||||||
|
if (dst_tmp.GlyphsSet.Storage.empty())
|
||||||
|
dst_tmp.GlyphsSet.Create(dst_tmp.GlyphsHighest + 1);
|
||||||
|
|
||||||
|
for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2)
|
||||||
|
for (int codepoint = src_range[0]; codepoint <= (int)src_range[1]; codepoint++)
|
||||||
|
{
|
||||||
|
if (dst_tmp.GlyphsSet.TestBit(codepoint)) // Don't overwrite existing glyphs. We could make this an option (e.g. MergeOverwrite)
|
||||||
|
continue;
|
||||||
|
uint32_t glyph_index = FT_Get_Char_Index(src_tmp.Font.Face, codepoint); // It is actually in the font? (FIXME-OPT: We are not storing the glyph_index..)
|
||||||
|
if (glyph_index == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Add to avail set/counters
|
||||||
|
src_tmp.GlyphsCount++;
|
||||||
|
dst_tmp.GlyphsCount++;
|
||||||
|
src_tmp.GlyphsSet.SetBit(codepoint);
|
||||||
|
dst_tmp.GlyphsSet.SetBit(codepoint);
|
||||||
|
total_glyphs_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Unpack our bit map into a flat list (we now have all the Unicode points that we know are requested _and_ available _and_ not overlapping another)
|
||||||
|
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
|
||||||
|
{
|
||||||
|
ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
|
||||||
|
src_tmp.GlyphsList.reserve(src_tmp.GlyphsCount);
|
||||||
|
|
||||||
|
IM_ASSERT(sizeof(src_tmp.GlyphsSet.Storage.Data[0]) == sizeof(ImU32));
|
||||||
|
const ImU32* it_begin = src_tmp.GlyphsSet.Storage.begin();
|
||||||
|
const ImU32* it_end = src_tmp.GlyphsSet.Storage.end();
|
||||||
|
for (const ImU32* it = it_begin; it < it_end; it++)
|
||||||
|
if (ImU32 entries_32 = *it)
|
||||||
|
for (ImU32 bit_n = 0; bit_n < 32; bit_n++)
|
||||||
|
if (entries_32 & ((ImU32)1 << bit_n))
|
||||||
|
{
|
||||||
|
ImFontBuildSrcGlyphFT src_glyph;
|
||||||
|
src_glyph.Codepoint = (ImWchar)(((it - it_begin) << 5) + bit_n);
|
||||||
|
//src_glyph.GlyphIndex = 0; // FIXME-OPT: We had this info in the previous step and lost it..
|
||||||
|
src_tmp.GlyphsList.push_back(src_glyph);
|
||||||
|
}
|
||||||
|
src_tmp.GlyphsSet.Clear();
|
||||||
|
IM_ASSERT(src_tmp.GlyphsList.Size == src_tmp.GlyphsCount);
|
||||||
|
}
|
||||||
|
for (int dst_i = 0; dst_i < dst_tmp_array.Size; dst_i++)
|
||||||
|
dst_tmp_array[dst_i].GlyphsSet.Clear();
|
||||||
|
dst_tmp_array.clear();
|
||||||
|
|
||||||
|
// Allocate packing character data and flag packed characters buffer as non-packed (x0=y0=x1=y1=0)
|
||||||
|
// (We technically don't need to zero-clear buf_rects, but let's do it for the sake of sanity)
|
||||||
|
ImVector<stbrp_rect> buf_rects;
|
||||||
|
buf_rects.resize(total_glyphs_count);
|
||||||
|
memset(buf_rects.Data, 0, (size_t)buf_rects.size_in_bytes());
|
||||||
|
|
||||||
|
// Allocate temporary rasterization data buffers.
|
||||||
|
// We could not find a way to retrieve accurate glyph size without rendering them.
|
||||||
|
// (e.g. slot->metrics->width not always matching bitmap->width, especially considering the Oblique transform)
|
||||||
|
// We allocate in chunks of 256 KB to not waste too much extra memory ahead. Hopefully users of FreeType won't mind the temporary allocations.
|
||||||
|
const int BITMAP_BUFFERS_CHUNK_SIZE = 256 * 1024;
|
||||||
|
int buf_bitmap_current_used_bytes = 0;
|
||||||
|
ImVector<unsigned char*> buf_bitmap_buffers;
|
||||||
|
buf_bitmap_buffers.push_back((unsigned char*)IM_ALLOC(BITMAP_BUFFERS_CHUNK_SIZE));
|
||||||
|
|
||||||
|
// 4. Gather glyphs sizes so we can pack them in our virtual canvas.
|
||||||
|
// 8. Render/rasterize font characters into the texture
|
||||||
|
int total_surface = 0;
|
||||||
|
int buf_rects_out_n = 0;
|
||||||
|
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
|
||||||
|
{
|
||||||
|
ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
|
||||||
|
ImFontConfig& cfg = atlas->ConfigData[src_i];
|
||||||
|
if (src_tmp.GlyphsCount == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
src_tmp.Rects = &buf_rects[buf_rects_out_n];
|
||||||
|
buf_rects_out_n += src_tmp.GlyphsCount;
|
||||||
|
|
||||||
|
// Compute multiply table if requested
|
||||||
|
const bool multiply_enabled = (cfg.RasterizerMultiply != 1.0f);
|
||||||
|
unsigned char multiply_table[256];
|
||||||
|
if (multiply_enabled)
|
||||||
|
ImFontAtlasBuildMultiplyCalcLookupTable(multiply_table, cfg.RasterizerMultiply);
|
||||||
|
|
||||||
|
// Gather the sizes of all rectangles we will need to pack
|
||||||
|
const int padding = atlas->TexGlyphPadding;
|
||||||
|
for (int glyph_i = 0; glyph_i < src_tmp.GlyphsList.Size; glyph_i++)
|
||||||
|
{
|
||||||
|
ImFontBuildSrcGlyphFT& src_glyph = src_tmp.GlyphsList[glyph_i];
|
||||||
|
|
||||||
|
const FT_Glyph_Metrics* metrics = src_tmp.Font.LoadGlyph(src_glyph.Codepoint);
|
||||||
|
if (metrics == nullptr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Render glyph into a bitmap (currently held by FreeType)
|
||||||
|
const FT_Bitmap* ft_bitmap = src_tmp.Font.RenderGlyphAndGetInfo(&src_glyph.Info);
|
||||||
|
if (ft_bitmap == nullptr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Allocate new temporary chunk if needed
|
||||||
|
const int bitmap_size_in_bytes = src_glyph.Info.Width * src_glyph.Info.Height * 4;
|
||||||
|
if (buf_bitmap_current_used_bytes + bitmap_size_in_bytes > BITMAP_BUFFERS_CHUNK_SIZE)
|
||||||
|
{
|
||||||
|
buf_bitmap_current_used_bytes = 0;
|
||||||
|
buf_bitmap_buffers.push_back((unsigned char*)IM_ALLOC(BITMAP_BUFFERS_CHUNK_SIZE));
|
||||||
|
}
|
||||||
|
IM_ASSERT(buf_bitmap_current_used_bytes + bitmap_size_in_bytes <= BITMAP_BUFFERS_CHUNK_SIZE); // We could probably allocate custom-sized buffer instead.
|
||||||
|
|
||||||
|
// Blit rasterized pixels to our temporary buffer and keep a pointer to it.
|
||||||
|
src_glyph.BitmapData = (unsigned int*)(buf_bitmap_buffers.back() + buf_bitmap_current_used_bytes);
|
||||||
|
buf_bitmap_current_used_bytes += bitmap_size_in_bytes;
|
||||||
|
src_tmp.Font.BlitGlyph(ft_bitmap, src_glyph.BitmapData, src_glyph.Info.Width, multiply_enabled ? multiply_table : nullptr);
|
||||||
|
|
||||||
|
src_tmp.Rects[glyph_i].w = (stbrp_coord)(src_glyph.Info.Width + padding);
|
||||||
|
src_tmp.Rects[glyph_i].h = (stbrp_coord)(src_glyph.Info.Height + padding);
|
||||||
|
total_surface += src_tmp.Rects[glyph_i].w * src_tmp.Rects[glyph_i].h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need a width for the skyline algorithm, any width!
|
||||||
|
// The exact width doesn't really matter much, but some API/GPU have texture size limitations and increasing width can decrease height.
|
||||||
|
// User can override TexDesiredWidth and TexGlyphPadding if they wish, otherwise we use a simple heuristic to select the width based on expected surface.
|
||||||
|
const int surface_sqrt = (int)ImSqrt((float)total_surface) + 1;
|
||||||
|
atlas->TexHeight = 0;
|
||||||
|
if (atlas->TexDesiredWidth > 0)
|
||||||
|
atlas->TexWidth = atlas->TexDesiredWidth;
|
||||||
|
else
|
||||||
|
atlas->TexWidth = (surface_sqrt >= 4096 * 0.7f) ? 4096 : (surface_sqrt >= 2048 * 0.7f) ? 2048 : (surface_sqrt >= 1024 * 0.7f) ? 1024 : 512;
|
||||||
|
|
||||||
|
// 5. Start packing
|
||||||
|
// Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values).
|
||||||
|
const int TEX_HEIGHT_MAX = 1024 * 32;
|
||||||
|
const int num_nodes_for_packing_algorithm = atlas->TexWidth - atlas->TexGlyphPadding;
|
||||||
|
ImVector<stbrp_node> pack_nodes;
|
||||||
|
pack_nodes.resize(num_nodes_for_packing_algorithm);
|
||||||
|
stbrp_context pack_context;
|
||||||
|
stbrp_init_target(&pack_context, atlas->TexWidth - atlas->TexGlyphPadding, TEX_HEIGHT_MAX - atlas->TexGlyphPadding, pack_nodes.Data, pack_nodes.Size);
|
||||||
|
ImFontAtlasBuildPackCustomRects(atlas, &pack_context);
|
||||||
|
|
||||||
|
// 6. Pack each source font. No rendering yet, we are working with rectangles in an infinitely tall texture at this point.
|
||||||
|
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
|
||||||
|
{
|
||||||
|
ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
|
||||||
|
if (src_tmp.GlyphsCount == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
stbrp_pack_rects(&pack_context, src_tmp.Rects, src_tmp.GlyphsCount);
|
||||||
|
|
||||||
|
// Extend texture height and mark missing glyphs as non-packed so we won't render them.
|
||||||
|
// FIXME: We are not handling packing failure here (would happen if we got off TEX_HEIGHT_MAX or if a single if larger than TexWidth?)
|
||||||
|
for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++)
|
||||||
|
if (src_tmp.Rects[glyph_i].was_packed)
|
||||||
|
atlas->TexHeight = ImMax(atlas->TexHeight, src_tmp.Rects[glyph_i].y + src_tmp.Rects[glyph_i].h);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. Allocate texture
|
||||||
|
atlas->TexHeight = (atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) ? (atlas->TexHeight + 1) : ImUpperPowerOfTwo(atlas->TexHeight);
|
||||||
|
atlas->TexUvScale = ImVec2(1.0f / atlas->TexWidth, 1.0f / atlas->TexHeight);
|
||||||
|
if (src_load_color)
|
||||||
|
{
|
||||||
|
size_t tex_size = (size_t)atlas->TexWidth * atlas->TexHeight * 4;
|
||||||
|
atlas->TexPixelsRGBA32 = (unsigned int*)IM_ALLOC(tex_size);
|
||||||
|
memset(atlas->TexPixelsRGBA32, 0, tex_size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
size_t tex_size = (size_t)atlas->TexWidth * atlas->TexHeight * 1;
|
||||||
|
atlas->TexPixelsAlpha8 = (unsigned char*)IM_ALLOC(tex_size);
|
||||||
|
memset(atlas->TexPixelsAlpha8, 0, tex_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8. Copy rasterized font characters back into the main texture
|
||||||
|
// 9. Setup ImFont and glyphs for runtime
|
||||||
|
bool tex_use_colors = false;
|
||||||
|
for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
|
||||||
|
{
|
||||||
|
ImFontBuildSrcDataFT& src_tmp = src_tmp_array[src_i];
|
||||||
|
if (src_tmp.GlyphsCount == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// When merging fonts with MergeMode=true:
|
||||||
|
// - We can have multiple input fonts writing into a same destination font.
|
||||||
|
// - dst_font->ConfigData is != from cfg which is our source configuration.
|
||||||
|
ImFontConfig& cfg = atlas->ConfigData[src_i];
|
||||||
|
ImFont* dst_font = cfg.DstFont;
|
||||||
|
|
||||||
|
const float ascent = src_tmp.Font.Info.Ascender;
|
||||||
|
const float descent = src_tmp.Font.Info.Descender;
|
||||||
|
ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent);
|
||||||
|
const float font_off_x = cfg.GlyphOffset.x;
|
||||||
|
const float font_off_y = cfg.GlyphOffset.y + IM_ROUND(dst_font->Ascent);
|
||||||
|
|
||||||
|
const int padding = atlas->TexGlyphPadding;
|
||||||
|
for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++)
|
||||||
|
{
|
||||||
|
ImFontBuildSrcGlyphFT& src_glyph = src_tmp.GlyphsList[glyph_i];
|
||||||
|
stbrp_rect& pack_rect = src_tmp.Rects[glyph_i];
|
||||||
|
IM_ASSERT(pack_rect.was_packed);
|
||||||
|
if (pack_rect.w == 0 && pack_rect.h == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
GlyphInfo& info = src_glyph.Info;
|
||||||
|
IM_ASSERT(info.Width + padding <= pack_rect.w);
|
||||||
|
IM_ASSERT(info.Height + padding <= pack_rect.h);
|
||||||
|
const int tx = pack_rect.x + padding;
|
||||||
|
const int ty = pack_rect.y + padding;
|
||||||
|
|
||||||
|
// Register glyph
|
||||||
|
float x0 = info.OffsetX * src_tmp.Font.InvRasterizationDensity + font_off_x;
|
||||||
|
float y0 = info.OffsetY * src_tmp.Font.InvRasterizationDensity + font_off_y;
|
||||||
|
float x1 = x0 + info.Width * src_tmp.Font.InvRasterizationDensity;
|
||||||
|
float y1 = y0 + info.Height * src_tmp.Font.InvRasterizationDensity;
|
||||||
|
float u0 = (tx) / (float)atlas->TexWidth;
|
||||||
|
float v0 = (ty) / (float)atlas->TexHeight;
|
||||||
|
float u1 = (tx + info.Width) / (float)atlas->TexWidth;
|
||||||
|
float v1 = (ty + info.Height) / (float)atlas->TexHeight;
|
||||||
|
dst_font->AddGlyph(&cfg, (ImWchar)src_glyph.Codepoint, x0, y0, x1, y1, u0, v0, u1, v1, info.AdvanceX * src_tmp.Font.InvRasterizationDensity);
|
||||||
|
|
||||||
|
ImFontGlyph* dst_glyph = &dst_font->Glyphs.back();
|
||||||
|
IM_ASSERT(dst_glyph->Codepoint == src_glyph.Codepoint);
|
||||||
|
if (src_glyph.Info.IsColored)
|
||||||
|
dst_glyph->Colored = tex_use_colors = true;
|
||||||
|
|
||||||
|
// Blit from temporary buffer to final texture
|
||||||
|
size_t blit_src_stride = (size_t)src_glyph.Info.Width;
|
||||||
|
size_t blit_dst_stride = (size_t)atlas->TexWidth;
|
||||||
|
unsigned int* blit_src = src_glyph.BitmapData;
|
||||||
|
if (atlas->TexPixelsAlpha8 != nullptr)
|
||||||
|
{
|
||||||
|
unsigned char* blit_dst = atlas->TexPixelsAlpha8 + (ty * blit_dst_stride) + tx;
|
||||||
|
for (int y = 0; y < info.Height; y++, blit_dst += blit_dst_stride, blit_src += blit_src_stride)
|
||||||
|
for (int x = 0; x < info.Width; x++)
|
||||||
|
blit_dst[x] = (unsigned char)((blit_src[x] >> IM_COL32_A_SHIFT) & 0xFF);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
unsigned int* blit_dst = atlas->TexPixelsRGBA32 + (ty * blit_dst_stride) + tx;
|
||||||
|
for (int y = 0; y < info.Height; y++, blit_dst += blit_dst_stride, blit_src += blit_src_stride)
|
||||||
|
for (int x = 0; x < info.Width; x++)
|
||||||
|
blit_dst[x] = blit_src[x];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
src_tmp.Rects = nullptr;
|
||||||
|
}
|
||||||
|
atlas->TexPixelsUseColors = tex_use_colors;
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
for (int buf_i = 0; buf_i < buf_bitmap_buffers.Size; buf_i++)
|
||||||
|
IM_FREE(buf_bitmap_buffers[buf_i]);
|
||||||
|
src_tmp_array.clear_destruct();
|
||||||
|
|
||||||
|
ImFontAtlasBuildFinish(atlas);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FreeType memory allocation callbacks
|
||||||
|
static void* FreeType_Alloc(FT_Memory /*memory*/, long size)
|
||||||
|
{
|
||||||
|
return GImGuiFreeTypeAllocFunc((size_t)size, GImGuiFreeTypeAllocatorUserData);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void FreeType_Free(FT_Memory /*memory*/, void* block)
|
||||||
|
{
|
||||||
|
GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void* FreeType_Realloc(FT_Memory /*memory*/, long cur_size, long new_size, void* block)
|
||||||
|
{
|
||||||
|
// Implement realloc() as we don't ask user to provide it.
|
||||||
|
if (block == nullptr)
|
||||||
|
return GImGuiFreeTypeAllocFunc((size_t)new_size, GImGuiFreeTypeAllocatorUserData);
|
||||||
|
|
||||||
|
if (new_size == 0)
|
||||||
|
{
|
||||||
|
GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_size > cur_size)
|
||||||
|
{
|
||||||
|
void* new_block = GImGuiFreeTypeAllocFunc((size_t)new_size, GImGuiFreeTypeAllocatorUserData);
|
||||||
|
memcpy(new_block, block, (size_t)cur_size);
|
||||||
|
GImGuiFreeTypeFreeFunc(block, GImGuiFreeTypeAllocatorUserData);
|
||||||
|
return new_block;
|
||||||
|
}
|
||||||
|
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool ImFontAtlasBuildWithFreeType(ImFontAtlas* atlas)
|
||||||
|
{
|
||||||
|
// FreeType memory management: https://www.freetype.org/freetype2/docs/design/design-4.html
|
||||||
|
FT_MemoryRec_ memory_rec = {};
|
||||||
|
memory_rec.user = nullptr;
|
||||||
|
memory_rec.alloc = &FreeType_Alloc;
|
||||||
|
memory_rec.free = &FreeType_Free;
|
||||||
|
memory_rec.realloc = &FreeType_Realloc;
|
||||||
|
|
||||||
|
// https://www.freetype.org/freetype2/docs/reference/ft2-module_management.html#FT_New_Library
|
||||||
|
FT_Library ft_library;
|
||||||
|
FT_Error error = FT_New_Library(&memory_rec, &ft_library);
|
||||||
|
if (error != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// If you don't call FT_Add_Default_Modules() the rest of code may work, but FreeType won't use our custom allocator.
|
||||||
|
FT_Add_Default_Modules(ft_library);
|
||||||
|
|
||||||
|
#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||||
|
// Install svg hooks for FreeType
|
||||||
|
// https://freetype.org/freetype2/docs/reference/ft2-properties.html#svg-hooks
|
||||||
|
// https://freetype.org/freetype2/docs/reference/ft2-svg_fonts.html#svg_fonts
|
||||||
|
SVG_RendererHooks hooks = { ImGuiLunasvgPortInit, ImGuiLunasvgPortFree, ImGuiLunasvgPortRender, ImGuiLunasvgPortPresetSlot };
|
||||||
|
FT_Property_Set(ft_library, "ot-svg", "svg-hooks", &hooks);
|
||||||
|
#endif // IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||||
|
|
||||||
|
bool ret = ImFontAtlasBuildWithFreeTypeEx(ft_library, atlas, atlas->FontBuilderFlags);
|
||||||
|
FT_Done_Library(ft_library);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ImFontBuilderIO* ImGuiFreeType::GetBuilderForFreeType()
|
||||||
|
{
|
||||||
|
static ImFontBuilderIO io;
|
||||||
|
io.FontBuilder_Build = ImFontAtlasBuildWithFreeType;
|
||||||
|
return &io;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiFreeType::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data)
|
||||||
|
{
|
||||||
|
GImGuiFreeTypeAllocFunc = alloc_func;
|
||||||
|
GImGuiFreeTypeFreeFunc = free_func;
|
||||||
|
GImGuiFreeTypeAllocatorUserData = user_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||||
|
// For more details, see https://gitlab.freedesktop.org/freetype/freetype-demos/-/blob/master/src/rsvg-port.c
|
||||||
|
// The original code from the demo is licensed under CeCILL-C Free Software License Agreement (https://gitlab.freedesktop.org/freetype/freetype/-/blob/master/LICENSE.TXT)
|
||||||
|
struct LunasvgPortState
|
||||||
|
{
|
||||||
|
FT_Error err = FT_Err_Ok;
|
||||||
|
lunasvg::Matrix matrix;
|
||||||
|
std::unique_ptr<lunasvg::Document> svg = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
static FT_Error ImGuiLunasvgPortInit(FT_Pointer* _state)
|
||||||
|
{
|
||||||
|
*_state = IM_NEW(LunasvgPortState)();
|
||||||
|
return FT_Err_Ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ImGuiLunasvgPortFree(FT_Pointer* _state)
|
||||||
|
{
|
||||||
|
IM_DELETE(*(LunasvgPortState**)_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static FT_Error ImGuiLunasvgPortRender(FT_GlyphSlot slot, FT_Pointer* _state)
|
||||||
|
{
|
||||||
|
LunasvgPortState* state = *(LunasvgPortState**)_state;
|
||||||
|
|
||||||
|
// If there was an error while loading the svg in ImGuiLunasvgPortPresetSlot(), the renderer hook still get called, so just returns the error.
|
||||||
|
if (state->err != FT_Err_Ok)
|
||||||
|
return state->err;
|
||||||
|
|
||||||
|
// rows is height, pitch (or stride) equals to width * sizeof(int32)
|
||||||
|
lunasvg::Bitmap bitmap((uint8_t*)slot->bitmap.buffer, slot->bitmap.width, slot->bitmap.rows, slot->bitmap.pitch);
|
||||||
|
state->svg->setMatrix(state->svg->matrix().identity()); // Reset the svg matrix to the default value
|
||||||
|
state->svg->render(bitmap, state->matrix); // state->matrix is already scaled and translated
|
||||||
|
state->err = FT_Err_Ok;
|
||||||
|
return state->err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FT_Error ImGuiLunasvgPortPresetSlot(FT_GlyphSlot slot, FT_Bool cache, FT_Pointer* _state)
|
||||||
|
{
|
||||||
|
FT_SVG_Document document = (FT_SVG_Document)slot->other;
|
||||||
|
LunasvgPortState* state = *(LunasvgPortState**)_state;
|
||||||
|
FT_Size_Metrics& metrics = document->metrics;
|
||||||
|
|
||||||
|
// This function is called twice, once in the FT_Load_Glyph() and another right before ImGuiLunasvgPortRender().
|
||||||
|
// If it's the latter, don't do anything because it's // already done in the former.
|
||||||
|
if (cache)
|
||||||
|
return state->err;
|
||||||
|
|
||||||
|
state->svg = lunasvg::Document::loadFromData((const char*)document->svg_document, document->svg_document_length);
|
||||||
|
if (state->svg == nullptr)
|
||||||
|
{
|
||||||
|
state->err = FT_Err_Invalid_SVG_Document;
|
||||||
|
return state->err;
|
||||||
|
}
|
||||||
|
|
||||||
|
lunasvg::Box box = state->svg->box();
|
||||||
|
double scale = std::min(metrics.x_ppem / box.w, metrics.y_ppem / box.h);
|
||||||
|
double xx = (double)document->transform.xx / (1 << 16);
|
||||||
|
double xy = -(double)document->transform.xy / (1 << 16);
|
||||||
|
double yx = -(double)document->transform.yx / (1 << 16);
|
||||||
|
double yy = (double)document->transform.yy / (1 << 16);
|
||||||
|
double x0 = (double)document->delta.x / 64 * box.w / metrics.x_ppem;
|
||||||
|
double y0 = -(double)document->delta.y / 64 * box.h / metrics.y_ppem;
|
||||||
|
|
||||||
|
// Scale and transform, we don't translate the svg yet
|
||||||
|
state->matrix.identity();
|
||||||
|
state->matrix.scale(scale, scale);
|
||||||
|
state->matrix.transform(xx, xy, yx, yy, x0, y0);
|
||||||
|
state->svg->setMatrix(state->matrix);
|
||||||
|
|
||||||
|
// Pre-translate the matrix for the rendering step
|
||||||
|
state->matrix.translate(-box.x, -box.y);
|
||||||
|
|
||||||
|
// Get the box again after the transformation
|
||||||
|
box = state->svg->box();
|
||||||
|
|
||||||
|
// Calculate the bitmap size
|
||||||
|
slot->bitmap_left = FT_Int(box.x);
|
||||||
|
slot->bitmap_top = FT_Int(-box.y);
|
||||||
|
slot->bitmap.rows = (unsigned int)(ImCeil((float)box.h));
|
||||||
|
slot->bitmap.width = (unsigned int)(ImCeil((float)box.w));
|
||||||
|
slot->bitmap.pitch = slot->bitmap.width * 4;
|
||||||
|
slot->bitmap.pixel_mode = FT_PIXEL_MODE_BGRA;
|
||||||
|
|
||||||
|
// Compute all the bearings and set them correctly. The outline is scaled already, we just need to use the bounding box.
|
||||||
|
double metrics_width = box.w;
|
||||||
|
double metrics_height = box.h;
|
||||||
|
double horiBearingX = box.x;
|
||||||
|
double horiBearingY = -box.y;
|
||||||
|
double vertBearingX = slot->metrics.horiBearingX / 64.0 - slot->metrics.horiAdvance / 64.0 / 2.0;
|
||||||
|
double vertBearingY = (slot->metrics.vertAdvance / 64.0 - slot->metrics.height / 64.0) / 2.0;
|
||||||
|
slot->metrics.width = FT_Pos(IM_ROUND(metrics_width * 64.0)); // Using IM_ROUND() assume width and height are positive
|
||||||
|
slot->metrics.height = FT_Pos(IM_ROUND(metrics_height * 64.0));
|
||||||
|
slot->metrics.horiBearingX = FT_Pos(horiBearingX * 64);
|
||||||
|
slot->metrics.horiBearingY = FT_Pos(horiBearingY * 64);
|
||||||
|
slot->metrics.vertBearingX = FT_Pos(vertBearingX * 64);
|
||||||
|
slot->metrics.vertBearingY = FT_Pos(vertBearingY * 64);
|
||||||
|
|
||||||
|
if (slot->metrics.vertAdvance == 0)
|
||||||
|
slot->metrics.vertAdvance = FT_Pos(metrics_height * 1.2 * 64.0);
|
||||||
|
|
||||||
|
state->err = FT_Err_Ok;
|
||||||
|
return state->err;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // #ifdef IMGUI_ENABLE_FREETYPE_LUNASVG
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#ifdef __GNUC__
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning (pop)
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,551 @@
|
|||||||
|
#include "IUI/ImGuiExtensions.h"
|
||||||
|
|
||||||
|
#include "IUI/ImGui/imgui.h"
|
||||||
|
#include "IUI/ImGui/imgui_internal.h"
|
||||||
|
#include "IUI/extras/imguiwrap.dear.h"
|
||||||
|
|
||||||
|
#include <math/scalar.h>
|
||||||
|
#include <math/vec3.h>
|
||||||
|
#include <math/quat.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
|
||||||
|
namespace ImGui
|
||||||
|
{
|
||||||
|
ImU32 ColorU32(kit::color::sRGBAi c)
|
||||||
|
{
|
||||||
|
return (c.a << 24) | (c.b << 16) | (c.g << 8) | c.r;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImU32 ColorU32(kit::color::sRGBi c)
|
||||||
|
{
|
||||||
|
return (0xFF << 24) | (c.b << 16) | (c.g << 8) | c.r;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddDottedLine(ImDrawList& list, ImVec2 p1, ImVec2 p2, ImU32 col, float dotLength, float thickness)
|
||||||
|
{
|
||||||
|
if (dotLength == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ImVec2 dir = p2 - p1;
|
||||||
|
if (dir.x == 0.f && dir.y == 0.f)
|
||||||
|
return;
|
||||||
|
const float length = math::sqrt(dir.x * dir.x + dir.y * dir.y);
|
||||||
|
dir /= length;
|
||||||
|
|
||||||
|
const size_t segments = size_t(length / dotLength) / 2;
|
||||||
|
const ImVec2 inc = dir * dotLength;
|
||||||
|
ImVec2 p = p1;
|
||||||
|
for (size_t i = 0; i < segments; i++)
|
||||||
|
{
|
||||||
|
ImVec2 pp2 = p + inc;
|
||||||
|
list.AddLine(p, pp2, col, thickness);
|
||||||
|
p = pp2 + inc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec2 GetWindowContentPos()
|
||||||
|
{
|
||||||
|
return GetWindowPos() + GetWindowContentRegionMin();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec2 GetWindowContentSize()
|
||||||
|
{
|
||||||
|
return GetWindowContentRegionMax() - GetWindowContentRegionMin();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec2 GetWindowContentBottomRightSide()
|
||||||
|
{
|
||||||
|
return GetWindowSize() - ImVec2(GetStyle().WindowPadding);
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t GetMouseButtonClicked()
|
||||||
|
{
|
||||||
|
const bool leftBtn = IsMouseClicked(ImGuiMouseButton_Left);
|
||||||
|
const bool midBtn = IsMouseClicked(ImGuiMouseButton_Middle);
|
||||||
|
const bool rightBtn = IsMouseClicked(ImGuiMouseButton_Right);
|
||||||
|
return leftBtn ? ImGuiMouseButton_Left : midBtn ? ImGuiMouseButton_Middle : rightBtn ? ImGuiMouseButton_Right : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t GetMouseButtonDown()
|
||||||
|
{
|
||||||
|
const bool leftBtn = IsMouseDown(ImGuiMouseButton_Left);
|
||||||
|
const bool midBtn = IsMouseDown(ImGuiMouseButton_Middle);
|
||||||
|
const bool rightBtn = IsMouseDown(ImGuiMouseButton_Right);
|
||||||
|
return leftBtn ? ImGuiMouseButton_Left : midBtn ? ImGuiMouseButton_Middle : rightBtn ? ImGuiMouseButton_Right : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t GetMouseButtonReleased()
|
||||||
|
{
|
||||||
|
const bool leftBtn = IsMouseReleased(ImGuiMouseButton_Left);
|
||||||
|
const bool midBtn = IsMouseReleased(ImGuiMouseButton_Middle);
|
||||||
|
const bool rightBtn = IsMouseReleased(ImGuiMouseButton_Right);
|
||||||
|
return leftBtn ? ImGuiMouseButton_Left : midBtn ? ImGuiMouseButton_Middle : rightBtn ? ImGuiMouseButton_Right : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static tl::pair<kit::color::sRGBf, float> decomposeHDR(kit::color::lRGBhdr color)
|
||||||
|
{
|
||||||
|
const auto c = kit::color::cast_to<math::vec3f>(color);
|
||||||
|
const float maxComponent = math::max(math::cwise::max_component(c), 1.f);
|
||||||
|
const kit::color::sRGBf ldr = kit::color::cast_to_space<kit::color::Space::sRGB>(kit::color::cast_to<kit::color::lRGBf>(c / maxComponent));
|
||||||
|
return tl::make_pair(ldr, maxComponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
static kit::color::sRGBf hdrToLdr(kit::color::lRGBhdr color, float intensity)
|
||||||
|
{
|
||||||
|
const auto c = kit::color::cast_to<math::vec3f>(color);
|
||||||
|
return kit::color::cast_to_space<kit::color::Space::sRGB>(kit::color::cast_to<kit::color::lRGBf>(c / intensity));
|
||||||
|
}
|
||||||
|
|
||||||
|
static kit::color::lRGBAhdr recomposeHDR(kit::color::sRGBAf srgba, float intensity)
|
||||||
|
{
|
||||||
|
auto c = kit::color::cast_to<math::vec4f>(kit::color::cast_to_space<kit::color::Space::Linear>(srgba));
|
||||||
|
c.x *= intensity;
|
||||||
|
c.y *= intensity;
|
||||||
|
c.z *= intensity;
|
||||||
|
return kit::color::cast_to<kit::color::lRGBAhdr>(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ColorEditor(kit::color::lRGBAhdr& io_color, bool& saveUndo, bool isHDR, bool hasAlpha, const tl::string& label, float alignNameX) noexcept
|
||||||
|
{
|
||||||
|
bool hasChanges = false;
|
||||||
|
|
||||||
|
ImGuiColorEditFlags flags = ImGuiColorEditFlags_Float;
|
||||||
|
flags |= hasAlpha ? (ImGuiColorEditFlags_AlphaBar | ImGuiColorEditFlags_AlphaPreviewHalf) : 0;
|
||||||
|
|
||||||
|
TextUnformatted(label.c_str());
|
||||||
|
SameLine();
|
||||||
|
SetCursorPosX(GetCursorPosX() + alignNameX - GetItemRectSize().x);
|
||||||
|
|
||||||
|
const float square_sz = GetFrameHeight();
|
||||||
|
const float w_button = square_sz + GetStyle().ItemSpacing.x;
|
||||||
|
|
||||||
|
{
|
||||||
|
dear::ScopedItemWidth iw(-w_button);
|
||||||
|
if (hasAlpha)
|
||||||
|
hasChanges |= ColorEdit4("##edit", &io_color.r, flags | ImGuiColorEditFlags_NoSmallPreview);
|
||||||
|
else
|
||||||
|
hasChanges |= ColorEdit3("##edit", &io_color.r, flags | ImGuiColorEditFlags_NoSmallPreview);
|
||||||
|
if (IsItemDeactivatedAfterEdit())
|
||||||
|
saveUndo = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//using this static to provide a stable intensity value while editing.
|
||||||
|
//Otherwise there is a conflict between the intensity and the brightness of the color
|
||||||
|
static float stableIntensity = 1.f;
|
||||||
|
static ImVec4 initialColor;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto [sRGB, intensity] = decomposeHDR(kit::color::cast_to<kit::color::lRGBhdr>(io_color));
|
||||||
|
const ImVec4 color(sRGB.r, sRGB.g, sRGB.b, io_color.a);
|
||||||
|
SameLine();
|
||||||
|
if (ColorButton("##button", color, flags))
|
||||||
|
{
|
||||||
|
initialColor = color;
|
||||||
|
//when opening the picker, compute the real, current intensity and store it for the whole picker session.
|
||||||
|
stableIntensity = intensity;
|
||||||
|
OpenPopup("Picker");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.f, 8.f));
|
||||||
|
if (BeginPopup("Picker"))
|
||||||
|
{
|
||||||
|
const auto sRGB = hdrToLdr(kit::color::cast_to<kit::color::lRGBhdr>(io_color), stableIntensity);
|
||||||
|
ImVec4 color(sRGB.r, sRGB.g, sRGB.b, io_color.a);
|
||||||
|
|
||||||
|
Text("Picker");
|
||||||
|
Separator();
|
||||||
|
if (ColorPicker4("##picker", &color.x, flags, &initialColor.x))
|
||||||
|
{
|
||||||
|
hasChanges = true;
|
||||||
|
io_color = recomposeHDR(kit::color::sRGBAf(color.x, color.y, color.z, color.w), stableIntensity);
|
||||||
|
}
|
||||||
|
if (IsItemDeactivatedAfterEdit())
|
||||||
|
saveUndo = true;
|
||||||
|
|
||||||
|
if (isHDR)
|
||||||
|
{
|
||||||
|
if (SliderFloat("Intensity", &stableIntensity, 1.f, 1048576.f, "%.3f", ImGuiSliderFlags_AlwaysClamp))
|
||||||
|
{
|
||||||
|
hasChanges = true;
|
||||||
|
io_color = recomposeHDR(kit::color::sRGBAf(color.x, color.y, color.z, color.w), stableIntensity);
|
||||||
|
}
|
||||||
|
if (IsItemDeactivatedAfterEdit())
|
||||||
|
saveUndo = true;
|
||||||
|
|
||||||
|
float stops = std::log2f(stableIntensity);
|
||||||
|
if (SliderFloat("f-Stops", &stops, 0.f, 20.f, "%.4f", ImGuiSliderFlags_AlwaysClamp))
|
||||||
|
{
|
||||||
|
stableIntensity = std::pow(2.f, stops);
|
||||||
|
hasChanges = true;
|
||||||
|
io_color = recomposeHDR(kit::color::sRGBAf(color.x, color.y, color.z, color.w), stableIntensity);
|
||||||
|
}
|
||||||
|
if (IsItemDeactivatedAfterEdit())
|
||||||
|
saveUndo = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
EndPopup();
|
||||||
|
}
|
||||||
|
PopStyleVar();
|
||||||
|
|
||||||
|
return hasChanges;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool CheckBoxTristate(const char* label, int* v_tristate)
|
||||||
|
{
|
||||||
|
bool ret;
|
||||||
|
if (*v_tristate == -1)
|
||||||
|
{
|
||||||
|
PushItemFlag(ImGuiItemFlags_MixedValue, true);
|
||||||
|
bool b = true;
|
||||||
|
ret = Checkbox(label, &b);
|
||||||
|
if (ret)
|
||||||
|
*v_tristate = 0;
|
||||||
|
PopItemFlag();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bool b = (*v_tristate != 0);
|
||||||
|
ret = Checkbox(label, &b);
|
||||||
|
if (ret)
|
||||||
|
*v_tristate = (int)b;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BeginIconMenu(kit::icons::Icon icon, const char* label)
|
||||||
|
{
|
||||||
|
const float lineHeight = GetFrameHeight();
|
||||||
|
const float iconSize = lineHeight * 0.8f;
|
||||||
|
|
||||||
|
Icon(icon, {lineHeight, lineHeight}, {iconSize, iconSize});
|
||||||
|
SameLine();
|
||||||
|
|
||||||
|
//AlignTextToFramePadding();
|
||||||
|
return BeginMenu(label);
|
||||||
|
}
|
||||||
|
bool BeginIconMenu(kit::icons::FontIcon icon, const char* label)
|
||||||
|
{
|
||||||
|
//AlignTextToFramePadding();
|
||||||
|
TextUnformatted(icon.icon);
|
||||||
|
SameLine();
|
||||||
|
|
||||||
|
//AlignTextToFramePadding();
|
||||||
|
return BeginMenu(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MenuIconItem(kit::icons::Icon icon, const char* label, bool* selected)
|
||||||
|
{
|
||||||
|
const float lineHeight = GetFrameHeight();
|
||||||
|
const float iconSize = lineHeight * 0.8f;
|
||||||
|
|
||||||
|
Icon(icon, {lineHeight, lineHeight}, {iconSize, iconSize});
|
||||||
|
SameLine();
|
||||||
|
|
||||||
|
PushID(label);
|
||||||
|
const bool activated = MenuItem(label, "", selected);
|
||||||
|
PopID();
|
||||||
|
|
||||||
|
return activated;
|
||||||
|
}
|
||||||
|
bool MenuIconItem(kit::icons::FontIcon icon, const char* label, bool* selected)
|
||||||
|
{
|
||||||
|
TextUnformatted(icon.icon);
|
||||||
|
SameLine();
|
||||||
|
|
||||||
|
PushID(label);
|
||||||
|
const bool activated = MenuItem(label, "", selected);
|
||||||
|
PopID();
|
||||||
|
|
||||||
|
return activated;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Icon(kit::icons::FontIcon icon, ImVec2 frameSize, ImVec2 _size, ImVec4 tint_col, ImVec4 border_col)
|
||||||
|
{
|
||||||
|
TextUnformatted(icon.icon);
|
||||||
|
}
|
||||||
|
void Icon(kit::icons::Icon icon, ImVec2 frameSize, ImVec2 _size, ImVec4 tint_col, ImVec4 border_col)
|
||||||
|
{
|
||||||
|
ImVec2 size = _size;
|
||||||
|
if (size.x == 0)
|
||||||
|
size.x = frameSize.x;
|
||||||
|
if (size.y == 0)
|
||||||
|
size.y = frameSize.y;
|
||||||
|
|
||||||
|
const ImVec2 offset = (frameSize - size) * 0.5f;
|
||||||
|
|
||||||
|
const ImGuiWindow* window = GetCurrentWindow();
|
||||||
|
if (window->SkipItems)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ImRect bb(window->DC.CursorPos, window->DC.CursorPos + frameSize);
|
||||||
|
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 + offset, bb.Max - offset, GetColorU32(border_col), 0.0f);
|
||||||
|
window->DrawList->AddImage(icon.texture.get(),
|
||||||
|
bb.Min + ImVec2(1, 1) + offset,
|
||||||
|
bb.Max - ImVec2(1, 1) - offset,
|
||||||
|
icon.uv0,
|
||||||
|
icon.uv1,
|
||||||
|
GetColorU32(tint_col));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
window->DrawList->AddImage(icon.texture.get(),
|
||||||
|
bb.Min + offset,
|
||||||
|
bb.Max - offset,
|
||||||
|
icon.uv0,
|
||||||
|
icon.uv1,
|
||||||
|
GetColorU32(tint_col));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IconButton(const char* str_id, kit::icons::Icon icon, ImVec2 size, bool active, ImVec4 bg_col, ImVec4 tint_col)
|
||||||
|
{
|
||||||
|
if (active)
|
||||||
|
PushStyleColor(ImGuiCol_Button, GetStyle().Colors[ImGuiCol_ButtonActive]);
|
||||||
|
|
||||||
|
const bool clicked = ImageButton(str_id ? str_id : "",
|
||||||
|
icon.texture.get(),
|
||||||
|
size,
|
||||||
|
icon.uv0,
|
||||||
|
icon.uv1,
|
||||||
|
bg_col,
|
||||||
|
tint_col);
|
||||||
|
|
||||||
|
if (active)
|
||||||
|
PopStyleColor();
|
||||||
|
|
||||||
|
return clicked;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IconButton(const char* str_id, kit::icons::FontIcon icon, const char* label, bool active)
|
||||||
|
{
|
||||||
|
if (active)
|
||||||
|
PushStyleColor(ImGuiCol_Button, GetStyle().Colors[ImGuiCol_ButtonActive]);
|
||||||
|
|
||||||
|
PushID(str_id ? str_id : icon.icon);
|
||||||
|
|
||||||
|
const tl::string text = icon ? tl::format("{}{}", icon.icon, label ? label : "") : tl::string(label);
|
||||||
|
const bool clicked = ButtonEx(text.c_str());
|
||||||
|
|
||||||
|
PopID();
|
||||||
|
|
||||||
|
if (active)
|
||||||
|
PopStyleColor();
|
||||||
|
|
||||||
|
return clicked;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ToggleButton(const char* str_id, bool* v)
|
||||||
|
{
|
||||||
|
const ImVec4* colors = GetStyle().Colors;
|
||||||
|
ImDrawList* draw_list = GetWindowDrawList();
|
||||||
|
|
||||||
|
ImVec2 frame_padding = GetStyle().FramePadding;
|
||||||
|
const ImVec2 p = GetCursorScreenPos() + frame_padding;
|
||||||
|
const float height = GetFrameHeight() - frame_padding.y * 2;
|
||||||
|
const float width = height * 1.55f;
|
||||||
|
const float radius = height * 0.50f;
|
||||||
|
|
||||||
|
bool clicked = false;
|
||||||
|
InvisibleButton(str_id, ImVec2(width, height));
|
||||||
|
if (IsItemClicked())
|
||||||
|
{
|
||||||
|
*v = !*v;
|
||||||
|
clicked = true;
|
||||||
|
}
|
||||||
|
//ImGuiContext& gg = *GImGui;
|
||||||
|
//float ANIM_SPEED = 0.085f;
|
||||||
|
//if (gg.LastActiveId == gg.CurrentWindow->GetID(str_id)) // && g.LastActiveIdTimer < ANIM_SPEED)
|
||||||
|
// float t_anim = ImSaturate(gg.LastActiveIdTimer / ANIM_SPEED);
|
||||||
|
if (IsItemHovered())
|
||||||
|
draw_list->AddRectFilled(p, ImVec2(p.x + width, p.y + height), GetColorU32(*v ? colors[ImGuiCol_ButtonActive] : ImVec4(0.78f, 0.78f, 0.78f, 1.0f)), height * 0.5f);
|
||||||
|
else
|
||||||
|
draw_list->AddRectFilled(p, ImVec2(p.x + width, p.y + height), GetColorU32(*v ? colors[ImGuiCol_Button] : ImVec4(0.85f, 0.85f, 0.85f, 1.0f)), height * 0.50f);
|
||||||
|
draw_list->AddCircleFilled(ImVec2(p.x + radius + (*v ? 1 : 0) * (width - radius * 2.0f), p.y + radius), radius - 1.5f, IM_COL32(255, 255, 255, 255));
|
||||||
|
|
||||||
|
return clicked;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Splitter(bool split_vertically, float thickness, float* size1, float* size2, float min_size1, float min_size2, float splitter_long_axis_size)
|
||||||
|
{
|
||||||
|
using namespace ImGui;
|
||||||
|
const ImGuiContext& g = *GImGui;
|
||||||
|
ImGuiWindow* window = g.CurrentWindow;
|
||||||
|
const ImGuiID id = window->GetID("##Splitter");
|
||||||
|
ImRect bb;
|
||||||
|
bb.Min = window->DC.CursorPos + (split_vertically ? ImVec2(*size1, 0.0f) : ImVec2(0.0f, *size1));
|
||||||
|
bb.Max = bb.Min + CalcItemSize(split_vertically ? ImVec2(thickness, splitter_long_axis_size) : ImVec2(splitter_long_axis_size, thickness), 0.0f, 0.0f);
|
||||||
|
return SplitterBehavior(bb, id, split_vertically ? ImGuiAxis_X : ImGuiAxis_Y, size1, size2, min_size1, min_size2, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ImVector<ImRect> s_GroupPanelLabelStack;
|
||||||
|
|
||||||
|
void BeginGroupPanel(const char* name, ImVec2 size)
|
||||||
|
{
|
||||||
|
BeginGroup();
|
||||||
|
|
||||||
|
auto cursorPos = GetCursorScreenPos();
|
||||||
|
const auto itemSpacing = GetStyle().ItemSpacing;
|
||||||
|
PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f));
|
||||||
|
PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f));
|
||||||
|
|
||||||
|
auto frameHeight = GetFrameHeight();
|
||||||
|
BeginGroup();
|
||||||
|
|
||||||
|
ImVec2 effectiveSize = size;
|
||||||
|
if (size.x < 0.0f)
|
||||||
|
effectiveSize.x = GetContentRegionAvail().x;
|
||||||
|
else
|
||||||
|
effectiveSize.x = size.x;
|
||||||
|
Dummy(ImVec2(effectiveSize.x, 0.0f));
|
||||||
|
|
||||||
|
Dummy(ImVec2(frameHeight * 0.5f, 0.0f));
|
||||||
|
SameLine(0.0f, 0.0f);
|
||||||
|
BeginGroup();
|
||||||
|
Dummy(ImVec2(frameHeight * 0.5f, 0.0f));
|
||||||
|
SameLine(0.0f, 0.0f);
|
||||||
|
TextUnformatted(name);
|
||||||
|
const auto labelMin = GetItemRectMin();
|
||||||
|
const auto labelMax = GetItemRectMax();
|
||||||
|
SameLine(0.0f, 0.0f);
|
||||||
|
Dummy(ImVec2(0.0, frameHeight + itemSpacing.y));
|
||||||
|
BeginGroup();
|
||||||
|
|
||||||
|
//GetWindowDrawList()->AddRect(labelMin, labelMax, IM_COL32(255, 0, 255, 255));
|
||||||
|
|
||||||
|
PopStyleVar(2);
|
||||||
|
|
||||||
|
#if IMGUI_VERSION_NUM >= 17301
|
||||||
|
GetCurrentWindow()->ContentRegionRect.Max.x -= frameHeight * 0.5f;
|
||||||
|
GetCurrentWindow()->WorkRect.Max.x -= frameHeight * 0.5f;
|
||||||
|
GetCurrentWindow()->InnerRect.Max.x -= frameHeight * 0.5f;
|
||||||
|
#else
|
||||||
|
GetCurrentWindow()->ContentsRegionRect.Max.x -= frameHeight * 0.5f;
|
||||||
|
#endif
|
||||||
|
GetCurrentWindow()->Size.x -= frameHeight;
|
||||||
|
|
||||||
|
const auto itemWidth = CalcItemWidth();
|
||||||
|
PushItemWidth(ImMax(0.0f, itemWidth - frameHeight));
|
||||||
|
|
||||||
|
s_GroupPanelLabelStack.push_back(ImRect(labelMin, labelMax));
|
||||||
|
}
|
||||||
|
|
||||||
|
void EndGroupPanel()
|
||||||
|
{
|
||||||
|
PopItemWidth();
|
||||||
|
|
||||||
|
const auto itemSpacing = GetStyle().ItemSpacing;
|
||||||
|
|
||||||
|
PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f));
|
||||||
|
PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0.0f, 0.0f));
|
||||||
|
|
||||||
|
auto frameHeight = GetFrameHeight();
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
|
||||||
|
//GetWindowDrawList()->AddRectFilled(GetItemRectMin(), GetItemRectMax(), IM_COL32(0, 255, 0, 64), 4.0f);
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
|
||||||
|
SameLine(0.0f, 0.0f);
|
||||||
|
Dummy(ImVec2(frameHeight * 0.5f, 0.0f));
|
||||||
|
Dummy(ImVec2(0.0, frameHeight - frameHeight * 0.5f - itemSpacing.y));
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
|
||||||
|
const auto itemMin = GetItemRectMin();
|
||||||
|
const auto itemMax = GetItemRectMax();
|
||||||
|
//GetWindowDrawList()->AddRectFilled(itemMin, itemMax, IM_COL32(255, 0, 0, 64), 4.0f);
|
||||||
|
|
||||||
|
auto labelRect = s_GroupPanelLabelStack.back();
|
||||||
|
s_GroupPanelLabelStack.pop_back();
|
||||||
|
|
||||||
|
const ImVec2 halfFrame = ImVec2(frameHeight * 0.25f, frameHeight) * 0.5f;
|
||||||
|
const ImRect frameRect = ImRect(itemMin + halfFrame, itemMax - ImVec2(halfFrame.x, 0.0f));
|
||||||
|
labelRect.Min.x -= itemSpacing.x;
|
||||||
|
labelRect.Max.x += itemSpacing.x;
|
||||||
|
for (int i = 0; i < 4; ++i)
|
||||||
|
{
|
||||||
|
switch (i)
|
||||||
|
{
|
||||||
|
// left half-plane
|
||||||
|
case 0: PushClipRect(ImVec2(-FLT_MAX, -FLT_MAX), ImVec2(labelRect.Min.x, FLT_MAX), true);
|
||||||
|
break;
|
||||||
|
// right half-plane
|
||||||
|
case 1: PushClipRect(ImVec2(labelRect.Max.x, -FLT_MAX), ImVec2(FLT_MAX, FLT_MAX), true);
|
||||||
|
break;
|
||||||
|
// top
|
||||||
|
case 2: PushClipRect(ImVec2(labelRect.Min.x, -FLT_MAX), ImVec2(labelRect.Max.x, labelRect.Min.y), true);
|
||||||
|
break;
|
||||||
|
// bottom
|
||||||
|
case 3: PushClipRect(ImVec2(labelRect.Min.x, labelRect.Max.y), ImVec2(labelRect.Max.x, FLT_MAX), true);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
GetWindowDrawList()->AddRect(
|
||||||
|
frameRect.Min,
|
||||||
|
frameRect.Max,
|
||||||
|
ImColor(GetStyleColorVec4(ImGuiCol_Border)),
|
||||||
|
halfFrame.x);
|
||||||
|
|
||||||
|
PopClipRect();
|
||||||
|
}
|
||||||
|
|
||||||
|
PopStyleVar(2);
|
||||||
|
|
||||||
|
#if IMGUI_VERSION_NUM >= 17301
|
||||||
|
GetCurrentWindow()->ContentRegionRect.Max.x += frameHeight * 0.5f;
|
||||||
|
GetCurrentWindow()->WorkRect.Max.x += frameHeight * 0.5f;
|
||||||
|
GetCurrentWindow()->InnerRect.Max.x += frameHeight * 0.5f;
|
||||||
|
#else
|
||||||
|
GetCurrentWindow()->ContentsRegionRect.Max.x += frameHeight * 0.5f;
|
||||||
|
#endif
|
||||||
|
GetCurrentWindow()->Size.x += frameHeight;
|
||||||
|
|
||||||
|
Dummy(ImVec2(0.0f, 0.0f));
|
||||||
|
|
||||||
|
EndGroup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddUnderLine(ImColor col)
|
||||||
|
{
|
||||||
|
ImVec2 min = GetItemRectMin();
|
||||||
|
const ImVec2 max = GetItemRectMax();
|
||||||
|
min.y = max.y;
|
||||||
|
GetWindowDrawList()->AddLine(min, max, col, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// hyperlink urls
|
||||||
|
void TextURL(const char* name, const char* url)
|
||||||
|
{
|
||||||
|
PushStyleColor(ImGuiCol_Text, GetStyle().Colors[ImGuiCol_ButtonHovered]);
|
||||||
|
Text(name);
|
||||||
|
PopStyleColor();
|
||||||
|
if (IsItemHovered())
|
||||||
|
{
|
||||||
|
//if (IsMouseClicked(0))
|
||||||
|
//PlatformOpenURLInBrowser(url);
|
||||||
|
|
||||||
|
AddUnderLine( GetStyle().Colors[ImGuiCol_ButtonHovered] );
|
||||||
|
SetTooltip("Open in browser\n%s", url);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
AddUnderLine( GetStyle().Colors[ImGuiCol_Button] );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} // namespace ImGuiExt
|
||||||
@@ -0,0 +1,266 @@
|
|||||||
|
#include "IUI/ImGuiFilamentBridge.h"
|
||||||
|
|
||||||
|
#include <utils/EntityManager.h>
|
||||||
|
#include <tl/vector.h>
|
||||||
|
#include "IUI/ImGui/imgui.h"
|
||||||
|
#include <filamat/MaterialBuilder.h>
|
||||||
|
#include <filament/IndexBuffer.h>
|
||||||
|
#include <filament/Material.h>
|
||||||
|
#include <filament/MaterialInstance.h>
|
||||||
|
#include <filament/RenderableManager.h>
|
||||||
|
#include <filament/Scene.h>
|
||||||
|
#include <filament/Texture.h>
|
||||||
|
#include <filament/TextureSampler.h>
|
||||||
|
#include <filament/VertexBuffer.h>
|
||||||
|
#include <Kit/EngineManager.h>
|
||||||
|
|
||||||
|
#include "tl/narrow_cast.h"
|
||||||
|
|
||||||
|
using namespace filament::math;
|
||||||
|
using namespace filament;
|
||||||
|
using namespace utils;
|
||||||
|
|
||||||
|
namespace kit
|
||||||
|
{
|
||||||
|
static constexpr uint8_t IMGUI_PACKAGE[] =
|
||||||
|
{
|
||||||
|
// ReSharper disable All
|
||||||
|
#include "Materials/ImGuiMaterial.h"
|
||||||
|
// ReSharper restore All
|
||||||
|
};
|
||||||
|
|
||||||
|
ImGuiFilamentBridge::ImGuiFilamentBridge(tl::lent_ref<EngineManager> engineManager, tl::lent_ref<filament::Scene> flmScene)
|
||||||
|
: m_engineManager(std::move(engineManager))
|
||||||
|
, m_scene(std::move(flmScene))
|
||||||
|
{
|
||||||
|
// Create a simple alpha-blended 2D blitting material.
|
||||||
|
auto builder = Material::Builder()
|
||||||
|
.package(IMGUI_PACKAGE, sizeof(IMGUI_PACKAGE));
|
||||||
|
|
||||||
|
m_material = m_engineManager->adopt(builder.build(*m_engineManager->getEngine()));
|
||||||
|
|
||||||
|
EntityManager& em = utils::EntityManager::get();
|
||||||
|
m_renderable = em.create();
|
||||||
|
m_scene->addEntity(m_renderable);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImTextureID ImGuiFilamentBridge::createAtlasTexture()
|
||||||
|
{
|
||||||
|
const ImGuiIO& io = ImGui::GetIO();
|
||||||
|
// Create the grayscale texture that ImGui uses for its glyph atlas.
|
||||||
|
unsigned char* pixels;
|
||||||
|
int width, height;
|
||||||
|
io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height);
|
||||||
|
const size_t size = tl::narrow<size_t>(width * height * 4);
|
||||||
|
uint8_t* buffer = m_engineManager->acquireUploadMemory(size);
|
||||||
|
memcpy(buffer, pixels, size);
|
||||||
|
|
||||||
|
auto builder = Texture::Builder()
|
||||||
|
.width(tl::narrow<uint32_t>(width))
|
||||||
|
.height(tl::narrow<uint32_t>(height))
|
||||||
|
.levels(tl::narrow<uint8_t>(1))
|
||||||
|
.format(Texture::InternalFormat::RGBA8)
|
||||||
|
.sampler(Texture::Sampler::SAMPLER_2D);
|
||||||
|
|
||||||
|
m_texture = m_engineManager->adopt(builder.build(*m_engineManager->getEngine()));
|
||||||
|
m_texture->setImage(*m_engineManager->getEngine(), 0, Texture::PixelBufferDescriptor(buffer, size, Texture::Format::RGBA, Texture::Type::UBYTE));
|
||||||
|
|
||||||
|
const TextureSampler sampler(TextureSampler::MinFilter::LINEAR_MIPMAP_LINEAR, TextureSampler::MagFilter::LINEAR);
|
||||||
|
m_material->setDefaultParameter("albedo", m_texture.get(), sampler);
|
||||||
|
for (const auto& mi : m_materialInstances)
|
||||||
|
mi->setParameter("albedo", m_texture.get(), sampler);
|
||||||
|
|
||||||
|
return m_texture.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGuiFilamentBridge::~ImGuiFilamentBridge()
|
||||||
|
{
|
||||||
|
m_engineManager->destroy(m_renderable);
|
||||||
|
}
|
||||||
|
|
||||||
|
//void ImGuiFilamentBridge::setDisplaySize(int width, int height, float scaleX, float scaleY)
|
||||||
|
//{
|
||||||
|
// setContext();
|
||||||
|
// ImGuiIO& io = ImGui::GetIO();
|
||||||
|
// io.DisplaySize = ImVec2((float)width, (float)height);
|
||||||
|
// io.DisplayFramebufferScale.x = scaleX;
|
||||||
|
// io.DisplayFramebufferScale.y = scaleY;
|
||||||
|
//}
|
||||||
|
|
||||||
|
void ImGuiFilamentBridge::processImGuiCommands(ImDrawData& commands, const ImGuiIO& io)
|
||||||
|
{
|
||||||
|
auto& rcm = m_engineManager->getEngine()->getRenderableManager();
|
||||||
|
|
||||||
|
// Avoid rendering when minimized and scale coordinates for retina displays.
|
||||||
|
const int fbwidth = static_cast<int>(commands.DisplaySize.x * commands.FramebufferScale.x);
|
||||||
|
const int fbheight = static_cast<int>(commands.DisplaySize.y * commands.FramebufferScale.y);
|
||||||
|
if (fbwidth == 0 || fbheight == 0)
|
||||||
|
return;
|
||||||
|
//commands.ScaleClipRects(commands.FramebufferScale);
|
||||||
|
|
||||||
|
// Ensure that we have enough vertex buffers and index buffers.
|
||||||
|
createBuffers(commands.CmdListsCount);
|
||||||
|
|
||||||
|
// Count how many primitives we'll need, then create a Renderable builder.
|
||||||
|
size_t primitiveCount = 0;
|
||||||
|
//tl::unordered_map<uint64_t, filament::MaterialInstance*> scissorRects;
|
||||||
|
for (int cmdListIndex = 0; cmdListIndex < commands.CmdListsCount; cmdListIndex++)
|
||||||
|
{
|
||||||
|
const ImDrawList* cmds = commands.CmdLists[cmdListIndex];
|
||||||
|
primitiveCount += cmds->CmdBuffer.size();
|
||||||
|
}
|
||||||
|
auto rbuilder = RenderableManager::Builder(primitiveCount);
|
||||||
|
rbuilder.boundingBox({{0, 0, 0}, {10000, 10000, 10000}}).culling(false);
|
||||||
|
|
||||||
|
// Ensure that we have a material instance for each primitive.
|
||||||
|
const size_t previousSize = m_materialInstances.size();
|
||||||
|
if (primitiveCount > m_materialInstances.size())
|
||||||
|
{
|
||||||
|
//m_materialInstances.resize(primitiveCount);
|
||||||
|
for (size_t i = previousSize; i < primitiveCount; i++)
|
||||||
|
m_materialInstances.push_back(m_engineManager->adopt(m_material->createInstance()));
|
||||||
|
}
|
||||||
|
|
||||||
|
const ImVec2 clipOff = commands.DisplayPos; // (0,0) unless using multi-viewports
|
||||||
|
const ImVec2 clipScale = commands.FramebufferScale; // (1,1) unless using retina display which are often (2,2)
|
||||||
|
|
||||||
|
// Recreate the Renderable component and point it to the vertex buffers.
|
||||||
|
rcm.destroy(m_renderable);
|
||||||
|
size_t bufferIndex = 0;
|
||||||
|
size_t primIndex = 0;
|
||||||
|
for (int cmdListIndex = 0; cmdListIndex < commands.CmdListsCount; cmdListIndex++)
|
||||||
|
{
|
||||||
|
const ImDrawList* cmds = commands.CmdLists[cmdListIndex];
|
||||||
|
size_t indexOffset = 0;
|
||||||
|
populateVertexData(bufferIndex,
|
||||||
|
cmds->VtxBuffer.Size * sizeof(ImDrawVert),
|
||||||
|
cmds->VtxBuffer.Data,
|
||||||
|
cmds->IdxBuffer.Size * sizeof(ImDrawIdx),
|
||||||
|
cmds->IdxBuffer.Data);
|
||||||
|
for (const auto& pcmd : cmds->CmdBuffer)
|
||||||
|
{
|
||||||
|
if (pcmd.UserCallback)
|
||||||
|
pcmd.UserCallback(cmds, &pcmd);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MaterialInstance& materialInstance = *m_materialInstances[primIndex];
|
||||||
|
|
||||||
|
// Project scissor/clipping rectangles into framebuffer space
|
||||||
|
const ImVec2 clipMin((pcmd.ClipRect.x - clipOff.x) * clipScale.x, (pcmd.ClipRect.y - clipOff.y) * clipScale.y);
|
||||||
|
const ImVec2 clipMax((pcmd.ClipRect.z - clipOff.x) * clipScale.x, (pcmd.ClipRect.w - clipOff.y) * clipScale.y);
|
||||||
|
|
||||||
|
materialInstance.setScissor(uint32_t(clipMin.x),
|
||||||
|
uint32_t(fbheight - clipMax.y),
|
||||||
|
(uint16_t)(clipMax.x - clipMin.x),
|
||||||
|
(uint16_t)(clipMax.y - clipMin.y));
|
||||||
|
TextureSampler sampler(TextureSampler::MinFilter::LINEAR_MIPMAP_LINEAR, TextureSampler::MagFilter::LINEAR);
|
||||||
|
if (pcmd.TextureId)
|
||||||
|
materialInstance.setParameter("albedo", (Texture const*)pcmd.TextureId, sampler);
|
||||||
|
else
|
||||||
|
materialInstance.setParameter("albedo", m_texture.get(), sampler);
|
||||||
|
|
||||||
|
rbuilder
|
||||||
|
.geometry(primIndex,
|
||||||
|
RenderableManager::PrimitiveType::TRIANGLES,
|
||||||
|
m_vertexBuffers[bufferIndex].get(),
|
||||||
|
m_indexBuffers[bufferIndex].get(),
|
||||||
|
indexOffset,
|
||||||
|
pcmd.ElemCount)
|
||||||
|
.blendOrder(primIndex, tl::narrow<uint16_t>(primIndex))
|
||||||
|
.material(primIndex, &materialInstance);
|
||||||
|
primIndex++;
|
||||||
|
}
|
||||||
|
indexOffset += pcmd.ElemCount;
|
||||||
|
}
|
||||||
|
bufferIndex++;
|
||||||
|
}
|
||||||
|
if (commands.CmdListsCount > 0)
|
||||||
|
rbuilder.build(*m_engineManager->getEngine(), m_renderable);
|
||||||
|
}
|
||||||
|
|
||||||
|
tl::unique_ref<filament::VertexBuffer> ImGuiFilamentBridge::createVertexBuffer(size_t capacity) const
|
||||||
|
{
|
||||||
|
auto builder = VertexBuffer::Builder()
|
||||||
|
.vertexCount(uint32_t(capacity))
|
||||||
|
.bufferCount(1)
|
||||||
|
.attribute(VertexAttribute::POSITION,
|
||||||
|
0,
|
||||||
|
VertexBuffer::AttributeType::FLOAT2,
|
||||||
|
0,
|
||||||
|
sizeof(ImDrawVert))
|
||||||
|
.attribute(VertexAttribute::UV0,
|
||||||
|
0,
|
||||||
|
VertexBuffer::AttributeType::FLOAT2,
|
||||||
|
sizeof(filament::math::float2),
|
||||||
|
sizeof(ImDrawVert))
|
||||||
|
.attribute(VertexAttribute::COLOR,
|
||||||
|
0,
|
||||||
|
VertexBuffer::AttributeType::UBYTE4,
|
||||||
|
2 * sizeof(filament::math::float2),
|
||||||
|
sizeof(ImDrawVert))
|
||||||
|
.normalized(COLOR);
|
||||||
|
|
||||||
|
return m_engineManager->adopt(builder.build(*m_engineManager->getEngine()));
|
||||||
|
}
|
||||||
|
|
||||||
|
tl::unique_ref<filament::IndexBuffer> ImGuiFilamentBridge::createIndexBuffer(size_t capacity) const
|
||||||
|
{
|
||||||
|
auto builder = IndexBuffer::Builder()
|
||||||
|
.indexCount(uint32_t(capacity))
|
||||||
|
.bufferType(IndexBuffer::IndexType::UINT);
|
||||||
|
return m_engineManager->adopt(builder.build(*m_engineManager->getEngine()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiFilamentBridge::createBuffers(int numRequiredBuffers)
|
||||||
|
{
|
||||||
|
if (numRequiredBuffers > m_vertexBuffers.size())
|
||||||
|
{
|
||||||
|
const size_t previousSize = m_vertexBuffers.size();
|
||||||
|
for (size_t i = previousSize; i < numRequiredBuffers; i++)
|
||||||
|
{
|
||||||
|
// Pick a reasonable starting capacity; it will grow if needed.
|
||||||
|
m_vertexBuffers.push_back(createVertexBuffer(1000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (numRequiredBuffers > m_indexBuffers.size())
|
||||||
|
{
|
||||||
|
const size_t previousSize = m_indexBuffers.size();
|
||||||
|
for (size_t i = previousSize; i < numRequiredBuffers; i++)
|
||||||
|
{
|
||||||
|
// Pick a reasonable starting capacity; it will grow if needed.
|
||||||
|
m_indexBuffers.push_back(createIndexBuffer(1000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiFilamentBridge::populateVertexData(size_t bufferIndex, size_t vbSizeInBytes, void* vbImguiData, size_t ibSizeInBytes, void* ibImguiData)
|
||||||
|
{
|
||||||
|
// Create a new vertex buffer if the size isn't large enough, then copy the ImGui data into
|
||||||
|
// a staging area since Filament's render thread might consume the data at any time.
|
||||||
|
const size_t requiredVertCount = vbSizeInBytes / sizeof(ImDrawVert);
|
||||||
|
const size_t capacityVertCount = m_vertexBuffers[bufferIndex]->getVertexCount();
|
||||||
|
if (requiredVertCount > capacityVertCount)
|
||||||
|
m_vertexBuffers[bufferIndex] = createVertexBuffer(requiredVertCount);
|
||||||
|
|
||||||
|
{
|
||||||
|
const size_t nVbBytes = requiredVertCount * sizeof(ImDrawVert);
|
||||||
|
uint8_t* vbFilamentData = m_engineManager->acquireUploadMemory(nVbBytes);
|
||||||
|
memcpy(vbFilamentData, vbImguiData, nVbBytes);
|
||||||
|
m_vertexBuffers[bufferIndex]->setBufferAt(*m_engineManager->getEngine(), 0, VertexBuffer::BufferDescriptor(vbFilamentData, nVbBytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new index buffer if the size isn't large enough, then copy the ImGui data into
|
||||||
|
// a staging area since Filament's render thread might consume the data at any time.
|
||||||
|
const size_t requiredIndexCount = ibSizeInBytes / sizeof(ImDrawIdx);
|
||||||
|
const size_t capacityIndexCount = m_indexBuffers[bufferIndex]->getIndexCount();
|
||||||
|
if (requiredIndexCount > capacityIndexCount)
|
||||||
|
m_indexBuffers[bufferIndex] = createIndexBuffer(requiredIndexCount);
|
||||||
|
|
||||||
|
{
|
||||||
|
const size_t nIbBytes = requiredIndexCount * sizeof(ImDrawIdx);
|
||||||
|
uint8_t* ibFilamentData = m_engineManager->acquireUploadMemory(nIbBytes);
|
||||||
|
memcpy(ibFilamentData, ibImguiData, nIbBytes);
|
||||||
|
m_indexBuffers[bufferIndex]->setBuffer(*m_engineManager->getEngine(), IndexBuffer::BufferDescriptor(ibFilamentData, nIbBytes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace filagui
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
|||||||
|
material {
|
||||||
|
name : ImGui,
|
||||||
|
parameters : [
|
||||||
|
{
|
||||||
|
type : sampler2d,
|
||||||
|
name : albedo
|
||||||
|
}
|
||||||
|
],
|
||||||
|
requires : [
|
||||||
|
uv0,
|
||||||
|
color
|
||||||
|
],
|
||||||
|
shadingModel : unlit,
|
||||||
|
culling : none,
|
||||||
|
depthCulling: false,
|
||||||
|
blending : transparent
|
||||||
|
}
|
||||||
|
|
||||||
|
fragment {
|
||||||
|
void material(inout MaterialInputs material)
|
||||||
|
{
|
||||||
|
prepareMaterial(material);
|
||||||
|
vec2 uv = getUV0();
|
||||||
|
uv.y = 1.0 - uv.y;
|
||||||
|
vec4 albedo = texture(materialParams_albedo, uv);
|
||||||
|
material.baseColor = getColor() * albedo;
|
||||||
|
material.baseColor.rgb *= material.baseColor.a;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
..\..\..\filament\release\bin\matc.exe --api all -o ImGuiMaterial.h -f header ImGuiMaterial.mat
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
../../../filament/release/bin/matc --api all -o ImGuiMaterial.h -f header ImGuiMaterial.mat
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
#include "StdAfx.h"
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <cmath>
|
||||||
|
#include <utility>
|
||||||
|
#include <compare>
|
||||||
|
#include <iterator>
|
||||||
|
|
||||||
|
#include <tl/vector.h>
|
||||||
|
#include <tl/vector_map.h>
|
||||||
|
#include <tl/vector_set.h>
|
||||||
|
#include <tl/array.h>
|
||||||
|
#include <tl/deque.h>
|
||||||
|
#include <tl/unordered_map.h>
|
||||||
|
#include <tl/unordered_set.h>
|
||||||
|
#include <tl/map.h>
|
||||||
|
#include <tl/set.h>
|
||||||
|
#include <tl/optional.h>
|
||||||
|
#include <tl/variant.h>
|
||||||
|
#include <tl/span.h>
|
||||||
|
#include <tl/atomic.h>
|
||||||
|
#include <tl/chrono.h>
|
||||||
|
#include <tl/shared_ptr.h>
|
||||||
|
#include <tl/unique_ptr.h>
|
||||||
|
#include <tl/numeric.h>
|
||||||
|
#include <tl/functional.h>
|
||||||
|
#include <tl/string.h>
|
||||||
|
#include <tl/string_view.h>
|
||||||
|
#include <tl/sort.h>
|
||||||
|
#include <tl/numeric_limits.h>
|
||||||
|
#include <tl/result.h>
|
||||||
|
#include <tl/format.h>
|
||||||
|
#include <tl/functional.h>
|
||||||
|
#include <tl/abs_path.h>
|
||||||
|
#include <tl/rel_path.h>
|
||||||
|
|
||||||
|
#include "Logger.h"
|
||||||
|
|
||||||
@@ -0,0 +1,890 @@
|
|||||||
|
// Crude implementation of JSON value object and parser.
|
||||||
|
//
|
||||||
|
// VERSION 0.1
|
||||||
|
//
|
||||||
|
// LICENSE
|
||||||
|
// This software is dual-licensed to the public domain and under the following
|
||||||
|
// license: you are granted a perpetual, irrevocable license to copy, modify,
|
||||||
|
// publish, and distribute this file as you see fit.
|
||||||
|
//
|
||||||
|
// CREDITS
|
||||||
|
// Written by Michal Cichon
|
||||||
|
# include "IUI/nodes/crude_json.h"
|
||||||
|
# include <iomanip>
|
||||||
|
# include <limits>
|
||||||
|
# include <cstdlib>
|
||||||
|
# include <clocale>
|
||||||
|
# include <cmath>
|
||||||
|
# include <cstring>
|
||||||
|
# if CRUDE_JSON_IO
|
||||||
|
# include <stdio.h>
|
||||||
|
# include <memory>
|
||||||
|
# endif
|
||||||
|
|
||||||
|
namespace crude_json {
|
||||||
|
|
||||||
|
value::value(value&& other)
|
||||||
|
: m_Type(other.m_Type)
|
||||||
|
{
|
||||||
|
switch (m_Type)
|
||||||
|
{
|
||||||
|
case type_t::object: construct(m_Storage, std::move( *object_ptr(other.m_Storage))); break;
|
||||||
|
case type_t::array: construct(m_Storage, std::move( *array_ptr(other.m_Storage))); break;
|
||||||
|
case type_t::string: construct(m_Storage, std::move( *string_ptr(other.m_Storage))); break;
|
||||||
|
case type_t::boolean: construct(m_Storage, std::move(*boolean_ptr(other.m_Storage))); break;
|
||||||
|
case type_t::number: construct(m_Storage, std::move( *number_ptr(other.m_Storage))); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
destruct(other.m_Storage, other.m_Type);
|
||||||
|
other.m_Type = type_t::null;
|
||||||
|
}
|
||||||
|
|
||||||
|
value::value(const value& other)
|
||||||
|
: m_Type(other.m_Type)
|
||||||
|
{
|
||||||
|
switch (m_Type)
|
||||||
|
{
|
||||||
|
case type_t::object: construct(m_Storage, *object_ptr(other.m_Storage)); break;
|
||||||
|
case type_t::array: construct(m_Storage, *array_ptr(other.m_Storage)); break;
|
||||||
|
case type_t::string: construct(m_Storage, *string_ptr(other.m_Storage)); break;
|
||||||
|
case type_t::boolean: construct(m_Storage, *boolean_ptr(other.m_Storage)); break;
|
||||||
|
case type_t::number: construct(m_Storage, *number_ptr(other.m_Storage)); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value& value::operator[](size_t index)
|
||||||
|
{
|
||||||
|
if (is_null())
|
||||||
|
m_Type = construct(m_Storage, type_t::array);
|
||||||
|
|
||||||
|
if (is_array())
|
||||||
|
{
|
||||||
|
auto& v = *array_ptr(m_Storage);
|
||||||
|
if (index >= v.size())
|
||||||
|
v.insert(v.end(), index - v.size() + 1, value());
|
||||||
|
|
||||||
|
return v[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
CRUDE_ASSERT(false && "operator[] on unsupported type");
|
||||||
|
std::terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
const value& value::operator[](size_t index) const
|
||||||
|
{
|
||||||
|
if (is_array())
|
||||||
|
return (*array_ptr(m_Storage))[index];
|
||||||
|
|
||||||
|
CRUDE_ASSERT(false && "operator[] on unsupported type");
|
||||||
|
std::terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
value& value::operator[](const string& key)
|
||||||
|
{
|
||||||
|
if (is_null())
|
||||||
|
m_Type = construct(m_Storage, type_t::object);
|
||||||
|
|
||||||
|
if (is_object())
|
||||||
|
return (*object_ptr(m_Storage))[key];
|
||||||
|
|
||||||
|
CRUDE_ASSERT(false && "operator[] on unsupported type");
|
||||||
|
std::terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
const value& value::operator[](const string& key) const
|
||||||
|
{
|
||||||
|
if (is_object())
|
||||||
|
{
|
||||||
|
auto& o = *object_ptr(m_Storage);
|
||||||
|
auto it = o.find(key);
|
||||||
|
CRUDE_ASSERT(it != o.end());
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
CRUDE_ASSERT(false && "operator[] on unsupported type");
|
||||||
|
std::terminate();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool value::contains(const string& key) const
|
||||||
|
{
|
||||||
|
if (is_object())
|
||||||
|
{
|
||||||
|
auto& o = *object_ptr(m_Storage);
|
||||||
|
auto it = o.find(key);
|
||||||
|
return it != o.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void value::push_back(const value& value)
|
||||||
|
{
|
||||||
|
if (is_null())
|
||||||
|
m_Type = construct(m_Storage, type_t::array);
|
||||||
|
|
||||||
|
if (is_array())
|
||||||
|
{
|
||||||
|
auto& v = *array_ptr(m_Storage);
|
||||||
|
v.push_back(value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CRUDE_ASSERT(false && "operator[] on unsupported type");
|
||||||
|
std::terminate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void value::push_back(value&& value)
|
||||||
|
{
|
||||||
|
if (is_null())
|
||||||
|
m_Type = construct(m_Storage, type_t::array);
|
||||||
|
|
||||||
|
if (is_array())
|
||||||
|
{
|
||||||
|
auto& v = *array_ptr(m_Storage);
|
||||||
|
v.push_back(std::move(value));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CRUDE_ASSERT(false && "operator[] on unsupported type");
|
||||||
|
std::terminate();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t value::erase(const string& key)
|
||||||
|
{
|
||||||
|
if (!is_object())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
auto& o = *object_ptr(m_Storage);
|
||||||
|
auto it = o.find(key);
|
||||||
|
|
||||||
|
if (it == o.end())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
o.erase(it);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void value::swap(value& other)
|
||||||
|
{
|
||||||
|
using std::swap;
|
||||||
|
|
||||||
|
if (m_Type == other.m_Type)
|
||||||
|
{
|
||||||
|
switch (m_Type)
|
||||||
|
{
|
||||||
|
case type_t::object: swap(*object_ptr(m_Storage), *object_ptr(other.m_Storage)); break;
|
||||||
|
case type_t::array: swap(*array_ptr(m_Storage), *array_ptr(other.m_Storage)); break;
|
||||||
|
case type_t::string: swap(*string_ptr(m_Storage), *string_ptr(other.m_Storage)); break;
|
||||||
|
case type_t::boolean: swap(*boolean_ptr(m_Storage), *boolean_ptr(other.m_Storage)); break;
|
||||||
|
case type_t::number: swap(*number_ptr(m_Storage), *number_ptr(other.m_Storage)); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value tmp(std::move(other));
|
||||||
|
other.~value();
|
||||||
|
new (&other) value(std::move(*this));
|
||||||
|
this->~value();
|
||||||
|
new (this) value(std::move(tmp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string value::dump(const int indent, const char indent_char) const
|
||||||
|
{
|
||||||
|
dump_context_t context(indent, indent_char);
|
||||||
|
|
||||||
|
context.out.precision(std::numeric_limits<double>::max_digits10 + 1);
|
||||||
|
context.out << std::defaultfloat;
|
||||||
|
|
||||||
|
dump(context, 0);
|
||||||
|
return context.out.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void value::dump_context_t::write_indent(int level)
|
||||||
|
{
|
||||||
|
if (indent <= 0 || level == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
out.fill(indent_char);
|
||||||
|
out.width(indent * level);
|
||||||
|
out << indent_char;
|
||||||
|
out.width(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void value::dump_context_t::write_separator()
|
||||||
|
{
|
||||||
|
if (indent < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
out.put(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
void value::dump_context_t::write_newline()
|
||||||
|
{
|
||||||
|
if (indent < 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
out.put('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
void value::dump(dump_context_t& context, int level) const
|
||||||
|
{
|
||||||
|
context.write_indent(level);
|
||||||
|
|
||||||
|
switch (m_Type)
|
||||||
|
{
|
||||||
|
case type_t::null:
|
||||||
|
context.out << "null";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case type_t::object:
|
||||||
|
context.out << '{';
|
||||||
|
{
|
||||||
|
context.write_newline();
|
||||||
|
bool first = true;
|
||||||
|
for (auto& entry : *object_ptr(m_Storage))
|
||||||
|
{
|
||||||
|
if (!first) { context.out << ','; context.write_newline(); } else first = false;
|
||||||
|
context.write_indent(level + 1);
|
||||||
|
context.out << '\"' << entry.first << "\":";
|
||||||
|
if (!entry.second.is_structured())
|
||||||
|
{
|
||||||
|
context.write_separator();
|
||||||
|
entry.second.dump(context, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
context.write_newline();
|
||||||
|
entry.second.dump(context, level + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!first)
|
||||||
|
context.write_newline();
|
||||||
|
}
|
||||||
|
context.write_indent(level);
|
||||||
|
context.out << '}';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case type_t::array:
|
||||||
|
context.out << '[';
|
||||||
|
{
|
||||||
|
context.write_newline();
|
||||||
|
bool first = true;
|
||||||
|
for (auto& entry : *array_ptr(m_Storage))
|
||||||
|
{
|
||||||
|
if (!first) { context.out << ','; context.write_newline(); } else first = false;
|
||||||
|
if (!entry.is_structured())
|
||||||
|
{
|
||||||
|
context.write_indent(level + 1);
|
||||||
|
entry.dump(context, 0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
entry.dump(context, level + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!first)
|
||||||
|
context.write_newline();
|
||||||
|
}
|
||||||
|
context.write_indent(level);
|
||||||
|
context.out << ']';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case type_t::string:
|
||||||
|
context.out << '\"';
|
||||||
|
|
||||||
|
if (string_ptr(m_Storage)->find_first_of("\"\\/\b\f\n\r") != string::npos || string_ptr(m_Storage)->find('\0') != string::npos)
|
||||||
|
{
|
||||||
|
for (auto c : *string_ptr(m_Storage))
|
||||||
|
{
|
||||||
|
if (c == '\"') context.out << "\\\"";
|
||||||
|
else if (c == '\\') context.out << "\\\\";
|
||||||
|
else if (c == '/') context.out << "\\/";
|
||||||
|
else if (c == '\b') context.out << "\\b";
|
||||||
|
else if (c == '\f') context.out << "\\f";
|
||||||
|
else if (c == '\n') context.out << "\\n";
|
||||||
|
else if (c == '\r') context.out << "\\r";
|
||||||
|
else if (c == '\t') context.out << "\\t";
|
||||||
|
else if (c == 0) context.out << "\\u0000";
|
||||||
|
else context.out << c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
context.out << *string_ptr(m_Storage);
|
||||||
|
context.out << '\"';
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
case type_t::boolean:
|
||||||
|
if (*boolean_ptr(m_Storage))
|
||||||
|
context.out << "true";
|
||||||
|
else
|
||||||
|
context.out << "false";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case type_t::number:
|
||||||
|
context.out << *number_ptr(m_Storage);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct value::parser
|
||||||
|
{
|
||||||
|
parser(const char* begin, const char* end)
|
||||||
|
: m_Cursor(begin)
|
||||||
|
, m_End(end)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
value parse()
|
||||||
|
{
|
||||||
|
value v;
|
||||||
|
|
||||||
|
// Switch to C locale to make strtod and strtol work as expected
|
||||||
|
auto previous_locale = std::setlocale(LC_NUMERIC, "C");
|
||||||
|
|
||||||
|
// Accept single value only when end of the stream is reached.
|
||||||
|
if (!accept_element(v) || !eof())
|
||||||
|
v = value(type_t::discarded);
|
||||||
|
|
||||||
|
if (previous_locale && strcmp(previous_locale, "C") != 0)
|
||||||
|
std::setlocale(LC_NUMERIC, previous_locale);
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct cursor_state
|
||||||
|
{
|
||||||
|
cursor_state(parser* p)
|
||||||
|
: m_Owner(p)
|
||||||
|
, m_LastCursor(p->m_Cursor)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset()
|
||||||
|
{
|
||||||
|
m_Owner->m_Cursor = m_LastCursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator()(bool accept)
|
||||||
|
{
|
||||||
|
if (!accept)
|
||||||
|
reset();
|
||||||
|
else
|
||||||
|
m_LastCursor = m_Owner->m_Cursor;
|
||||||
|
return accept;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
parser* m_Owner;
|
||||||
|
const char* m_LastCursor;
|
||||||
|
};
|
||||||
|
|
||||||
|
cursor_state state()
|
||||||
|
{
|
||||||
|
return cursor_state(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool accept_value(value& result)
|
||||||
|
{
|
||||||
|
return accept_object(result)
|
||||||
|
|| accept_array(result)
|
||||||
|
|| accept_string(result)
|
||||||
|
|| accept_number(result)
|
||||||
|
|| accept_boolean(result)
|
||||||
|
|| accept_null(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool accept_object(value& result)
|
||||||
|
{
|
||||||
|
auto s = state();
|
||||||
|
|
||||||
|
object o;
|
||||||
|
if (s(accept('{') && accept_ws() && accept('}')))
|
||||||
|
{
|
||||||
|
result = o;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (s(accept('{') && accept_members(o) && accept('}')))
|
||||||
|
{
|
||||||
|
result = std::move(o);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool accept_members(object& o)
|
||||||
|
{
|
||||||
|
if (!accept_member(o))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
auto s = state();
|
||||||
|
if (!s(accept(',') && accept_member(o)))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool accept_member(object& o)
|
||||||
|
{
|
||||||
|
auto s = state();
|
||||||
|
|
||||||
|
value key;
|
||||||
|
value v;
|
||||||
|
if (s(accept_ws() && accept_string(key) && accept_ws() && accept(':') && accept_element(v)))
|
||||||
|
{
|
||||||
|
o.emplace(std::move(key.get<string>()), std::move(v));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool accept_array(value& result)
|
||||||
|
{
|
||||||
|
auto s = state();
|
||||||
|
|
||||||
|
if (s(accept('[') && accept_ws() && accept(']')))
|
||||||
|
{
|
||||||
|
result = array();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
array a;
|
||||||
|
if (s(accept('[') && accept_elements(a) && accept(']')))
|
||||||
|
{
|
||||||
|
result = std::move(a);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool accept_elements(array& a)
|
||||||
|
{
|
||||||
|
value v;
|
||||||
|
if (!accept_element(v))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
a.emplace_back(std::move(v));
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
auto s = state();
|
||||||
|
v = nullptr;
|
||||||
|
if (!s(accept(',') && accept_element(v)))
|
||||||
|
break;
|
||||||
|
a.emplace_back(std::move(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool accept_element(value& result)
|
||||||
|
{
|
||||||
|
auto s = state();
|
||||||
|
return s(accept_ws() && accept_value(result) && accept_ws());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool accept_string(value& result)
|
||||||
|
{
|
||||||
|
auto s = state();
|
||||||
|
|
||||||
|
string v;
|
||||||
|
if (s(accept('\"') && accept_characters(v) && accept('\"')))
|
||||||
|
{
|
||||||
|
result = std::move(v);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool accept_characters(string& result)
|
||||||
|
{
|
||||||
|
int c;
|
||||||
|
while (accept_character(c))
|
||||||
|
{
|
||||||
|
CRUDE_ASSERT(c < 128); // #todo: convert characters > 127 to UTF-8
|
||||||
|
result.push_back(static_cast<char>(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool accept_character(int& c)
|
||||||
|
{
|
||||||
|
auto s = state();
|
||||||
|
|
||||||
|
if (accept('\\'))
|
||||||
|
{
|
||||||
|
return accept_escape(c);
|
||||||
|
}
|
||||||
|
else if (expect('\"'))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// #todo: Handle UTF-8 sequences.
|
||||||
|
return s((c = peek()) >= 0) && advance();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool accept_escape(int& c)
|
||||||
|
{
|
||||||
|
if (accept('\"')) { c = '\"'; return true; }
|
||||||
|
if (accept('\\')) { c = '\\'; return true; }
|
||||||
|
if (accept('/')) { c = '/'; return true; }
|
||||||
|
if (accept('b')) { c = '\b'; return true; }
|
||||||
|
if (accept('f')) { c = '\f'; return true; }
|
||||||
|
if (accept('n')) { c = '\n'; return true; }
|
||||||
|
if (accept('r')) { c = '\r'; return true; }
|
||||||
|
if (accept('t')) { c = '\t'; return true; }
|
||||||
|
|
||||||
|
auto s = state();
|
||||||
|
|
||||||
|
string hex;
|
||||||
|
hex.reserve(4);
|
||||||
|
if (s(accept('u') && accept_hex(hex) && accept_hex(hex) && accept_hex(hex) && accept_hex(hex)))
|
||||||
|
{
|
||||||
|
char* end = nullptr;
|
||||||
|
auto v = std::strtol(hex.c_str(), &end, 16);
|
||||||
|
if (end != hex.c_str() + hex.size())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
c = v;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool accept_hex(string& result)
|
||||||
|
{
|
||||||
|
if (accept_digit(result))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
auto c = peek();
|
||||||
|
if ((c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'))
|
||||||
|
{
|
||||||
|
advance();
|
||||||
|
result.push_back(static_cast<char>(c));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool accept_number(value& result)
|
||||||
|
{
|
||||||
|
auto s = state();
|
||||||
|
|
||||||
|
string n;
|
||||||
|
if (s(accept_int(n) && accept_frac(n) && accept_exp(n)))
|
||||||
|
{
|
||||||
|
char* end = nullptr;
|
||||||
|
auto v = std::strtod(n.c_str(), &end);
|
||||||
|
if (end != n.c_str() + n.size())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (v != 0 && !std::isnormal(v))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
result = v;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool accept_int(string& result)
|
||||||
|
{
|
||||||
|
auto s = state();
|
||||||
|
|
||||||
|
string part;
|
||||||
|
if (s(accept_onenine(part) && accept_digits(part)))
|
||||||
|
{
|
||||||
|
result += std::move(part);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
part.resize(0);
|
||||||
|
if (accept_digit(part))
|
||||||
|
{
|
||||||
|
result += std::move(part);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
part.resize(0);
|
||||||
|
if (s(accept('-') && accept_onenine(part) && accept_digits(part)))
|
||||||
|
{
|
||||||
|
result += '-';
|
||||||
|
result += std::move(part);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
part.resize(0);
|
||||||
|
if (s(accept('-') && accept_digit(part)))
|
||||||
|
{
|
||||||
|
result += '-';
|
||||||
|
result += std::move(part);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool accept_digits(string& result)
|
||||||
|
{
|
||||||
|
string part;
|
||||||
|
if (!accept_digit(part))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
while (accept_digit(part))
|
||||||
|
;
|
||||||
|
|
||||||
|
result += std::move(part);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool accept_digit(string& result)
|
||||||
|
{
|
||||||
|
if (accept('0'))
|
||||||
|
{
|
||||||
|
result.push_back('0');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (accept_onenine(result))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool accept_onenine(string& result)
|
||||||
|
{
|
||||||
|
auto c = peek();
|
||||||
|
if (c >= '1' && c <= '9')
|
||||||
|
{
|
||||||
|
result.push_back(static_cast<char>(c));
|
||||||
|
return advance();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool accept_frac(string& result)
|
||||||
|
{
|
||||||
|
auto s = state();
|
||||||
|
|
||||||
|
string part;
|
||||||
|
if (s(accept('.') && accept_digits(part)))
|
||||||
|
{
|
||||||
|
result += '.';
|
||||||
|
result += std::move(part);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool accept_exp(string& result)
|
||||||
|
{
|
||||||
|
auto s = state();
|
||||||
|
|
||||||
|
string part;
|
||||||
|
if (s(accept('e') && accept_sign(part) && accept_digits(part)))
|
||||||
|
{
|
||||||
|
result += 'e';
|
||||||
|
result += std::move(part);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
part.resize(0);
|
||||||
|
if (s(accept('E') && accept_sign(part) && accept_digits(part)))
|
||||||
|
{
|
||||||
|
result += 'E';
|
||||||
|
result += std::move(part);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool accept_sign(string& result)
|
||||||
|
{
|
||||||
|
if (accept('+'))
|
||||||
|
result.push_back('+');
|
||||||
|
else if (accept('-'))
|
||||||
|
result.push_back('-');
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool accept_ws()
|
||||||
|
{
|
||||||
|
while (expect('\x09') || expect('\x0A') || expect('\x0D') || expect('\x20'))
|
||||||
|
advance();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool accept_boolean(value& result)
|
||||||
|
{
|
||||||
|
if (accept("true"))
|
||||||
|
{
|
||||||
|
result = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (accept("false"))
|
||||||
|
{
|
||||||
|
result = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool accept_null(value& result)
|
||||||
|
{
|
||||||
|
if (accept("null"))
|
||||||
|
{
|
||||||
|
result = nullptr;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool accept(char c)
|
||||||
|
{
|
||||||
|
if (expect(c))
|
||||||
|
return advance();
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool accept(const char* str)
|
||||||
|
{
|
||||||
|
auto last = m_Cursor;
|
||||||
|
|
||||||
|
while (*str)
|
||||||
|
{
|
||||||
|
if (eof() || *str != *m_Cursor)
|
||||||
|
{
|
||||||
|
m_Cursor = last;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
advance();
|
||||||
|
++str;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int peek() const
|
||||||
|
{
|
||||||
|
if (!eof())
|
||||||
|
return *m_Cursor;
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool expect(char c)
|
||||||
|
{
|
||||||
|
return peek() == c;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool advance(int count = 1)
|
||||||
|
{
|
||||||
|
if (m_Cursor + count > m_End)
|
||||||
|
{
|
||||||
|
m_Cursor = m_End;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_Cursor += count;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool eof() const
|
||||||
|
{
|
||||||
|
return m_Cursor == m_End;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* m_Cursor;
|
||||||
|
const char* m_End;
|
||||||
|
};
|
||||||
|
|
||||||
|
value value::parse(const string& data)
|
||||||
|
{
|
||||||
|
auto p = parser(data.c_str(), data.c_str() + data.size());
|
||||||
|
|
||||||
|
auto v = p.parse();
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
# if CRUDE_JSON_IO
|
||||||
|
std::pair<value, bool> value::load(const string& path)
|
||||||
|
{
|
||||||
|
// Modern C++, so beautiful...
|
||||||
|
std::unique_ptr<FILE, void(*)(FILE*)> file{nullptr, [](FILE* file) { if (file) fclose(file); }};
|
||||||
|
# if defined(_MSC_VER) || (defined(__STDC_LIB_EXT1__) && __STDC_WANT_LIB_EXT1__)
|
||||||
|
FILE* handle = nullptr;
|
||||||
|
if (fopen_s(&handle, path.c_str(), "rb") != 0)
|
||||||
|
return {value{}, false};
|
||||||
|
file.reset(handle);
|
||||||
|
# else
|
||||||
|
file.reset(fopen(path.c_str(), "rb"));
|
||||||
|
# endif
|
||||||
|
|
||||||
|
if (!file)
|
||||||
|
return {value{}, false};
|
||||||
|
|
||||||
|
fseek(file.get(), 0, SEEK_END);
|
||||||
|
auto size = static_cast<size_t>(ftell(file.get()));
|
||||||
|
fseek(file.get(), 0, SEEK_SET);
|
||||||
|
|
||||||
|
string data;
|
||||||
|
data.resize(size);
|
||||||
|
if (fread(const_cast<char*>(data.data()), size, 1, file.get()) != 1)
|
||||||
|
return {value{}, false};
|
||||||
|
|
||||||
|
return {parse(data), true};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool value::save(const string& path, const int indent, const char indent_char) const
|
||||||
|
{
|
||||||
|
// Modern C++, so beautiful...
|
||||||
|
std::unique_ptr<FILE, void(*)(FILE*)> file{nullptr, [](FILE* file) { if (file) fclose(file); }};
|
||||||
|
# if defined(_MSC_VER) || (defined(__STDC_LIB_EXT1__) && __STDC_WANT_LIB_EXT1__)
|
||||||
|
FILE* handle = nullptr;
|
||||||
|
if (fopen_s(&handle, path.c_str(), "wb") != 0)
|
||||||
|
return false;
|
||||||
|
file.reset(handle);
|
||||||
|
# else
|
||||||
|
file.reset(fopen(path.c_str(), "wb"));
|
||||||
|
# endif
|
||||||
|
|
||||||
|
if (!file)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto data = dump(indent, indent_char);
|
||||||
|
|
||||||
|
if (fwrite(data.data(), data.size(), 1, file.get()) != 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
# endif
|
||||||
|
|
||||||
|
} // namespace crude_json
|
||||||
@@ -0,0 +1,550 @@
|
|||||||
|
# define IMGUI_DEFINE_MATH_OPERATORS
|
||||||
|
# include "IUI/nodes/imgui_canvas.h"
|
||||||
|
# include <type_traits>
|
||||||
|
|
||||||
|
// https://stackoverflow.com/a/36079786
|
||||||
|
# define DECLARE_HAS_MEMBER(__trait_name__, __member_name__) \
|
||||||
|
\
|
||||||
|
template <typename __boost_has_member_T__> \
|
||||||
|
class __trait_name__ \
|
||||||
|
{ \
|
||||||
|
using check_type = ::std::remove_const_t<__boost_has_member_T__>; \
|
||||||
|
struct no_type {char x[2];}; \
|
||||||
|
using yes_type = char; \
|
||||||
|
\
|
||||||
|
struct base { void __member_name__() {}}; \
|
||||||
|
struct mixin : public base, public check_type {}; \
|
||||||
|
\
|
||||||
|
template <void (base::*)()> struct aux {}; \
|
||||||
|
\
|
||||||
|
template <typename U> static no_type test(aux<&U::__member_name__>*); \
|
||||||
|
template <typename U> static yes_type test(...); \
|
||||||
|
\
|
||||||
|
public: \
|
||||||
|
\
|
||||||
|
static constexpr bool value = (sizeof(yes_type) == sizeof(test<mixin>(0))); \
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace ImCanvasDetails {
|
||||||
|
|
||||||
|
DECLARE_HAS_MEMBER(HasFringeScale, _FringeScale);
|
||||||
|
|
||||||
|
struct FringeScaleRef
|
||||||
|
{
|
||||||
|
// Overload is present when ImDrawList does have _FringeScale member variable.
|
||||||
|
template <typename T>
|
||||||
|
static float& Get(typename std::enable_if<HasFringeScale<T>::value, T>::type* drawList)
|
||||||
|
{
|
||||||
|
return drawList->_FringeScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overload is present when ImDrawList does not have _FringeScale member variable.
|
||||||
|
template <typename T>
|
||||||
|
static float& Get(typename std::enable_if<!HasFringeScale<T>::value, T>::type*)
|
||||||
|
{
|
||||||
|
static float placeholder = 1.0f;
|
||||||
|
return placeholder;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
DECLARE_HAS_MEMBER(HasVtxCurrentOffset, _VtxCurrentOffset);
|
||||||
|
|
||||||
|
struct VtxCurrentOffsetRef
|
||||||
|
{
|
||||||
|
// Overload is present when ImDrawList does have _FringeScale member variable.
|
||||||
|
template <typename T>
|
||||||
|
static unsigned int& Get(typename std::enable_if<HasVtxCurrentOffset<T>::value, T>::type* drawList)
|
||||||
|
{
|
||||||
|
return drawList->_VtxCurrentOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Overload is present when ImDrawList does not have _FringeScale member variable.
|
||||||
|
template <typename T>
|
||||||
|
static unsigned int& Get(typename std::enable_if<!HasVtxCurrentOffset<T>::value, T>::type* drawList)
|
||||||
|
{
|
||||||
|
return drawList->_CmdHeader.VtxOffset;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ImCanvasDetails
|
||||||
|
|
||||||
|
// Returns a reference to _FringeScale extension to ImDrawList
|
||||||
|
//
|
||||||
|
// If ImDrawList does not have _FringeScale a placeholder is returned.
|
||||||
|
static inline float& ImFringeScaleRef(ImDrawList* drawList)
|
||||||
|
{
|
||||||
|
using namespace ImCanvasDetails;
|
||||||
|
return FringeScaleRef::Get<ImDrawList>(drawList);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned int& ImVtxOffsetRef(ImDrawList* drawList)
|
||||||
|
{
|
||||||
|
using namespace ImCanvasDetails;
|
||||||
|
return VtxCurrentOffsetRef::Get<ImDrawList>(drawList);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline ImVec2 ImSelectPositive(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x > 0.0f ? lhs.x : rhs.x, lhs.y > 0.0f ? lhs.y : rhs.y); }
|
||||||
|
|
||||||
|
bool ImGuiEx::Canvas::Begin(const char* id, const ImVec2& size)
|
||||||
|
{
|
||||||
|
return Begin(ImGui::GetID(id), size);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ImGuiEx::Canvas::Begin(ImGuiID id, const ImVec2& size)
|
||||||
|
{
|
||||||
|
IM_ASSERT(m_InBeginEnd == false);
|
||||||
|
|
||||||
|
m_WidgetPosition = ImGui::GetCursorScreenPos();
|
||||||
|
m_WidgetSize = ImSelectPositive(size, ImGui::GetContentRegionAvail());
|
||||||
|
m_WidgetRect = ImRect(m_WidgetPosition, m_WidgetPosition + m_WidgetSize);
|
||||||
|
m_DrawList = ImGui::GetWindowDrawList();
|
||||||
|
|
||||||
|
UpdateViewTransformPosition();
|
||||||
|
|
||||||
|
# if IMGUI_VERSION_NUM > 18415
|
||||||
|
if (ImGui::IsClippedEx(m_WidgetRect, id))
|
||||||
|
return false;
|
||||||
|
# else
|
||||||
|
if (ImGui::IsClippedEx(m_WidgetRect, id, false))
|
||||||
|
return false;
|
||||||
|
# endif
|
||||||
|
|
||||||
|
// Save current channel, so we can assert when user
|
||||||
|
// call canvas API with different one.
|
||||||
|
m_ExpectedChannel = m_DrawList->_Splitter._Current;
|
||||||
|
|
||||||
|
// #debug: Canvas content.
|
||||||
|
//m_DrawList->AddRectFilled(m_StartPos, m_StartPos + m_CurrentSize, IM_COL32(0, 0, 0, 64));
|
||||||
|
//m_DrawList->AddRect(m_WidgetRect.Min, m_WidgetRect.Max, IM_COL32(255, 0, 255, 64));
|
||||||
|
|
||||||
|
ImGui::SetCursorScreenPos(ImVec2(0.0f, 0.0f));
|
||||||
|
|
||||||
|
# if IMGUI_EX_CANVAS_DEFERED()
|
||||||
|
m_Ranges.resize(0);
|
||||||
|
# endif
|
||||||
|
|
||||||
|
SaveInputState();
|
||||||
|
SaveViewportState();
|
||||||
|
|
||||||
|
// Record cursor max to prevent scrollbars from appearing.
|
||||||
|
m_WindowCursorMaxBackup = ImGui::GetCurrentWindow()->DC.CursorMaxPos;
|
||||||
|
|
||||||
|
EnterLocalSpace();
|
||||||
|
|
||||||
|
// Emit dummy widget matching bounds of the canvas.
|
||||||
|
ImGui::SetCursorScreenPos(m_ViewRect.Min);
|
||||||
|
ImGui::Dummy(m_ViewRect.GetSize());
|
||||||
|
|
||||||
|
ImGui::SetCursorScreenPos(ImVec2(0.0f, 0.0f));
|
||||||
|
|
||||||
|
m_InBeginEnd = true;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiEx::Canvas::End()
|
||||||
|
{
|
||||||
|
// If you're here your call to Begin() returned false,
|
||||||
|
// or Begin() wasn't called at all.
|
||||||
|
IM_ASSERT(m_InBeginEnd == true);
|
||||||
|
|
||||||
|
// If you're here, please make sure you do not interleave
|
||||||
|
// channel splitter with canvas.
|
||||||
|
// Always call canvas function with using same channel.
|
||||||
|
IM_ASSERT(m_DrawList->_Splitter._Current == m_ExpectedChannel);
|
||||||
|
|
||||||
|
//auto& io = ImGui::GetIO();
|
||||||
|
|
||||||
|
// Check: Unmatched calls to Suspend() / Resume(). Please check your code.
|
||||||
|
IM_ASSERT(m_SuspendCounter == 0);
|
||||||
|
|
||||||
|
LeaveLocalSpace();
|
||||||
|
|
||||||
|
ImGui::GetCurrentWindow()->DC.CursorMaxPos = m_WindowCursorMaxBackup;
|
||||||
|
|
||||||
|
ImGui::SetItemAllowOverlap();
|
||||||
|
|
||||||
|
// Emit dummy widget matching bounds of the canvas.
|
||||||
|
ImGui::SetCursorScreenPos(m_WidgetPosition);
|
||||||
|
ImGui::Dummy(m_WidgetSize);
|
||||||
|
|
||||||
|
// #debug: Rect around canvas. Content should be inside these bounds.
|
||||||
|
//m_DrawList->AddRect(m_WidgetPosition - ImVec2(1.0f, 1.0f), m_WidgetPosition + m_WidgetSize + ImVec2(1.0f, 1.0f), IM_COL32(196, 0, 0, 255));
|
||||||
|
|
||||||
|
m_InBeginEnd = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiEx::Canvas::SetView(const ImVec2& origin, float scale)
|
||||||
|
{
|
||||||
|
SetView(CanvasView(origin, scale));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiEx::Canvas::SetView(const CanvasView& view)
|
||||||
|
{
|
||||||
|
if (m_InBeginEnd)
|
||||||
|
LeaveLocalSpace();
|
||||||
|
|
||||||
|
if (m_View.Origin.x != view.Origin.x || m_View.Origin.y != view.Origin.y)
|
||||||
|
{
|
||||||
|
m_View.Origin = view.Origin;
|
||||||
|
|
||||||
|
UpdateViewTransformPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_View.Scale != view.Scale)
|
||||||
|
{
|
||||||
|
m_View.Scale = view.Scale;
|
||||||
|
m_View.InvScale = view.InvScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_InBeginEnd)
|
||||||
|
EnterLocalSpace();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiEx::Canvas::CenterView(const ImVec2& canvasPoint)
|
||||||
|
{
|
||||||
|
auto view = CalcCenterView(canvasPoint);
|
||||||
|
SetView(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGuiEx::CanvasView ImGuiEx::Canvas::CalcCenterView(const ImVec2& canvasPoint) const
|
||||||
|
{
|
||||||
|
auto localCenter = ToLocal(m_WidgetPosition + m_WidgetSize * 0.5f);
|
||||||
|
auto localOffset = canvasPoint - localCenter;
|
||||||
|
auto offset = FromLocalV(localOffset);
|
||||||
|
|
||||||
|
return CanvasView{ m_View.Origin - offset, m_View.Scale };
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiEx::Canvas::CenterView(const ImRect& canvasRect)
|
||||||
|
{
|
||||||
|
auto view = CalcCenterView(canvasRect);
|
||||||
|
|
||||||
|
SetView(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGuiEx::CanvasView ImGuiEx::Canvas::CalcCenterView(const ImRect& canvasRect) const
|
||||||
|
{
|
||||||
|
auto canvasRectSize = canvasRect.GetSize();
|
||||||
|
|
||||||
|
if (canvasRectSize.x <= 0.0f || canvasRectSize.y <= 0.0f)
|
||||||
|
return View();
|
||||||
|
|
||||||
|
auto widgetAspectRatio = m_WidgetSize.y > 0.0f ? m_WidgetSize.x / m_WidgetSize.y : 0.0f;
|
||||||
|
auto canvasRectAspectRatio = canvasRectSize.y > 0.0f ? canvasRectSize.x / canvasRectSize.y : 0.0f;
|
||||||
|
|
||||||
|
if (widgetAspectRatio <= 0.0f || canvasRectAspectRatio <= 0.0f)
|
||||||
|
return View();
|
||||||
|
|
||||||
|
auto newOrigin = m_View.Origin;
|
||||||
|
auto newScale = m_View.Scale;
|
||||||
|
if (canvasRectAspectRatio > widgetAspectRatio)
|
||||||
|
{
|
||||||
|
// width span across view
|
||||||
|
newScale = m_WidgetSize.x / canvasRectSize.x;
|
||||||
|
newOrigin = canvasRect.Min * -newScale;
|
||||||
|
newOrigin.y += (m_WidgetSize.y - canvasRectSize.y * newScale) * 0.5f;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// height span across view
|
||||||
|
newScale = m_WidgetSize.y / canvasRectSize.y;
|
||||||
|
newOrigin = canvasRect.Min * -newScale;
|
||||||
|
newOrigin.x += (m_WidgetSize.x - canvasRectSize.x * newScale) * 0.5f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return CanvasView{ newOrigin, newScale };
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiEx::Canvas::Suspend()
|
||||||
|
{
|
||||||
|
// If you're here, please make sure you do not interleave
|
||||||
|
// channel splitter with canvas.
|
||||||
|
// Always call canvas function with using same channel.
|
||||||
|
IM_ASSERT(m_DrawList->_Splitter._Current == m_ExpectedChannel);
|
||||||
|
|
||||||
|
if (m_SuspendCounter == 0)
|
||||||
|
LeaveLocalSpace();
|
||||||
|
|
||||||
|
++m_SuspendCounter;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiEx::Canvas::Resume()
|
||||||
|
{
|
||||||
|
// If you're here, please make sure you do not interleave
|
||||||
|
// channel splitter with canvas.
|
||||||
|
// Always call canvas function with using same channel.
|
||||||
|
IM_ASSERT(m_DrawList->_Splitter._Current == m_ExpectedChannel);
|
||||||
|
|
||||||
|
// Check: Number of calls to Resume() do not match calls to Suspend(). Please check your code.
|
||||||
|
IM_ASSERT(m_SuspendCounter > 0);
|
||||||
|
if (--m_SuspendCounter == 0)
|
||||||
|
EnterLocalSpace();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec2 ImGuiEx::Canvas::FromLocal(const ImVec2& point) const
|
||||||
|
{
|
||||||
|
return point * m_View.Scale + m_ViewTransformPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec2 ImGuiEx::Canvas::FromLocal(const ImVec2& point, const CanvasView& view) const
|
||||||
|
{
|
||||||
|
return point * view.Scale + view.Origin + m_WidgetPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec2 ImGuiEx::Canvas::FromLocalV(const ImVec2& vector) const
|
||||||
|
{
|
||||||
|
return vector * m_View.Scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec2 ImGuiEx::Canvas::FromLocalV(const ImVec2& vector, const CanvasView& view) const
|
||||||
|
{
|
||||||
|
return vector * view.Scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec2 ImGuiEx::Canvas::ToLocal(const ImVec2& point) const
|
||||||
|
{
|
||||||
|
return (point - m_ViewTransformPosition) * m_View.InvScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec2 ImGuiEx::Canvas::ToLocal(const ImVec2& point, const CanvasView& view) const
|
||||||
|
{
|
||||||
|
return (point - view.Origin - m_WidgetPosition) * view.InvScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec2 ImGuiEx::Canvas::ToLocalV(const ImVec2& vector) const
|
||||||
|
{
|
||||||
|
return vector * m_View.InvScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec2 ImGuiEx::Canvas::ToLocalV(const ImVec2& vector, const CanvasView& view) const
|
||||||
|
{
|
||||||
|
return vector * view.InvScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImRect ImGuiEx::Canvas::CalcViewRect(const CanvasView& view) const
|
||||||
|
{
|
||||||
|
ImRect result;
|
||||||
|
result.Min = ImVec2(-view.Origin.x, -view.Origin.y) * view.InvScale;
|
||||||
|
result.Max = (m_WidgetSize - view.Origin) * view.InvScale;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiEx::Canvas::UpdateViewTransformPosition()
|
||||||
|
{
|
||||||
|
m_ViewTransformPosition = m_View.Origin + m_WidgetPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiEx::Canvas::SaveInputState()
|
||||||
|
{
|
||||||
|
auto& io = ImGui::GetIO();
|
||||||
|
m_MousePosBackup = io.MousePos;
|
||||||
|
m_MousePosPrevBackup = io.MousePosPrev;
|
||||||
|
for (auto i = 0; i < IM_ARRAYSIZE(m_MouseClickedPosBackup); ++i)
|
||||||
|
m_MouseClickedPosBackup[i] = io.MouseClickedPos[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiEx::Canvas::RestoreInputState()
|
||||||
|
{
|
||||||
|
auto& io = ImGui::GetIO();
|
||||||
|
io.MousePos = m_MousePosBackup;
|
||||||
|
io.MousePosPrev = m_MousePosPrevBackup;
|
||||||
|
for (auto i = 0; i < IM_ARRAYSIZE(m_MouseClickedPosBackup); ++i)
|
||||||
|
io.MouseClickedPos[i] = m_MouseClickedPosBackup[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiEx::Canvas::SaveViewportState()
|
||||||
|
{
|
||||||
|
# if defined(IMGUI_HAS_VIEWPORT)
|
||||||
|
auto window = ImGui::GetCurrentWindow();
|
||||||
|
auto viewport = ImGui::GetWindowViewport();
|
||||||
|
|
||||||
|
m_WindowPosBackup = window->Pos;
|
||||||
|
m_ViewportPosBackup = viewport->Pos;
|
||||||
|
m_ViewportSizeBackup = viewport->Size;
|
||||||
|
# if IMGUI_VERSION_NUM > 18002
|
||||||
|
m_ViewportWorkPosBackup = viewport->WorkPos;
|
||||||
|
m_ViewportWorkSizeBackup = viewport->WorkSize;
|
||||||
|
# else
|
||||||
|
m_ViewportWorkOffsetMinBackup = viewport->WorkOffsetMin;
|
||||||
|
m_ViewportWorkOffsetMaxBackup = viewport->WorkOffsetMax;
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiEx::Canvas::RestoreViewportState()
|
||||||
|
{
|
||||||
|
# if defined(IMGUI_HAS_VIEWPORT)
|
||||||
|
auto window = ImGui::GetCurrentWindow();
|
||||||
|
auto viewport = ImGui::GetWindowViewport();
|
||||||
|
|
||||||
|
window->Pos = m_WindowPosBackup;
|
||||||
|
viewport->Pos = m_ViewportPosBackup;
|
||||||
|
viewport->Size = m_ViewportSizeBackup;
|
||||||
|
# if IMGUI_VERSION_NUM > 18002
|
||||||
|
viewport->WorkPos = m_ViewportWorkPosBackup;
|
||||||
|
viewport->WorkSize = m_ViewportWorkSizeBackup;
|
||||||
|
# else
|
||||||
|
viewport->WorkOffsetMin = m_ViewportWorkOffsetMinBackup;
|
||||||
|
viewport->WorkOffsetMax = m_ViewportWorkOffsetMaxBackup;
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiEx::Canvas::EnterLocalSpace()
|
||||||
|
{
|
||||||
|
// Prepare ImDrawList for drawing in local coordinate system:
|
||||||
|
// - determine visible part of the canvas
|
||||||
|
// - start unique draw command
|
||||||
|
// - add clip rect matching canvas size
|
||||||
|
// - record current command index
|
||||||
|
// - record current vertex write index
|
||||||
|
|
||||||
|
// Determine visible part of the canvas. Make it before
|
||||||
|
// adding new command, to avoid round rip where command
|
||||||
|
// is removed in PopClipRect() and added again next PushClipRect().
|
||||||
|
ImGui::PushClipRect(m_WidgetPosition, m_WidgetPosition + m_WidgetSize, true);
|
||||||
|
auto clipped_clip_rect = m_DrawList->_ClipRectStack.back();
|
||||||
|
ImGui::PopClipRect();
|
||||||
|
|
||||||
|
// Make sure we do not share draw command with anyone. We don't want to mess
|
||||||
|
// with someones clip rectangle.
|
||||||
|
|
||||||
|
// #FIXME:
|
||||||
|
// This condition is not enough to avoid when user choose
|
||||||
|
// to use channel splitter.
|
||||||
|
//
|
||||||
|
// To deal with Suspend()/Resume() calls empty draw command
|
||||||
|
// is always added then splitter is active. Otherwise
|
||||||
|
// channel merger will collapse our draw command one with
|
||||||
|
// different clip rectangle.
|
||||||
|
//
|
||||||
|
// More investigation is needed. To get to the bottom of this.
|
||||||
|
if ((!m_DrawList->CmdBuffer.empty() && m_DrawList->CmdBuffer.back().ElemCount > 0) || m_DrawList->_Splitter._Count > 1)
|
||||||
|
m_DrawList->AddDrawCmd();
|
||||||
|
|
||||||
|
# if IMGUI_EX_CANVAS_DEFERED()
|
||||||
|
m_Ranges.resize(m_Ranges.Size + 1);
|
||||||
|
m_CurrentRange = &m_Ranges.back();
|
||||||
|
m_CurrentRange->BeginComandIndex = ImMax(m_DrawList->CmdBuffer.Size - 1, 0);
|
||||||
|
m_CurrentRange->BeginVertexIndex = m_DrawList->_VtxCurrentIdx + ImVtxOffsetRef(m_DrawList);
|
||||||
|
# endif
|
||||||
|
m_DrawListCommadBufferSize = ImMax(m_DrawList->CmdBuffer.Size - 1, 0);
|
||||||
|
m_DrawListStartVertexIndex = m_DrawList->_VtxCurrentIdx + ImVtxOffsetRef(m_DrawList);
|
||||||
|
|
||||||
|
# if defined(IMGUI_HAS_VIEWPORT)
|
||||||
|
auto window = ImGui::GetCurrentWindow();
|
||||||
|
window->Pos = ImVec2(0.0f, 0.0f);
|
||||||
|
|
||||||
|
auto viewport_min = m_ViewportPosBackup;
|
||||||
|
auto viewport_max = m_ViewportPosBackup + m_ViewportSizeBackup;
|
||||||
|
|
||||||
|
viewport_min.x = (viewport_min.x - m_ViewTransformPosition.x) * m_View.InvScale;
|
||||||
|
viewport_min.y = (viewport_min.y - m_ViewTransformPosition.y) * m_View.InvScale;
|
||||||
|
viewport_max.x = (viewport_max.x - m_ViewTransformPosition.x) * m_View.InvScale;
|
||||||
|
viewport_max.y = (viewport_max.y - m_ViewTransformPosition.y) * m_View.InvScale;
|
||||||
|
|
||||||
|
auto viewport = ImGui::GetWindowViewport();
|
||||||
|
viewport->Pos = viewport_min;
|
||||||
|
viewport->Size = viewport_max - viewport_min;
|
||||||
|
|
||||||
|
# if IMGUI_VERSION_NUM > 18002
|
||||||
|
viewport->WorkPos = m_ViewportWorkPosBackup * m_View.InvScale;
|
||||||
|
viewport->WorkSize = m_ViewportWorkSizeBackup * m_View.InvScale;
|
||||||
|
# else
|
||||||
|
viewport->WorkOffsetMin = m_ViewportWorkOffsetMinBackup * m_View.InvScale;
|
||||||
|
viewport->WorkOffsetMax = m_ViewportWorkOffsetMaxBackup * m_View.InvScale;
|
||||||
|
# endif
|
||||||
|
# endif
|
||||||
|
|
||||||
|
// Clip rectangle in parent canvas space and move it to local space.
|
||||||
|
clipped_clip_rect.x = (clipped_clip_rect.x - m_ViewTransformPosition.x) * m_View.InvScale;
|
||||||
|
clipped_clip_rect.y = (clipped_clip_rect.y - m_ViewTransformPosition.y) * m_View.InvScale;
|
||||||
|
clipped_clip_rect.z = (clipped_clip_rect.z - m_ViewTransformPosition.x) * m_View.InvScale;
|
||||||
|
clipped_clip_rect.w = (clipped_clip_rect.w - m_ViewTransformPosition.y) * m_View.InvScale;
|
||||||
|
ImGui::PushClipRect(ImVec2(clipped_clip_rect.x, clipped_clip_rect.y), ImVec2(clipped_clip_rect.z, clipped_clip_rect.w), false);
|
||||||
|
|
||||||
|
// Transform mouse position to local space.
|
||||||
|
auto& io = ImGui::GetIO();
|
||||||
|
io.MousePos = (m_MousePosBackup - m_ViewTransformPosition) * m_View.InvScale;
|
||||||
|
io.MousePosPrev = (m_MousePosPrevBackup - m_ViewTransformPosition) * m_View.InvScale;
|
||||||
|
for (auto i = 0; i < IM_ARRAYSIZE(m_MouseClickedPosBackup); ++i)
|
||||||
|
io.MouseClickedPos[i] = (m_MouseClickedPosBackup[i] - m_ViewTransformPosition) * m_View.InvScale;
|
||||||
|
|
||||||
|
m_ViewRect = CalcViewRect(m_View);;
|
||||||
|
|
||||||
|
auto& fringeScale = ImFringeScaleRef(m_DrawList);
|
||||||
|
m_LastFringeScale = fringeScale;
|
||||||
|
fringeScale *= m_View.InvScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ImGuiEx::Canvas::LeaveLocalSpace()
|
||||||
|
{
|
||||||
|
IM_ASSERT(m_DrawList->_Splitter._Current == m_ExpectedChannel);
|
||||||
|
|
||||||
|
# if IMGUI_EX_CANVAS_DEFERED()
|
||||||
|
IM_ASSERT(m_CurrentRange != nullptr);
|
||||||
|
|
||||||
|
m_CurrentRange->EndVertexIndex = m_DrawList->_VtxCurrentIdx + ImVtxOffsetRef(m_DrawList);
|
||||||
|
m_CurrentRange->EndCommandIndex = m_DrawList->CmdBuffer.size();
|
||||||
|
if (m_CurrentRange->BeginVertexIndex == m_CurrentRange->EndVertexIndex)
|
||||||
|
{
|
||||||
|
// Drop empty range
|
||||||
|
m_Ranges.resize(m_Ranges.Size - 1);
|
||||||
|
}
|
||||||
|
m_CurrentRange = nullptr;
|
||||||
|
# endif
|
||||||
|
|
||||||
|
// Move vertices to screen space.
|
||||||
|
auto vertex = m_DrawList->VtxBuffer.Data + m_DrawListStartVertexIndex;
|
||||||
|
auto vertexEnd = m_DrawList->VtxBuffer.Data + m_DrawList->_VtxCurrentIdx + ImVtxOffsetRef(m_DrawList);
|
||||||
|
|
||||||
|
// If canvas view is not scaled take a faster path.
|
||||||
|
if (m_View.Scale != 1.0f)
|
||||||
|
{
|
||||||
|
while (vertex < vertexEnd)
|
||||||
|
{
|
||||||
|
vertex->pos.x = vertex->pos.x * m_View.Scale + m_ViewTransformPosition.x;
|
||||||
|
vertex->pos.y = vertex->pos.y * m_View.Scale + m_ViewTransformPosition.y;
|
||||||
|
++vertex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move clip rectangles to screen space.
|
||||||
|
for (int i = m_DrawListCommadBufferSize; i < m_DrawList->CmdBuffer.size(); ++i)
|
||||||
|
{
|
||||||
|
auto& command = m_DrawList->CmdBuffer[i];
|
||||||
|
command.ClipRect.x = command.ClipRect.x * m_View.Scale + m_ViewTransformPosition.x;
|
||||||
|
command.ClipRect.y = command.ClipRect.y * m_View.Scale + m_ViewTransformPosition.y;
|
||||||
|
command.ClipRect.z = command.ClipRect.z * m_View.Scale + m_ViewTransformPosition.x;
|
||||||
|
command.ClipRect.w = command.ClipRect.w * m_View.Scale + m_ViewTransformPosition.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
while (vertex < vertexEnd)
|
||||||
|
{
|
||||||
|
vertex->pos.x = vertex->pos.x + m_ViewTransformPosition.x;
|
||||||
|
vertex->pos.y = vertex->pos.y + m_ViewTransformPosition.y;
|
||||||
|
++vertex;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move clip rectangles to screen space.
|
||||||
|
for (int i = m_DrawListCommadBufferSize; i < m_DrawList->CmdBuffer.size(); ++i)
|
||||||
|
{
|
||||||
|
auto& command = m_DrawList->CmdBuffer[i];
|
||||||
|
command.ClipRect.x = command.ClipRect.x + m_ViewTransformPosition.x;
|
||||||
|
command.ClipRect.y = command.ClipRect.y + m_ViewTransformPosition.y;
|
||||||
|
command.ClipRect.z = command.ClipRect.z + m_ViewTransformPosition.x;
|
||||||
|
command.ClipRect.w = command.ClipRect.w + m_ViewTransformPosition.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& fringeScale = ImFringeScaleRef(m_DrawList);
|
||||||
|
fringeScale = m_LastFringeScale;
|
||||||
|
|
||||||
|
// And pop \o/
|
||||||
|
ImGui::PopClipRect();
|
||||||
|
|
||||||
|
RestoreInputState();
|
||||||
|
RestoreViewportState();
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,762 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// VERSION 0.9.1
|
||||||
|
//
|
||||||
|
// LICENSE
|
||||||
|
// This software is dual-licensed to the public domain and under the following
|
||||||
|
// license: you are granted a perpetual, irrevocable license to copy, modify,
|
||||||
|
// publish, and distribute this file as you see fit.
|
||||||
|
//
|
||||||
|
// CREDITS
|
||||||
|
// Written by Michal Cichon
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
# include "IUI/nodes/imgui_node_editor_internal.h"
|
||||||
|
# include <algorithm>
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
static ax::NodeEditor::Detail::EditorContext* s_Editor = nullptr;
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
template <typename C, typename I, typename F>
|
||||||
|
static int BuildIdList(C& container, I* list, int listSize, F&& accept)
|
||||||
|
{
|
||||||
|
if (list != nullptr)
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
for (auto object : container)
|
||||||
|
{
|
||||||
|
if (listSize <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (accept(object))
|
||||||
|
{
|
||||||
|
list[count] = I(object->ID().AsPointer());
|
||||||
|
++count;
|
||||||
|
--listSize;}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return static_cast<int>(std::count_if(container.begin(), container.end(), accept));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
ax::NodeEditor::EditorContext* ax::NodeEditor::CreateEditor(const Config* config)
|
||||||
|
{
|
||||||
|
return reinterpret_cast<ax::NodeEditor::EditorContext*>(new ax::NodeEditor::Detail::EditorContext(config));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::DestroyEditor(EditorContext* ctx)
|
||||||
|
{
|
||||||
|
auto lastContext = GetCurrentEditor();
|
||||||
|
|
||||||
|
// Set context we're about to destroy as current, to give callback valid context
|
||||||
|
if (lastContext != ctx)
|
||||||
|
SetCurrentEditor(ctx);
|
||||||
|
|
||||||
|
auto editor = reinterpret_cast<ax::NodeEditor::Detail::EditorContext*>(ctx);
|
||||||
|
|
||||||
|
delete editor;
|
||||||
|
|
||||||
|
if (lastContext != ctx)
|
||||||
|
SetCurrentEditor(lastContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ax::NodeEditor::Config& ax::NodeEditor::GetConfig(EditorContext* ctx)
|
||||||
|
{
|
||||||
|
if (ctx == nullptr)
|
||||||
|
ctx = GetCurrentEditor();
|
||||||
|
|
||||||
|
if (ctx)
|
||||||
|
{
|
||||||
|
auto editor = reinterpret_cast<ax::NodeEditor::Detail::EditorContext*>(ctx);
|
||||||
|
|
||||||
|
return editor->GetConfig();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
static Config s_EmptyConfig;
|
||||||
|
return s_EmptyConfig;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::SetCurrentEditor(EditorContext* ctx)
|
||||||
|
{
|
||||||
|
s_Editor = reinterpret_cast<ax::NodeEditor::Detail::EditorContext*>(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
ax::NodeEditor::EditorContext* ax::NodeEditor::GetCurrentEditor()
|
||||||
|
{
|
||||||
|
return reinterpret_cast<ax::NodeEditor::EditorContext*>(s_Editor);
|
||||||
|
}
|
||||||
|
|
||||||
|
ax::NodeEditor::Style& ax::NodeEditor::GetStyle()
|
||||||
|
{
|
||||||
|
return s_Editor->GetStyle();
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* ax::NodeEditor::GetStyleColorName(StyleColor colorIndex)
|
||||||
|
{
|
||||||
|
return s_Editor->GetStyle().GetColorName(colorIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::PushStyleColor(StyleColor colorIndex, const ImVec4& color)
|
||||||
|
{
|
||||||
|
s_Editor->GetStyle().PushColor(colorIndex, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::PopStyleColor(int count)
|
||||||
|
{
|
||||||
|
s_Editor->GetStyle().PopColor(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::PushStyleVar(StyleVar varIndex, float value)
|
||||||
|
{
|
||||||
|
s_Editor->GetStyle().PushVar(varIndex, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::PushStyleVar(StyleVar varIndex, const ImVec2& value)
|
||||||
|
{
|
||||||
|
s_Editor->GetStyle().PushVar(varIndex, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::PushStyleVar(StyleVar varIndex, const ImVec4& value)
|
||||||
|
{
|
||||||
|
s_Editor->GetStyle().PushVar(varIndex, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::PopStyleVar(int count)
|
||||||
|
{
|
||||||
|
s_Editor->GetStyle().PopVar(count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::Begin(const char* id, const ImVec2& size)
|
||||||
|
{
|
||||||
|
s_Editor->Begin(id, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::End()
|
||||||
|
{
|
||||||
|
s_Editor->End();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::BeginNode(NodeId id)
|
||||||
|
{
|
||||||
|
s_Editor->GetNodeBuilder().Begin(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::BeginPin(PinId id, PinKind kind)
|
||||||
|
{
|
||||||
|
s_Editor->GetNodeBuilder().BeginPin(id, kind);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::PinRect(const ImVec2& a, const ImVec2& b)
|
||||||
|
{
|
||||||
|
s_Editor->GetNodeBuilder().PinRect(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::PinPivotRect(const ImVec2& a, const ImVec2& b)
|
||||||
|
{
|
||||||
|
s_Editor->GetNodeBuilder().PinPivotRect(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::PinPivotSize(const ImVec2& size)
|
||||||
|
{
|
||||||
|
s_Editor->GetNodeBuilder().PinPivotSize(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::PinPivotScale(const ImVec2& scale)
|
||||||
|
{
|
||||||
|
s_Editor->GetNodeBuilder().PinPivotScale(scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::PinPivotAlignment(const ImVec2& alignment)
|
||||||
|
{
|
||||||
|
s_Editor->GetNodeBuilder().PinPivotAlignment(alignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::EndPin()
|
||||||
|
{
|
||||||
|
s_Editor->GetNodeBuilder().EndPin();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::Group(const ImVec2& size)
|
||||||
|
{
|
||||||
|
s_Editor->GetNodeBuilder().Group(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::EndNode()
|
||||||
|
{
|
||||||
|
s_Editor->GetNodeBuilder().End();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::BeginGroupHint(NodeId nodeId)
|
||||||
|
{
|
||||||
|
return s_Editor->GetHintBuilder().Begin(nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec2 ax::NodeEditor::GetGroupMin()
|
||||||
|
{
|
||||||
|
return s_Editor->GetHintBuilder().GetGroupMin();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec2 ax::NodeEditor::GetGroupMax()
|
||||||
|
{
|
||||||
|
return s_Editor->GetHintBuilder().GetGroupMax();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImDrawList* ax::NodeEditor::GetHintForegroundDrawList()
|
||||||
|
{
|
||||||
|
return s_Editor->GetHintBuilder().GetForegroundDrawList();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImDrawList* ax::NodeEditor::GetHintBackgroundDrawList()
|
||||||
|
{
|
||||||
|
return s_Editor->GetHintBuilder().GetBackgroundDrawList();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::EndGroupHint()
|
||||||
|
{
|
||||||
|
s_Editor->GetHintBuilder().End();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImDrawList* ax::NodeEditor::GetNodeBackgroundDrawList(NodeId nodeId)
|
||||||
|
{
|
||||||
|
if (auto node = s_Editor->FindNode(nodeId))
|
||||||
|
return s_Editor->GetNodeBuilder().GetUserBackgroundDrawList(node);
|
||||||
|
else
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::Link(LinkId id, PinId startPinId, PinId endPinId, const ImVec4& color/* = ImVec4(1, 1, 1, 1)*/, float thickness/* = 1.0f*/)
|
||||||
|
{
|
||||||
|
return s_Editor->DoLink(id, startPinId, endPinId, ImColor(color), thickness);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::Flow(LinkId linkId, FlowDirection direction)
|
||||||
|
{
|
||||||
|
if (auto link = s_Editor->FindLink(linkId))
|
||||||
|
s_Editor->Flow(link, direction);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::BeginCreate(const ImVec4& color, float thickness)
|
||||||
|
{
|
||||||
|
auto& context = s_Editor->GetItemCreator();
|
||||||
|
|
||||||
|
if (context.Begin())
|
||||||
|
{
|
||||||
|
context.SetStyle(ImColor(color), thickness);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::QueryNewLink(PinId* startId, PinId* endId)
|
||||||
|
{
|
||||||
|
using Result = ax::NodeEditor::Detail::CreateItemAction::Result;
|
||||||
|
|
||||||
|
auto& context = s_Editor->GetItemCreator();
|
||||||
|
|
||||||
|
return context.QueryLink(startId, endId) == Result::True;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::QueryNewLink(PinId* startId, PinId* endId, const ImVec4& color, float thickness)
|
||||||
|
{
|
||||||
|
using Result = ax::NodeEditor::Detail::CreateItemAction::Result;
|
||||||
|
|
||||||
|
auto& context = s_Editor->GetItemCreator();
|
||||||
|
|
||||||
|
auto result = context.QueryLink(startId, endId);
|
||||||
|
if (result != Result::Indeterminate)
|
||||||
|
context.SetStyle(ImColor(color), thickness);
|
||||||
|
|
||||||
|
return result == Result::True;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::QueryNewNode(PinId* pinId)
|
||||||
|
{
|
||||||
|
using Result = ax::NodeEditor::Detail::CreateItemAction::Result;
|
||||||
|
|
||||||
|
auto& context = s_Editor->GetItemCreator();
|
||||||
|
|
||||||
|
return context.QueryNode(pinId) == Result::True;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::QueryNewNode(PinId* pinId, const ImVec4& color, float thickness)
|
||||||
|
{
|
||||||
|
using Result = ax::NodeEditor::Detail::CreateItemAction::Result;
|
||||||
|
|
||||||
|
auto& context = s_Editor->GetItemCreator();
|
||||||
|
|
||||||
|
auto result = context.QueryNode(pinId);
|
||||||
|
if (result != Result::Indeterminate)
|
||||||
|
context.SetStyle(ImColor(color), thickness);
|
||||||
|
|
||||||
|
return result == Result::True;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::AcceptNewItem()
|
||||||
|
{
|
||||||
|
using Result = ax::NodeEditor::Detail::CreateItemAction::Result;
|
||||||
|
|
||||||
|
auto& context = s_Editor->GetItemCreator();
|
||||||
|
|
||||||
|
return context.AcceptItem() == Result::True;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::AcceptNewItem(const ImVec4& color, float thickness)
|
||||||
|
{
|
||||||
|
using Result = ax::NodeEditor::Detail::CreateItemAction::Result;
|
||||||
|
|
||||||
|
auto& context = s_Editor->GetItemCreator();
|
||||||
|
|
||||||
|
auto result = context.AcceptItem();
|
||||||
|
if (result != Result::Indeterminate)
|
||||||
|
context.SetStyle(ImColor(color), thickness);
|
||||||
|
|
||||||
|
return result == Result::True;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::RejectNewItem()
|
||||||
|
{
|
||||||
|
auto& context = s_Editor->GetItemCreator();
|
||||||
|
|
||||||
|
context.RejectItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::RejectNewItem(const ImVec4& color, float thickness)
|
||||||
|
{
|
||||||
|
using Result = ax::NodeEditor::Detail::CreateItemAction::Result;
|
||||||
|
|
||||||
|
auto& context = s_Editor->GetItemCreator();
|
||||||
|
|
||||||
|
if (context.RejectItem() != Result::Indeterminate)
|
||||||
|
context.SetStyle(ImColor(color), thickness);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::EndCreate()
|
||||||
|
{
|
||||||
|
auto& context = s_Editor->GetItemCreator();
|
||||||
|
|
||||||
|
context.End();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::BeginDelete()
|
||||||
|
{
|
||||||
|
auto& context = s_Editor->GetItemDeleter();
|
||||||
|
|
||||||
|
return context.Begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::QueryDeletedLink(LinkId* linkId, PinId* startId, PinId* endId)
|
||||||
|
{
|
||||||
|
auto& context = s_Editor->GetItemDeleter();
|
||||||
|
|
||||||
|
return context.QueryLink(linkId, startId, endId);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::QueryDeletedNode(NodeId* nodeId)
|
||||||
|
{
|
||||||
|
auto& context = s_Editor->GetItemDeleter();
|
||||||
|
|
||||||
|
return context.QueryNode(nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::AcceptDeletedItem(bool deleteDependencies)
|
||||||
|
{
|
||||||
|
auto& context = s_Editor->GetItemDeleter();
|
||||||
|
|
||||||
|
return context.AcceptItem(deleteDependencies);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::RejectDeletedItem()
|
||||||
|
{
|
||||||
|
auto& context = s_Editor->GetItemDeleter();
|
||||||
|
|
||||||
|
context.RejectItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::EndDelete()
|
||||||
|
{
|
||||||
|
auto& context = s_Editor->GetItemDeleter();
|
||||||
|
|
||||||
|
context.End();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::SetNodePosition(NodeId nodeId, const ImVec2& position)
|
||||||
|
{
|
||||||
|
s_Editor->SetNodePosition(nodeId, position);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::SetGroupSize(NodeId nodeId, const ImVec2& size)
|
||||||
|
{
|
||||||
|
s_Editor->SetGroupSize(nodeId, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec2 ax::NodeEditor::GetNodePosition(NodeId nodeId)
|
||||||
|
{
|
||||||
|
return s_Editor->GetNodePosition(nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec2 ax::NodeEditor::GetNodeSize(NodeId nodeId)
|
||||||
|
{
|
||||||
|
return s_Editor->GetNodeSize(nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::CenterNodeOnScreen(NodeId nodeId)
|
||||||
|
{
|
||||||
|
if (auto node = s_Editor->FindNode(nodeId))
|
||||||
|
node->CenterOnScreenInNextFrame();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::SetNodeZPosition(NodeId nodeId, float z)
|
||||||
|
{
|
||||||
|
s_Editor->SetNodeZPosition(nodeId, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
float ax::NodeEditor::GetNodeZPosition(NodeId nodeId)
|
||||||
|
{
|
||||||
|
return s_Editor->GetNodeZPosition(nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::RestoreNodeState(NodeId nodeId)
|
||||||
|
{
|
||||||
|
if (auto node = s_Editor->FindNode(nodeId))
|
||||||
|
s_Editor->MarkNodeToRestoreState(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::Suspend()
|
||||||
|
{
|
||||||
|
s_Editor->Suspend();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::Resume()
|
||||||
|
{
|
||||||
|
s_Editor->Resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::IsSuspended()
|
||||||
|
{
|
||||||
|
return s_Editor->IsSuspended();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::IsActive()
|
||||||
|
{
|
||||||
|
return s_Editor->IsFocused();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::HasSelectionChanged()
|
||||||
|
{
|
||||||
|
return s_Editor->HasSelectionChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
int ax::NodeEditor::GetSelectedObjectCount()
|
||||||
|
{
|
||||||
|
return (int)s_Editor->GetSelectedObjects().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
int ax::NodeEditor::GetSelectedNodes(NodeId* nodes, int size)
|
||||||
|
{
|
||||||
|
return BuildIdList(s_Editor->GetSelectedObjects(), nodes, size, [](auto object)
|
||||||
|
{
|
||||||
|
return object->AsNode() != nullptr;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
int ax::NodeEditor::GetSelectedLinks(LinkId* links, int size)
|
||||||
|
{
|
||||||
|
return BuildIdList(s_Editor->GetSelectedObjects(), links, size, [](auto object)
|
||||||
|
{
|
||||||
|
return object->AsLink() != nullptr;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::IsNodeSelected(NodeId nodeId)
|
||||||
|
{
|
||||||
|
if (auto node = s_Editor->FindNode(nodeId))
|
||||||
|
return s_Editor->IsSelected(node);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::IsLinkSelected(LinkId linkId)
|
||||||
|
{
|
||||||
|
if (auto link = s_Editor->FindLink(linkId))
|
||||||
|
return s_Editor->IsSelected(link);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::ClearSelection()
|
||||||
|
{
|
||||||
|
s_Editor->ClearSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::SelectNode(NodeId nodeId, bool append)
|
||||||
|
{
|
||||||
|
if (auto node = s_Editor->FindNode(nodeId))
|
||||||
|
{
|
||||||
|
if (append)
|
||||||
|
s_Editor->SelectObject(node);
|
||||||
|
else
|
||||||
|
s_Editor->SetSelectedObject(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::SelectLink(LinkId linkId, bool append)
|
||||||
|
{
|
||||||
|
if (auto link = s_Editor->FindLink(linkId))
|
||||||
|
{
|
||||||
|
if (append)
|
||||||
|
s_Editor->SelectObject(link);
|
||||||
|
else
|
||||||
|
s_Editor->SetSelectedObject(link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::DeselectNode(NodeId nodeId)
|
||||||
|
{
|
||||||
|
if (auto node = s_Editor->FindNode(nodeId))
|
||||||
|
s_Editor->DeselectObject(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::DeselectLink(LinkId linkId)
|
||||||
|
{
|
||||||
|
if (auto link = s_Editor->FindLink(linkId))
|
||||||
|
s_Editor->DeselectObject(link);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::DeleteNode(NodeId nodeId)
|
||||||
|
{
|
||||||
|
if (auto node = s_Editor->FindNode(nodeId))
|
||||||
|
return s_Editor->GetItemDeleter().Add(node);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::DeleteLink(LinkId linkId)
|
||||||
|
{
|
||||||
|
if (auto link = s_Editor->FindLink(linkId))
|
||||||
|
return s_Editor->GetItemDeleter().Add(link);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::HasAnyLinks(NodeId nodeId)
|
||||||
|
{
|
||||||
|
return s_Editor->HasAnyLinks(nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::HasAnyLinks(PinId pinId)
|
||||||
|
{
|
||||||
|
return s_Editor->HasAnyLinks(pinId);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ax::NodeEditor::BreakLinks(NodeId nodeId)
|
||||||
|
{
|
||||||
|
return s_Editor->BreakLinks(nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ax::NodeEditor::BreakLinks(PinId pinId)
|
||||||
|
{
|
||||||
|
return s_Editor->BreakLinks(pinId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::NavigateToContent(float duration)
|
||||||
|
{
|
||||||
|
s_Editor->NavigateTo(s_Editor->GetContentBounds(), true, duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::NavigateToSelection(bool zoomIn, float duration)
|
||||||
|
{
|
||||||
|
s_Editor->NavigateTo(s_Editor->GetSelectionBounds(), zoomIn, duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::ShowNodeContextMenu(NodeId* nodeId)
|
||||||
|
{
|
||||||
|
return s_Editor->GetContextMenu().ShowNodeContextMenu(nodeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::ShowPinContextMenu(PinId* pinId)
|
||||||
|
{
|
||||||
|
return s_Editor->GetContextMenu().ShowPinContextMenu(pinId);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::ShowLinkContextMenu(LinkId* linkId)
|
||||||
|
{
|
||||||
|
return s_Editor->GetContextMenu().ShowLinkContextMenu(linkId);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::ShowBackgroundContextMenu()
|
||||||
|
{
|
||||||
|
return s_Editor->GetContextMenu().ShowBackgroundContextMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::EnableShortcuts(bool enable)
|
||||||
|
{
|
||||||
|
s_Editor->EnableShortcuts(enable);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::AreShortcutsEnabled()
|
||||||
|
{
|
||||||
|
return s_Editor->AreShortcutsEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::BeginShortcut()
|
||||||
|
{
|
||||||
|
return s_Editor->GetShortcut().Begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::AcceptCut()
|
||||||
|
{
|
||||||
|
return s_Editor->GetShortcut().AcceptCut();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::AcceptCopy()
|
||||||
|
{
|
||||||
|
return s_Editor->GetShortcut().AcceptCopy();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::AcceptPaste()
|
||||||
|
{
|
||||||
|
return s_Editor->GetShortcut().AcceptPaste();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::AcceptDuplicate()
|
||||||
|
{
|
||||||
|
return s_Editor->GetShortcut().AcceptDuplicate();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::AcceptCreateNode()
|
||||||
|
{
|
||||||
|
return s_Editor->GetShortcut().AcceptCreateNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
int ax::NodeEditor::GetActionContextSize()
|
||||||
|
{
|
||||||
|
return static_cast<int>(s_Editor->GetShortcut().m_Context.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
int ax::NodeEditor::GetActionContextNodes(NodeId* nodes, int size)
|
||||||
|
{
|
||||||
|
return BuildIdList(s_Editor->GetSelectedObjects(), nodes, size, [](auto object)
|
||||||
|
{
|
||||||
|
return object->AsNode() != nullptr;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
int ax::NodeEditor::GetActionContextLinks(LinkId* links, int size)
|
||||||
|
{
|
||||||
|
return BuildIdList(s_Editor->GetSelectedObjects(), links, size, [](auto object)
|
||||||
|
{
|
||||||
|
return object->AsLink() != nullptr;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ax::NodeEditor::EndShortcut()
|
||||||
|
{
|
||||||
|
return s_Editor->GetShortcut().End();
|
||||||
|
}
|
||||||
|
|
||||||
|
float ax::NodeEditor::GetCurrentZoom()
|
||||||
|
{
|
||||||
|
return s_Editor->GetView().InvScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
ax::NodeEditor::NodeId ax::NodeEditor::GetHoveredNode()
|
||||||
|
{
|
||||||
|
return s_Editor->GetHoveredNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
ax::NodeEditor::PinId ax::NodeEditor::GetHoveredPin()
|
||||||
|
{
|
||||||
|
return s_Editor->GetHoveredPin();
|
||||||
|
}
|
||||||
|
|
||||||
|
ax::NodeEditor::LinkId ax::NodeEditor::GetHoveredLink()
|
||||||
|
{
|
||||||
|
return s_Editor->GetHoveredLink();
|
||||||
|
}
|
||||||
|
|
||||||
|
ax::NodeEditor::NodeId ax::NodeEditor::GetDoubleClickedNode()
|
||||||
|
{
|
||||||
|
return s_Editor->GetDoubleClickedNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
ax::NodeEditor::PinId ax::NodeEditor::GetDoubleClickedPin()
|
||||||
|
{
|
||||||
|
return s_Editor->GetDoubleClickedPin();
|
||||||
|
}
|
||||||
|
|
||||||
|
ax::NodeEditor::LinkId ax::NodeEditor::GetDoubleClickedLink()
|
||||||
|
{
|
||||||
|
return s_Editor->GetDoubleClickedLink();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::IsBackgroundClicked()
|
||||||
|
{
|
||||||
|
return s_Editor->IsBackgroundClicked();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::IsBackgroundDoubleClicked()
|
||||||
|
{
|
||||||
|
return s_Editor->IsBackgroundDoubleClicked();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGuiMouseButton ax::NodeEditor::GetBackgroundClickButtonIndex()
|
||||||
|
{
|
||||||
|
return s_Editor->GetBackgroundClickButtonIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGuiMouseButton ax::NodeEditor::GetBackgroundDoubleClickButtonIndex()
|
||||||
|
{
|
||||||
|
return s_Editor->GetBackgroundDoubleClickButtonIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::GetLinkPins(LinkId linkId, PinId* startPinId, PinId* endPinId)
|
||||||
|
{
|
||||||
|
auto link = s_Editor->FindLink(linkId);
|
||||||
|
if (!link)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (startPinId)
|
||||||
|
*startPinId = link->m_StartPin->m_ID;
|
||||||
|
if (endPinId)
|
||||||
|
*endPinId = link->m_EndPin->m_ID;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ax::NodeEditor::PinHadAnyLinks(PinId pinId)
|
||||||
|
{
|
||||||
|
return s_Editor->PinHadAnyLinks(pinId);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec2 ax::NodeEditor::GetScreenSize()
|
||||||
|
{
|
||||||
|
return s_Editor->GetRect().GetSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec2 ax::NodeEditor::ScreenToCanvas(const ImVec2& pos)
|
||||||
|
{
|
||||||
|
return s_Editor->ToCanvas(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImVec2 ax::NodeEditor::CanvasToScreen(const ImVec2& pos)
|
||||||
|
{
|
||||||
|
return s_Editor->ToScreen(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ax::NodeEditor::GetNodeCount()
|
||||||
|
{
|
||||||
|
return s_Editor->CountLiveNodes();
|
||||||
|
}
|
||||||
|
|
||||||
|
int ax::NodeEditor::GetOrderedNodeIds(NodeId* nodes, int size)
|
||||||
|
{
|
||||||
|
return s_Editor->GetNodeIds(nodes, size);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user